From 5899a26aadca3f47385160340d67c28e12ec8b5f Mon Sep 17 00:00:00 2001 From: auria Date: Sat, 30 Nov 2013 21:33:06 +0000 Subject: [PATCH] Merge cand's graphical improvements branch!! There are many known issues folks, so don't cry victory just now, this is just the beginning. Especially, shadows do not work correctly at this time git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/trunk@14590 178a84e3-b1eb-0310-8ba1-8eac791a3b58 --- data/gfx/snow.xml | 7 +- data/gui/custom_video_settings.stkgui | 51 +- data/gui/options_video.stkgui | 10 - data/shaders/MLAA_COPYING | 39 + data/shaders/bloom.frag | 13 + data/shaders/bloomblend.frag | 10 + data/shaders/bloompower.frag | 11 + data/shaders/bubble.frag | 7 +- data/shaders/caustics.frag | 17 + data/shaders/collapse.frag | 25 + data/shaders/colorize.frag | 6 + data/shaders/colorize_ref.frag | 11 + data/shaders/displace.frag | 40 + data/shaders/displace.vert | 9 + data/shaders/farplane.vert | 6 + data/shaders/flip.frag | 9 + data/shaders/fog.frag | 34 + data/shaders/gaussian3h.frag | 19 + data/shaders/gaussian3v.frag | 19 + data/shaders/gaussian6h.frag | 21 + data/shaders/gaussian6v.frag | 21 + data/shaders/glow.frag | 16 + data/shaders/godfade.frag | 16 + data/shaders/godray.frag | 29 + data/shaders/grass.frag | 50 +- data/shaders/grass.vert | 36 +- data/shaders/lightblend.frag | 19 + data/shaders/mipviz.frag | 52 ++ data/shaders/mlaa_blend2.frag | 112 +++ data/shaders/mlaa_color1.frag | 25 + data/shaders/mlaa_neigh3.frag | 38 + data/shaders/mlaa_offset.vert | 12 + data/shaders/motion_blur.frag | 10 +- data/shaders/multiply.frag | 10 + data/shaders/normalmap.frag | 24 +- data/shaders/objectpass.frag | 40 + data/shaders/objectpass.vert | 14 + data/shaders/objectpass_ref.frag | 37 + data/shaders/objectpass_rimlit.frag | 45 + data/shaders/objectpass_rimlit.vert | 19 + data/shaders/objectpass_spheremap.frag | 42 + data/shaders/pass.frag | 6 + data/shaders/pass.vert | 6 + data/shaders/penumbrah.frag | 52 ++ data/shaders/penumbrav.frag | 52 ++ data/shaders/pointlight.frag | 50 ++ data/shaders/ppdisplace.frag | 26 + data/shaders/rain.frag | 6 + data/shaders/rain.vert | 33 + data/shaders/shadowgen.frag | 44 + data/shaders/shadowimportance.frag | 70 ++ data/shaders/shadowimportance.vert | 30 + data/shaders/shadowpass.frag | 34 + data/shaders/shadowpass.vert | 25 + data/shaders/shadowwarph.frag | 54 ++ data/shaders/shadowwarpv.frag | 54 ++ data/shaders/snow.frag | 18 + data/shaders/snow.vert | 7 + data/shaders/splatting.frag | 74 +- data/shaders/ssao.frag | 78 ++ data/shaders/sunlight.frag | 58 ++ data/shaders/sunlightshadow.frag | 106 +++ data/shaders/water.frag | 49 +- data/shaders/water.vert | 25 +- data/shaders/water_fog.frag | 75 -- data/shaders/water_fog.vert | 49 -- data/shaders/white.frag | 4 + data/stk_config.xml | 3 + doc/graphics_views.txt | 21 + doc/graphics_xml.txt | 157 ++++ doc/pipeline_overview.txt | 44 + sources.cmake | 29 + src/Makefile.am | 29 +- src/addons/network_http.cpp | 10 +- src/animations/animation_base.cpp | 8 +- src/animations/ipo.cpp | 8 +- src/config/stk_config.cpp | 26 + src/config/stk_config.hpp | 5 + src/config/user_config.hpp | 23 +- src/graphics/callbacks.cpp | 775 ++++++++++++++++ src/graphics/callbacks.hpp | 712 +++++++++++++++ src/graphics/camera.cpp | 7 +- src/graphics/glow.cpp | 85 ++ src/graphics/glow.hpp | 53 ++ src/graphics/glwrap.hpp | 21 + src/graphics/irr_driver.cpp | 345 +++++--- src/graphics/irr_driver.hpp | 141 ++- src/graphics/large_mesh_buffer.hpp | 58 ++ src/graphics/lens_flare.cpp | 264 ++++++ src/graphics/lens_flare.hpp | 117 +++ src/graphics/light.cpp | 93 ++ src/graphics/light.hpp | 64 ++ src/graphics/lod_node.cpp | 18 +- src/graphics/lod_node.hpp | 2 + src/graphics/material.cpp | 604 ++----------- src/graphics/material.hpp | 28 +- src/graphics/material_manager.cpp | 4 - src/graphics/mlaa_areamap.hpp | 200 +++++ src/graphics/particle_emitter.cpp | 60 +- src/graphics/particle_kind.cpp | 10 + src/graphics/particle_kind.hpp | 10 +- src/graphics/per_camera_node.cpp | 7 +- src/graphics/per_camera_node.hpp | 2 +- src/graphics/post_processing.cpp | 743 +++++++++++++--- src/graphics/post_processing.hpp | 37 +- src/graphics/rain.cpp | 198 +++-- src/graphics/rain.hpp | 13 +- src/graphics/referee.cpp | 4 + src/graphics/referee.hpp | 5 +- src/graphics/render.cpp | 826 ++++++++++++++++++ src/graphics/rtts.cpp | 141 +++ src/graphics/rtts.hpp | 83 ++ src/graphics/screenquad.cpp | 30 + src/graphics/screenquad.hpp | 91 ++ src/graphics/shaders.cpp | 260 ++++++ src/graphics/shaders.hpp | 117 +++ src/graphics/shadow_importance.cpp | 169 ++++ src/graphics/shadow_importance.hpp | 41 + src/graphics/sun.cpp | 161 ++++ src/graphics/sun.hpp | 40 + src/graphics/water.cpp | 94 ++ src/graphics/water.hpp | 72 ++ src/graphics/wind.cpp | 40 + src/graphics/wind.hpp | 38 + src/guiengine/layout_manager.cpp | 5 +- src/guiengine/widget.cpp | 6 +- .../widgets/dynamic_ribbon_widget.cpp | 5 +- src/guiengine/widgets/ribbon_widget.cpp | 5 +- src/input/input_manager.cpp | 48 +- src/items/attachment.cpp | 2 + src/items/flyable.cpp | 5 +- src/items/item.cpp | 3 + src/items/rubber_band.cpp | 13 +- src/karts/controller/skidding_ai.cpp | 8 +- src/karts/kart.cpp | 18 +- src/karts/kart_model.cpp | 10 + src/karts/moveable.cpp | 1 + src/modes/world.cpp | 3 + src/physics/kart_motion_state.hpp | 7 +- .../dialogs/custom_video_settings.cpp | 25 +- src/states_screens/feature_unlocked.cpp | 13 +- src/states_screens/feature_unlocked.hpp | 2 +- src/states_screens/grand_prix_lose.cpp | 13 +- src/states_screens/grand_prix_lose.hpp | 2 +- src/states_screens/grand_prix_win.cpp | 16 +- src/states_screens/grand_prix_win.hpp | 2 +- src/states_screens/options_screen_video.cpp | 75 +- src/states_screens/race_gui_base.cpp | 15 +- src/tracks/quad_graph.cpp | 28 +- src/tracks/track.cpp | 68 +- src/tracks/track.hpp | 37 +- src/tracks/track_object.cpp | 38 +- src/tracks/track_object_presentation.cpp | 27 +- src/tracks/track_object_presentation.hpp | 5 +- src/utils/helpers.cpp | 220 +++++ src/utils/helpers.hpp | 40 + src/utils/vs.hpp | 12 + 157 files changed, 8699 insertions(+), 1440 deletions(-) create mode 100644 data/shaders/MLAA_COPYING create mode 100644 data/shaders/bloom.frag create mode 100644 data/shaders/bloomblend.frag create mode 100644 data/shaders/bloompower.frag create mode 100644 data/shaders/caustics.frag create mode 100644 data/shaders/collapse.frag create mode 100644 data/shaders/colorize.frag create mode 100644 data/shaders/colorize_ref.frag create mode 100644 data/shaders/displace.frag create mode 100644 data/shaders/displace.vert create mode 100644 data/shaders/farplane.vert create mode 100644 data/shaders/flip.frag create mode 100644 data/shaders/fog.frag create mode 100644 data/shaders/gaussian3h.frag create mode 100644 data/shaders/gaussian3v.frag create mode 100644 data/shaders/gaussian6h.frag create mode 100644 data/shaders/gaussian6v.frag create mode 100644 data/shaders/glow.frag create mode 100644 data/shaders/godfade.frag create mode 100644 data/shaders/godray.frag create mode 100644 data/shaders/lightblend.frag create mode 100644 data/shaders/mipviz.frag create mode 100644 data/shaders/mlaa_blend2.frag create mode 100644 data/shaders/mlaa_color1.frag create mode 100644 data/shaders/mlaa_neigh3.frag create mode 100644 data/shaders/mlaa_offset.vert create mode 100644 data/shaders/multiply.frag create mode 100644 data/shaders/objectpass.frag create mode 100644 data/shaders/objectpass.vert create mode 100644 data/shaders/objectpass_ref.frag create mode 100644 data/shaders/objectpass_rimlit.frag create mode 100644 data/shaders/objectpass_rimlit.vert create mode 100644 data/shaders/objectpass_spheremap.frag create mode 100644 data/shaders/pass.frag create mode 100644 data/shaders/pass.vert create mode 100644 data/shaders/penumbrah.frag create mode 100644 data/shaders/penumbrav.frag create mode 100644 data/shaders/pointlight.frag create mode 100644 data/shaders/ppdisplace.frag create mode 100644 data/shaders/rain.frag create mode 100644 data/shaders/rain.vert create mode 100644 data/shaders/shadowgen.frag create mode 100644 data/shaders/shadowimportance.frag create mode 100644 data/shaders/shadowimportance.vert create mode 100644 data/shaders/shadowpass.frag create mode 100644 data/shaders/shadowpass.vert create mode 100644 data/shaders/shadowwarph.frag create mode 100644 data/shaders/shadowwarpv.frag create mode 100644 data/shaders/snow.frag create mode 100644 data/shaders/snow.vert create mode 100644 data/shaders/ssao.frag create mode 100644 data/shaders/sunlight.frag create mode 100644 data/shaders/sunlightshadow.frag delete mode 100644 data/shaders/water_fog.frag delete mode 100644 data/shaders/water_fog.vert create mode 100644 data/shaders/white.frag create mode 100644 doc/graphics_views.txt create mode 100644 doc/graphics_xml.txt create mode 100644 doc/pipeline_overview.txt create mode 100644 src/graphics/callbacks.cpp create mode 100644 src/graphics/callbacks.hpp create mode 100644 src/graphics/glow.cpp create mode 100644 src/graphics/glow.hpp create mode 100644 src/graphics/glwrap.hpp create mode 100644 src/graphics/large_mesh_buffer.hpp create mode 100644 src/graphics/lens_flare.cpp create mode 100644 src/graphics/lens_flare.hpp create mode 100644 src/graphics/light.cpp create mode 100644 src/graphics/light.hpp create mode 100644 src/graphics/mlaa_areamap.hpp create mode 100644 src/graphics/render.cpp create mode 100644 src/graphics/rtts.cpp create mode 100644 src/graphics/rtts.hpp create mode 100644 src/graphics/screenquad.cpp create mode 100644 src/graphics/screenquad.hpp create mode 100644 src/graphics/shaders.cpp create mode 100644 src/graphics/shaders.hpp create mode 100644 src/graphics/shadow_importance.cpp create mode 100644 src/graphics/shadow_importance.hpp create mode 100644 src/graphics/sun.cpp create mode 100644 src/graphics/sun.hpp create mode 100644 src/graphics/water.cpp create mode 100644 src/graphics/water.hpp create mode 100644 src/graphics/wind.cpp create mode 100644 src/graphics/wind.hpp create mode 100644 src/utils/helpers.cpp create mode 100644 src/utils/helpers.hpp create mode 100644 src/utils/vs.hpp diff --git a/data/gfx/snow.xml b/data/gfx/snow.xml index bd3eb7395..e34c24ffb 100644 --- a/data/gfx/snow.xml +++ b/data/gfx/snow.xml @@ -9,7 +9,7 @@ y="-0.01" z="0.0" /> - + - + + diff --git a/data/gui/custom_video_settings.stkgui b/data/gui/custom_video_settings.stkgui index 466036696..d1d8c5eaf 100644 --- a/data/gui/custom_video_settings.stkgui +++ b/data/gui/custom_video_settings.stkgui @@ -1,18 +1,33 @@ -
+
- + + +
+ + +
+ +
- +
@@ -20,7 +35,7 @@
- +
- +
- +
- +
- + -
- - - + + +
- +
+ + + +
+
diff --git a/data/gui/options_video.stkgui b/data/gui/options_video.stkgui index c90eb4851..9f679add6 100644 --- a/data/gui/options_video.stkgui +++ b/data/gui/options_video.stkgui @@ -40,16 +40,6 @@
- - - -
- - - -
- diff --git a/data/shaders/MLAA_COPYING b/data/shaders/MLAA_COPYING new file mode 100644 index 000000000..cb39bd220 --- /dev/null +++ b/data/shaders/MLAA_COPYING @@ -0,0 +1,39 @@ +/** + * Copyright (C) 2010 Jorge Jimenez (jorge@iryoku.com) + * Copyright (C) 2010 Belen Masia (bmasia@unizar.es) + * Copyright (C) 2010 Jose I. Echevarria (joseignacioechevarria@gmail.com) + * Copyright (C) 2010 Fernando Navarro (fernandn@microsoft.com) + * Copyright (C) 2010 Diego Gutierrez (diegog@unizar.es) + * Copyright (C) 2011 Lauri Kasanen (cand@gmx.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the following statement: + * + * "Uses Jimenez's MLAA. Copyright (C) 2010 by Jorge Jimenez, Belen Masia, + * Jose I. Echevarria, Fernando Navarro and Diego Gutierrez." + * + * Only for use in the Mesa project, this point 2 is filled by naming the + * technique Jimenez's MLAA in the Mesa config options. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS + * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of the copyright holders. + */ diff --git a/data/shaders/bloom.frag b/data/shaders/bloom.frag new file mode 100644 index 000000000..f00f76c1e --- /dev/null +++ b/data/shaders/bloom.frag @@ -0,0 +1,13 @@ +uniform sampler2D tex; +uniform float low; + +void main() +{ + vec3 weights = vec3(0.2126, 0.7152, 0.0722); // ITU-R BT. 709 + vec3 col = texture2D(tex, gl_TexCoord[0].xy).xyz; + float luma = dot(weights, col); + + col *= smoothstep(low, 0.9, luma); + + gl_FragColor = vec4(col, 1.0); +} diff --git a/data/shaders/bloomblend.frag b/data/shaders/bloomblend.frag new file mode 100644 index 000000000..6cb0ec93d --- /dev/null +++ b/data/shaders/bloomblend.frag @@ -0,0 +1,10 @@ +uniform sampler2D tex; + +void main() +{ + vec4 col = texture2D(tex, gl_TexCoord[0].xy); + + col.xyz *= 10.0 * col.a; + + gl_FragColor = vec4(col.xyz, 1.0); +} diff --git a/data/shaders/bloompower.frag b/data/shaders/bloompower.frag new file mode 100644 index 000000000..441090cb6 --- /dev/null +++ b/data/shaders/bloompower.frag @@ -0,0 +1,11 @@ +uniform float power; +uniform sampler2D tex; + +void main() +{ + vec4 col = texture2D(tex, gl_TexCoord[0].xy); + if (col.a < 0.5) + discard; + + gl_FragColor = vec4(col.xyz, power); +} diff --git a/data/shaders/bubble.frag b/data/shaders/bubble.frag index 9fc686210..6e900c98e 100644 --- a/data/shaders/bubble.frag +++ b/data/shaders/bubble.frag @@ -15,13 +15,12 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - -uniform sampler2D main_texture; +uniform sampler2D tex; uniform float transparency; varying vec2 uv; void main() { - gl_FragColor = texture2D(main_texture, uv); - gl_FragColor.a *= transparency; + gl_FragColor = texture2D(tex, uv); + gl_FragColor.a *= transparency; } diff --git a/data/shaders/caustics.frag b/data/shaders/caustics.frag new file mode 100644 index 000000000..8ce7a6303 --- /dev/null +++ b/data/shaders/caustics.frag @@ -0,0 +1,17 @@ +uniform sampler2D tex; +uniform sampler2D caustictex; +uniform vec2 dir; +uniform vec2 dir2; + +void main() +{ + vec2 tc = gl_TexCoord[0].xy; + + vec3 col = texture2D(tex, tc).xyz; + float caustic = texture2D(caustictex, tc + dir).x; + float caustic2 = texture2D(caustictex, (tc.yx + dir2 * vec2(-0.6, 0.3)) * vec2(0.6)).x; + + col += caustic * caustic2 * 10.0; + + gl_FragColor = vec4(col, 1.0); +} diff --git a/data/shaders/collapse.frag b/data/shaders/collapse.frag new file mode 100644 index 000000000..7e2e8b64c --- /dev/null +++ b/data/shaders/collapse.frag @@ -0,0 +1,25 @@ +uniform sampler2D tex; +uniform sampler2D oldtex; +uniform vec2 pixel; +uniform vec2 multi; +uniform int size; + +void main() +{ + float res = 0.0; + vec2 tc = gl_TexCoord[0].xy; +// tc.y = 1.0 - tc.y; + tc *= multi; + + for (int i = 0; i < size; i++) + { + float col = texture2D(tex, tc).x; + res = max(col, res); + + tc += pixel; + } + + float old = texture2D(oldtex, gl_TexCoord[0].xy).x; + + gl_FragColor = vec4(mix(old, res, 0.7)); +} diff --git a/data/shaders/colorize.frag b/data/shaders/colorize.frag new file mode 100644 index 000000000..2e44f6a07 --- /dev/null +++ b/data/shaders/colorize.frag @@ -0,0 +1,6 @@ +uniform vec3 col; + +void main() +{ + gl_FragColor = vec4(col, 1.0); +} diff --git a/data/shaders/colorize_ref.frag b/data/shaders/colorize_ref.frag new file mode 100644 index 000000000..c7dac6c99 --- /dev/null +++ b/data/shaders/colorize_ref.frag @@ -0,0 +1,11 @@ +uniform vec3 col; +uniform sampler2D tex; + +void main() +{ + float alpha = texture2D(tex, gl_TexCoord[0].xy).a; + if (alpha < 0.5) + discard; + + gl_FragColor = vec4(col, 1.0); +} diff --git a/data/shaders/displace.frag b/data/shaders/displace.frag new file mode 100644 index 000000000..4ab1a4feb --- /dev/null +++ b/data/shaders/displace.frag @@ -0,0 +1,40 @@ +uniform sampler2D tex; +uniform vec2 screen; +uniform vec2 dir; +uniform vec2 dir2; + +varying float camdist; + +void main() +{ + vec2 tc = gl_TexCoord[0].xy; + + vec4 col = vec4(0.0); + const float maxlen = 0.02; + + float horiz = texture2D(tex, tc + dir).x; + float vert = texture2D(tex, (tc.yx + dir2) * vec2(0.9)).x; + + vec2 offset = vec2(horiz, vert); + offset *= 2.0; + offset -= 1.0; + + // Fade according to distance to cam + float fade = 1.0 - smoothstep(1.0, 40.0, camdist); + + // Fade according to distance from the edges + vec2 edger = gl_TexCoord[1].xy; + const float mindist = 0.1; + fade *= smoothstep(0.0, mindist, edger.x) * smoothstep(0.0, mindist, edger.y) * + (1.0 - smoothstep(1.0 - mindist, 1.0, edger.x)) * + (1.0 - smoothstep(1.0 - mindist, 1.0, edger.y)); + + offset *= 50.0 * fade * maxlen; + + col.r = step(offset.x, 0.0) * -offset.x; + col.g = step(0.0, offset.x) * offset.x; + col.b = step(offset.y, 0.0) * -offset.y; + col.a = step(0.0, offset.y) * offset.y; + + gl_FragColor = col; +} diff --git a/data/shaders/displace.vert b/data/shaders/displace.vert new file mode 100644 index 000000000..f2fe1c59f --- /dev/null +++ b/data/shaders/displace.vert @@ -0,0 +1,9 @@ +varying float camdist; + +void main() { + gl_Position = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + + camdist = length((gl_ModelViewMatrix * gl_Vertex).xyz); +} diff --git a/data/shaders/farplane.vert b/data/shaders/farplane.vert new file mode 100644 index 000000000..a15bb824f --- /dev/null +++ b/data/shaders/farplane.vert @@ -0,0 +1,6 @@ +void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_Position = (gl_ModelViewProjectionMatrix * gl_Vertex).xyww; + gl_FrontColor = gl_Color; +} diff --git a/data/shaders/flip.frag b/data/shaders/flip.frag new file mode 100644 index 000000000..dda3a7076 --- /dev/null +++ b/data/shaders/flip.frag @@ -0,0 +1,9 @@ +uniform sampler2D tex; + +void main() +{ + vec2 texc = gl_TexCoord[0].xy; + texc.y = 1.0 - texc.y; + + gl_FragColor = texture2D(tex, texc); +} diff --git a/data/shaders/fog.frag b/data/shaders/fog.frag new file mode 100644 index 000000000..f2ed93327 --- /dev/null +++ b/data/shaders/fog.frag @@ -0,0 +1,34 @@ +uniform sampler2D tex; + +uniform float fogmax; +uniform float startH; +uniform float endH; +uniform float start; +uniform float end; +uniform vec3 col; +uniform vec3 campos; +uniform mat4 ipvmat; + +float decdepth(vec4 rgba) { + return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); +} + +void main() +{ + float z = decdepth(vec4(texture2D(tex, gl_TexCoord[0].xy).xyz, 0.0)); + + vec3 tmp = vec3(gl_TexCoord[0].xy, z); + tmp = tmp * 2.0 - 1.0; + + vec4 xpos = vec4(tmp, 1.0); + xpos = ipvmat * xpos; + xpos.xyz /= xpos.w; + + float dist = distance(campos, xpos.xyz); + float fog = smoothstep(start, end, dist); + fog *= 1.0 - smoothstep(startH, endH, xpos.y); + + fog = min(fog, fogmax); + + gl_FragColor = vec4(col, fog); +} diff --git a/data/shaders/gaussian3h.frag b/data/shaders/gaussian3h.frag new file mode 100644 index 000000000..f1ec80b89 --- /dev/null +++ b/data/shaders/gaussian3h.frag @@ -0,0 +1,19 @@ +uniform sampler2D tex; +uniform vec2 pixel; + +// Gaussian separated blur with radius 3. + +void main() +{ + vec4 sum = vec4(0.0); + float X = gl_TexCoord[0].x; + float Y = gl_TexCoord[0].y; + + sum += texture2D(tex, vec2(X - 3.0 * pixel.x, Y)) * 0.03125; + sum += texture2D(tex, vec2(X - 1.3333 * pixel.x, Y)) * 0.328125; + sum += texture2D(tex, vec2(X, Y)) * 0.273438; + sum += texture2D(tex, vec2(X + 1.3333 * pixel.x, Y)) * 0.328125; + sum += texture2D(tex, vec2(X + 3.0 * pixel.x, Y)) * 0.03125; + + gl_FragColor = sum; +} diff --git a/data/shaders/gaussian3v.frag b/data/shaders/gaussian3v.frag new file mode 100644 index 000000000..fec9ce8b1 --- /dev/null +++ b/data/shaders/gaussian3v.frag @@ -0,0 +1,19 @@ +uniform sampler2D tex; +uniform vec2 pixel; + +// Gaussian separated blur with radius 3. + +void main() +{ + vec4 sum = vec4(0.0); + float X = gl_TexCoord[0].x; + float Y = gl_TexCoord[0].y; + + sum += texture2D(tex, vec2(X, Y - 3.0 * pixel.y)) * 0.03125; + sum += texture2D(tex, vec2(X, Y - 1.3333 * pixel.y)) * 0.328125; + sum += texture2D(tex, vec2(X, Y)) * 0.273438; + sum += texture2D(tex, vec2(X, Y + 1.3333 * pixel.y)) * 0.328125; + sum += texture2D(tex, vec2(X, Y + 3.0 * pixel.y)) * 0.03125; + + gl_FragColor = sum; +} diff --git a/data/shaders/gaussian6h.frag b/data/shaders/gaussian6h.frag new file mode 100644 index 000000000..3aad9b7c0 --- /dev/null +++ b/data/shaders/gaussian6h.frag @@ -0,0 +1,21 @@ +uniform sampler2D tex; +uniform vec2 pixel; + +// Gaussian separated blur with radius 6. + +void main() +{ + vec4 sum = vec4(0.0); + float X = gl_TexCoord[0].x; + float Y = gl_TexCoord[0].y; + + sum += texture2D(tex, vec2(X - 5.13333 * pixel.x, Y)) * 0.00640869; + sum += texture2D(tex, vec2(X - 3.26667 * pixel.x, Y)) * 0.083313; + sum += texture2D(tex, vec2(X - 1.4 * pixel.x, Y)) * 0.305481; + sum += texture2D(tex, vec2(X, Y)) * 0.209473; + sum += texture2D(tex, vec2(X + 1.4 * pixel.x, Y)) * 0.305481; + sum += texture2D(tex, vec2(X + 3.26667 * pixel.x, Y)) * 0.083313; + sum += texture2D(tex, vec2(X + 5.13333 * pixel.x, Y)) * 0.00640869; + + gl_FragColor = sum; +} diff --git a/data/shaders/gaussian6v.frag b/data/shaders/gaussian6v.frag new file mode 100644 index 000000000..5b3e70ee2 --- /dev/null +++ b/data/shaders/gaussian6v.frag @@ -0,0 +1,21 @@ +uniform sampler2D tex; +uniform vec2 pixel; + +// Gaussian separated blur with radius 6. + +void main() +{ + vec4 sum = vec4(0.0); + float X = gl_TexCoord[0].x; + float Y = gl_TexCoord[0].y; + + sum += texture2D(tex, vec2(X, Y - 5.13333 * pixel.y)) * 0.00640869; + sum += texture2D(tex, vec2(X, Y - 3.26667 * pixel.y)) * 0.083313; + sum += texture2D(tex, vec2(X, Y - 1.4 * pixel.y)) * 0.305481; + sum += texture2D(tex, vec2(X, Y)) * 0.209473; + sum += texture2D(tex, vec2(X, Y + 1.4 * pixel.y)) * 0.305481; + sum += texture2D(tex, vec2(X, Y + 3.26667 * pixel.y)) * 0.083313; + sum += texture2D(tex, vec2(X, Y + 5.13333 * pixel.y)) * 0.00640869; + + gl_FragColor = sum; +} diff --git a/data/shaders/glow.frag b/data/shaders/glow.frag new file mode 100644 index 000000000..3932dc42e --- /dev/null +++ b/data/shaders/glow.frag @@ -0,0 +1,16 @@ +uniform sampler2D tex; +uniform vec2 res; + +void main() +{ + vec2 coords = gl_FragCoord.xy / res; + + vec4 col = texture2D(tex, coords); + float alpha = col.a; + + if (alpha < 0.04) discard; + + col *= vec4(vec3(4.0), 1.5); + + gl_FragColor = col; +} diff --git a/data/shaders/godfade.frag b/data/shaders/godfade.frag new file mode 100644 index 000000000..659742738 --- /dev/null +++ b/data/shaders/godfade.frag @@ -0,0 +1,16 @@ +uniform sampler2D tex; +uniform vec3 col; + +void main() +{ + vec4 res = texture2D(tex, gl_TexCoord[0].xy); + + // Keep the sun fully bright, but fade the sky + float mul = distance(res.xyz, col); + mul = step(mul, 0.02); + mul *= 0.97; + + res = res * vec4(mul); + + gl_FragColor = res; +} diff --git a/data/shaders/godray.frag b/data/shaders/godray.frag new file mode 100644 index 000000000..0e06dfb03 --- /dev/null +++ b/data/shaders/godray.frag @@ -0,0 +1,29 @@ +uniform sampler2D tex; +uniform vec2 sunpos; + +#define SAMPLES 12 + +const float decaystep = 0.88; + +void main() +{ + vec2 texc = gl_TexCoord[0].xy; + vec2 tosun = sunpos - texc; + + if (dot(tosun, tosun) > 0.49) discard; + + vec2 dist = tosun * 1.0/(float(SAMPLES) * 1.12); + + vec3 col = texture2D(tex, texc).xyz; + float decay = 1.0; + + for (int i = 0; i < SAMPLES; i++) { + texc += dist; + vec3 here = texture2D(tex, texc).xyz; + here *= decay; + col += here; + decay *= decaystep; + } + + gl_FragColor = vec4(col, 1.0) * 0.8; +} diff --git a/data/shaders/grass.frag b/data/shaders/grass.frag index e70be4351..9bd1e0555 100644 --- a/data/shaders/grass.frag +++ b/data/shaders/grass.frag @@ -1,54 +1,8 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 the SuperTuxKart team -// -// 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. - - -uniform vec4 fogColor; -uniform float fogFrom; -uniform float fogTo; -uniform int fog; uniform sampler2D tex; -varying vec4 coord; void main() { - vec4 color = texture2D(tex, gl_TexCoord[0].st); - vec4 solidColor = vec4(color.r, color.g, color.b, 1); + vec4 color = texture2D(tex, gl_TexCoord[0].st); - if (fog == 1) - { - if (coord.z > fogTo) - { - gl_FragColor = fogColor; - gl_FragColor.a = color.a; - } - else if (coord.z > fogFrom) - { - float fogIntensity = (coord.z - fogFrom) / (fogTo - fogFrom); - vec4 color2 = fogIntensity*fogColor + (1.0 - fogIntensity)*solidColor; - color2.a = color.a; - gl_FragColor = color2; - } - else - { - gl_FragColor = color; - } - } - else - { - gl_FragColor = color; - } + gl_FragColor = color; } diff --git a/data/shaders/grass.vert b/data/shaders/grass.vert index 8aeee783c..e2208de2c 100644 --- a/data/shaders/grass.vert +++ b/data/shaders/grass.vert @@ -1,33 +1,11 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 the SuperTuxKart team -// -// 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. - - -uniform float angle; -uniform float amplitude; -varying vec4 coord; - +uniform vec3 windDir; void main() { - gl_TexCoord[0] = gl_MultiTexCoord0; - vec4 vertexPosition = ftransform(); //gl_ModelViewMatrix * gl_Vertex; - vertexPosition += vec4(1,0,0,0) * amplitude * gl_Color.r * sin(angle); - gl_Position = vertexPosition; - gl_FrontColor = vec4(1,1,1,1); - gl_BackColor = vec4(1,1,1,1); - coord = vertexPosition; + gl_TexCoord[0] = gl_MultiTexCoord0; + + vec4 vertexPosition = gl_Vertex; + vertexPosition.xyz += windDir * gl_Color.r; + + gl_Position = gl_ModelViewProjectionMatrix * vertexPosition; } diff --git a/data/shaders/lightblend.frag b/data/shaders/lightblend.frag new file mode 100644 index 000000000..64530205e --- /dev/null +++ b/data/shaders/lightblend.frag @@ -0,0 +1,19 @@ +uniform sampler2D tex; +uniform vec3 ambient; +uniform sampler2D spectex; + +void main() +{ + vec2 texc = gl_TexCoord[0].xy; + + vec4 col = texture2D(tex, texc); + vec4 specular = texture2D(spectex, texc); + + col.xyz += ambient; + float spec = col.a - 0.05; + spec *= specular.a; + col.xyz += spec; + col.a = 1.0; + + gl_FragColor = col; +} diff --git a/data/shaders/mipviz.frag b/data/shaders/mipviz.frag new file mode 100644 index 000000000..e6bd31741 --- /dev/null +++ b/data/shaders/mipviz.frag @@ -0,0 +1,52 @@ +#version 120 + +uniform sampler2D tex; +uniform vec2 texsize; +uniform int notex; + +float miplevel(in vec2 texture_coordinate) +{ + // The OpenGL Graphics System: A Specification 4.2 + // - chapter 3.9.11, equation 3.21 + + vec2 dx_vtc = dFdx(texture_coordinate); + vec2 dy_vtc = dFdy(texture_coordinate); + float delta_max_sqr = max(dot(dx_vtc, dx_vtc), dot(dy_vtc, dy_vtc)); + + return 0.5 * log2(delta_max_sqr); // == log2(sqrt(delta_max_sqr)); +} + +void main() { + + if (notex != 0) { + gl_FragColor = gl_Color; + return; + } + + // Buggy Intel windows driver workaround + vec4 levels[6] = vec4[]( + vec4(0.0, 0.0, 1.0, 0.8), + vec4(0.0, 0.5, 1.0, 0.4), + vec4(1.0, 1.0, 1.0, 0.0), + vec4(1.0, 0.7, 0.0, 0.2), + vec4(1.0, 0.3, 0.0, 0.6), + vec4(1.0, 0.0, 0.0, 0.8) + ); + + float mip = miplevel(texsize * gl_TexCoord[0].xy) + 2.0; + mip = clamp(mip, 0.0, 5.0); + + int lowmip = int(mip); + int highmip = lowmip + 1; + if (highmip > 5) + highmip = 5; + + float mixer = fract(mip); + + vec4 mixcol = mix(levels[lowmip], levels[highmip], mixer); + vec4 tcol = texture2D(tex, gl_TexCoord[0].xy); + + vec3 col = mix(tcol.xyz, mixcol.xyz, mixcol.a); + + gl_FragColor = vec4(col, tcol.a); +} diff --git a/data/shaders/mlaa_blend2.frag b/data/shaders/mlaa_blend2.frag new file mode 100644 index 000000000..0be80576b --- /dev/null +++ b/data/shaders/mlaa_blend2.frag @@ -0,0 +1,112 @@ +#define MAX_SEARCH_STEPS 8.0 +#define MAX_DISTANCE 33.0 + +#extension GL_ARB_shader_texture_lod: enable + +uniform sampler2D edgesMap; +uniform sampler2D areaMap; + +uniform vec2 PIXEL_SIZE; + +/** + * This one just returns the first level of a mip map chain, which allow us to + * avoid the nasty ddx/ddy warnings, even improving the performance a little + * bit. + */ +vec4 tex2Doffset(sampler2D map, vec2 texcoord, vec2 offset) { + return texture2DLod(map, texcoord + PIXEL_SIZE * offset, 0.0); +} + +float SearchXLeft(vec2 texcoord) { + // We compare with 0.9 to prevent bilinear access precision problems. + float i; + float e = 0.0; + for (i = -1.5; i > -2.0 * MAX_SEARCH_STEPS; i -= 2.0) { + e = tex2Doffset(edgesMap, texcoord, vec2(i, 0.0)).g; + if (e < 0.9) break; + } + return max(i + 1.5 - 2.0 * e, -2.0 * MAX_SEARCH_STEPS); +} + +float SearchXRight(vec2 texcoord) { + float i; + float e = 0.0; + for (i = 1.5; i < 2.0 * MAX_SEARCH_STEPS; i += 2.0) { + e = tex2Doffset(edgesMap, texcoord, vec2(i, 0.0)).g; + if (e < 0.9) break; + } + return min(i - 1.5 + 2.0 * e, 2.0 * MAX_SEARCH_STEPS); +} + +float SearchYDown(vec2 texcoord) { + float i; + float e = 0.0; + for (i = -1.5; i > -2.0 * MAX_SEARCH_STEPS; i -= 2.0) { + e = tex2Doffset(edgesMap, texcoord, vec2(i, 0.0).yx).r; + if (e < 0.9) break; + } + return max(i + 1.5 - 2.0 * e, -2.0 * MAX_SEARCH_STEPS); +} + +float SearchYUp(vec2 texcoord) { + float i; + float e = 0.0; + for (i = 1.5; i < 2.0 * MAX_SEARCH_STEPS; i += 2.0) { + e = tex2Doffset(edgesMap, texcoord, vec2(i, 0.0).yx).r; + if (e < 0.9) break; + } + return min(i - 1.5 + 2.0 * e, 2.0 * MAX_SEARCH_STEPS); +} + + +vec2 round(vec2 invec) { + return vec2(floor(abs(invec) + vec2(0.5)) * sign(invec)); +} + +vec2 Area(vec2 distance, float e1, float e2) { + // * By dividing by areaSize - 1.0 below we are implicitely offsetting to + // always fall inside of a pixel + // * Rounding prevents bilinear access precision problems + float areaSize = MAX_DISTANCE * 5.0; + vec2 pixcoord = MAX_DISTANCE * round(4.0 * vec2(e1, e2)) + distance; + vec2 texcoord = pixcoord / (areaSize - 1.0); + return texture2DLod(areaMap, texcoord, 0.0).ra; +} + +void main() { + vec4 areas = vec4(0.0); + + vec2 e = texture2D(edgesMap, gl_TexCoord[0].xy).rg; + + if (e.g != 0.0) { // Edge at north + + // Search distances to the left and to the right: + vec2 d = vec2(SearchXLeft(gl_TexCoord[0].xy), SearchXRight(gl_TexCoord[0].xy)); + + // Now fetch the crossing edges. Instead of sampling between edgels, we + // sample at 0.25, to be able to discern what value has each edgel: + vec4 coords = vec4(d.x, 0.25, d.y + 1.0, 0.25) * PIXEL_SIZE.xyxy + gl_TexCoord[0].xyxy; + float e1 = texture2DLod(edgesMap, coords.xy, 0.0).r; + float e2 = texture2DLod(edgesMap, coords.zw, 0.0).r; + + // Ok, we know how this pattern looks like, now it is time for getting + // the actual area: + areas.rg = Area(abs(d), e1, e2); + } + + if (e.r != 0.0) { // Edge at west + + // Search distances to the top and to the bottom: + vec2 d = vec2(SearchYUp(gl_TexCoord[0].xy), SearchYDown(gl_TexCoord[0].xy)); + + // Now fetch the crossing edges (yet again): + vec4 coords = vec4(-0.25, d.x, -0.25, d.y - 1.0) * PIXEL_SIZE.xyxy + gl_TexCoord[0].xyxy; + float e1 = texture2DLod(edgesMap, coords.xy, 0.0).g; + float e2 = texture2DLod(edgesMap, coords.zw, 0.0).g; + + // Get the area for this direction: + areas.ba = Area(abs(d), e1, e2); + } + + gl_FragColor = areas; +} diff --git a/data/shaders/mlaa_color1.frag b/data/shaders/mlaa_color1.frag new file mode 100644 index 000000000..44ac9965b --- /dev/null +++ b/data/shaders/mlaa_color1.frag @@ -0,0 +1,25 @@ +varying vec4 offset[2]; + +uniform sampler2D colorMapG; +const float threshold = 0.1f; + +void main() { + vec3 weights = vec3(0.2126,0.7152, 0.0722); // ITU-R BT. 709 + + /** + * Luma calculation requires gamma-corrected colors: + */ + float L = dot(texture2D(colorMapG, gl_TexCoord[0].xy).rgb, weights); + float Lleft = dot(texture2D(colorMapG, offset[0].xy).rgb, weights); + float Ltop = dot(texture2D(colorMapG, offset[0].zw).rgb, weights); + float Lright = dot(texture2D(colorMapG, offset[1].xy).rgb, weights); + float Lbottom = dot(texture2D(colorMapG, offset[1].zw).rgb, weights); + + vec4 delta = abs(vec4(L) - vec4(Lleft, Ltop, Lright, Lbottom)); + vec4 edges = step(vec4(threshold), delta); + + if (dot(edges, vec4(1.0)) == 0.0) + discard; + + gl_FragColor = edges; +} diff --git a/data/shaders/mlaa_neigh3.frag b/data/shaders/mlaa_neigh3.frag new file mode 100644 index 000000000..6f9bfabb0 --- /dev/null +++ b/data/shaders/mlaa_neigh3.frag @@ -0,0 +1,38 @@ +varying vec4 offset[2]; + +uniform sampler2D blendMap; +uniform sampler2D colorMap; + +void main() { + // Fetch the blending weights for current pixel: + vec4 topLeft = texture2D(blendMap, gl_TexCoord[0].xy); + float bottom = texture2D(blendMap, offset[1].zw).g; + float right = texture2D(blendMap, offset[1].xy).a; + vec4 a = vec4(topLeft.r, bottom, topLeft.b, right); + + // Up to 4 lines can be crossing a pixel (one in each edge). So, we perform + // a weighted average, where the weight of each line is 'a' cubed, which + // favors blending and works well in practice. + vec4 w = a * a * a; + + // There is some blending weight with a value greater than 0.0? + float sum = dot(w, vec4(1.0)); + if (sum < 1e-5) + discard; + + vec4 color = vec4(0.0); + + // Add the contributions of the possible 4 lines that can cross this pixel: + vec4 C = texture2D(colorMap, gl_TexCoord[0].xy); + vec4 Cleft = texture2D(colorMap, offset[0].xy); + vec4 Ctop = texture2D(colorMap, offset[0].zw); + vec4 Cright = texture2D(colorMap, offset[1].xy); + vec4 Cbottom = texture2D(colorMap, offset[1].zw); + color = mix(C, Ctop, a.r) * w.r + color; + color = mix(C, Cbottom, a.g) * w.g + color; + color = mix(C, Cleft, a.b) * w.b + color; + color = mix(C, Cright, a.a) * w.a + color; + + // Normalize the resulting color and we are finished! + gl_FragColor = color / sum; +} diff --git a/data/shaders/mlaa_offset.vert b/data/shaders/mlaa_offset.vert new file mode 100644 index 000000000..a0b03702e --- /dev/null +++ b/data/shaders/mlaa_offset.vert @@ -0,0 +1,12 @@ +varying vec4 offset[2]; +uniform vec2 PIXEL_SIZE; + +void main() { + gl_Position = ftransform(); + vec4 invy = gl_MultiTexCoord0; +// invy.y = 1.0 - invy.y; + gl_TexCoord[0] = invy; + + offset[0] = invy.xyxy + PIXEL_SIZE.xyxy * vec4(-1.0, 0.0, 0.0, 1.0); + offset[1] = invy.xyxy + PIXEL_SIZE.xyxy * vec4( 1.0, 0.0, 0.0, -1.0); +} diff --git a/data/shaders/motion_blur.frag b/data/shaders/motion_blur.frag index c57abbf59..aef0843fb 100644 --- a/data/shaders/motion_blur.frag +++ b/data/shaders/motion_blur.frag @@ -41,7 +41,7 @@ uniform float mask_radius; uniform float max_tex_height; // Number of samples used for blurring -#define NB_SAMPLES 12 +#define NB_SAMPLES 8 void main() { @@ -50,14 +50,6 @@ void main() // Sample the color buffer vec3 color = texture2D(color_buffer, texcoords).rgb; - // If no motion blur is needed, don't do any of the blur computation, - // just return the color from the texture. - if(boost_amount==0.0) - { - gl_FragColor = vec4(color, 1.0); - return; - } - // Compute the blur direction. // IMPORTANT: we don't normalize it so that it avoids a glitch around 'center', // plus it naturally scales the motion blur in a cool way :) diff --git a/data/shaders/multiply.frag b/data/shaders/multiply.frag new file mode 100644 index 000000000..9394e9595 --- /dev/null +++ b/data/shaders/multiply.frag @@ -0,0 +1,10 @@ +uniform sampler2D tex1; +uniform sampler2D tex2; + +void main() +{ + vec4 col1 = texture2D(tex1, gl_TexCoord[0].xy); + vec4 col2 = vec4(vec3(texture2D(tex2, gl_TexCoord[0].xy).x), 1.0); + + gl_FragColor = col1 * col2; +} diff --git a/data/shaders/normalmap.frag b/data/shaders/normalmap.frag index dfc10424b..1040e1662 100644 --- a/data/shaders/normalmap.frag +++ b/data/shaders/normalmap.frag @@ -4,7 +4,7 @@ uniform sampler2D BumpTex; //The bump-map uniform sampler2D DecalTex; //The texture uniform sampler2D LightMapTex; - int HasLightMap; +uniform int HasLightMap; // New bumpmapping varying vec3 lightVec; @@ -19,18 +19,18 @@ void main() normal = normalize (normal); // compute diffuse lighting - float lamberFactor = max (dot (lightVec, normal), 0.0) ; + float lamberFactor = max (dot (lightVec, normal), 0.0); vec4 diffuseMaterial; - diffuseMaterial = texture2D (DecalTex, gl_TexCoord[0].st); + diffuseMaterial = texture2D (DecalTex, gl_TexCoord[0].st); - if (HasLightMap < 1) - { - // 0.5 is the ambient light - gl_FragColor = diffuseMaterial * (0.5 + lamberFactor*0.5); - } - else - { - gl_FragColor = diffuseMaterial * (0.5 + lamberFactor*0.5) * texture2D(LightMapTex, gl_TexCoord[0].st); - } + if (HasLightMap < 1) + { + // 0.5 is the ambient light + gl_FragColor = diffuseMaterial * (0.5 + lamberFactor*0.5); + } + else + { + gl_FragColor = diffuseMaterial * (0.5 + lamberFactor*0.5) * texture2D(LightMapTex, gl_TexCoord[0].st); + } } diff --git a/data/shaders/objectpass.frag b/data/shaders/objectpass.frag new file mode 100644 index 000000000..c557894d2 --- /dev/null +++ b/data/shaders/objectpass.frag @@ -0,0 +1,40 @@ +varying vec3 nor; + +uniform sampler2D tex; +uniform sampler2D lighttex; +uniform float far; +uniform int hastex; +uniform int haslightmap; +uniform float objectid; + +const float near = 1.0; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() { + + float linear_z = (2.0 * near) / (far + near - gl_FragCoord.z * (far - near)); + + // Tune for better inside range without losing outdoors + linear_z *= 2.0; + + vec4 light = vec4(1.0); + + if (haslightmap != 0) { + light = texture2D(lighttex, gl_TexCoord[1].xy); + } + + if (hastex != 0) + gl_FragData[0] = texture2D(tex, gl_TexCoord[0].xy) * light; + else + gl_FragData[0] = gl_Color; + + gl_FragData[1] = vec4(nor, linear_z); + gl_FragData[2] = vec4(encdepth(gl_FragCoord.z).xyz, objectid); +} + diff --git a/data/shaders/objectpass.vert b/data/shaders/objectpass.vert new file mode 100644 index 000000000..76e749908 --- /dev/null +++ b/data/shaders/objectpass.vert @@ -0,0 +1,14 @@ +varying vec3 nor; +uniform mat4 invtworldm; + +void main() { + + nor = (invtworldm * vec4(gl_Normal, 0.0)).xyz; + nor = normalize(nor); + nor = nor * 0.5 + 0.5; + + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_Position = ftransform(); + gl_FrontColor = gl_Color; +} diff --git a/data/shaders/objectpass_ref.frag b/data/shaders/objectpass_ref.frag new file mode 100644 index 000000000..daa022752 --- /dev/null +++ b/data/shaders/objectpass_ref.frag @@ -0,0 +1,37 @@ +varying vec3 nor; +uniform sampler2D tex; +uniform float far; +uniform int hastex; +uniform float objectid; + +const float near = 1.0; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() { + + float linear_z = (2.0 * near) / (far + near - gl_FragCoord.z * (far - near)); + + // Tune for better inside range without losing outdoors + linear_z *= 2.0; + + if (hastex != 0) { + vec4 col = texture2D(tex, gl_TexCoord[0].xy); + + if (col.a < 0.5) + discard; + + gl_FragData[0] = col; + } else { + gl_FragData[0] = gl_Color; + } + + gl_FragData[1] = vec4(nor, linear_z); + gl_FragData[2] = vec4(encdepth(gl_FragCoord.z).xyz, objectid); +} + diff --git a/data/shaders/objectpass_rimlit.frag b/data/shaders/objectpass_rimlit.frag new file mode 100644 index 000000000..5979f2214 --- /dev/null +++ b/data/shaders/objectpass_rimlit.frag @@ -0,0 +1,45 @@ +varying vec3 nor; +uniform sampler2D tex; +uniform float far; +uniform int hastex; +uniform float objectid; + +varying vec3 eyenor; +varying vec3 viewpos; + +const float near = 1.0; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() { + + float linear_z = (2.0 * near) / (far + near - gl_FragCoord.z * (far - near)); + + // Tune for better inside range without losing outdoors + linear_z *= 2.0; + + float rim = 1.0 - dot(eyenor, viewpos); + rim = smoothstep(0.5, 1.5, rim) * 0.35; + + if (hastex != 0) { + vec4 col = texture2D(tex, gl_TexCoord[0].xy); + + if (col.a < 0.5) + discard; + + col.xyz += rim; + + gl_FragData[0] = col; + } else { + gl_FragData[0] = gl_Color + vec4(vec3(rim), 0.0); + } + + gl_FragData[1] = vec4(nor, linear_z); + gl_FragData[2] = vec4(encdepth(gl_FragCoord.z).xyz, objectid); +} + diff --git a/data/shaders/objectpass_rimlit.vert b/data/shaders/objectpass_rimlit.vert new file mode 100644 index 000000000..b3d98e449 --- /dev/null +++ b/data/shaders/objectpass_rimlit.vert @@ -0,0 +1,19 @@ +varying vec3 nor; +uniform mat4 invtworldm; + +varying vec3 eyenor; +varying vec3 viewpos; + +void main() { + + nor = (invtworldm * vec4(gl_Normal, 0.0)).xyz; + nor = normalize(nor); + nor = nor * 0.5 + 0.5; + + eyenor = gl_NormalMatrix * gl_Normal; + viewpos = -normalize((gl_ModelViewMatrix * gl_Vertex).xyz); + + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); + gl_FrontColor = gl_Color; +} diff --git a/data/shaders/objectpass_spheremap.frag b/data/shaders/objectpass_spheremap.frag new file mode 100644 index 000000000..f8abac686 --- /dev/null +++ b/data/shaders/objectpass_spheremap.frag @@ -0,0 +1,42 @@ +varying vec3 nor; +uniform sampler2D tex; +uniform float far; +uniform float objectid; + +varying vec3 eyenor; +varying vec3 viewpos; + +const float near = 1.0; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() { + + float linear_z = (2.0 * near) / (far + near - gl_FragCoord.z * (far - near)); + + // Tune for better inside range without losing outdoors + linear_z *= 2.0; + + // Calculate the spherical UV + const vec3 forward = vec3(0.0, 0.0, 1.0); + + // get the angle between the forward vector and the horizontal portion of the normal + vec3 normal_x = normalize(vec3(eyenor.x, 0.0, eyenor.z)); + float sin_theta_x = length(cross( forward, normal_x )) * eyenor.x/abs(eyenor.x); + + // get the angle between the forward vector and the vertical portion of the normal + vec3 normal_y = normalize(vec3(0.0, eyenor.y, eyenor.z)); + float sin_theta_y = length(cross( forward, normal_y )) * eyenor.y/abs(eyenor.y); + + vec4 detail0 = texture2D(tex, vec2(0.5 + sin_theta_x*0.5, 0.5 + sin_theta_y*0.5)); + + gl_FragData[0] = detail0 * gl_Color; + + gl_FragData[1] = vec4(nor, linear_z); + gl_FragData[2] = vec4(encdepth(gl_FragCoord.z).xyz, objectid); +} diff --git a/data/shaders/pass.frag b/data/shaders/pass.frag new file mode 100644 index 000000000..10f94ce0d --- /dev/null +++ b/data/shaders/pass.frag @@ -0,0 +1,6 @@ +uniform sampler2D tex; + +void main() +{ + gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); +} diff --git a/data/shaders/pass.vert b/data/shaders/pass.vert new file mode 100644 index 000000000..2fd32c1ac --- /dev/null +++ b/data/shaders/pass.vert @@ -0,0 +1,6 @@ +void main() { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; + gl_Position = ftransform(); + gl_FrontColor = gl_Color; +} diff --git a/data/shaders/penumbrah.frag b/data/shaders/penumbrah.frag new file mode 100644 index 000000000..822c73e56 --- /dev/null +++ b/data/shaders/penumbrah.frag @@ -0,0 +1,52 @@ +uniform sampler2D tex; +uniform vec2 pixel; + +// Separated penumbra, horizontal + +void main() +{ + float sum = 0.0; + vec4 tmp; + float X = gl_TexCoord[0].x; + float Y = gl_TexCoord[0].y; + float width = 0.0; + float zsum = 0.00001; + + tmp = texture2D(tex, vec2(X - 5.13333 * pixel.x, Y)); + sum += tmp.x * 0.00640869; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X - 3.26667 * pixel.x, Y)); + sum += tmp.x * 0.083313; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X - 1.4 * pixel.x, Y)); + sum += tmp.x * 0.305481; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y)); + sum += tmp.x * 0.209473; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X + 1.4 * pixel.x, Y)); + sum += tmp.x * 0.305481; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X + 3.26667 * pixel.x, Y)); + sum += tmp.x * 0.083313; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X + 5.13333 * pixel.x, Y)); + sum += tmp.x * 0.00640869; + zsum += tmp.z; + width += tmp.y; + + float hasz = step(0.7, zsum); + gl_FragColor = vec4(sum, (width / zsum) * hasz, hasz, 1.0); +} diff --git a/data/shaders/penumbrav.frag b/data/shaders/penumbrav.frag new file mode 100644 index 000000000..6a6ef2ca0 --- /dev/null +++ b/data/shaders/penumbrav.frag @@ -0,0 +1,52 @@ +uniform sampler2D tex; +uniform vec2 pixel; + +// Separated penumbra, vertical + +void main() +{ + float sum = 0.0; + vec4 tmp; + float X = gl_TexCoord[0].x; + float Y = gl_TexCoord[0].y; + float width = 0.0; + float zsum = 0.00001; + + tmp = texture2D(tex, vec2(X, Y - 5.13333 * pixel.y)); + sum += tmp.x * 0.00640869; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y - 3.26667 * pixel.y)); + sum += tmp.x * 0.083313; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y - 1.4 * pixel.y)); + sum += tmp.x * 0.305481; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y)); + sum += tmp.x * 0.209473; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y + 1.4 * pixel.y)); + sum += tmp.x * 0.305481; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y + 3.26667 * pixel.y)); + sum += tmp.x * 0.083313; + zsum += tmp.z; + width += tmp.y; + + tmp = texture2D(tex, vec2(X, Y + 5.13333 * pixel.y)); + sum += tmp.x * 0.00640869; + zsum += tmp.z; + width += tmp.y; + + float hasz = step(0.7, zsum); + gl_FragColor = vec4(sum, (width / zsum) * hasz, hasz, 1.0); +} diff --git a/data/shaders/pointlight.frag b/data/shaders/pointlight.frag new file mode 100644 index 000000000..facc40c0e --- /dev/null +++ b/data/shaders/pointlight.frag @@ -0,0 +1,50 @@ +uniform sampler2D ntex; +uniform sampler2D dtex; + +uniform vec3 center; +uniform vec3 col; +uniform vec3 campos; +uniform float r; +uniform float spec; +uniform vec2 screen; +uniform mat4 invprojview; + +float decdepth(vec4 rgba) { + return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); +} + +void main() { + + vec2 texc = gl_FragCoord.xy / screen; + float z = decdepth(vec4(texture2D(dtex, texc).xyz, 0.0)); + + if (z < 0.03) discard; + + vec3 tmp = vec3(texc, z); + tmp = tmp * 2.0 - 1.0; + + vec4 xpos = vec4(tmp, 1.0); + xpos = invprojview * xpos; + xpos.xyz /= xpos.w; + + float d = distance(center, xpos.xyz); + if (d > r) discard; + + float att = 1.0 - smoothstep(0.0, r, d); + + vec3 norm = texture2D(ntex, texc).xyz; + norm = (norm - 0.5) * 2.0; + + vec3 camdir = normalize(campos - xpos.xyz); + + vec3 L = normalize(center - xpos.xyz); + vec3 H = normalize(L + camdir); + + float NdotL = max(0.0, dot(norm, L)) * att; + if (NdotL < 0.01) discard; + float NdotH = max(0.0, dot(norm, H)); + NdotH = pow(NdotH, spec); + NdotH += 0.05; // offset so that the alpha test doesn't kill us + + gl_FragColor = NdotL * vec4(col, NdotH); +} diff --git a/data/shaders/ppdisplace.frag b/data/shaders/ppdisplace.frag new file mode 100644 index 000000000..a06293649 --- /dev/null +++ b/data/shaders/ppdisplace.frag @@ -0,0 +1,26 @@ +uniform sampler2D tex; +uniform sampler2D dtex; + +uniform int viz; + +void main() +{ + vec2 tc = gl_TexCoord[0].xy; + + vec4 shiftval = texture2D(dtex, tc) / vec4(50.0); + vec2 shift; + shift.x = -shiftval.x + shiftval.y; + shift.y = -shiftval.z + shiftval.w; + + tc += shift; + + vec4 newcol = texture2D(tex, tc); + + if (viz < 1) + { + gl_FragColor = newcol; + } else + { + gl_FragColor = shiftval * vec4(50.0); + } +} diff --git a/data/shaders/rain.frag b/data/shaders/rain.frag new file mode 100644 index 000000000..10f94ce0d --- /dev/null +++ b/data/shaders/rain.frag @@ -0,0 +1,6 @@ +uniform sampler2D tex; + +void main() +{ + gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); +} diff --git a/data/shaders/rain.vert b/data/shaders/rain.vert new file mode 100644 index 000000000..f2a764ede --- /dev/null +++ b/data/shaders/rain.vert @@ -0,0 +1,33 @@ +uniform float screenw; +uniform float time; +uniform mat4 viewm; +uniform vec3 campos; + +void main() +{ + const float size = 0.5; + + // This simulation will run accurately for a bit under five days. + vec4 start = gl_Vertex; + start.y -= time; + + // How many times has it fell? + float count = floor(start.y / 24.0); + start.x += sin(count); + start.z += cos(count); + + vec2 signs = sign(start.xz); + start.xz = mod(start.xz, 17.5) * signs; + + start.y = mod(start.y, 24.0) - 3.0; + + start.xyz += campos; + + vec4 eyepos = viewm * start; + vec4 projCorner = gl_ProjectionMatrix * vec4(vec2(size), eyepos.z, eyepos.w); + + gl_PointSize = screenw * projCorner.x / projCorner.w; + gl_Position = gl_ProjectionMatrix * eyepos; + + gl_TexCoord[0] = gl_MultiTexCoord0; +} diff --git a/data/shaders/shadowgen.frag b/data/shaders/shadowgen.frag new file mode 100644 index 000000000..6c2d8501a --- /dev/null +++ b/data/shaders/shadowgen.frag @@ -0,0 +1,44 @@ +uniform sampler2D halft; // half is a reserved word +uniform sampler2D quarter; +uniform sampler2D eighth; + +void main() +{ + vec3 val[3]; + val[0] = texture2D(halft, gl_TexCoord[0].xy).xyz; + val[1] = texture2D(quarter, gl_TexCoord[0].xy).xyz; + val[2] = texture2D(eighth, gl_TexCoord[0].xy).xyz; + + // Find the first level with a penumbra value + int i; + float q = 0.0; + float outval = 1.0; + + float hasshadow = dot(vec3(1.0), vec3(val[0].z, val[1].z, val[2].z)); + + if (hasshadow > 0.9) + { + for (i = 0; i < 3; i++) + { + if (val[i].z > 0.9) + { + q = val[i].y; + break; + } + } + + q *= 8.0; + q = max(1.0, q); + q = log2(q); + q = min(1.9, q); + + // q is now between 0 and 1.9. + int down = int(floor(q)); + int up = down + 1; + float interp = q - float(down); + + outval = 1.0 - mix(val[down].x, val[up].x, interp); + } + + gl_FragColor = vec4(vec3(outval), 1.0); +} diff --git a/data/shaders/shadowimportance.frag b/data/shaders/shadowimportance.frag new file mode 100644 index 000000000..190d40418 --- /dev/null +++ b/data/shaders/shadowimportance.frag @@ -0,0 +1,70 @@ +uniform sampler2D ntex; +uniform sampler2D ctex; +uniform vec3 campos; +uniform int low; + +varying vec3 wpos; +varying vec2 texc; + +float luminanceImp() +{ + // A full-res fetch kills on low-end + if (low > 0) return 1.0; + + const vec3 weights = vec3(0.2126, 0.7152, 0.0722); // ITU-R BT. 709 + vec3 col = texture2D(ctex, texc).xyz; + + float luma = dot(weights, col); + + // Dark surfaces need less resolution + float f = smoothstep(0.1, 0.4, luma); + f = max(0.05, f); + + return f; +} + +float normalImp(vec3 normal) +{ + vec3 camdir = normalize(campos - wpos); + vec3 N = normalize(normal); + + // Boost surfaces facing the viewer directly + float f = 2.0 * max(0.0, dot(N, camdir)); + + return f; +} + +float depthImp(float linearz) +{ +/* const float skip = 0.7; + + float f = min(linearz, skip); + f *= 1.0/skip;*/ + + float z = log(1.0 + linearz * 9.0) / log(10.0); + + float f = 1.0 - (z * 0.9); + + return f; +} + +void main() +{ + vec4 ntmp = texture2D(ntex, texc); + vec3 normal = ntmp.xyz * 2.0 - 1.0; + float linearz = ntmp.a; + + float importance = normalImp(normal) * depthImp(linearz) * luminanceImp(); + importance = clamp(importance, 0.0, 1.0); + + float low = step(0.001, importance); + + // Quantize it + const float steps = 16.0; + importance *= steps; + importance = ceil(importance) * low; + importance /= steps; + + gl_FragColor = vec4(importance); + gl_FragDepth = 1.0 - importance; +} diff --git a/data/shaders/shadowimportance.vert b/data/shaders/shadowimportance.vert new file mode 100644 index 000000000..87ab5c783 --- /dev/null +++ b/data/shaders/shadowimportance.vert @@ -0,0 +1,30 @@ +uniform sampler2D dtex; +uniform mat4 ipvmat; +uniform mat4 shadowmat; + +varying vec3 wpos; +varying vec2 texc; + +float decdepth(vec4 rgba) { + return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); +} + +void main() +{ + texc = gl_Vertex.xy / vec2(32767.0); + float z = decdepth(vec4(texture2D(dtex, texc).xyz, 0.0)); + + vec3 tmp = vec3(texc, z); + tmp = tmp * 2.0 - 1.0; + + vec4 xpos = vec4(tmp, 1.0); + xpos = ipvmat * xpos; + xpos.xyz /= xpos.w; + + wpos = xpos.xyz; + + // Now we have this pixel's world-space position. Convert to shadow space. + vec4 pos = shadowmat * vec4(xpos.xyz, 1.0); + + gl_Position = pos; +} diff --git a/data/shaders/shadowpass.frag b/data/shaders/shadowpass.frag new file mode 100644 index 000000000..974cce97f --- /dev/null +++ b/data/shaders/shadowpass.frag @@ -0,0 +1,34 @@ +uniform sampler2D tex; +uniform int hastex; +uniform int viz; +uniform int wireframe; +uniform float objectid; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() { + + if (hastex != 0) { + float alpha = texture2D(tex, gl_TexCoord[0].xy).a; + + if (alpha < 0.5) + discard; + } + + if (viz < 1) + { + gl_FragColor = vec4(encdepth(gl_FragCoord.z).xyz, objectid); + } + else { + if (wireframe > 0) + gl_FragColor = vec4(1.0); + else + gl_FragColor = texture2D(tex, gl_TexCoord[0].xy); + } +} + diff --git a/data/shaders/shadowpass.vert b/data/shaders/shadowpass.vert new file mode 100644 index 000000000..631dbb5a6 --- /dev/null +++ b/data/shaders/shadowpass.vert @@ -0,0 +1,25 @@ +uniform sampler2D warpx; +uniform sampler2D warpy; + +float decdepth(vec4 rgba) { + return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); +} + +void main() +{ + vec4 pos = ftransform(); + gl_TexCoord[0] = gl_MultiTexCoord0; + + vec2 tc = pos.xy * vec2(0.5) + vec2(0.5); + + float movex = decdepth(texture2D(warpx, tc)); + float movey = decdepth(texture2D(warpy, tc)); + + float dx = movex * 2.0 - 1.0; + float dy = movey * 2.0 - 1.0; + + dx *= 2.0; + dy *= 2.0; + + gl_Position = pos + vec4(dx, dy, vec2(0.0)); +} diff --git a/data/shaders/shadowwarph.frag b/data/shaders/shadowwarph.frag new file mode 100644 index 000000000..e7e9d5632 --- /dev/null +++ b/data/shaders/shadowwarph.frag @@ -0,0 +1,54 @@ +uniform sampler2D tex; +uniform int size; +uniform vec2 pixel; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() +{ + vec2 origtc = gl_TexCoord[0].xy; + + // Get total sum + float first = 1.0, last = 0.0; + float lower = 0.0; + float total = 0.0; + vec2 tc = 0.5 * pixel; + + for (int i = 0; i < size; i++) + { + float col = texture2D(tex, tc).x; + + lower += col * step(tc.x, origtc.x); + total += col; + + if (col > 0.0001) + { + first = min(first, tc.x); + last = max(last, tc.x); + } + + tc += pixel; + } + + float res = (lower / total) - origtc.x; + + // Outside the edges? + if (origtc.x <= first) + { + res = origtc.x * -2.1; + } + else if (origtc.x >= last) + { + res = (1.0 - origtc.x) * 2.1; + } + + res = res * 0.5 + 0.5; + res = clamp(res, 0.01, 0.99); + + gl_FragColor = encdepth(res); +} diff --git a/data/shaders/shadowwarpv.frag b/data/shaders/shadowwarpv.frag new file mode 100644 index 000000000..6998c61e7 --- /dev/null +++ b/data/shaders/shadowwarpv.frag @@ -0,0 +1,54 @@ +uniform sampler2D tex; +uniform int size; +uniform vec2 pixel; + +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() +{ + vec2 origtc = gl_TexCoord[0].xy; + + // Get total sum + float first = 1.0, last = 0.0; + float lower = 0.0; + float total = 0.0; + vec2 tc = pixel * 0.5; + + for (int i = 0; i < size; i++) + { + float col = texture2D(tex, tc).x; + + lower += col * step(tc.y, origtc.y); + total += col; + + if (col > 0.0001) + { + first = min(first, tc.y); + last = max(last, tc.y); + } + + tc += pixel; + } + + float res = (lower / total) - origtc.y; + + // Outside the edges? + if (origtc.y <= first) + { + res = origtc.y * -2.1; + } + else if (origtc.y >= last) + { + res = (1.0 - origtc.y) * 2.1; + } + + res = res * 0.5 + 0.5; + res = clamp(res, 0.01, 0.99); + + gl_FragColor = encdepth(res); +} diff --git a/data/shaders/snow.frag b/data/shaders/snow.frag new file mode 100644 index 000000000..3fddd77ec --- /dev/null +++ b/data/shaders/snow.frag @@ -0,0 +1,18 @@ +uniform sampler2D tex; +uniform float time; + +void main() +{ + vec2 change; + change.x = abs(sin(time * gl_Color.r)); + change.y = abs(cos(time * gl_Color.g)); + + change = smoothstep(0.0, 1.0, change) * 0.5; + + vec2 tc = gl_TexCoord[0].xy; + tc = smoothstep(0.5 - change, 0.5 + change, tc); + + vec4 tcol = texture2D(tex, tc); + + gl_FragColor = tcol; +} diff --git a/data/shaders/snow.vert b/data/shaders/snow.vert new file mode 100644 index 000000000..b0d8dfd55 --- /dev/null +++ b/data/shaders/snow.vert @@ -0,0 +1,7 @@ +void main() +{ + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = ftransform(); + + gl_FrontColor = gl_Color; +} diff --git a/data/shaders/splatting.frag b/data/shaders/splatting.frag index 2fc3de57d..ac5e9f7ac 100644 --- a/data/shaders/splatting.frag +++ b/data/shaders/splatting.frag @@ -1,44 +1,48 @@ -// SuperTuxKart - a fun racing game with go-kart -// Copyright (C) 2013 the SuperTuxKart team -// -// 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. - +varying vec3 nor; +uniform float far; +uniform float objectid; uniform sampler2D tex_layout; uniform sampler2D tex_detail0; uniform sampler2D tex_detail1; uniform sampler2D tex_detail2; uniform sampler2D tex_detail3; -uniform sampler2D tex_detail4; -varying vec3 normal; -varying vec3 lightdir2; -varying vec4 vertex_color; +//uniform sampler2D tex_detail4; -void main() -{ - vec4 splatting = texture2D(tex_layout, gl_TexCoord[1].st); - vec4 detail0 = texture2D(tex_detail0, gl_TexCoord[0].st); - vec4 detail1 = texture2D(tex_detail1, gl_TexCoord[0].st); - vec4 detail2 = texture2D(tex_detail2, gl_TexCoord[0].st); - vec4 detail3 = texture2D(tex_detail3, gl_TexCoord[0].st); - vec4 detail4 = texture2D(tex_detail4, gl_TexCoord[0].st); +const float near = 1.0; - gl_FragColor = (splatting.r * detail0 + - splatting.g * detail1 + - splatting.b * detail2 + - (1.0 - splatting.r - splatting.g - splatting.b) * detail3 + - (1.0 - splatting.a) * detail4) - * min(1.0, 0.2 + dot(lightdir2, normal)) * vertex_color; // 0.2 is the ambient light. +vec4 encdepth(float v) { + vec4 enc = vec4(1.0, 255.0, 65025.0, 16581375.0) * v; + enc = fract(enc); + enc -= enc.yzww * vec4(1.0/255.0, 1.0/255.0, 1.0/255.0, 0.0); + return enc; +} + +void main() { + + float linear_z = (2.0 * near) / (far + near - gl_FragCoord.z * (far - near)); + + // Tune for better inside range without losing outdoors + linear_z *= 2.0; + + // Splatting part + vec4 splatting = texture2D(tex_layout, gl_TexCoord[1].st); + vec4 detail0 = texture2D(tex_detail0, gl_TexCoord[0].st); + vec4 detail1 = texture2D(tex_detail1, gl_TexCoord[0].st); + vec4 detail2 = texture2D(tex_detail2, gl_TexCoord[0].st); + vec4 detail3 = texture2D(tex_detail3, gl_TexCoord[0].st); +// vec4 detail4 = texture2D(tex_detail4, gl_TexCoord[0].st); + vec4 detail4 = vec4(0.0); + + vec4 splatted = (splatting.r * detail0 + + splatting.g * detail1 + + splatting.b * detail2 + + (1.0 - splatting.r - splatting.g - splatting.b) * detail3 + + (1.0 - splatting.a) * detail4) + * gl_Color; + + gl_FragData[0] = splatted; + + gl_FragData[1] = vec4(nor, linear_z); + gl_FragData[2] = vec4(encdepth(gl_FragCoord.z).xyz, objectid); } diff --git a/data/shaders/ssao.frag b/data/shaders/ssao.frag new file mode 100644 index 000000000..833364bf9 --- /dev/null +++ b/data/shaders/ssao.frag @@ -0,0 +1,78 @@ +#version 120 + +uniform sampler2D tex; +uniform sampler2D oldtex; + +const float totStrength = 2.38; +const float strength = 0.07; +const float falloff = 0.000002; + +#define SAMPLES 16 + +const float invSamples = 1.0 / SAMPLES; + +void main(void) +{ + // A set of Random(tm) vec2's. 8 1s, 6 0.7s, 2 0.4 + // Again not using const because of broken Intel Windows drivers + vec2 vecs[16] = vec2[](vec2(0.43589, -0.9), vec2(-0.9, 0.43589), + vec2(-0.8, -0.6), vec2(0.6, 0.8), + vec2(0.866025, -0.5), vec2(-0.5, 0.866025), + vec2(-0.3, -0.953939), vec2(0.953939, 0.3), + vec2(0.3, -0.781025), vec2(-0.781025, 0.3), + vec2(-0.56, -0.621611), vec2(0.621611, 0.56), + vec2(0.734847, -0.4), vec2(-0.4, 0.734847), + vec2(-0.2, -0.6), vec2(0.6, 0.2)); + + vec2 uv = gl_TexCoord[0].xy; + + vec4 cur = texture2D(tex, uv); + float curdepth = cur.a; + + // Will we skip this pixel? (if it's the sky) + float len = dot(vec3(1.0), abs(cur.xyz)); + if (len < 0.2 || curdepth > 0.8) discard; + + float mytotstrength = 3.0 * totStrength * curdepth * (1.0 - curdepth); + + // get the normal of current fragment + vec3 norm = normalize(cur.xyz * vec3(2.0) - vec3(1.0)); + + float bl = 0.0; + + // adjust for the depth, 0.1 close, 0.01 far + float radD = 0.10 - 0.09 * smoothstep(0.0, 0.2, curdepth); + + for(int i = 0; i < SAMPLES; ++i) { + + vec2 ray = uv + radD * vecs[i]; + + // get the depth of the occluder fragment + vec4 occluderFragment = texture2D(tex, ray); + float normAcceptable = step(0.2, dot(vec3(1.0), abs(occluderFragment.xyz))); + + // get the normal of the occluder fragment + vec3 occNorm = normalize(occluderFragment.xyz * vec3(2.0) - vec3(1.0)); + + // if depthDifference is negative = occluder is behind current fragment + float depthDifference = curdepth - occluderFragment.a; + + // calculate the difference between the normals as a weight + float normDiff = 1.0 - max(dot(occNorm, norm), 0.0); + normDiff = smoothstep(0.1, 0.3, normDiff); + + // the falloff equation, starts at falloff and is kind of 1/x^2 falling + bl += step(falloff, depthDifference) * normDiff * normAcceptable * + (1.0 - smoothstep(falloff, strength, depthDifference)); + } + + // output the result + float ao = 1.0 - mytotstrength * bl * invSamples; + + // Mix with old result to avoid flicker + float oldao = texture2D(oldtex, uv).x; + + ao = mix(ao, oldao, 0.3); + + gl_FragColor = vec4(vec3(ao), curdepth); +} diff --git a/data/shaders/sunlight.frag b/data/shaders/sunlight.frag new file mode 100644 index 000000000..1553bfc0c --- /dev/null +++ b/data/shaders/sunlight.frag @@ -0,0 +1,58 @@ +uniform sampler2D ntex; +uniform sampler2D dtex; +uniform sampler2D cloudtex; + +uniform vec3 center; +uniform vec3 col; +uniform vec2 screen; +uniform mat4 invprojview; +uniform int hasclouds; +uniform vec2 wind; + +float decdepth(vec4 rgba) { + return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); +} + +void main() { + + vec2 texc = gl_FragCoord.xy / screen; + float z = decdepth(vec4(texture2D(dtex, texc).xyz, 0.0)); + + if (z < 0.03) + { + // Skyboxes are fully lit + gl_FragData[0] = vec4(1.0); + gl_FragData[1] = vec4(1.0); + return; + } + + vec3 norm = texture2D(ntex, texc).xyz; + norm = (norm - 0.5) * 2.0; + + // Normalized on the cpu + vec3 L = center; + + float NdotL = max(0.0, dot(norm, L)); + if (NdotL < 0.01) discard; + + vec3 outcol = NdotL * col; + + if (hasclouds == 1) + { + vec3 tmp = vec3(texc, z); + tmp = tmp * 2.0 - 1.0; + + vec4 xpos = vec4(tmp, 1.0); + xpos = invprojview * xpos; + xpos.xyz /= xpos.w; + + vec2 cloudcoord = (xpos.xz * 0.00833333) + wind; + float cloud = texture2D(cloudtex, cloudcoord).x; + //float cloud = step(0.5, cloudcoord.x) * step(0.5, cloudcoord.y); + + outcol *= cloud; + } + + gl_FragData[0] = vec4(outcol, 0.05); + gl_FragData[1] = vec4(1.0); +} diff --git a/data/shaders/sunlightshadow.frag b/data/shaders/sunlightshadow.frag new file mode 100644 index 000000000..89bc576f9 --- /dev/null +++ b/data/shaders/sunlightshadow.frag @@ -0,0 +1,106 @@ +uniform sampler2D ntex; +uniform sampler2D dtex; +uniform sampler2D cloudtex; +uniform sampler2D shadowtex; +uniform sampler2D warpx; +uniform sampler2D warpy; + +uniform vec3 center; +uniform vec3 col; +uniform vec2 screen; +uniform mat4 invprojview; +uniform mat4 shadowmat; +uniform int hasclouds; +uniform vec2 wind; +uniform float shadowoffset; + +float decdepth(vec4 rgba) { + return dot(rgba, vec4(1.0, 1.0/255.0, 1.0/65025.0, 1.0/16581375.0)); +} + +void main() { + + vec2 texc = gl_FragCoord.xy / screen; + vec4 depthread = texture2D(dtex, texc); + float z = decdepth(vec4(depthread.xyz, 0.0)); + + if (z < 0.03) + { + // Skyboxes are fully lit + gl_FragData[0] = vec4(1.0); + gl_FragData[1] = vec4(0.0); + return; + } + + vec3 norm = texture2D(ntex, texc).xyz; + norm = (norm - 0.5) * 2.0; + + // Normalized on the cpu + vec3 L = center; + + float NdotL = max(0.0, dot(norm, L)); + if (NdotL < 0.01) discard; + + vec3 outcol = NdotL * col; + + // World-space position + vec3 tmp = vec3(texc, z); + tmp = tmp * 2.0 - 1.0; + + vec4 xpos = vec4(tmp, 1.0); + xpos = invprojview * xpos; + xpos.xyz /= xpos.w; + + if (hasclouds == 1) + { + vec2 cloudcoord = (xpos.xz * 0.00833333) + wind; + float cloud = texture2D(cloudtex, cloudcoord).x; + //float cloud = step(0.5, cloudcoord.x) * step(0.5, cloudcoord.y); + + outcol *= cloud; + } + + // Shadows + vec3 shadowcoord = (shadowmat * vec4(xpos.xyz, 1.0)).xyz; + shadowcoord = (shadowcoord * 0.5) + vec3(0.5); + + float movex = decdepth(texture2D(warpx, shadowcoord.xy)); + float movey = decdepth(texture2D(warpy, shadowcoord.xy)); + float dx = movex * 2.0 - 1.0; + float dy = movey * 2.0 - 1.0; + shadowcoord.xy += vec2(dx, dy); + + vec4 shadowread = texture2D(shadowtex, shadowcoord.xy); + float shadowmapz = decdepth(vec4(shadowread.xyz, 0.0)); + + float moved = (abs(dx) + abs(dy)) * 0.5; + + float bias = 0.002 * tan(acos(NdotL)); // According to the slope + bias += smoothstep(0.001, 0.1, moved) * 0.014; // According to the warping + bias = clamp(bias, 0.001, 0.014); + + // This ID, and four IDs around this must match for a shadow pixel + float right = texture2D(shadowtex, shadowcoord.xy + vec2(shadowoffset, 0.0)).a; + float left = texture2D(shadowtex, shadowcoord.xy + vec2(-shadowoffset, 0.0)).a; + float up = texture2D(shadowtex, shadowcoord.xy + vec2(0.0, shadowoffset)).a; + float down = texture2D(shadowtex, shadowcoord.xy + vec2(0.0, -shadowoffset)).a; + + float matching = ((right + left + up + down) * 0.25) - shadowread.a; + matching = abs(matching) * 400.0; + + // If the ID is different, we're likely in shadow - cut the bias to cut peter panning + float off = 7.0 - step(abs(shadowread.a - depthread.a) - matching, 0.004) * 6.0; + bias /= off; + + const float softness = 8.0; // How soft is the light? + float shadowed = step(shadowmapz + bias, shadowcoord.z); + float dist = (shadowcoord.z / shadowmapz) - 1.0; + float penumbra = dist * softness / gl_FragCoord.z; + penumbra *= shadowed; + +/* outcol.r = (shadowcoord.z - shadowmapz) * 50.0; + outcol.g = moved;*/ + + gl_FragData[0] = vec4(outcol, 0.05); + gl_FragData[1] = vec4(shadowed, penumbra, shadowed, shadowed); +} diff --git a/data/shaders/water.frag b/data/shaders/water.frag index 70899ea2e..3f36b8b72 100644 --- a/data/shaders/water.frag +++ b/data/shaders/water.frag @@ -16,11 +16,11 @@ void main() { // lookup normal from normal map, move from [0,1] to [-1, 1] range, normalize vec3 normal = 2.0 * texture2D (BumpTex1, gl_TexCoord[0].st + delta1).rgb - 1.0; - vec3 normal2 = 2.0 * texture2D (BumpTex2, gl_TexCoord[0].st + delta2).rgb - 1.0; + vec3 normal2 = 2.0 * texture2D (BumpTex2, gl_TexCoord[0].st + delta2).rgb - 1.0; - // scale normals - normal.y = 4.0*normal.y; - normal2.y = 4.0*normal2.y; + // scale normals + normal.y = 4.0*normal.y; + normal2.y = 4.0*normal2.y; normal = (normalize(normal) + normalize(normal2))/2.0; @@ -29,31 +29,28 @@ void main() vec4 diffuseMaterial; vec4 diffuseLight; - diffuseMaterial = texture2D (DecalTex, gl_TexCoord[0].st + vec2(delta1.x, 0.0)); - diffuseLight = vec4(1.0, 1.0, 1.0, 1.0); + diffuseMaterial = texture2D (DecalTex, gl_TexCoord[0].st + vec2(delta1.x, 0.0)); + diffuseLight = vec4(1.0, 1.0, 1.0, 1.0); - //if (lamberFactor < 0.7) - //{ - // lamberFactor = 0.0; - //} + vec3 col = diffuseMaterial.xyz * (0.3 + lamberFactor*0.7); - gl_FragColor = diffuseMaterial * (0.3 + lamberFactor*0.7); + // specular (phong) + vec3 R = normalize(reflect(lightVec, normal)); + float specular = max(dot(R, eyeVec), 0.0); - // specular (phong) - vec3 R = normalize(reflect(lightVec, normal)); - float specular = max(dot(R,eyeVec),0.0); + // weak specular + specular = specular*specular; + specular = specular*specular; + float specular_weak = specular*0.05; + col += vec3(specular_weak, specular_weak, specular_weak); - if (specular > 0.0) - { - // weak specular - specular = specular*specular; - specular = specular*specular; - float specular_weak = specular*0.05; - gl_FragColor += vec4(specular_weak, specular_weak, specular_weak, 0.0); + // strong specular + specular = specular*specular; + float specular_strong = specular*0.3; + col += vec3(specular_strong, specular_strong, specular_strong); - // strong specular - specular = specular*specular; - float specular_strong = specular*0.3; - gl_FragColor += vec4(specular_strong, specular_strong, specular_strong, 0.0); - } + float summed = dot(vec3(1.0), col) / 3.0; + float alpha = 0.9 + 0.1 * smoothstep(0.0, 1.0, summed); + + gl_FragColor = vec4(col, alpha); } diff --git a/data/shaders/water.vert b/data/shaders/water.vert index 9b88f362d..5a58fda94 100644 --- a/data/shaders/water.vert +++ b/data/shaders/water.vert @@ -1,25 +1,30 @@ // Shader based on work by Fabien Sanglard // Released under the terms of CC-BY 3.0 +uniform float speed; +uniform float height; +uniform float length; + +uniform vec3 lightdir; + varying vec3 lightVec; varying vec3 halfVec; varying vec3 eyeVec; -uniform vec3 lightdir; - void main() { + vec4 pos = gl_Vertex; - gl_TexCoord[0] = gl_MultiTexCoord0; + pos.y += (sin(pos.x/length + speed) + cos(pos.z/length + speed)) * height; + + vec3 vertexPosition = vec3(gl_ModelViewMatrix * pos); // Building the matrix Eye Space -> Tangent Space vec3 n = normalize (gl_NormalMatrix * gl_Normal); - // gl_MultiTexCoord1.xyz + // gl_MultiTexCoord1.xyz vec3 t = normalize (gl_NormalMatrix * vec3(1.0, 0.0, 0.0)); // tangent vec3 b = cross (n, t); - vec3 vertexPosition = vec3(gl_ModelViewMatrix * gl_Vertex); - // transform light and half angle vectors by tangent basis vec3 v; v.x = dot (lightdir, t); @@ -29,7 +34,7 @@ void main() vertexPosition = normalize(vertexPosition); - eyeVec = normalize(-vertexPosition); // we are in Eye Coordinates, so EyePos is (0,0,0) + eyeVec = normalize(-vertexPosition); // we are in Eye Coordinates, so EyePos is (0,0,0) // Normalize the halfVector to pass it to the fragment shader @@ -44,5 +49,7 @@ void main() //normalize (v); halfVec = v ; - gl_Position = ftransform(); -} \ No newline at end of file + gl_Position = gl_ModelViewProjectionMatrix * pos; + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_TexCoord[1] = gl_MultiTexCoord1; +} diff --git a/data/shaders/water_fog.frag b/data/shaders/water_fog.frag deleted file mode 100644 index 63a8f5a70..000000000 --- a/data/shaders/water_fog.frag +++ /dev/null @@ -1,75 +0,0 @@ -// Shader based on work by Fabien Sanglard -// Released under the terms of CC-BY 3.0 - -uniform sampler2D BumpTex1; // Normal map 1 -uniform sampler2D BumpTex2; // Normal map 2 -uniform sampler2D DecalTex; //The texture - -uniform vec2 delta1; -uniform vec2 delta2; - -varying vec3 lightVec; -varying vec3 halfVec; -varying vec3 eyeVec; -varying vec4 coord; - -uniform vec4 fogColor; -uniform float fogFrom; -uniform float fogTo; - -void main() -{ - // lookup normal from normal map, move from [0,1] to [-1, 1] range, normalize - vec3 normal = 2.0 * texture2D (BumpTex1, gl_TexCoord[0].st + delta1).rgb - 1.0; - vec3 normal2 = 2.0 * texture2D (BumpTex2, gl_TexCoord[0].st + delta2).rgb - 1.0; - - // scale normals - normal.y = 4.0*normal.y; - normal2.y = 4.0*normal2.y; - - normal = (normalize(normal) + normalize(normal2))/2.0; - - // compute diffuse lighting - float lamberFactor = max (dot (lightVec, normal), 0.0); - vec4 diffuseMaterial; - vec4 diffuseLight; - - diffuseMaterial = texture2D (DecalTex, gl_TexCoord[0].st + vec2(delta1.x, 0.0)); - diffuseLight = vec4(1.0, 1.0, 1.0, 1.0); - - //if (lamberFactor < 0.7) - //{ - // lamberFactor = 0.0; - //} - - gl_FragColor = diffuseMaterial * (0.3 + lamberFactor*0.7); - - // specular (phong) - vec3 R = normalize(reflect(lightVec, normal)); - float specular = max(dot(R,eyeVec),0.0); - - if (specular > 0.0) - { - // weak specular - specular = specular*specular; - specular = specular*specular; - float specular_weak = specular*0.05; - gl_FragColor += vec4(specular_weak, specular_weak, specular_weak, 0.0); - - // strong specular - specular = specular*specular; - float specular_strong = specular*0.3; - gl_FragColor += vec4(specular_strong, specular_strong, specular_strong, 0.0); - } - - if (coord.z > fogTo) - { - gl_FragColor = fogColor; - } - else if (coord.z > fogFrom) - { - float fogIntensity = (coord.z - fogFrom) / (fogTo - fogFrom); - gl_FragColor = fogIntensity*fogColor + (1.0 - fogIntensity)*gl_FragColor; - } -} - diff --git a/data/shaders/water_fog.vert b/data/shaders/water_fog.vert deleted file mode 100644 index 4419ca428..000000000 --- a/data/shaders/water_fog.vert +++ /dev/null @@ -1,49 +0,0 @@ -// Shader based on work by Fabien Sanglard -// Released under the terms of CC-BY 3.0 - -varying vec3 lightVec; -varying vec3 halfVec; -varying vec3 eyeVec; -varying vec4 coord; -uniform vec3 lightdir; - -void main() -{ - - gl_TexCoord[0] = gl_MultiTexCoord0; - - // Building the matrix Eye Space -> Tangent Space - vec3 n = normalize (gl_NormalMatrix * gl_Normal); - // gl_MultiTexCoord1.xyz - vec3 t = normalize (gl_NormalMatrix * vec3(1.0, 0.0, 0.0)); // tangent - vec3 b = cross (n, t); - - vec3 vertexPosition = vec3(gl_ModelViewMatrix * gl_Vertex); - - // transform light and half angle vectors by tangent basis - vec3 v; - v.x = dot (lightdir, t); - v.y = dot (lightdir, b); - v.z = dot (lightdir, n); - lightVec = normalize (v); - - vertexPosition = normalize(vertexPosition); - - eyeVec = normalize(-vertexPosition); // we are in Eye Coordinates, so EyePos is (0,0,0) - - // Normalize the halfVector to pass it to the fragment shader - - // No need to divide by two, the result is normalized anyway. - // vec3 halfVector = normalize((vertexPosition + lightDir) / 2.0); - vec3 halfVector = normalize(vertexPosition + lightdir); - v.x = dot (halfVector, t); - v.y = dot (halfVector, b); - v.z = dot (halfVector, n); - - // No need to normalize, t,b,n and halfVector are normal vectors. - //normalize (v); - halfVec = v ; - - gl_Position = ftransform(); - coord = gl_Position; -} diff --git a/data/shaders/white.frag b/data/shaders/white.frag new file mode 100644 index 000000000..1cc07d303 --- /dev/null +++ b/data/shaders/white.frag @@ -0,0 +1,4 @@ +void main() +{ + gl_FragColor = vec4(1.0); +} diff --git a/data/stk_config.xml b/data/stk_config.xml index 18d1cb51c..5bed7c8eb 100644 --- a/data/stk_config.xml +++ b/data/stk_config.xml @@ -135,6 +135,9 @@ + + + diff --git a/doc/graphics_views.txt b/doc/graphics_views.txt new file mode 100644 index 000000000..d96268a6e --- /dev/null +++ b/doc/graphics_views.txt @@ -0,0 +1,21 @@ +Graphics debug views +==================== + +There are some graphics debug views, to be used both by artists when developing, +and by users when reporting bugs. + +These should be available in normal (non-artist) mode +to enable easier bug reports (go there, press this key, screenshot) +without requiring editing the UTF-32 config files (which is inconvenient, most editors +do not support that weird encoding). + +They don't give gameplay advantages like the item keys, so there should be little harm +leaving them available. Accidental keypresses ought to be minimized by their location. + +Home - wireframe view +End - mipmap visualization (is a texture too high or low resolution, red = too high) +Del - normal view +PgDown - SSAO view +PgUp - light view +Ins - shadow view +ScrlLock - displacement view diff --git a/doc/graphics_xml.txt b/doc/graphics_xml.txt new file mode 100644 index 000000000..ba9062611 --- /dev/null +++ b/doc/graphics_xml.txt @@ -0,0 +1,157 @@ +Graphics GSOC XML additions +=========================== + +This document will list in depth all new XML values added during the graphics GSOC, for use +by the blender exporter scripts. + +Glow colors +----------- + +The glow colors for nitro bottles and boxes are settable in stk_config.xml. +They are three-value RGB colors. + +Example: + + +Particle wind +------------- + +Particles may be affected by wind. This is used with the snow particles. + +Flips is a boolean controlling whether each particle will spin about its own two axis. +It is only applicable to symmetric textures, such as snow flakes. + +Speed is a float in no real units. + +Example: + + +Forced bloom objects +-------------------- + +Any object may be marked as forced bloom. This will include the object in the bloom pass +regardless of its brightness. + +Example: forcedbloom="Y" inside the tag, in scene.xml. + +Forced bloom objects may optionally include a multiplier, ranging from 0.5 to 10: +bloompower="2.5" + + +Glowing objects +--------------- + +Any object may be marked as glowing. The glow is a RGB value. + +Example: glow="0 255 0" inside the tag, in scene.xml. + + +Caustics +-------- + +A material effect, ie GE_CAUSTICS, to mark a texture as receiving caustics. To be used +for the ocean floor, puddle floor, etc. + +Example: graphics-effect="caustics" in materials.xml. + + +Refractions/reflections +----------------------- + +A per-object boolean flag, typically set for subsea windows or the ocean surface. + +Example: displacing="Y" in the tag in scene.xml. + + + + Per-track tags + -------------- + +These are all specified inside track.xml, among the track's author, music, etc. + +Cloud shadows +------------- + +Boolean whether to have the sun blocked by cloud shadows, moving with the wind. +Disabled by default. + +The effect is completely fake, doesn't correspond to any real or simulated clouds, but +nobody's checking, so it looks nice. + +Example: clouds="Y" inside the tag. + + +Bloom +----- + +Boolean whether to enable global bloom for this track. Enabled by default. + +Example: bloom="Y" inside the tag. + + +Bloom threshold +--------------- + +Float that specifies the minimum luminance needed for a pixel to participate in the bloom. +The default is 0.75. + +Example: bloom-threshold="0.75" + + +Lens flare +---------- + +Boolean that specifies whether a lens flare is shown when the player looks at the sun. +The strength and angle depend on the relation of the camera to the sun. Disabled by default. + +Example: lens-flare="Y" + + +Dynamic shadows +--------------- + +Boolean on whether this track should use dynamic shadows. Enabled by default. + +If disabled, the fallback fake kart shadows will be used. + +Example: shadows="N" + + +God rays +-------- + +Boolean on rendering god rays when the player looks at the sun. Disabled by default. + +Example: god-rays="Y" + + +Displacement speed +------------------ + +Float on the relative speed of any displacing objects in this level. Default 1.0. +2.0 = double speed, 0.5 = half speed, etc. + +Example: displacement-speed="1.5" + + +Caustics speed +-------------- + +Float on the relative speed of any caustics objects in this level. Default 1.0. +2.0 = double speed, 0.5 = half speed, etc. + +Example: caustics-speed="1.5" + + +Fog +--- + +The density tag was removed (it was unused anyway). +The max, height start, and height end tags were added. + +fog-max: float capping the amount of fog to apply, default 1.0. +fog-start-height: minimum height of fog, default 0.0. +fog-end-height: maximum height of fog, default 100.0. + +The fog scales smoothly according to both the height, and the distance from +camera. diff --git a/doc/pipeline_overview.txt b/doc/pipeline_overview.txt new file mode 100644 index 000000000..2a50dd298 --- /dev/null +++ b/doc/pipeline_overview.txt @@ -0,0 +1,44 @@ +New pipeline overview +===================== + +Organized by RTTs +----------------- + +To color: + - initialize the glow representations (no rendering yet) + - render the 3d skybox, if any (includes the usual skybox) + +To the MRT (color, normals, depth): + - solids + +To quarter1: + - glowmap + +To various shadow maps: + - shadows + +To tmp1: + - lights + +To color: + - lights from tmp1 are multiplicatively blended + - skybox if there is no 3d skybox + - lens flare/god rays occlusion query (no rendering) + - solid post-processing + - transparents + +To displace: + - displacing nodes, if any + +To the screen: + - the post-processing chain + - GUI + + +Organized by passes +------------------- + +Init: glow init -> 3d skybox +Main: solids -> glows -> shadows -> lights -> occlusion query -> solid PP +Transparent: transparents -> displacing -> PP +GUI: gui diff --git a/sources.cmake b/sources.cmake index eff433e36..dcc1e6c8b 100644 --- a/sources.cmake +++ b/sources.cmake @@ -25,12 +25,16 @@ src/config/player.cpp src/config/saved_grand_prix.cpp src/config/stk_config.cpp src/config/user_config.cpp +src/graphics/callbacks.cpp src/graphics/camera.cpp src/graphics/CBatchingMesh.cpp src/graphics/explosion.cpp +src/graphics/glow.cpp src/graphics/hardware_skinning.cpp src/graphics/hit_sfx.cpp src/graphics/irr_driver.cpp +src/graphics/lens_flare.cpp +src/graphics/light.cpp src/graphics/lod_node.cpp src/graphics/material.cpp src/graphics/material_manager.cpp @@ -43,11 +47,19 @@ src/graphics/per_camera_node.cpp src/graphics/post_processing.cpp src/graphics/rain.cpp src/graphics/referee.cpp +src/graphics/render.cpp +src/graphics/rtts.cpp +src/graphics/screenquad.cpp +src/graphics/shaders.cpp src/graphics/shadow.cpp +src/graphics/shadow_importance.cpp src/graphics/show_curve.cpp src/graphics/skid_marks.cpp src/graphics/slip_stream.cpp src/graphics/stars.cpp +src/graphics/sun.cpp +src/graphics/water.cpp +src/graphics/wind.cpp src/guiengine/abstract_state_manager.cpp src/guiengine/abstract_top_level_container.cpp src/guiengine/CGUISpriteBank.cpp @@ -234,6 +246,7 @@ src/tracks/track_object_presentation.cpp src/tracks/track_sector.cpp src/utils/constants.cpp src/utils/crash_reporting.cpp +src/utils/helpers.cpp src/utils/leak_check.cpp src/utils/log.cpp src/utils/profiler.cpp @@ -274,17 +287,24 @@ src/config/player.hpp src/config/saved_grand_prix.hpp src/config/stk_config.hpp src/config/user_config.hpp +src/graphics/callbacks.hpp src/graphics/camera.hpp src/graphics/CBatchingMesh.hpp src/graphics/explosion.hpp +src/graphics/glow.hpp +src/graphics/glwrap.hpp src/graphics/hardware_skinning.hpp src/graphics/hit_effect.hpp src/graphics/hit_sfx.hpp src/graphics/irr_driver.hpp +src/graphics/large_mesh_buffer.hpp +src/graphics/lens_flare.hpp +src/graphics/light.hpp src/graphics/lod_node.hpp src/graphics/material.hpp src/graphics/material_manager.hpp src/graphics/mesh_tools.hpp +src/graphics/mlaa_areamap.hpp src/graphics/moving_texture.hpp src/graphics/particle_emitter.hpp src/graphics/particle_kind.hpp @@ -293,11 +313,18 @@ src/graphics/per_camera_node.hpp src/graphics/post_processing.hpp src/graphics/rain.hpp src/graphics/referee.hpp +src/graphics/rtts.hpp +src/graphics/screenquad.hpp +src/graphics/shaders.hpp src/graphics/shadow.hpp +src/graphics/shadow_importance.hpp src/graphics/show_curve.hpp src/graphics/skid_marks.hpp src/graphics/slip_stream.hpp src/graphics/stars.hpp +src/graphics/sun.hpp +src/graphics/water.hpp +src/graphics/wind.hpp src/guiengine/abstract_state_manager.hpp src/guiengine/abstract_top_level_container.hpp src/guiengine/engine.hpp @@ -499,6 +526,7 @@ src/tracks/track_object_presentation.hpp src/tracks/track_sector.hpp src/utils/aligned_array.hpp src/utils/constants.hpp +src/utils/helpers.hpp src/utils/interpolation_array.hpp src/utils/crash_reporting.hpp src/utils/leak_check.hpp @@ -512,4 +540,5 @@ src/utils/synchronised.hpp src/utils/time.hpp src/utils/translation.hpp src/utils/vec3.hpp +src/utils/vs.hpp ) diff --git a/src/Makefile.am b/src/Makefile.am index 16177fb07..40d714555 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,10 +69,14 @@ supertuxkart_SOURCES = \ config/device_config.hpp \ graphics/CBatchingMesh.cpp \ graphics/CBatchingMesh.hpp \ + graphics/callbacks.cpp \ + graphics/callbacks.hpp \ graphics/camera.cpp \ graphics/camera.hpp \ graphics/explosion.cpp \ graphics/explosion.hpp \ + graphics/glow.cpp \ + graphics/glow.hpp \ graphics/hardware_skinning.cpp \ graphics/hardware_skinning.hpp \ graphics/hit_effect.hpp \ @@ -80,6 +84,11 @@ supertuxkart_SOURCES = \ graphics/hit_sfx.hpp \ graphics/irr_driver.cpp \ graphics/irr_driver.hpp \ + graphics/large_mesh_buffer.hpp \ + graphics/lens_flare.cpp \ + graphics/lens_flare.hpp \ + graphics/light.hpp \ + graphics/light.cpp \ graphics/lod_node.cpp \ graphics/lod_node.hpp \ graphics/material.cpp \ @@ -102,10 +111,19 @@ supertuxkart_SOURCES = \ graphics/post_processing.hpp \ graphics/rain.cpp \ graphics/rain.hpp \ + graphics/render.cpp \ graphics/referee.cpp \ graphics/referee.hpp \ + graphics/rtts.hpp \ + graphics/rtts.cpp \ + graphics/screenquad.cpp \ + graphics/screenquad.hpp \ + graphics/shaders.cpp \ + graphics/shaders.hpp \ graphics/shadow.cpp \ graphics/shadow.hpp \ + graphics/shadow_importance.cpp \ + graphics/shadow_importance.hpp \ graphics/show_curve.cpp \ graphics/show_curve.hpp \ graphics/skid_marks.cpp \ @@ -114,6 +132,12 @@ supertuxkart_SOURCES = \ graphics/slip_stream.hpp \ graphics/stars.cpp \ graphics/stars.hpp \ + graphics/sun.hpp \ + graphics/sun.cpp \ + graphics/water.hpp \ + graphics/water.cpp \ + graphics/wind.hpp \ + graphics/wind.cpp \ guiengine/CGUISpriteBank.cpp \ guiengine/CGUISpriteBank.h \ guiengine/abstract_state_manager.cpp \ @@ -496,6 +520,8 @@ supertuxkart_SOURCES = \ utils/aligned_array.hpp \ utils/constants.hpp \ utils/constants.cpp \ + utils/helpers.hpp \ + utils/helpers.cpp \ utils/leak_check.cpp \ utils/leak_check.hpp \ utils/log.cpp \ @@ -518,7 +544,8 @@ supertuxkart_SOURCES = \ utils/utf8/core.h \ utils/utf8/unchecked.h \ utils/vec3.cpp \ - utils/vec3.hpp + utils/vec3.hpp \ + utils/vs.hpp # Link in the specific gcc 4.1 bug work around supertuxkart_LDADD = \ diff --git a/src/addons/network_http.cpp b/src/addons/network_http.cpp index d5b36d7f1..569e2e6dc 100644 --- a/src/addons/network_http.cpp +++ b/src/addons/network_http.cpp @@ -24,14 +24,7 @@ #include #include #include - -#if defined(WIN32) && !defined(__CYGWIN__) -# include -# define isnan _isnan -#else -# include -# include -#endif +#include #include "addons/news_manager.hpp" #include "addons/request.hpp" @@ -42,6 +35,7 @@ #include "utils/string_utils.hpp" #include "utils/time.hpp" #include "utils/translation.hpp" +#include "utils/vs.hpp" // ---------------------------------------------------------------------------- /** Create a thread that handles all network functions independent of the diff --git a/src/animations/animation_base.cpp b/src/animations/animation_base.cpp index 4d5fbbbc5..cd51bf3c1 100644 --- a/src/animations/animation_base.cpp +++ b/src/animations/animation_base.cpp @@ -21,14 +21,10 @@ #include "animations/ipo.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" +#include "utils/vs.hpp" #include - -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include AnimationBase::AnimationBase(const XMLNode &node) diff --git a/src/animations/ipo.cpp b/src/animations/ipo.cpp index 3094329ce..d5c4db071 100644 --- a/src/animations/ipo.cpp +++ b/src/animations/ipo.cpp @@ -19,15 +19,11 @@ #include "animations/ipo.hpp" #include "io/xml_node.hpp" +#include "utils/vs.hpp" #include #include - -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include const std::string Ipo::m_all_channel_names[IPO_MAX] = {"LocX", "LocY", "LocZ", "LocXYZ", diff --git a/src/config/stk_config.cpp b/src/config/stk_config.cpp index c60e0daf5..d65cab865 100644 --- a/src/config/stk_config.cpp +++ b/src/config/stk_config.cpp @@ -181,6 +181,13 @@ void STKConfig::init_defaults() m_disable_steer_while_unskid = false; m_camera_follow_skid = false; + m_nitro_glow_color[0] = 1.0f; + m_nitro_glow_color[1] = 1.0f; + m_nitro_glow_color[2] = 1.0f; + m_box_glow_color[0] = 1.0f; + m_box_glow_color[1] = 1.0f; + m_box_glow_color[2] = 1.0f; + m_score_increase.clear(); m_leader_intervals.clear(); m_switch_items.clear(); @@ -362,6 +369,25 @@ void STKConfig::getAllData(const XMLNode * root) replay_node->get("delta-pos", &m_replay_delta_pos2 ); replay_node->get("delta-t", &m_replay_dt ); } + + if(const XMLNode *colors = root->getNode("glow-colors")) + { + video::SColor tmpcol; + if (colors->get("nitro", &tmpcol)) + { + m_nitro_glow_color[0] = tmpcol.getRed() / 255.0f; + m_nitro_glow_color[1] = tmpcol.getGreen() / 255.0f; + m_nitro_glow_color[2] = tmpcol.getBlue() / 255.0f; + } + + if (colors->get("box", &tmpcol)) + { + m_box_glow_color[0] = tmpcol.getRed() / 255.0f; + m_box_glow_color[1] = tmpcol.getGreen() / 255.0f; + m_box_glow_color[2] = tmpcol.getBlue() / 255.0f; + } + } + // Get the default KartProperties // ------------------------------ const XMLNode *node = root -> getNode("general-kart-defaults"); diff --git a/src/config/stk_config.hpp b/src/config/stk_config.hpp index 2fcdbeed5..1d394f571 100644 --- a/src/config/stk_config.hpp +++ b/src/config/stk_config.hpp @@ -148,6 +148,11 @@ public: * be generated. */ float m_replay_delta_angle; + /** Colors for glows */ + float m_nitro_glow_color[3]; + + float m_box_glow_color[3]; + private: /** True if stk_config has been loaded. This is necessary if the * --stk-config command line parameter has been specified to avoid diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp index 3f1d1ddfe..055b1f6b8 100644 --- a/src/config/user_config.hpp +++ b/src/config/user_config.hpp @@ -510,10 +510,6 @@ namespace UserConfigParams #define FBO_DEFAULT true #endif - PARAM_PREFIX BoolUserConfigParam m_fbo - PARAM_DEFAULT( BoolUserConfigParam(FBO_DEFAULT, "fbo", - &m_graphics_quality, "Use frame buffer objects (FBOs)") ); - PARAM_PREFIX BoolUserConfigParam m_graphical_effects PARAM_DEFAULT( BoolUserConfigParam(true, "anim_gfx", &m_graphics_quality, "Scenery animations") ); @@ -552,11 +548,22 @@ namespace UserConfigParams PARAM_DEFAULT( BoolUserConfigParam(true, "pixel_shaders", &m_graphics_quality, "Whether to enable pixel shaders (splatting, normal maps, ...)") ); - PARAM_PREFIX BoolUserConfigParam m_postprocess_enabled + PARAM_PREFIX BoolUserConfigParam m_motionblur PARAM_DEFAULT( BoolUserConfigParam(false, - "postprocess_enabled", &m_graphics_quality, - "Whether post-processing (motion blur...) should " - "be enabled") ); + "motionblur_enabled", &m_graphics_quality, + "Whether motion blur should be enabled") ); + PARAM_PREFIX BoolUserConfigParam m_mlaa + PARAM_DEFAULT( BoolUserConfigParam(false, + "mlaa", &m_graphics_quality, + "Whether MLAA should be enabled") ); + PARAM_PREFIX IntUserConfigParam m_ssao + PARAM_DEFAULT( IntUserConfigParam(0, + "ssao", &m_graphics_quality, + "Whether SSAO is enabled (0 = disabled, 1 = low, 2 = high") ); + PARAM_PREFIX IntUserConfigParam m_shadows + PARAM_DEFAULT( IntUserConfigParam(0, + "shadows", &m_graphics_quality, + "Whether shadows are enabled (0 = disabled, 1 = low, 2 = high") ); // ---- Misc PARAM_PREFIX BoolUserConfigParam m_cache_overworld diff --git a/src/graphics/callbacks.cpp b/src/graphics/callbacks.cpp new file mode 100644 index 000000000..cab38eb3c --- /dev/null +++ b/src/graphics/callbacks.cpp @@ -0,0 +1,775 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 "graphics/callbacks.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/wind.hpp" +#include "guiengine/engine.hpp" +#include "modes/world.hpp" +#include "tracks/track.hpp" +#include "utils/helpers.hpp" + +using namespace video; +using namespace core; + +//------------------------------------- + +void NormalMapProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + s32 decaltex = 0; + srv->setPixelShaderConstant("DecalTex", &decaltex, 1); + + s32 bumptex = 1; + srv->setPixelShaderConstant("BumpTex", &bumptex, 1); + + s32 lightmapTex = (m_with_lightmap ? 2 : 0); + srv->setPixelShaderConstant("LightMapTex", &lightmapTex, 1); + + s32 hasLightMap = (m_with_lightmap ? 1 : 0); + srv->setPixelShaderConstant("HasLightMap", &hasLightMap, 1); + + // We could calculate light direction as coming from the sun (then we'd need to + // transform it into camera space). But I find that pretending light + // comes from the camera gives good results + const float lightdir[] = {0.1852f, -0.1852f, -0.9259f}; + srv->setVertexShaderConstant("lightdir", lightdir, 3); + + + firstdone = true; + } +} + +//------------------------------------- + +void WaterShaderProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + float strength = time; + strength = 1.4f - fabsf(noise2d(strength / 30.0f + 133)) * 0.8f; + + m_dx_1 += GUIEngine::getLatestDt() * m_water_shader_speed_1 * strength; + m_dy_1 += GUIEngine::getLatestDt() * m_water_shader_speed_1 * strength; + + m_dx_2 += GUIEngine::getLatestDt() * m_water_shader_speed_2 * strength; + m_dy_2 -= GUIEngine::getLatestDt() * m_water_shader_speed_2 * strength; + + if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f; + if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f; + if (m_dx_2 > 1.0f) m_dx_2 -= 1.0f; + if (m_dy_2 < 0.0f) m_dy_2 += 1.0f; + + const float d1[2] = { m_dx_1, m_dy_1 }; + const float d2[2] = { m_dx_2, m_dy_2 }; + + srv->setVertexShaderConstant("delta1", d1, 2); + srv->setVertexShaderConstant("delta2", d2, 2); + + const float speed = irr_driver->getDevice()->getTimer()->getTime() / m_speed; + const float height = m_height * strength; + + srv->setVertexShaderConstant("height", &height, 1); + srv->setVertexShaderConstant("speed", &speed, 1); + srv->setVertexShaderConstant("length", &m_length, 1); + + // Can't use the firstdone optimization, as the callback is shared + //if (!firstdone) + { + s32 decaltex = 0; + srv->setPixelShaderConstant("DecalTex", &decaltex, 1); + + s32 bumptex = 1; + srv->setPixelShaderConstant("BumpTex1", &bumptex, 1); + + bumptex = 2; + srv->setPixelShaderConstant("BumpTex2", &bumptex, 1); + + // Calculate light direction as coming from the sun. + matrix4 normalm = srv->getVideoDriver()->getTransform(ETS_VIEW); + normalm.makeInverse(); + normalm = normalm.getTransposed(); + vector3df tmp = m_sunpos; + normalm.transformVect(tmp); + tmp.normalize(); + + const float lightdir[] = {tmp.X, tmp.Y, tmp.Z}; + srv->setVertexShaderConstant("lightdir", lightdir, 3); + + firstdone = true; + } +} + +//------------------------------------- + +void GrassShaderProvider::OnSetConstants(IMaterialRendererServices *srv, int userData) +{ + IVideoDriver * const drv = srv->getVideoDriver(); + const core::vector3df pos = drv->getTransform(ETS_WORLD).getTranslation(); + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + float strength = (pos.X + pos.Y + pos.Z) * 1.2f + time * m_speed; + strength = noise2d(strength / 10.0f) * m_amplitude * 5; + // * 5 is to work with the existing amplitude values. + + // Pre-multiply on the cpu + vector3df wind = irr_driver->getWind() * strength; + + srv->setVertexShaderConstant("windDir", &wind.X, 3); + + if (!firstdone) + { + s32 tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void SplattingProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float camfar = irr_driver->getSceneManager()->getActiveCamera()->getFarValue(); + srv->setVertexShaderConstant("far", &camfar, 1); + + // The normal is transformed by the inverse transposed world matrix + // because we want world-space normals + matrix4 invtworldm = irr_driver->getVideoDriver()->getTransform(ETS_WORLD); + invtworldm.makeInverse(); + invtworldm = invtworldm.getTransposed(); + + srv->setVertexShaderConstant("invtworldm", invtworldm.pointer(), 16); + + float objectid = 0; + const stringc name = mat.TextureLayer[0].Texture->getName().getPath(); + objectid = shash8((const u8 *) name.c_str(), name.size()) / 255.0f; + srv->setVertexShaderConstant("objectid", &objectid, 1); + + if (!firstdone) + { + s32 tex_layout = 1; + srv->setPixelShaderConstant("tex_layout", &tex_layout, 1); + + s32 tex_detail0 = 2; + srv->setPixelShaderConstant("tex_detail0", &tex_detail0, 1); + + s32 tex_detail1 = 3; + srv->setPixelShaderConstant("tex_detail1", &tex_detail1, 1); + + s32 tex_detail2 = 4; + srv->setPixelShaderConstant("tex_detail2", &tex_detail2, 1); + + s32 tex_detail3 = 5; + srv->setPixelShaderConstant("tex_detail3", &tex_detail3, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void BubbleEffectProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float start = fabsf(mat.MaterialTypeParam2); + const bool visible = mat.MaterialTypeParam2 > 0; + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + float transparency; + + const float diff = (time - start) / 3.0f; + + if (visible) + { + transparency = diff; + } + else + { + transparency = 1.0 - diff; + } + + transparency = clampf(transparency, 0, 1); + + srv->setVertexShaderConstant("time", &time, 1); + srv->setVertexShaderConstant("transparency", &transparency, 1); +} + +//------------------------------------- + +void RainEffectProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float screenw = UserConfigParams::m_width; + const float time = irr_driver->getDevice()->getTimer()->getTime() / 90.0f; + const matrix4 viewm = srv->getVideoDriver()->getTransform(ETS_VIEW); + const vector3df campos = irr_driver->getSceneManager()->getActiveCamera()->getPosition(); + + srv->setVertexShaderConstant("screenw", &screenw, 1); + srv->setVertexShaderConstant("time", &time, 1); + srv->setVertexShaderConstant("viewm", viewm.pointer(), 16); + srv->setVertexShaderConstant("campos", &campos.X, 3); +} + +//------------------------------------- + +void SnowEffectProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + srv->setVertexShaderConstant("time", &time, 1); +} + +//------------------------------------- + +void MotionBlurProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + // We need the maximum texture coordinates: + float max_tex_height = m_maxheight[m_current_camera]; + srv->setPixelShaderConstant("max_tex_height", &max_tex_height, 1); + + // Scale the boost time to get a usable boost amount: + float boost_amount = m_boost_time[m_current_camera] * 0.7f; + + // Especially for single screen the top of the screen is less blurred + // in the fragment shader by multiplying the blurr factor by + // (max_tex_height - texcoords.t), where max_tex_height is the maximum + // texture coordinate (1.0 or 0.5). In split screen this factor is too + // small (half the value compared with non-split screen), so we + // multiply this by 2. + if(Camera::getNumCameras() > 1) + boost_amount *= 2.0f; + + srv->setPixelShaderConstant("boost_amount", &boost_amount, 1); + srv->setPixelShaderConstant("center", + &(m_center[m_current_camera].X), 2); + srv->setPixelShaderConstant("direction", + &(m_direction[m_current_camera].X), 2); + + // Use a radius of 0.15 when showing a single kart, otherwise (2-4 karts + // on splitscreen) use only 0.75. + float radius = Camera::getNumCameras()==1 ? 0.15f : 0.075f; + srv->setPixelShaderConstant("mask_radius", &radius, 1); + + const int texunit = 0; + srv->setPixelShaderConstant("color_buffer", &texunit, 1); +} + +//------------------------------------- + +void GaussianBlurProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("pixel", m_pixel, 2); +} + +//------------------------------------- + +void MipVizProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const ITexture * const tex = mat.TextureLayer[0].Texture; + + const int notex = (mat.TextureLayer[0].Texture == NULL); + srv->setVertexShaderConstant("notex", ¬ex, 1); + if (!tex) return; + + const dimension2du size = tex->getSize(); + + const float texsize[2] = { + size.Width, + size.Height + }; + + srv->setVertexShaderConstant("texsize", texsize, 2); +} + +//------------------------------------- + +void ColorizeProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("col", m_color, 3); +} + +//------------------------------------- + +void GlowProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("res", m_res, 2); +} + +//------------------------------------- + +void ObjectPassProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float camfar = irr_driver->getSceneManager()->getActiveCamera()->getFarValue(); + srv->setVertexShaderConstant("far", &camfar, 1); + + // The normal is transformed by the inverse transposed world matrix + // because we want world-space normals + matrix4 invtworldm = irr_driver->getVideoDriver()->getTransform(ETS_WORLD); + invtworldm.makeInverse(); + invtworldm = invtworldm.getTransposed(); + + srv->setVertexShaderConstant("invtworldm", invtworldm.pointer(), 16); + + const int hastex = mat.TextureLayer[0].Texture != NULL; + srv->setVertexShaderConstant("hastex", &hastex, 1); + + const int haslightmap = mat.TextureLayer[1].Texture != NULL; + srv->setVertexShaderConstant("haslightmap", &haslightmap, 1); + + float objectid = 0; + if (hastex) + { + const stringc name = mat.TextureLayer[0].Texture->getName().getPath(); + objectid = shash8((const u8 *) name.c_str(), name.size()) / 255.0f; + } + srv->setVertexShaderConstant("objectid", &objectid, 1); + + //if (!firstdone) + // Can't use the firstdone optimization, as this callback is used for multiple shaders + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("lighttex", &tex, 1); + } +} + +//------------------------------------- + +void LightBlendProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const SColorf s = irr_driver->getSceneManager()->getAmbientLight(); + + float ambient[3] = { s.r, s.g, s.b }; + srv->setVertexShaderConstant("ambient", ambient, 3); + + int spectex = 1; + srv->setVertexShaderConstant("spectex", &spectex, 1); +} + +//------------------------------------- + +void PointLightProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("screen", m_screen, 2); + srv->setVertexShaderConstant("spec", &m_specular, 1); + srv->setVertexShaderConstant("col", m_color, 3); + srv->setVertexShaderConstant("campos", m_campos, 3); + srv->setVertexShaderConstant("center", m_pos, 3); + srv->setVertexShaderConstant("r", &m_radius, 1); + srv->setVertexShaderConstant("invprojview", m_invprojview.pointer(), 16); + + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("ntex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("dtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void SunLightProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const int hasclouds = World::getWorld()->getTrack()->hasClouds() && + UserConfigParams::m_weather_effects; + + srv->setVertexShaderConstant("screen", m_screen, 2); + srv->setVertexShaderConstant("col", m_color, 3); + srv->setVertexShaderConstant("center", m_pos, 3); + srv->setVertexShaderConstant("invprojview", m_invprojview.pointer(), 16); + srv->setVertexShaderConstant("hasclouds", &hasclouds, 1); + + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + + float strength = time; + strength = fabsf(noise2d(strength / 10.0f)) * 0.003f; + + const vector3df winddir = irr_driver->getWind() * strength; + m_wind[0] += winddir.X; + m_wind[1] += winddir.Z; + srv->setVertexShaderConstant("wind", m_wind, 2); + + if (UserConfigParams::m_shadows) + { + srv->setVertexShaderConstant("shadowmat", m_shadowmat.pointer(), 16); + } + + // Can't use the firstdone optimization, as this callback is used for multiple shaders + //if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("ntex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("dtex", &tex, 1); + + tex = 2; + srv->setVertexShaderConstant("cloudtex", &tex, 1); + + tex = 3; + srv->setVertexShaderConstant("shadowtex", &tex, 1); + + tex = 4; + srv->setVertexShaderConstant("warpx", &tex, 1); + + tex = 5; + srv->setVertexShaderConstant("warpy", &tex, 1); + + const float shadowoffset = 1.0f / irr_driver->getRTT(RTT_SHADOW)->getSize().Width; + srv->setVertexShaderConstant("shadowoffset", &shadowoffset, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void BloomProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("low", &m_threshold, 1); +} + +//------------------------------------- + +void MLAAColor1Provider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + const float pixels[2] = { + 1.0f / UserConfigParams::m_width, + 1.0f / UserConfigParams::m_height + }; + + srv->setPixelShaderConstant("PIXEL_SIZE", pixels, 2); + + firstdone = true; + } +} + +//------------------------------------- + +void MLAABlend2Provider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + const float pixels[2] = { + 1.0f / UserConfigParams::m_width, + 1.0f / UserConfigParams::m_height + }; + + srv->setPixelShaderConstant("PIXEL_SIZE", pixels, 2); + + + int tex = 0; + srv->setPixelShaderConstant("edgesMap", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("areaMap", &tex, 1); + + + firstdone = true; + } +} + +//------------------------------------- + +void MLAANeigh3Provider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + const float pixels[2] = { + 1.0f / UserConfigParams::m_width, + 1.0f / UserConfigParams::m_height + }; + + srv->setPixelShaderConstant("PIXEL_SIZE", pixels, 2); + + + int tex = 0; + srv->setPixelShaderConstant("blendMap", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("colorMap", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void SSAOProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + int tex = 0; + srv->setPixelShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("oldtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void GodRayProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setPixelShaderConstant("sunpos", m_sunpos, 2); +} + +//------------------------------------- + +void ShadowPassProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const int hastex = mat.TextureLayer[0].Texture != NULL; + srv->setVertexShaderConstant("hastex", &hastex, 1); + + int viz = irr_driver->getShadowViz(); + srv->setVertexShaderConstant("viz", &viz, 1); + + int wireframe = mat.Wireframe; + srv->setVertexShaderConstant("wireframe", &wireframe, 1); + + float objectid = 0; + if (hastex) + { + const stringc name = mat.TextureLayer[0].Texture->getName().getPath(); + objectid = shash8((const u8 *) name.c_str(), name.size()) / 255.0f; + } + srv->setVertexShaderConstant("objectid", &objectid, 1); + + //if (!firstdone) + // Can't use the firstdone optimization, as this callback is used for multiple shaders + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("warpx", &tex, 1); + tex = 2; + srv->setVertexShaderConstant("warpy", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void ShadowImportanceProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("shadowmat", m_shadowmat.pointer(), 16); + srv->setVertexShaderConstant("ipvmat", m_invprojview.pointer(), 16); + + srv->setVertexShaderConstant("campos", m_campos, 3); + + int low = UserConfigParams::m_shadows == 1; + srv->setVertexShaderConstant("low", &low, 1); + + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("ntex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("dtex", &tex, 1); + + tex = 2; + srv->setVertexShaderConstant("ctex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void CollapseProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("pixel", m_pixel, 2); + srv->setVertexShaderConstant("multi", m_multi, 2); + srv->setVertexShaderConstant("size", &m_size, 1); + + //if (!firstdone) + // Can't use the firstdone optimization, as this callback is used for multiple shaders + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("oldtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void BloomPowerProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + srv->setVertexShaderConstant("power", &m_power, 1); +} + +//------------------------------------- + +void MultiplyProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("tex1", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("tex2", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void ShadowGenProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("halft", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("quarter", &tex, 1); + + tex = 2; + srv->setVertexShaderConstant("eighth", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void CausticsProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + const float speed = World::getWorld()->getTrack()->getCausticsSpeed(); + + float strength = time; + strength = fabsf(noise2d(strength / 10.0f)) * 0.006f + 0.001f; + + vector3df wind = irr_driver->getWind() * strength * speed; + m_dir[0] += wind.X; + m_dir[1] += wind.Z; + + strength = time * 0.56f + sinf(time); + strength = fabsf(noise2d(0.0, strength / 6.0f)) * 0.0095f + 0.001f; + + wind = irr_driver->getWind() * strength * speed; + wind.rotateXZBy(cosf(time)); + m_dir2[0] += wind.X; + m_dir2[1] += wind.Z; + + srv->setVertexShaderConstant("dir", m_dir, 2); + srv->setVertexShaderConstant("dir2", m_dir2, 2); + + if (!firstdone) + { + int tex = 0; + srv->setVertexShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setVertexShaderConstant("caustictex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void DisplaceProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + const float speed = World::getWorld()->getTrack()->getDisplacementSpeed(); + + float strength = time; + strength = fabsf(noise2d(strength / 10.0f)) * 0.006f + 0.002f; + + vector3df wind = irr_driver->getWind() * strength * speed; + m_dir[0] += wind.X; + m_dir[1] += wind.Z; + + strength = time * 0.56f + sinf(time); + strength = fabsf(noise2d(0.0, strength / 6.0f)) * 0.0095f + 0.0025f; + + wind = irr_driver->getWind() * strength * speed; + wind.rotateXZBy(cosf(time)); + m_dir2[0] += wind.X; + m_dir2[1] += wind.Z; + + srv->setVertexShaderConstant("dir", m_dir, 2); + srv->setVertexShaderConstant("dir2", m_dir2, 2); + + srv->setVertexShaderConstant("screen", m_screen, 2); +} + +//------------------------------------- + +void PPDisplaceProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + int viz = irr_driver->getDistortViz(); + srv->setPixelShaderConstant("viz", &viz, 1); + + if (!firstdone) + { + int tex = 0; + srv->setPixelShaderConstant("tex", &tex, 1); + + tex = 1; + srv->setPixelShaderConstant("dtex", &tex, 1); + + firstdone = true; + } +} + +//------------------------------------- + +void FogProvider::OnSetConstants(IMaterialRendererServices *srv, int) +{ + const Track * const track = World::getWorld()->getTrack(); + + // This function is only called once per frame - thus no need for setters. + const float fogmax = track->getFogMax(); + const float startH = track->getFogStartHeight(); + const float endH = track->getFogEndHeight(); + const float start = track->getFogStart(); + const float end = track->getFogEnd(); + const SColor tmpcol = track->getFogColor(); + + const float col[3] = { tmpcol.getRed() / 255.0f, + tmpcol.getGreen() / 255.0f, + tmpcol.getBlue() / 255.0f }; + + srv->setPixelShaderConstant("fogmax", &fogmax, 1); + srv->setPixelShaderConstant("startH", &startH, 1); + srv->setPixelShaderConstant("endH", &endH, 1); + srv->setPixelShaderConstant("start", &start, 1); + srv->setPixelShaderConstant("end", &end, 1); + srv->setPixelShaderConstant("col", col, 3); + srv->setVertexShaderConstant("ipvmat", m_invprojview.pointer(), 16); + srv->setVertexShaderConstant("campos", m_campos, 3); +} diff --git a/src/graphics/callbacks.hpp b/src/graphics/callbacks.hpp new file mode 100644 index 000000000..6bdee62f2 --- /dev/null +++ b/src/graphics/callbacks.hpp @@ -0,0 +1,712 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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_CALLBACKS_HPP +#define HEADER_CALLBACKS_HPP + +#include "config/user_config.hpp" +#include "graphics/irr_driver.hpp" + +#include +#include +#include +#include +#include +#include +#include + +using namespace irr; + +class CallBase: public video::IShaderConstantSetCallBack +{ +public: + CallBase() + { + firstdone = 0; + } + + virtual void OnSetMaterial(const video::SMaterial &material) + { + mat = material; + } + +protected: + bool firstdone; + video::SMaterial mat; +}; + +// + +class NormalMapProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + NormalMapProvider(bool withLightmap) + { + m_with_lightmap = withLightmap; + } + +private: + bool m_with_lightmap; +}; + +// + +class WaterShaderProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setSpeed(const float s1, const float s2) + { + m_water_shader_speed_1 = s1; + m_water_shader_speed_2 = s2; + } + + WaterShaderProvider() + { + m_dx_1 = 0.0f; + m_dx_2 = 0.0f; + m_dy_1 = 0.0f; + m_dy_2 = 0.0f; + + m_water_shader_speed_1 = + m_water_shader_speed_2 = 0.0f; + } + + void setSunPosition(const core::vector3df &in) + { + m_sunpos = in; + m_sunpos.normalize(); + } + + void setSpeed(float speed) { m_speed = speed; } + void setHeight(float height) { m_height = height; } + void setLength(float length) { m_length = length; } + +private: + core::vector3df m_sunpos; + + float m_dx_1, m_dy_1, m_dx_2, m_dy_2; + float m_water_shader_speed_1; + float m_water_shader_speed_2; + + float m_speed; + float m_height; + float m_length; +}; + +// + +class GrassShaderProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + GrassShaderProvider() + { + m_amplitude = + m_speed = 0.0f; + } + + void setSpeed(float speed) + { + m_speed = speed; + } + + void setAmplitude(float amp) + { + m_amplitude = amp; + } + +private: + float m_amplitude, m_speed; +}; + +// + +class SplattingProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class BubbleEffectProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + BubbleEffectProvider() + { + } + + // We hijack the material type param 2 of bubbles. + // It's time to start the fade, negative if fade out, positive if in. + // It'd be unused otherwise. + + void onMadeVisible(scene::IMeshBuffer * const mb) + { + if (!contains(mb)) + return; + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f; + } + + void onHidden(scene::IMeshBuffer * const mb) + { + if (!contains(mb)) + return; + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = irr_driver->getDevice()->getTimer()->getTime() / -1000.0f; + } + + void isInitiallyHidden(scene::IMeshBuffer * const mb) + { + if (!contains(mb)) + return; + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = irr_driver->getDevice()->getTimer()->getTime() / -1000.0f; + } + + void removeBubble(const scene::IMeshBuffer * const mb) + { + m_bubbles.erase(mb); + } + + void addBubble(scene::IMeshBuffer * const mb) + { + m_bubbles.insert(mb); + + video::SMaterial &mat = mb->getMaterial(); + mat.MaterialTypeParam2 = 1; + } + + bool contains(const scene::IMeshBuffer * const mb) const + { + return m_bubbles.count(mb); + } + +private: + std::set m_bubbles; +}; + +// + +class RainEffectProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class SnowEffectProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class MotionBlurProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setMaxHeight(u32 who, float height) + { + assert(who < MAX_PLAYER_COUNT); + m_maxheight[who] = height; + } + + void setBoostTime(u32 who, float time) + { + assert(who < MAX_PLAYER_COUNT); + m_boost_time[who] = time; + } + + void setCenter(u32 who, float X, float Y) + { + assert(who < MAX_PLAYER_COUNT); + m_center[who].X = X; + m_center[who].Y = Y; + } + + void setDirection(u32 who, float X, float Y) + { + assert(who < MAX_PLAYER_COUNT); + m_direction[who].X = X; + m_direction[who].Y = Y; + } + + void setCurrentCamera(u32 who) + { + m_current_camera = who; + } + +private: + float m_maxheight[MAX_PLAYER_COUNT]; + u32 m_current_camera; + float m_boost_time[MAX_PLAYER_COUNT]; + core::vector2df m_center[MAX_PLAYER_COUNT]; + core::vector2df m_direction[MAX_PLAYER_COUNT]; +}; + +// + +class GaussianBlurProvider: public CallBase +{ +public: + GaussianBlurProvider() + { + m_pixel[0] = 1.0f / UserConfigParams::m_width; + m_pixel[1] = 1.0f / UserConfigParams::m_height; + } + + void setResolution(float x, float y) + { + m_pixel[0] = 1.0f / x; + m_pixel[1] = 1.0f / y; + } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + +private: + float m_pixel[2]; +}; + +// + +class MipVizProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class ColorizeProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setColor(float r, float g, float b) + { + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + } + +private: + float m_color[3]; +}; + +// + +class GlowProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setResolution(float x, float y) + { + m_res[0] = x; + m_res[1] = y; + } + +private: + float m_res[2]; +}; + +// + +class ObjectPassProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class LightBlendProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class PointLightProvider: public CallBase +{ +public: + PointLightProvider() + { + m_screen[0] = UserConfigParams::m_width; + m_screen[1] = UserConfigParams::m_height; + + m_specular = 200; + } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setColor(float r, float g, float b) + { + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + } + + void setPosition(float x, float y, float z) + { + m_pos[0] = x; + m_pos[1] = y; + m_pos[2] = z; + } + + void setRadius(float r) + { + m_radius = r; + } + + void setSpecular(float s) + { + m_specular = s; + } + + void updateIPVMatrix() + { + // Update the campos and IPV matrix, only once per frame since it's costly + const core::vector3df &campos = + irr_driver->getSceneManager()->getActiveCamera()->getAbsolutePosition(); + m_campos[0] = campos.X; + m_campos[1] = campos.Y; + m_campos[2] = campos.Z; + + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + +private: + core::matrix4 m_invprojview; + + float m_campos[3]; + float m_color[3]; + float m_pos[3]; + float m_screen[2]; + float m_radius; + float m_specular; +}; + +// + +class SunLightProvider: public CallBase +{ +public: + SunLightProvider() + { + m_screen[0] = UserConfigParams::m_width; + m_screen[1] = UserConfigParams::m_height; + + m_wind[0] = m_wind[1] = 0; + } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setColor(float r, float g, float b) + { + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; + } + + void setPosition(float x, float y, float z) + { + m_pos[0] = x; + m_pos[1] = y; + m_pos[2] = z; + } + + void updateIPVMatrix() + { + // Update the IPV matrix, only once per frame since it's costly + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + + void setShadowMatrix(const core::matrix4 &mat) + { + m_shadowmat = mat; + } + +private: + core::matrix4 m_invprojview, m_shadowmat; + float m_color[3]; + float m_pos[3]; + float m_screen[2]; + float m_wind[2]; +}; + +// + +class BloomProvider: public CallBase +{ +public: + BloomProvider() { m_threshold = 0.75f; } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setThreshold(const float f) { m_threshold = f; } + +private: + float m_threshold; +}; + +// + +class MLAAColor1Provider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class MLAABlend2Provider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class MLAANeigh3Provider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class SSAOProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class GodRayProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + // In texcoords + void setSunPosition(float x, float y) { m_sunpos[0] = x; m_sunpos[1] = y; } + +private: + float m_sunpos[2]; +}; + +// + +class ShadowPassProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class ShadowImportanceProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void updateIPVMatrix() + { + // Update the IPV matrix, only once per frame since it's costly + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + const core::vector3df &campos = + irr_driver->getSceneManager()->getActiveCamera()->getAbsolutePosition(); + m_campos[0] = campos.X; + m_campos[1] = campos.Y; + m_campos[2] = campos.Z; + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + + void setShadowMatrix(const core::matrix4 &mat) + { + m_shadowmat = mat; + } + +private: + core::matrix4 m_invprojview, m_shadowmat; + float m_campos[3]; +}; + +// + +class CollapseProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setResolution(const float x, const float y) + { + m_pixel[0] = 1.0f / x; + m_pixel[1] = 1.0f / y; + + m_multi[0] = m_multi[1] = 1; + + if (x < 2 || y < 2) + { + u32 i; + for (i = 0; i < 2; i++) + { + // No increase for the other direction + if (m_pixel[i] > 0.9f) m_pixel[i] = m_multi[i] = 0; + } + + std::swap(m_multi[0], m_multi[1]); + } + + m_size = std::max(x, y); + } + +private: + float m_pixel[2]; + int m_size; + float m_multi[2]; +}; + +// + +class BloomPowerProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void setPower(float power) + { + m_power = power / 10.0f; + } + +private: + float m_power; +}; + +// + +class MultiplyProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class ShadowGenProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class CausticsProvider: public CallBase +{ +public: + CausticsProvider() { m_dir[0] = m_dir[1] = m_dir2[0] = m_dir2[1] = 0; } + + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + +private: + float m_dir[2], m_dir2[2]; +}; + +// + +class DisplaceProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + DisplaceProvider() + { + m_screen[0] = UserConfigParams::m_width; + m_screen[1] = UserConfigParams::m_height; + + m_dir[0] = m_dir[1] = m_dir2[0] = m_dir2[1] = 0; + } + +private: + float m_screen[2]; + float m_dir[2], m_dir2[2]; +}; + +// + +class PPDisplaceProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); +}; + +// + +class FogProvider: public CallBase +{ +public: + virtual void OnSetConstants(video::IMaterialRendererServices *srv, int); + + void updateIPVMatrix() + { + // Update the campos and IPV matrix, only once per frame since it's costly + const core::vector3df &campos = + irr_driver->getSceneManager()->getActiveCamera()->getAbsolutePosition(); + m_campos[0] = campos.X; + m_campos[1] = campos.Y; + m_campos[2] = campos.Z; + + const video::IVideoDriver * const drv = irr_driver->getVideoDriver(); + + m_invprojview = drv->getTransform(video::ETS_PROJECTION); + m_invprojview *= drv->getTransform(video::ETS_VIEW); + m_invprojview.makeInverse(); + } + +private: + core::matrix4 m_invprojview; + float m_campos[3]; +}; + +#endif diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 89d9bdae7..0e9026e85 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -19,11 +19,7 @@ #include "graphics/camera.hpp" -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include #include "audio/music_manager.hpp" #include "config/user_config.hpp" @@ -40,6 +36,7 @@ #include "tracks/track.hpp" #include "utils/aligned_array.hpp" #include "utils/constants.hpp" +#include "utils/vs.hpp" #include "ICameraSceneNode.h" #include "ISceneManager.h" diff --git a/src/graphics/glow.cpp b/src/graphics/glow.cpp new file mode 100644 index 000000000..780c506b0 --- /dev/null +++ b/src/graphics/glow.cpp @@ -0,0 +1,85 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/glow.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +IMesh * GlowNode::sphere = NULL; +SMaterial GlowNode::mat; +aabbox3df GlowNode::box; + + +GlowNode::GlowNode(scene::ISceneManager* mgr, float radius): ISceneNode(mgr->getRootSceneNode(), mgr, -1) +{ + if (!sphere) + { + mat.Lighting = false; + mat.MaterialType = irr_driver->getShader(ES_GLOW); + + mat.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + mat.TextureLayer[0].TextureWrapU = + mat.TextureLayer[0].TextureWrapV = ETC_CLAMP_TO_EDGE; + mat.setFlag(EMF_TRILINEAR_FILTER, true); + mat.BlendOperation = EBO_ADD; + + sphere = mgr->getGeometryCreator()->createSphereMesh(1, 4, 4); + box = sphere->getBoundingBox(); + } + + setScale(vector3df(radius)); +} + +GlowNode::~GlowNode() +{ +} + +void GlowNode::render() +{ + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(mat); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 0, ~0); + glEnable(GL_STENCIL_TEST); + + drv->drawMeshBuffer(sphere->getMeshBuffer(0)); + + glDisable(GL_STENCIL_TEST); +} + +void GlowNode::OnRegisterSceneNode() +{ + if (IsVisible) + { + SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); + } + + ISceneNode::OnRegisterSceneNode(); +} diff --git a/src/graphics/glow.hpp b/src/graphics/glow.hpp new file mode 100644 index 000000000..8d9fa12f9 --- /dev/null +++ b/src/graphics/glow.hpp @@ -0,0 +1,53 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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_GLOW_HPP +#define HEADER_GLOW_HPP + +#include +#include + +using namespace irr; + +// The actual node +class GlowNode: public scene::ISceneNode +{ +public: + GlowNode(scene::ISceneManager* mgr, float radius); + ~GlowNode(); + + virtual void render(); + + virtual const core::aabbox3d& getBoundingBox() const + { + return box; + } + + virtual void OnRegisterSceneNode(); + + virtual u32 getMaterialCount() const { return 1; } + virtual video::SMaterial& getMaterial(u32 i) { return mat; } + +private: + static video::SMaterial mat; + static core::aabbox3df box; + + static scene::IMesh *sphere; +}; + +#endif diff --git a/src/graphics/glwrap.hpp b/src/graphics/glwrap.hpp new file mode 100644 index 000000000..fcac96621 --- /dev/null +++ b/src/graphics/glwrap.hpp @@ -0,0 +1,21 @@ +#ifndef GLWRAP_HEADER_H +#define GLWRAP_HEADER_H + +#if defined(__APPLE__) +# include +#elif defined(ANDROID) +# include +#elif defined(WIN32) +# define _WINSOCKAPI_ +// has to be included before gl.h because of WINGDIAPI and APIENTRY definitions +# include +# include +#else +# include +#endif + +// already includes glext.h, which defines useful GL constants. +// COpenGLDriver has already loaded the extension GL functions we use (e.g glBeginQuery) +#include "../../lib/irrlicht/source/Irrlicht/COpenGLDriver.h" + +#endif diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp index 3df8c8af4..22ed46a45 100644 --- a/src/graphics/irr_driver.cpp +++ b/src/graphics/irr_driver.cpp @@ -19,13 +19,23 @@ #include "graphics/irr_driver.hpp" #include "config/user_config.hpp" +#include "graphics/callbacks.hpp" #include "graphics/camera.hpp" +#include "graphics/glwrap.hpp" #include "graphics/hardware_skinning.hpp" +#include "graphics/lens_flare.hpp" +#include "graphics/light.hpp" #include "graphics/material_manager.hpp" #include "graphics/particle_kind_manager.hpp" #include "graphics/per_camera_node.hpp" #include "graphics/post_processing.hpp" #include "graphics/referee.hpp" +#include "graphics/shaders.hpp" +#include "graphics/shadow_importance.hpp" +#include "graphics/sun.hpp" +#include "graphics/rtts.hpp" +#include "graphics/water.hpp" +#include "graphics/wind.hpp" #include "guiengine/engine.hpp" #include "guiengine/modaldialog.hpp" #include "guiengine/scalable_font.hpp" @@ -47,6 +57,7 @@ #include "utils/constants.hpp" #include "utils/log.hpp" #include "utils/profiler.hpp" +#include "utils/vs.hpp" #include @@ -62,19 +73,12 @@ using namespace irr; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - #ifdef WIN32 #include #endif #if defined(__linux__) && !defined(ANDROID) -namespace X11 -{ - #include - #include -} +#include +#include #endif /** singleton */ @@ -96,6 +100,11 @@ IrrDriver::IrrDriver() m_resolution_changing = RES_CHANGE_NONE; m_device = createDevice(video::EDT_NULL); m_request_screenshot = false; + m_shaders = NULL; + m_rtts = NULL; + m_wind = new Wind(); + m_mipviz = m_wireframe = m_normals = m_ssaoviz = \ + m_lightviz = m_shadowviz = m_distortviz = 0; } // IrrDriver // ---------------------------------------------------------------------------- @@ -117,6 +126,9 @@ IrrDriver::~IrrDriver() m_device->drop(); m_device = NULL; m_modes.clear(); + + delete m_shaders; + delete m_wind; } // ~IrrDriver // ---------------------------------------------------------------------------- @@ -124,7 +136,7 @@ IrrDriver::~IrrDriver() */ void IrrDriver::reset() { - m_post_processing->reset(); + if (m_glsl) m_post_processing->reset(); } // reset // ---------------------------------------------------------------------------- @@ -135,22 +147,22 @@ Returns the parent window of "window" (i.e. the ancestor of window that is a direct child of the root, or window itself if it is a direct child). If window is the root window, returns window. */ -X11::Window get_toplevel_parent(X11::Display* display, X11::Window window) +Window get_toplevel_parent(Display* display, Window window) { - X11::Window parent; - X11::Window root; - X11::Window * children; + Window parent; + Window root; + Window * children; unsigned int num_children; while (true) { - if (0 == X11::XQueryTree(display, window, &root, + if (0 == XQueryTree(display, window, &root, &parent, &children, &num_children)) { Log::fatal("irr_driver", "XQueryTree error\n"); } if (children) { //must test for null - X11::XFree(children); + XFree(children); } if (window == root || parent == root) { return window; @@ -201,7 +213,6 @@ void IrrDriver::updateConfigIfRelevant() Log::warn("irr_driver", "Could not retrieve window location\n"); } #elif defined(__linux__) && !defined(ANDROID) - using namespace X11; const video::SExposedVideoData& videoData = m_device->getVideoDriver()->getExposedVideoData(); Display* display = (Display*)videoData.OpenGLLinux.X11Display; @@ -384,7 +395,8 @@ void IrrDriver::initDevice() m_gui_env = m_device->getGUIEnvironment(); m_video_driver = m_device->getVideoDriver(); m_glsl = m_video_driver->queryFeature(video::EVDF_ARB_GLSL) && - m_video_driver->queryFeature(video::EVDF_TEXTURE_NPOT); + m_video_driver->queryFeature(video::EVDF_TEXTURE_NPOT) && + UserConfigParams::m_pixel_shaders; // This remaps the window, so it has to be done before the clear to avoid flicker m_device->setResizable(false); @@ -396,6 +408,44 @@ void IrrDriver::initDevice() if (m_glsl) { Log::info("irr_driver", "GLSL supported."); + + // Order matters, create RTTs as soon as possible, as they are the largest blocks. + m_rtts = new RTT(); + m_shaders = new Shaders(); + m_shadow_importance = new ShadowImportance(); + + m_mrt.clear(); + m_mrt.reallocate(3); + m_mrt.push_back(m_rtts->getRTT(RTT_COLOR)); + m_mrt.push_back(m_rtts->getRTT(RTT_NORMAL)); + m_mrt.push_back(m_rtts->getRTT(RTT_DEPTH)); + + irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver(); + gl_driver->extGlGenQueries(1, &m_lensflare_query); + + scene::IMesh * const sphere = m_scene_manager->getGeometryCreator()->createSphereMesh(1, 16, 16); + m_sun_interposer = m_scene_manager->addMeshSceneNode(sphere); + m_sun_interposer->grab(); + m_sun_interposer->setParent(NULL); + m_sun_interposer->setScale(core::vector3df(20)); + + m_sun_interposer->getMaterial(0).Lighting = false; + m_sun_interposer->getMaterial(0).ColorMask = video::ECP_NONE; + m_sun_interposer->getMaterial(0).ZWriteEnable = false; + m_sun_interposer->getMaterial(0).MaterialType = m_shaders->getShader(ES_PASSFAR); + + sphere->drop(); + + m_lensflare = new scene::CLensFlareSceneNode(NULL, m_scene_manager, -1); + video::ITexture * const tex = + m_video_driver->getTexture((file_manager->getTextureDir() + "lensflare.png").c_str()); + if (!tex) Log::fatal("irr_driver", "Cannot find lens flare texture"); + m_lensflare->setMaterialTexture(0, tex); + m_lensflare->setAutomaticCulling(scene::EAC_OFF); + + m_suncam = m_scene_manager->addCameraSceneNode(0, vector3df(0), vector3df(0), -1, false); + m_suncam->grab(); + m_suncam->setParent(NULL); } else { @@ -407,7 +457,6 @@ void IrrDriver::initDevice() { #if defined(__linux__) && !defined(ANDROID) // Set class hints on Linux, used by Window Managers. - using namespace X11; const video::SExposedVideoData& videoData = m_video_driver ->getExposedVideoData(); XClassHint* classhint = XAllocClassHint(); @@ -423,11 +472,6 @@ void IrrDriver::initDevice() ->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true); m_device->getVideoDriver() ->setTextureCreationFlag(video::ETCF_OPTIMIZED_FOR_QUALITY, true); - if (!UserConfigParams::m_fbo) - { - m_device->getVideoDriver() - ->disableFeature(video::EVDF_FRAMEBUFFER_OBJECT); - } // Force creation of mipmaps even if the mipmaps flag in a b3d file // does not set the 'enable mipmap' flag. @@ -526,7 +570,6 @@ bool IrrDriver::moveWindow(const int x, const int y) return false; } #elif defined(__linux__) && !defined(ANDROID) - using namespace X11; const video::SExposedVideoData& videoData = m_video_driver->getExposedVideoData(); // TODO: Actually handle possible failure XMoveWindow((Display*)videoData.OpenGLLinux.X11Display, @@ -771,6 +814,7 @@ void IrrDriver::setAllMaterialFlags(scene::IMesh *mesh) const * \param wave_length Lenght of a water wave. */ scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh, + scene::IMesh **welded, float wave_height, float wave_speed, float wave_length) @@ -778,11 +822,24 @@ scene::ISceneNode* IrrDriver::addWaterNode(scene::IMesh *mesh, mesh->setMaterialFlag(video::EMF_GOURAUD_SHADING, true); scene::IMesh* welded_mesh = m_scene_manager->getMeshManipulator() ->createMeshWelded(mesh); - scene::ISceneNode* out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh, + scene::ISceneNode* out = NULL; + + if (!m_glsl) + { + out = m_scene_manager->addWaterSurfaceSceneNode(welded_mesh, wave_height, wave_speed, wave_length); + } else + { + out = new WaterNode(m_scene_manager, welded_mesh, wave_height, wave_speed, + wave_length); + } + out->getMaterial(0).setFlag(video::EMF_GOURAUD_SHADING, true); welded_mesh->drop(); // The scene node keeps a reference + + *welded = welded_mesh; + return out; } // addWaterNode @@ -841,11 +898,22 @@ PerCameraNode *IrrDriver::addPerCameraMesh(scene::IMesh* mesh, scene::ICameraSceneNode* camera, scene::ISceneNode *parent) { + scene::ISceneNode *node = m_scene_manager->addMeshSceneNode(mesh); + return new PerCameraNode((parent ? parent : m_scene_manager->getRootSceneNode()), - m_scene_manager, -1, camera, mesh); + m_scene_manager, -1, camera, node); } // addMesh +PerCameraNode *IrrDriver::addPerCameraNode(scene::ISceneNode* node, + scene::ICameraSceneNode* camera, + scene::ISceneNode *parent) +{ + return new PerCameraNode((parent ? parent + : m_scene_manager->getRootSceneNode()), + m_scene_manager, -1, camera, node); +} // addNode + // ---------------------------------------------------------------------------- /** Adds a billboard node to scene. @@ -1627,6 +1695,8 @@ void IrrDriver::update(float dt) m_resolution_changing = RES_CHANGE_NONE; } + m_wind->update(); + World *world = World::getWorld(); // Handle cut scenes (which do not have any karts in it) @@ -1651,97 +1721,24 @@ void IrrDriver::update(float dt) m_video_driver->endScene(); return; } - - const bool inRace = world!=NULL; - - if (inRace) + else if (!world) { - // Start the RTT for post-processing. - // We do this before beginScene() because we want to capture the glClear() - // because of tracks that do not have skyboxes (generally add-on tracks) - m_post_processing->beginCapture(); - } - m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, - world ? world->getClearColor() - : video::SColor(255,100,101,140)); + video::SColor(255,100,101,140)); - if (inRace) - { - irr_driver->getVideoDriver()->enableMaterial2D(); - - RaceGUIBase *rg = world->getRaceGUI(); - if (rg) rg->update(dt); - - - for(unsigned int i=0; iactivate(); - rg->preRenderCallback(camera); // adjusts start referee - m_scene_manager->drawAll(); - - PROFILER_POP_CPU_MARKER(); - - // Note that drawAll must be called before rendering - // the bullet debug view, since otherwise the camera - // is not set up properly. This is only used for - // the bullet debug view. - if (UserConfigParams::m_artist_debug_mode) - World::getWorld()->getPhysics()->draw(); - } // for igetNumKarts() - - // Stop capturing for the post-processing - m_post_processing->endCapture(); - - // Render the post-processed scene - m_post_processing->render(); - - // Set the viewport back to the full screen for race gui - m_video_driver->setViewPort(core::recti(0, 0, - UserConfigParams::m_width, - UserConfigParams::m_height)); - - for(unsigned int i=0; irenderPlayerView(camera, dt); - - PROFILER_POP_CPU_MARKER(); - } // for iendScene(); + return; } - -#ifdef DEBUG - drawDebugMeshes(); -#endif - - m_video_driver->endScene(); + if (m_glsl) + renderGLSL(dt); + else + renderFixed(dt); if (m_request_screenshot) doScreenShot(); - getPostProcessing()->update(dt); - // Enable this next print statement to get render information printed // E.g. number of triangles rendered, culled etc. The stats is only // printed while the race is running and not while the in-game menu @@ -1775,7 +1772,7 @@ bool IrrDriver::OnEvent(const irr::SEvent &event) // Ignore 'normal' messages if (event.LogEvent.Level > 1) { - printf("[IrrDriver Temp Logger] Level %d: %s\n", + Log::warn("[IrrDriver Temp Logger]", "Level %d: %s\n", event.LogEvent.Level,event.LogEvent.Text); } return true; @@ -1791,7 +1788,7 @@ bool IrrDriver::OnEvent(const irr::SEvent &event) bool IrrDriver::supportsSplatting() { - return UserConfigParams::m_pixel_shaders && m_glsl; + return m_glsl; } // ---------------------------------------------------------------------------- @@ -1977,6 +1974,11 @@ video::ITexture* IrrDriver::RTTProvider::renderToTexture(float angle, if (angle != -1 && m_rtt_main_node != NULL) m_rtt_main_node->setRotation( core::vector3df(0, angle, 0) ); + video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial(); + overridemat.EnablePasses = scene::ESNRP_SOLID; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; + overridemat.Material.MaterialType = video::EMT_SOLID; + if (m_rtt_main_node == NULL) { irr_driver->getSceneManager()->drawAll(); @@ -1990,6 +1992,129 @@ video::ITexture* IrrDriver::RTTProvider::renderToTexture(float angle, m_light->setVisible(false); } + overridemat.EnablePasses = 0; + m_video_driver->setRenderTarget(0, false, false); return m_render_target_texture; } + +void IrrDriver::applyObjectPassShader(scene::ISceneNode * const node, bool rimlit) +{ + if (!m_glsl) + return; + + // Don't override sky + if (node->getType() == scene::ESNT_SKY_DOME || + node->getType() == scene::ESNT_SKY_BOX) + return; + + const u32 mcount = node->getMaterialCount(); + u32 i; + const video::E_MATERIAL_TYPE ref = rimlit ? m_shaders->getShader(ES_OBJECTPASS_RIMLIT): + m_shaders->getShader(ES_OBJECTPASS_REF); + const video::E_MATERIAL_TYPE pass = rimlit ? m_shaders->getShader(ES_OBJECTPASS_RIMLIT): + m_shaders->getShader(ES_OBJECTPASS); + + const video::E_MATERIAL_TYPE origref = m_shaders->getShader(ES_OBJECTPASS_REF); + const video::E_MATERIAL_TYPE origpass = m_shaders->getShader(ES_OBJECTPASS); + + bool viamb = false; + scene::IMesh *mesh = NULL; + if (node->getType() == scene::ESNT_ANIMATED_MESH) + { + viamb = ((scene::IAnimatedMeshSceneNode *) node)->isReadOnlyMaterials(); + mesh = ((scene::IAnimatedMeshSceneNode *) node)->getMesh(); + } + else if (node->getType() == scene::ESNT_MESH) + { + viamb = ((scene::IMeshSceneNode *) node)->isReadOnlyMaterials(); + mesh = ((scene::IMeshSceneNode *) node)->getMesh(); + } + + for (i = 0; i < mcount; i++) + { + video::SMaterial &nodemat = node->getMaterial(i); + video::SMaterial &mbmat = mesh ? mesh->getMeshBuffer(i)->getMaterial() : nodemat; + video::SMaterial *mat = &nodemat; + + if (viamb) + mat = &mbmat; + + if (mat->MaterialType == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF || + mat->MaterialType == origref) + mat->MaterialType = ref; + else if (mat->MaterialType == video::EMT_SOLID || + mat->MaterialType == origpass || + (mat->MaterialType >= video::EMT_LIGHTMAP && + mat->MaterialType <= video::EMT_LIGHTMAP_LIGHTING_M4)) + mat->MaterialType = pass; + } + + + core::list kids = node->getChildren(); + scene::ISceneNodeList::Iterator it = kids.begin(); + for (; it != kids.end(); ++it) + { + applyObjectPassShader(*it, rimlit); + } +} + +void IrrDriver::applyObjectPassShader() +{ + if (!m_glsl) + return; + + applyObjectPassShader(m_scene_manager->getRootSceneNode()); +} + +scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos, float radius, + float r, float g, float b, bool sun) +{ + if (m_glsl) + { + LightNode *light = NULL; + + if (!sun) + light = new LightNode(m_scene_manager, radius, r, g, b); + else + light = new SunNode(m_scene_manager, r, g, b); + + light->grab(); + light->setParent(NULL); + + light->setPosition(pos); + light->updateAbsolutePosition(); + + m_lights.push_back(light); + + if (sun) { + m_sun_interposer->setPosition(pos); + m_sun_interposer->updateAbsolutePosition(); + + m_lensflare->setPosition(pos); + m_lensflare->updateAbsolutePosition(); + + m_suncam->setPosition(pos); + m_suncam->updateAbsolutePosition(); + + ((WaterShaderProvider *) m_shaders->m_callbacks[ES_WATER])->setSunPosition(pos); + } + + return light; + } else + { + return m_scene_manager->addLightSceneNode(NULL, pos, video::SColorf(r, g, b), radius); + } +} + +void IrrDriver::clearLights() +{ + u32 i; + const u32 max = m_lights.size(); + for (i = 0; i < max; i++) + { + m_lights[i]->drop(); + } + + m_lights.clear(); +} diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp index c5497f8d4..fdcf4d9f6 100644 --- a/src/graphics/irr_driver.hpp +++ b/src/graphics/irr_driver.hpp @@ -39,11 +39,15 @@ namespace irr { namespace scene { class ISceneManager; class IMesh; class IAnimatedMeshSceneNode; class IAnimatedMesh; - class IMeshSceneNode; class IParticleSystemSceneNode; class ICameraSceneNode; class ILightSceneNode; } + class IMeshSceneNode; class IParticleSystemSceneNode; class ICameraSceneNode; class ILightSceneNode; + class CLensFlareSceneNode; } namespace gui { class IGUIEnvironment; class IGUIFont; } } using namespace irr; +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" +#include "graphics/wind.hpp" #include "utils/aligned_array.hpp" #include "utils/no_copy.hpp" #include "utils/ptr_vector.hpp" @@ -53,6 +57,8 @@ class AbstractKart; class Camera; class PerCameraNode; class PostProcessing; +class LightNode; +class ShadowImportance; /** * \brief class that creates the irrLicht device and offers higher-level @@ -74,11 +80,22 @@ private: gui::IGUIFont *m_race_font; /** Post-processing. */ PostProcessing *m_post_processing; + /** Shaders. */ + Shaders *m_shaders; + /** Wind. */ + Wind *m_wind; + /** RTTs. */ + RTT *m_rtts; + /** Shadow importance. */ + ShadowImportance *m_shadow_importance; /** Additional details to be shown in case that a texture is not found. * This is used to specify details like: "while loading kart '...'" */ std::string m_texture_error_message; + /** The main MRT setup. */ + core::array m_mrt; + /** Flag to indicate if a resolution change is pending (which will be * acted upon in the next update). None means no change, yes means * change to new resolution and trigger confirmation dialog. @@ -99,6 +116,12 @@ public: int getWidth() const {return m_width; } int getHeight() const {return m_height; } }; // VideoMode + + struct BloomData { + scene::ISceneNode * node; + float power; + }; + private: std::vector m_modes; @@ -116,6 +139,34 @@ private: bool m_request_screenshot; + bool m_wireframe; + bool m_mipviz; + bool m_normals; + bool m_ssaoviz; + bool m_shadowviz; + bool m_lightviz; + bool m_distortviz; + u32 m_renderpass; + u32 m_lensflare_query; + scene::IMeshSceneNode *m_sun_interposer; + scene::CLensFlareSceneNode *m_lensflare; + scene::ICameraSceneNode *m_suncam; + + struct GlowData { + scene::ISceneNode * node; + float r, g, b; + }; + + std::vector m_glowing; + + std::vector m_lights; + + std::vector m_forcedbloom; + + std::vector m_displacing; + + std::vector m_background; + #ifdef DEBUG /** Used to visualise skeletons. */ std::vector m_debug_meshes; @@ -126,6 +177,9 @@ private: irr::scene::ISkinnedMesh* mesh, int id); #endif + void renderFixed(float dt); + void renderGLSL(float dt); + void doScreenShot(); public: IrrDriver(); @@ -151,7 +205,8 @@ public: bool create_one_quad=false); scene::IMesh *createTexturedQuadMesh(const video::SMaterial *material, const double w, const double h); - scene::ISceneNode *addWaterNode(scene::IMesh *mesh, float wave_height, + scene::ISceneNode *addWaterNode(scene::IMesh *mesh, scene::IMesh **welded, + float wave_height, float wave_speed, float wave_length); scene::IMeshSceneNode*addOctTree(scene::IMesh *mesh); scene::IMeshSceneNode*addSphere(float radius, @@ -161,6 +216,9 @@ public: PerCameraNode *addPerCameraMesh(scene::IMesh* mesh, scene::ICameraSceneNode* node, scene::ISceneNode *parent = NULL); + PerCameraNode *addPerCameraNode(scene::ISceneNode* node, + scene::ICameraSceneNode* cam, + scene::ISceneNode *parent = NULL); scene::ISceneNode *addBillboard(const core::dimension2d< f32 > size, video::ITexture *texture, scene::ISceneNode* parent=NULL, bool alphaTesting = false); @@ -286,9 +344,86 @@ public: /** Returns a pointer to the post processing object. */ inline PostProcessing* getPostProcessing() {return m_post_processing;} // ------------------------------------------------------------------------ - + inline core::vector3df getWind() {return m_wind->getWind();} + // ------------------------------------------------------------------------ + inline video::E_MATERIAL_TYPE getShader(const ShaderType num) {return m_shaders->getShader(num);} + // ------------------------------------------------------------------------ + inline video::IShaderConstantSetCallBack* getCallback(const ShaderType num) {return m_shaders->m_callbacks[num];} + // ------------------------------------------------------------------------ + inline video::ITexture* getRTT(TypeRTT which) {return m_rtts->getRTT(which);} + // ------------------------------------------------------------------------ inline bool isGLSL() const { return m_glsl; } // ------------------------------------------------------------------------ + void toggleWireframe() { m_wireframe ^= 1; } + // ------------------------------------------------------------------------ + void toggleMipVisualization() { m_mipviz ^= 1; } + // ------------------------------------------------------------------------ + void toggleNormals() { m_normals ^= 1; } + // ------------------------------------------------------------------------ + bool getNormals() { return m_normals; } + // ------------------------------------------------------------------------ + void toggleSSAOViz() { m_ssaoviz ^= 1; } + // ------------------------------------------------------------------------ + void toggleLightViz() { m_lightviz ^= 1; } + // ------------------------------------------------------------------------ + bool getSSAOViz() { return m_ssaoviz; } + // ------------------------------------------------------------------------ + void toggleShadowViz() { m_shadowviz ^= 1; } + // ------------------------------------------------------------------------ + bool getShadowViz() { return m_shadowviz; } + // ------------------------------------------------------------------------ + void toggleDistortViz() { m_distortviz ^= 1; } + // ------------------------------------------------------------------------ + bool getDistortViz() { return m_distortviz; } + // ------------------------------------------------------------------------ + u32 getRenderPass() { return m_renderpass; } + // ------------------------------------------------------------------------ + void addGlowingNode(scene::ISceneNode *n, float r = 1.0f, float g = 1.0f, float b = 1.0f) + { + GlowData dat; + dat.node = n; + dat.r = r; + dat.g = g; + dat.b = b; + + m_glowing.push_back(dat); + } + // ------------------------------------------------------------------------ + void clearGlowingNodes() { m_glowing.clear(); } + // ------------------------------------------------------------------------ + void addForcedBloomNode(scene::ISceneNode *n, float power = 1) + { + BloomData dat; + dat.node = n; + dat.power = power; + + m_forcedbloom.push_back(dat); + } + // ------------------------------------------------------------------------ + void clearForcedBloom() { m_forcedbloom.clear(); } + // ------------------------------------------------------------------------ + const std::vector &getForcedBloom() const { return m_forcedbloom; } + // ------------------------------------------------------------------------ + void clearDisplacingNodes() { m_displacing.clear(); } + // ------------------------------------------------------------------------ + const std::vector &getDisplacingNodes() const { return m_displacing; } + // ------------------------------------------------------------------------ + void addDisplacingNode(scene::ISceneNode * const n) { m_displacing.push_back(n); } + // ------------------------------------------------------------------------ + void clearBackgroundNodes() { m_background.clear(); } + // ------------------------------------------------------------------------ + void addBackgroundNode(scene::ISceneNode * const n) { m_background.push_back(n); } + // ------------------------------------------------------------------------ + void applyObjectPassShader(); + void applyObjectPassShader(scene::ISceneNode * const node, bool rimlit = false); + // ------------------------------------------------------------------------ + scene::ISceneNode *addLight(const core::vector3df &pos, float radius = 1.0f, float r = 1.0f, + float g = 1.0f, float b = 1.0f, bool sun = false); + // ------------------------------------------------------------------------ + void clearLights(); + // ------------------------------------------------------------------------ + scene::IMeshSceneNode *getSunInterposer() { return m_sun_interposer; } + #ifdef DEBUG /** Removes debug meshes. */ void clearDebugMesh() { m_debug_meshes.clear(); } diff --git a/src/graphics/large_mesh_buffer.hpp b/src/graphics/large_mesh_buffer.hpp new file mode 100644 index 000000000..84c77fd17 --- /dev/null +++ b/src/graphics/large_mesh_buffer.hpp @@ -0,0 +1,58 @@ +// Copyright (C) 2002-2012 Nikolaus Gebhardt +// This file is part of the "Irrlicht Engine". +// For conditions of distribution and use, see copyright notice in irrlicht.h + +#ifndef __LARGE_MESH_BUFFER_H_INCLUDED__ +#define __LARGE_MESH_BUFFER_H_INCLUDED__ + +#include "irrArray.h" +#include "IMeshBuffer.h" +#include "CMeshBuffer.h" + +namespace irr +{ +namespace scene +{ + //! A SMeshBuffer with 32-bit indices + class LargeMeshBuffer : public SMeshBuffer + { + public: + //! Get type of index data which is stored in this meshbuffer. + /** \return Index type of this buffer. */ + virtual video::E_INDEX_TYPE getIndexType() const + { + return video::EIT_32BIT; + } + + //! Get pointer to indices + /** \return Pointer to indices. */ + virtual const u16* getIndices() const + { + return (u16 *) Indices.const_pointer(); + } + + + //! Get pointer to indices + /** \return Pointer to indices. */ + virtual u16* getIndices() + { + return (u16 *) Indices.pointer(); + } + + + //! Get number of indices + /** \return Number of indices. */ + virtual u32 getIndexCount() const + { + return Indices.size(); + } + + //! Indices into the vertices of this buffer. + core::array Indices; + }; +} // end namespace scene +} // end namespace irr + +#endif + + diff --git a/src/graphics/lens_flare.cpp b/src/graphics/lens_flare.cpp new file mode 100644 index 000000000..eebc27d16 --- /dev/null +++ b/src/graphics/lens_flare.cpp @@ -0,0 +1,264 @@ +/* TODO: copyright */ + +#include "graphics/lens_flare.hpp" +#include "IVideoDriver.h" +#include "ISceneManager.h" +#include "ISceneCollisionManager.h" + +namespace irr +{ +namespace scene +{ + +CLensFlareSceneNode::CLensFlareSceneNode(ISceneNode* parent, ISceneManager* mgr, s32 id) : + scene::ISceneNode(parent, mgr, id) +{ + #ifdef _DEBUG + setDebugName("CLensFlareSceneNode"); + #endif + + // set the bounding box + BBox.MaxEdge.set(0,0,0); + BBox.MinEdge.set(0,0,0); + + // set the initial Strength + Strength = 1.0f; + + // setup the vertices + Vertices[0] = video::S3DVertex(-1.f, -1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 0.f, 1.f); + Vertices[1] = video::S3DVertex(-1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 0.f, 0.f); + Vertices[2] = video::S3DVertex( 1.f, 1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 1.f, 0.f); + Vertices[3] = video::S3DVertex( 1.f, -1.f, 0.f, 0.f, 0.f, 1.f, 0xffffffff, 1.f, 1.f); + + // setup the indices + Indices[0] = 0; + Indices[1] = 1; + Indices[2] = 2; + Indices[3] = 2; + Indices[4] = 3; + Indices[5] = 0; + + // set the default material properties + Material.MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; + Material.Lighting = false; + Material.ZBuffer = video::ECFN_NEVER; + + FlareData.reallocate(30); + + // prepare the flare data array + // circles, halos and ring behind the sun + FlareData.push_back(SFlareData(EFT_CIRCLE, 0.5f, 0.12f, video::SColor(120, 60, 180, 35))); + FlareData.push_back(SFlareData(EFT_HALO, 0.45f, 0.4f, video::SColor(200, 100, 200, 60))); + FlareData.push_back(SFlareData(EFT_CIRCLE, 0.4f, 0.17f, video::SColor(240, 120, 220, 40))); + FlareData.push_back(SFlareData(EFT_CIRCLE, 0.2f, 0.35f, video::SColor(175, 175, 255, 20))); + FlareData.push_back(SFlareData(EFT_RING, 0.15f, 0.2f, video::SColor(120, 60, 255, 100))); + + // sun and glow effect at sun position + FlareData.push_back(SFlareData(EFT_SUN, 0.0f, 0.75f, video::SColor(255, 255, 255, 255))); +// FlareData.push_back(SFlareData(EFT_STREAKS, 0.0f, 2.9f, video::SColor(255, 255, 255, 255))); + FlareData.push_back(SFlareData(EFT_GLOW, 0.0f, 3.5f, video::SColor(255, 255, 255, 255))); +// FlareData.push_back(SFlareData(EFT_RING, 0.0f, 1.5f, video::SColor(120, 120, 120, 150))); + + // some lenses, halos and circles + FlareData.push_back(SFlareData(EFT_LENS, -0.15f, 0.15f, video::SColor(255, 60, 60, 90))); + FlareData.push_back(SFlareData(EFT_HALO, -0.3f, 0.3f, video::SColor(120, 60, 255, 180))); + FlareData.push_back(SFlareData(EFT_HALO, -0.4f, 0.2f, video::SColor(220, 80, 80, 98))); + FlareData.push_back(SFlareData(EFT_CIRCLE, -0.45f, 0.1f, video::SColor(220, 80, 80, 85))); + FlareData.push_back(SFlareData(EFT_RING, -0.42f, 0.3f, video::SColor(180, 60, 255, 110))); + + // some small lenses, halos and rings + FlareData.push_back(SFlareData(EFT_LENS, -0.55f, 0.2f, video::SColor(255, 60, 60, 130))); + FlareData.push_back(SFlareData(EFT_HALO, -0.6f, 0.3f, video::SColor(120, 60, 255, 80))); + FlareData.push_back(SFlareData(EFT_LENS, -0.7f, 0.2f, video::SColor(200, 60, 60, 130))); + FlareData.push_back(SFlareData(EFT_LENS, -0.71f, 0.2f, video::SColor(200, 60, 130, 60))); + FlareData.push_back(SFlareData(EFT_LENS, -0.72f, 0.2f, video::SColor(200, 130, 130, 60))); + FlareData.push_back(SFlareData(EFT_LENS, -0.74f, 0.2f, video::SColor(200, 130, 60, 60))); + + // some polyons, lenses and circle + FlareData.push_back(SFlareData(scene::EFT_POLY, -0.79f, 0.2f, video::SColor(200, 60, 130, 60))); + FlareData.push_back(SFlareData(scene::EFT_POLY, -0.86f, 0.3f, video::SColor(200, 130, 130, 60))); + FlareData.push_back(SFlareData(scene::EFT_LENS, -0.87f, 0.3f, video::SColor(180,255,192,178))); + FlareData.push_back(SFlareData(scene::EFT_CIRCLE, -0.9f, 0.1f, video::SColor(200, 60, 60, 130))); + FlareData.push_back(SFlareData(scene::EFT_POLY, -0.93f, 0.4f, video::SColor(200, 130, 60, 60))); + + // finally som polygons + FlareData.push_back(SFlareData(EFT_POLY, -0.95f, 0.6f, video::SColor(120, 60, 255, 120))); + FlareData.push_back(SFlareData(EFT_POLY, -1.0f, 0.15f, video::SColor(120, 20, 255, 85))); +} + +CLensFlareSceneNode::~CLensFlareSceneNode() +{ +} + +u32 CLensFlareSceneNode::getMaterialCount() const +{ + // return the material count (always one in our case) + return 1; +} + +video::SMaterial& CLensFlareSceneNode::getMaterial(u32 i) +{ + // return the material + return Material; +} + +core::array& CLensFlareSceneNode::getFlareData() +{ + // return the flare data array + return FlareData; +} + +void CLensFlareSceneNode::OnRegisterSceneNode() +{ + // if node is visible and Strength is greater than 0 register it for the ESNRP_TRANSPARENT_EFFECT pass + if(IsVisible && Strength > 0.0f) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT_EFFECT); + } + + // call base OnRegisterSceneNode + ISceneNode::OnRegisterSceneNode(); +} + +void CLensFlareSceneNode::render() +{ + // get the videodriver and the active camera + video::IVideoDriver* driver = SceneManager->getVideoDriver(); + ICameraSceneNode* camera = SceneManager->getActiveCamera(); + + // return if we don't have a valid driver or a valid camera + // or if we have no texture attached to the material + if (!camera || !driver || !Material.getTexture(0)) + return; + + // get screencenter + const core::vector2d screenCenter = core::vector2d( + SceneManager->getVideoDriver()->getScreenSize().Width, + SceneManager->getVideoDriver()->getScreenSize().Height)/2; + + // get screencoordinates of the node + const core::vector2d lightPos = SceneManager->getSceneCollisionManager()->getScreenCoordinatesFrom3DPosition( + getAbsolutePosition(), + camera); + + // store old projection matrix + core::matrix4 oldProjMat = driver->getTransform(video::ETS_PROJECTION); + + // store old view matrix + core::matrix4 oldViewMat = driver->getTransform(video::ETS_VIEW); + + // clear the projection matrix + driver->setTransform(video::ETS_PROJECTION, core::IdentityMatrix); + + // clear the view matrix + driver->setTransform(video::ETS_VIEW, core::IdentityMatrix); + + // set the transform + driver->setTransform(video::ETS_WORLD, core::IdentityMatrix); + + // set the material + driver->setMaterial(Material); + + // calculate some handy constants + const f32 texPos = 1.0f/EFT_COUNT; + const s32 texHeight = s32(Material.getTexture(0)->getSize().Height*0.5f); + const f32 screenWidth = f32(driver->getScreenSize().Width); + const f32 screenHeight = f32(driver->getScreenSize().Height); + + // render the flares + for (u32 i=0; iflarePos = screenCenter.getInterpolated(lightPos, -2.0*flare.Position); + + // calculate flareposition in vertex coordinates using the scalefactor of the flare + s32 flareScale = s32((texHeight*flare.Scale)); + core::rect flareRect = core::rect( + -1.f + 2.f * f32(flarePos.X-flareScale) / screenWidth, + -1.f + 2.f * f32(screenHeight-flarePos.Y-flareScale) / screenHeight, + -1.f + 2.f * f32(flarePos.X+flareScale) / screenWidth, + -1.f + 2.f * f32(screenHeight-flarePos.Y+flareScale) / screenHeight); + + // calculate flarecolor in dependence of occlusion + f32 flareAlpha = f32(flare.Color.getAlpha()) / 255.f; + video::SColor flareColor(255, + (u32)(Strength * flareAlpha * flare.Color.getRed()), + (u32)(Strength * flareAlpha * flare.Color.getGreen()), + (u32)(Strength * flareAlpha * flare.Color.getBlue())); + + // set vertex colors + Vertices[0].Color = flareColor; + Vertices[1].Color = flareColor; + Vertices[2].Color = flareColor; + Vertices[3].Color = flareColor; + + // set texture coordinates + Vertices[0].TCoords.set( flare.Type * texPos, 1.0f); + Vertices[1].TCoords.set( flare.Type * texPos, 0.0f); + Vertices[2].TCoords.set((flare.Type+1) * texPos, 0.0f); + Vertices[3].TCoords.set((flare.Type+1) * texPos, 1.0f); + + // set vertex positions + Vertices[0].Pos.set(flareRect.UpperLeftCorner.X, flareRect.UpperLeftCorner.Y, 0); + Vertices[1].Pos.set(flareRect.UpperLeftCorner.X, flareRect.LowerRightCorner .Y, 0); + Vertices[2].Pos.set(flareRect.LowerRightCorner.X, flareRect.LowerRightCorner.Y, 0); + Vertices[3].Pos.set(flareRect.LowerRightCorner.X, flareRect.UpperLeftCorner.Y, 0); + + //draw the mesh + driver->drawIndexedTriangleList(Vertices, 4, Indices, 2); + } + + // restore view matrix + driver->setTransform(video::ETS_VIEW, oldViewMat); + + // restore projection matrix + driver->setTransform(video::ETS_PROJECTION, oldProjMat); +} + +const core::aabbox3df& CLensFlareSceneNode::getBoundingBox() const +{ + // return the bounding box + return BBox; +} + +ESCENE_NODE_TYPE CLensFlareSceneNode::getType() const +{ + // return type of the scene node + // (important when using with a custom scene node factory) + return scene::ESNT_UNKNOWN; //(ESCENE_NODE_TYPE) ECSNT_LENSFLARE; +} + +void CLensFlareSceneNode::serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options) const +{ + // write attributes of the scene node. + ISceneNode::serializeAttributes(out, options); +} + +void CLensFlareSceneNode::deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options) +{ + // read attributes of the scene node. + ISceneNode::deserializeAttributes(in, options); +} + +ISceneNode* CLensFlareSceneNode::clone(ISceneNode* newParent, ISceneManager* newManager) +{ + if (!newParent) + newParent = Parent; + if (!newManager) + newManager = SceneManager; + + CLensFlareSceneNode* nb = new CLensFlareSceneNode(newParent, newManager, ID); + + nb->cloneMembers(this, newManager); + nb->Material = Material; + + nb->drop(); + return nb; +} + +}// end irr namespace +}// end scene namespace + diff --git a/src/graphics/lens_flare.hpp b/src/graphics/lens_flare.hpp new file mode 100644 index 000000000..50d7145b0 --- /dev/null +++ b/src/graphics/lens_flare.hpp @@ -0,0 +1,117 @@ +#ifndef _CLENSFLARESCENENODE_H +#define _CLENSFLARESCENENODE_H + +#include +#include + +namespace irr +{ +namespace scene +{ + // enum with different flare types (used by SFlareData) + enum E_FLARE_TYPE + { + EFT_SUN = 0, + EFT_GLOW, + EFT_LENS, + EFT_STREAKS, + EFT_RING, + EFT_HALO, + EFT_CIRCLE, + EFT_POLY, + EFT_COUNT + }; + + // struct holding the flare specification + struct SFlareData + { + public: + // constructor + SFlareData(const E_FLARE_TYPE type, const float position, + const float scale, const video::SColor &color) + { + Type = type; + Position = position; + Scale = scale; + Color = color; + } + + // flare type + E_FLARE_TYPE Type; + // position + f32 Position; + // flare scale + f32 Scale; + // flare color + video::SColor Color; + }; + + class CLensFlareSceneNode : public ISceneNode + { + public: + // constructor + CLensFlareSceneNode(ISceneNode* parent, scene::ISceneManager* mgr, s32 id = -1); + + // destructor + virtual ~CLensFlareSceneNode(); + + protected: + // material of the node + video::SMaterial Material; + + // Bounding box + core::aabbox3d BBox; + + // vertices and indices of a flare element + video::S3DVertex Vertices[4]; + u16 Indices[6]; + + // flare data array + core::array FlareData; + + // Strength of the flare effect (between 0 and 1) + f32 Strength; + + public: + // typical OnRegisterSceneNode function + virtual void OnRegisterSceneNode(); + + // renders the node + virtual void render(); + + // returns the bounding box + virtual const core::aabbox3d& getBoundingBox() const; + + // returns the node type + virtual ESCENE_NODE_TYPE getType() const; + + // returns the material count + virtual u32 getMaterialCount() const; + + // returns the material + virtual video::SMaterial& getMaterial(u32 i); + + // writes attributes of the scene node. + virtual void serializeAttributes(io::IAttributes* out, io::SAttributeReadWriteOptions* options = 0) const; + + // reads attributes of the scene node. + virtual void deserializeAttributes(io::IAttributes* in, io::SAttributeReadWriteOptions* options = 0); + + // clones the node + virtual ISceneNode* clone(ISceneNode* newParent = 0, ISceneManager* newManager = 0); + + // returns the flare data array + core::array& getFlareData(); + + // returns the strength (visibility) of the flares + f32 getStrength() { return Strength; } + + // sets the strength (visibility) of the flares + void setStrength(f32 strength) { Strength = core::clamp(strength, 0.0f, 0.7f); } + }; + +} // end namespace scene +} // end namespace irr + +#endif + diff --git a/src/graphics/light.cpp b/src/graphics/light.cpp new file mode 100644 index 000000000..bbc0d0da6 --- /dev/null +++ b/src/graphics/light.cpp @@ -0,0 +1,93 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/light.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +IMesh * LightNode::sphere = NULL; +SMaterial LightNode::mat; +aabbox3df LightNode::box; + + +LightNode::LightNode(scene::ISceneManager* mgr, float radius, float r, float g, float b): + ISceneNode(mgr->getRootSceneNode(), mgr, -1) +{ + if (!sphere) + { + mat.Lighting = false; + mat.MaterialType = irr_driver->getShader(ES_POINTLIGHT); + + mat.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + mat.setTexture(1, irr_driver->getRTT(RTT_DEPTH)); + + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) + { + mat.TextureLayer[i].TextureWrapU = + mat.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; + } + + mat.setFlag(EMF_BILINEAR_FILTER, false); + mat.setFlag(EMF_ZWRITE_ENABLE, false); + + mat.MaterialTypeParam = pack_textureBlendFunc(EBF_ONE, EBF_ONE); + mat.BlendOperation = EBO_ADD; + + sphere = mgr->getGeometryCreator()->createSphereMesh(1, 16, 16); + box = sphere->getBoundingBox(); + } + + setScale(vector3df(radius)); + m_radius = radius; + + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; +} + +LightNode::~LightNode() +{ +} + +void LightNode::render() +{ + PointLightProvider * const cb = (PointLightProvider *) irr_driver->getCallback(ES_POINTLIGHT); + cb->setColor(m_color[0], m_color[1], m_color[2]); + cb->setPosition(getPosition().X, getPosition().Y, getPosition().Z); + cb->setRadius(m_radius); + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(mat); + + drv->drawMeshBuffer(sphere->getMeshBuffer(0)); +} + +void LightNode::OnRegisterSceneNode() +{ // This node is only drawn manually. +} diff --git a/src/graphics/light.hpp b/src/graphics/light.hpp new file mode 100644 index 000000000..245a3250e --- /dev/null +++ b/src/graphics/light.hpp @@ -0,0 +1,64 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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_LIGHT_HPP +#define HEADER_LIGHT_HPP + +#include +#include + +using namespace irr; + +namespace irr +{ + namespace scene { class IMesh; } +} + +// The actual node +class LightNode: public scene::ISceneNode +{ +public: + LightNode(scene::ISceneManager* mgr, float radius, float r, float g, float b); + virtual ~LightNode(); + + virtual void render() OVERRIDE; + + virtual const core::aabbox3d& getBoundingBox() const OVERRIDE + { + return box; + } + + virtual void OnRegisterSceneNode() OVERRIDE; + + virtual u32 getMaterialCount() const OVERRIDE { return 1; } + virtual video::SMaterial& getMaterial(u32 i) OVERRIDE { return mat; } + + float getRadius() const { return m_radius; } + void getColor(float out[3]) const { memcpy(out, m_color, 3 * sizeof(float)); } + +protected: + static video::SMaterial mat; + static core::aabbox3df box; + + static scene::IMesh *sphere; + + float m_radius; + float m_color[3]; +}; + +#endif diff --git a/src/graphics/lod_node.cpp b/src/graphics/lod_node.cpp index 5943d632e..f8820d69d 100644 --- a/src/graphics/lod_node.cpp +++ b/src/graphics/lod_node.cpp @@ -49,6 +49,7 @@ LODNode::LODNode(std::string group_name, scene::ISceneNode* parent, drop(); m_forced_lod = -1; + m_last_tick = 0; } LODNode::~LODNode() @@ -81,6 +82,11 @@ int LODNode::getLevel() if (dist < m_detail[n]) return n; } + + // If it's the shadow pass, and we would have otherwise hidden the item, show the min one + if (curr_cam->isOrthogonal()) + return m_detail.size() - 1; + return -1; } // getLevel @@ -139,9 +145,12 @@ void LODNode::OnRegisterSceneNode() shown = true; } + const u32 now = irr_driver->getDevice()->getTimer()->getTime(); + // support an optional, mostly hard-coded fade-in/out effect for objects with a single level if (m_nodes.size() == 1 && (m_nodes[0]->getType() == scene::ESNT_MESH || - m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH)) + m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH) && + now > m_last_tick) { if (m_previous_visibility == WAS_HIDDEN && shown) { @@ -237,6 +246,7 @@ void LODNode::OnRegisterSceneNode() } m_previous_visibility = (shown ? WAS_SHOWN : WAS_HIDDEN); + m_last_tick = now; // If this node has children other than the LOD nodes, draw them core::list::Iterator it; @@ -279,7 +289,13 @@ void LODNode::add(int level, scene::ISceneNode* node, bool reparent) if(UserConfigParams::m_hw_skinning_enabled && node->getType() == scene::ESNT_ANIMATED_MESH) HardwareSkinning::prepareNode((scene::IAnimatedMeshSceneNode*)node); + if (node->getType() == scene::ESNT_ANIMATED_MESH) + ((scene::IAnimatedMeshSceneNode *) node)->setReadOnlyMaterials(true); + if (node->getType() == scene::ESNT_MESH) + ((scene::IMeshSceneNode *) node)->setReadOnlyMaterials(true); + node->drop(); node->updateAbsolutePosition(); + irr_driver->applyObjectPassShader(node); } diff --git a/src/graphics/lod_node.hpp b/src/graphics/lod_node.hpp index c3c3adc2d..3cca1bc28 100644 --- a/src/graphics/lod_node.hpp +++ b/src/graphics/lod_node.hpp @@ -71,6 +71,8 @@ private: PreviousVisibility m_previous_visibility; + u32 m_last_tick; + public: LODNode(std::string group_name, scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id=-1); diff --git a/src/graphics/material.cpp b/src/graphics/material.cpp index 70e3f9709..33babb91b 100644 --- a/src/graphics/material.cpp +++ b/src/graphics/material.cpp @@ -27,8 +27,10 @@ #include "config/user_config.hpp" #include "config/stk_config.hpp" #include "guiengine/engine.hpp" +#include "graphics/callbacks.hpp" #include "graphics/irr_driver.hpp" #include "graphics/particle_kind_manager.hpp" +#include "graphics/shaders.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "utils/string_utils.hpp" @@ -36,7 +38,6 @@ #include "tracks/track.hpp" #include "utils/log.hpp" -#include #include #include #include @@ -46,351 +47,6 @@ using namespace irr::video; const unsigned int UCLAMP = 1; const unsigned int VCLAMP = 2; -//----------------------------------------------------------------------------- - -class NormalMapProvider : public video::IShaderConstantSetCallBack -{ - bool m_with_lightmap; - -public: - LEAK_CHECK() - - NormalMapProvider(bool withLightmap) - { - m_with_lightmap = withLightmap; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - s32 decaltex = 0; - services->setPixelShaderConstant("DecalTex", &decaltex, 1); - - s32 bumptex = 1; - services->setPixelShaderConstant("BumpTex", &bumptex, 1); - - s32 lightmapTex = (m_with_lightmap ? 2 : 0); - services->setPixelShaderConstant("LightMapTex", &lightmapTex, 1); - - s32 hasLightMap = (m_with_lightmap ? 1 : 0); - services->setPixelShaderConstant("HasLightMap", &hasLightMap, 1); - - // We could calculate light direction as coming from the sun (then we'd need to - // transform it into camera space). But I find that pretending light - // comes from the camera gives good results - const float lightdir[] = {0.1852f, -0.1852f, -0.9259f}; - services->setVertexShaderConstant("lightdir", lightdir, 3); - } -}; - - -//----------------------------------------------------------------------------- - -class WaterShaderProvider : public video::IShaderConstantSetCallBack -{ - float m_dx_1, m_dy_1, m_dx_2, m_dy_2; - float m_water_shader_speed_1; - float m_water_shader_speed_2; - bool m_fog; - -public: - LEAK_CHECK() - - void enableFog(bool enable) - { - m_fog = enable; - } - - - WaterShaderProvider(float water_shader_speed_1, - float water_shader_speed_2) - { - m_dx_1 = 0.0f; - m_dy_1 = 0.0f; - m_dx_2 = 0.0f; - m_dy_2 = 0.0f; - - m_water_shader_speed_1 = water_shader_speed_1/100.0f; - m_water_shader_speed_2 = water_shader_speed_2/100.0f; - - m_fog = false; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - m_dx_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1; - m_dy_1 += GUIEngine::getLatestDt()*m_water_shader_speed_1; - - m_dx_2 += GUIEngine::getLatestDt()*m_water_shader_speed_2; - m_dy_2 -= GUIEngine::getLatestDt()*m_water_shader_speed_2; - - if (m_dx_1 > 1.0f) m_dx_1 -= 1.0f; - if (m_dy_1 > 1.0f) m_dy_1 -= 1.0f; - if (m_dx_2 > 1.0f) m_dx_2 -= 1.0f; - if (m_dy_2 < 0.0f) m_dy_2 += 1.0f; - - s32 decaltex = 0; - services->setPixelShaderConstant("DecalTex", &decaltex, 1); - - s32 bumptex = 1; - services->setPixelShaderConstant("BumpTex1", &bumptex, 1); - - bumptex = 2; - services->setPixelShaderConstant("BumpTex2", &bumptex, 1); - - // We could calculate light direction as coming from the sun (then we'd need to - // transform it into camera space). But I find that pretending light - // comes from the camera gives good results - const float lightdir[] = {-0.315f, 0.91f, -0.3f}; - services->setVertexShaderConstant("lightdir", lightdir, 3); - - services->setVertexShaderConstant("delta1", &m_dx_1, 2); - services->setVertexShaderConstant("delta2", &m_dx_2, 2); - - if (m_fog) - { - Track* t = World::getWorld()->getTrack(); - - float fogStart = t->getFogStart(); - services->setPixelShaderConstant("fogFrom", &fogStart, 1); - - float fogEnd = t->getFogEnd(); - services->setPixelShaderConstant("fogTo", &fogEnd, 1); - - video::SColor fogColor = t->getFogColor(); - float fogColorVec[] = {fogColor.getRed()/255.0f, - fogColor.getGreen()/255.0f, - fogColor.getBlue()/255.0f, 1.0f}; - services->setVertexShaderConstant("fogColor", fogColorVec, 4); - } - } -}; - -//----------------------------------------------------------------------------- - -// FIXME: refactor this hack to get per-instance properties, and apply the -// clean fix to all shaders why we're at it...... -std::map grass_shaders_times; -int grass_shaders_times_index = 0; - -class GrassShaderProvider : public video::IShaderConstantSetCallBack -{ - bool m_fog; - float m_angle; - float m_amplitude; - float m_speed; - -public: - LEAK_CHECK() - - - GrassShaderProvider(float amplitude, float speed) - { - m_fog = false; - m_angle = 0.0f; - m_amplitude = amplitude; - m_speed = speed; - } - - - void enableFog(bool enable) - { - m_fog = enable; - } - - void update(float dt) - { - m_angle += GUIEngine::getLatestDt()*m_speed; - if (m_angle > M_PI*2) m_angle -= M_PI*2; - } - - virtual void OnSetConstants(irr::video::IMaterialRendererServices *services, - s32 userData) - { - grass_shaders_times[userData] += GUIEngine::getLatestDt()*m_speed; - if (grass_shaders_times[userData] > M_PI*2) grass_shaders_times[userData] -= M_PI*2; - - services->setVertexShaderConstant("angle", &grass_shaders_times[userData], 1); - - int fog = (m_fog ? 1 : 0); - services->setVertexShaderConstant("fog", &fog, 1); - - s32 tex = 0; - services->setVertexShaderConstant("tex", &tex, 1); - - services->setVertexShaderConstant("amplitude", &m_amplitude, 1); - - if (m_fog) - { - Track* t = World::getWorld()->getTrack(); - - float fogStart = t->getFogStart(); - services->setPixelShaderConstant("fogFrom", &fogStart, 1); - - float fogEnd = t->getFogEnd(); - services->setPixelShaderConstant("fogTo", &fogEnd, 1); - - video::SColor fogColor = t->getFogColor(); - float fogColorVec[] = {fogColor.getRed()/255.0f, - fogColor.getGreen()/255.0f, - fogColor.getBlue()/255.0f, 1.0f}; - services->setVertexShaderConstant("fogColor", fogColorVec, 4); - } - } -}; - -//----------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#endif - -class SplattingProvider : public video::IShaderConstantSetCallBack -{ - core::vector3df m_light_direction; - bool m_light_dir_calculated; - bool m_lightmap; - -public: - LEAK_CHECK() - - SplattingProvider(bool lightmap) - { - m_light_dir_calculated = false; - m_lightmap = lightmap; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - if (!m_light_dir_calculated) - { - m_light_dir_calculated = true; - m_light_direction = -World::getWorld()->getTrack()->getSunRotation().rotationToDirection(); - } - - s32 tex_layout = 1; - services->setPixelShaderConstant("tex_layout", &tex_layout, 1); - - s32 tex_detail0 = 2; - services->setPixelShaderConstant("tex_detail0", &tex_detail0, 1); - - s32 tex_detail1 = 3; - services->setPixelShaderConstant("tex_detail1", &tex_detail1, 1); - - s32 tex_detail2 = 4; - services->setPixelShaderConstant("tex_detail2", &tex_detail2, 1); - - s32 tex_detail3 = 5; - services->setPixelShaderConstant("tex_detail3", &tex_detail3, 1); - - if (m_lightmap) - { - s32 tex_lightmap = 6; - services->setPixelShaderConstant("tex_lightmap", &tex_lightmap, 1); - } - - services->setVertexShaderConstant("lightdir", &m_light_direction.X, 3); - } -}; - -//----------------------------------------------------------------------------- - -#if 0 -#pragma mark - -#endif - -class SphereMapProvider: public video::IShaderConstantSetCallBack -{ - core::vector3df m_light_direction; - -public: - LEAK_CHECK() - - SphereMapProvider() - { - m_light_direction = core::vector3df(-0.6f, -0.5f, -0.63f); - //m_light_direction = core::vector3df(-0.315f, 0.91f, -0.3f); - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - s32 texture = 0; - services->setPixelShaderConstant("texture", &texture, 1); - - services->setVertexShaderConstant("lightdir", &m_light_direction.X, 3); - } -}; - -//----------------------------------------------------------------------------- -#if 0 -#pragma mark - -#endif - -class BubbleEffectProvider : public video::IShaderConstantSetCallBack -{ - irr::u32 initial_time; - float m_transparency; - bool m_is_visible; - -public: - LEAK_CHECK() - - BubbleEffectProvider() - { - initial_time = irr_driver->getDevice()->getTimer()->getRealTime(); - m_transparency = 1.0f; - m_is_visible = true; - } - - virtual void OnSetConstants( - irr::video::IMaterialRendererServices *services, - s32 userData) - { - if (m_is_visible && m_transparency < 1.0f) - { - m_transparency += GUIEngine::getLatestDt()*0.3f; - if (m_transparency > 1.0f) m_transparency = 1.0f; - } - else if (!m_is_visible && m_transparency > 0.0f) - { - m_transparency -= GUIEngine::getLatestDt()*0.3f; - if (m_transparency < 0.0f) m_transparency = 0.0f; - } - - float time = (irr_driver->getDevice()->getTimer()->getRealTime() - initial_time) / 1000.0f; - services->setVertexShaderConstant("time", &time, 1); - services->setVertexShaderConstant("transparency", &m_transparency, 1); - } - - void onMadeVisible() - { - m_is_visible = true; - } - - void onHidden() - { - m_is_visible = false; - m_transparency = 0.0f; - } - - void isInitiallyHidden() - { - m_is_visible = false; - m_transparency = 0.0f; - } -}; - -#if 0 -#pragma mark - -#endif - //----------------------------------------------------------------------------- /** Create a new material using the parameters specified in the xml file. * \param node Node containing the parameters for this material. @@ -568,7 +224,10 @@ Material::Material(const XMLNode *node, int index, bool deprecated) node->get("splatting-texture-2", &m_splatting_texture_2); node->get("splatting-texture-3", &m_splatting_texture_3); node->get("splatting-texture-4", &m_splatting_texture_4); - node->get("splatting-lightmap", &m_splatting_lightmap); + } + else if (s == "caustics") + { + m_graphical_effect = GE_CAUSTICS; } else if (s == "none") { @@ -740,8 +399,6 @@ void Material::init(unsigned int index) m_water_splash = false; m_is_jump_texture = false; - m_shaders.resize(SHADER_COUNT, NULL); - for (int n=0; nremoveTexture(m_texture); } - for (unsigned int n=0; ndrop(); - } - } - - for (std::map::iterator it = m_bubble_provider.begin(); - it != m_bubble_provider.end(); it++) - { - it->second->drop(); - } - // If a special sfx is installed (that isn't part of stk itself), the // entry needs to be removed from the sfx_manager's mapping, since other // tracks might use the same name. @@ -1032,24 +675,10 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } if (m_smooth_reflection_shader) { - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { - if (m_shaders[SHADER_SPHERE_MAP] == NULL) - { - m_shaders[SHADER_SPHERE_MAP] = new SphereMapProvider(); + m->MaterialType = irr_driver->getShader(ES_SPHERE_MAP); } - // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "spheremap.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "spheremap.frag").c_str(), - "main", video::EPST_PS_2_0, - m_shaders[SHADER_SPHERE_MAP], video::EMT_SOLID_2_LAYER ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; - } else { m->MaterialType = video::EMT_SPHERE_MAP; @@ -1068,6 +697,12 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } if (m_graphical_effect == GE_SPHERE_MAP) { + if (irr_driver->isGLSL()) + { + m->MaterialType = irr_driver->getShader(ES_SPHERE_MAP); + } + else + { m->MaterialType = video::EMT_SPHERE_MAP; // sphere map + alpha blending is a supported combination so in @@ -1081,7 +716,8 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m modes++; } } -#if !LIGHTMAP_VISUALISATION + } + if (m_lightmap) { m->MaterialType = video::EMT_LIGHTMAP; @@ -1092,7 +728,7 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m m->MaterialType = video::EMT_LIGHTMAP_ADD; modes++; } -#endif + if (m_add) { //m->MaterialType = video::EMT_TRANSPARENT_ADD_COLOR; @@ -1111,8 +747,7 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (m_graphical_effect == GE_NORMAL_MAP) { IVideoDriver* video_driver = irr_driver->getVideoDriver(); - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { ITexture* tex = irr_driver->getTexture(m_normal_map_tex); if (m_is_heightmap) @@ -1130,37 +765,9 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m with_lightmap = true; } - if (with_lightmap) - { - if (m_shaders[SHADER_NORMAL_MAP_WITH_LIGHTMAP] == NULL) - { - m_shaders[SHADER_NORMAL_MAP_WITH_LIGHTMAP] = - new NormalMapProvider(true); - } - } - else - { - if (m_shaders[SHADER_NORMAL_MAP] == NULL) - { - m_shaders[SHADER_NORMAL_MAP] = new NormalMapProvider(false); - } - } - - const char* vertex_shader = "normalmap.vert"; - const char* pixel_shader = "normalmap.frag"; - // Material and shaders - IGPUProgrammingServices* gpu = - video_driver->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + vertex_shader).c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + pixel_shader).c_str(), - "main", video::EPST_PS_2_0, - m_shaders[with_lightmap ? SHADER_NORMAL_MAP_WITH_LIGHTMAP - : SHADER_NORMAL_MAP], - video::EMT_SOLID_2_LAYER ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader( + with_lightmap ? ES_NORMAL_MAP_LIGHTMAP : ES_NORMAL_MAP ); m->Lighting = false; m->ZWriteEnable = true; @@ -1210,64 +817,20 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m } m->setTexture(5, tex); - if (m_splatting_lightmap.size() > 0) - { - tex = irr_driver->getTexture(m_splatting_lightmap); - } - m->setTexture(6, tex); - - if (m_splatting_lightmap.size() > 0) - { - if (m_shaders[SHADER_SPLATTING_LIGHTMAP] == NULL) - { - m_shaders[SHADER_SPLATTING_LIGHTMAP] = - new SplattingProvider(true); - } - } - else - { - if (m_shaders[SHADER_SPLATTING] == NULL) - { - m_shaders[SHADER_SPLATTING] = new SplattingProvider(false); - } - } - - // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - - if (m_splatting_lightmap.size() > 0) - { - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() - + "splatting_lightmap.vert").c_str(), - "main",video::EVST_VS_2_0, - (file_manager->getShaderDir() - + "splatting_lightmap.frag").c_str(), - "main",video::EPST_PS_2_0, - m_shaders[SHADER_SPLATTING_LIGHTMAP], - video::EMT_SOLID ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader(ES_SPLATTING); } else { - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() - + "splatting.vert").c_str(), - "main",video::EVST_VS_2_0, - (file_manager->getShaderDir() - + "splatting.frag").c_str(), - "main",video::EPST_PS_2_0, - m_shaders[SHADER_SPLATTING], video::EMT_SOLID ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = video::EMT_SOLID; } } - else + if (m_graphical_effect == GE_CAUSTICS && irr_driver->isGLSL()) { - m->MaterialType = video::EMT_SOLID; + m->MaterialType = irr_driver->getShader(ES_CAUSTICS); + + m->setTexture(1, irr_driver->getTexture((file_manager->getTextureDir() + "caustics.png").c_str())); } - } // Modify lightmap materials so that vertex colors are taken into account. @@ -1284,26 +847,14 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (m_graphical_effect == GE_BUBBLE && mb != NULL) { - IVideoDriver* video_driver = irr_driver->getVideoDriver(); - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { - if (m_bubble_provider.find(mb) == m_bubble_provider.end()) - { - m_bubble_provider[mb] = new BubbleEffectProvider(); - } + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + bubble->addBubble(mb); - // Material and shaders - IGPUProgrammingServices* gpu = video_driver->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "bubble.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "bubble.frag").c_str(), - "main", video::EPST_PS_2_0, - m_bubble_provider[mb], - (m_alpha_blending ? video::EMT_TRANSPARENT_ALPHA_CHANNEL - : video::EMT_SOLID) ); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader(ES_BUBBLES); + m->BlendOperation = video::EBO_ADD; // alpha blending and bubble shading can work together so when both are enabled // don't increment the 'modes' counter to not get the 'too many modes' warning @@ -1317,70 +868,40 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m if (m_graphical_effect == GE_WATER_SHADER) { - if (UserConfigParams::m_pixel_shaders && - irr_driver->isGLSL()) + if (irr_driver->isGLSL()) { - if (m_shaders[SHADER_WATER] == NULL) - { - m_shaders[SHADER_WATER] = - new WaterShaderProvider(m_water_shader_speed_1, - m_water_shader_speed_2); - } - m->setTexture(1, irr_driver->getTexture(file_manager->getTextureFile("waternormals.jpg"))); m->setTexture(2, irr_driver->getTexture(file_manager->getTextureFile("waternormals2.jpg"))); - bool fog = World::getWorld()->getTrack()->isFogEnabled(); - const char* vertex_shader = (fog ? "water_fog.vert" : "water.vert"); - const char* pixel_shader = (fog ? "water_fog.frag" : "water.frag"); + ((WaterShaderProvider *) irr_driver->getCallback(ES_WATER))-> + setSpeed(m_water_shader_speed_1/100.0f, m_water_shader_speed_2/100.0f); - ((WaterShaderProvider*)m_shaders[SHADER_WATER])->enableFog(fog); - - // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + vertex_shader).c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + pixel_shader ).c_str(), - "main", video::EPST_PS_2_0, - m_shaders[SHADER_WATER], - video::EMT_TRANSPARENT_ALPHA_CHANNEL); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + m->MaterialType = irr_driver->getShader(ES_WATER); } modes++; } if (m_graphical_effect == GE_GRASS) { - if (UserConfigParams::m_pixel_shaders && + if (UserConfigParams::m_weather_effects && irr_driver->isGLSL()) { - if (m_shaders[SHADER_GRASS] == NULL) - { - m_shaders[SHADER_GRASS] = - new GrassShaderProvider(m_grass_amplitude, m_grass_speed); - } - - bool fog = World::getWorld()->getTrack()->isFogEnabled(); - ((GrassShaderProvider*)m_shaders[SHADER_GRASS])->enableFog(fog); - - grass_shaders_times[grass_shaders_times_index] = (rand() % 500)/500.0f * M_PI * 2.0f; + // Only one grass speed & amplitude per map for now + ((GrassShaderProvider *) irr_driver->getCallback(ES_GRASS))-> + setSpeed(m_grass_speed); + ((GrassShaderProvider *) irr_driver->getCallback(ES_GRASS))-> + setAmplitude(m_grass_amplitude); // Material and shaders - IGPUProgrammingServices* gpu = - irr_driver->getVideoDriver()->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "grass.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "grass.frag").c_str(), - "main", video::EPST_PS_2_0, - m_shaders[SHADER_GRASS], - video::EMT_TRANSPARENT_ALPHA_CHANNEL, - grass_shaders_times_index); - m->MaterialType = (E_MATERIAL_TYPE)material_type; + if (m_alpha_testing) + { + m->MaterialType = irr_driver->getShader(ES_GRASS_REF); + } + else { + m->MaterialType = irr_driver->getShader(ES_GRASS); + m->BlendOperation = video::EBO_ADD; + } - grass_shaders_times_index++; } } @@ -1475,6 +996,10 @@ void Material::setMaterialProperties(video::SMaterial *m, scene::IMeshBuffer* m void Material::adjustForFog(scene::ISceneNode* parent, video::SMaterial *m, bool use_fog) const { + // The new pipeline does fog as a post-process effect. + if (irr_driver->isGLSL()) + return; + m->setFlag(video::EMF_FOG_ENABLE, m_fog && use_fog); if (parent != NULL) @@ -1488,10 +1013,9 @@ void Material::adjustForFog(scene::ISceneNode* parent, video::SMaterial *m, /** Callback from LOD nodes to create some effects */ void Material::onMadeVisible(scene::IMeshBuffer* who) { - if (m_bubble_provider.find(who) != m_bubble_provider.end()) - { - m_bubble_provider[who]->onMadeVisible(); - } + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + bubble->onMadeVisible(who); } //----------------------------------------------------------------------------- @@ -1499,20 +1023,18 @@ void Material::onMadeVisible(scene::IMeshBuffer* who) /** Callback from LOD nodes to create some effects */ void Material::onHidden(scene::IMeshBuffer* who) { - if (m_bubble_provider.find(who) != m_bubble_provider.end()) - { - m_bubble_provider[who]->onHidden(); - } + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + bubble->onHidden(who); } //----------------------------------------------------------------------------- void Material::isInitiallyHidden(scene::IMeshBuffer* who) { - if (m_bubble_provider.find(who) != m_bubble_provider.end()) - { - m_bubble_provider[who]->isInitiallyHidden(); - } + BubbleEffectProvider * bubble = (BubbleEffectProvider *) + irr_driver->getCallback(ES_BUBBLES); + bubble->isInitiallyHidden(who); } //----------------------------------------------------------------------------- diff --git a/src/graphics/material.hpp b/src/graphics/material.hpp index 3c6a342e1..c56bc980a 100644 --- a/src/graphics/material.hpp +++ b/src/graphics/material.hpp @@ -28,8 +28,6 @@ #include -#define LIGHTMAP_VISUALISATION 0 - namespace irr { @@ -42,10 +40,6 @@ class XMLNode; class SFXBase; class ParticleKind; -class NormalMapProvider; -class SplattingProvider; -class BubbleEffectProvider; - /** * \ingroup graphics */ @@ -60,7 +54,8 @@ public: GE_WATER_SHADER, GE_SPHERE_MAP, GE_SPLATTING, - GE_NORMAL_MAP}; + GE_NORMAL_MAP, + GE_CAUSTICS}; enum ParticleConditions { @@ -80,18 +75,6 @@ public: private: - enum Shaders - { - SHADER_NORMAL_MAP, - SHADER_NORMAL_MAP_WITH_LIGHTMAP, - SHADER_SPLATTING, - SHADER_WATER, - SHADER_SPHERE_MAP, - SHADER_SPLATTING_LIGHTMAP, - SHADER_GRASS, - SHADER_COUNT - }; - video::ITexture *m_texture; unsigned int m_index; std::string m_texname; @@ -234,13 +217,6 @@ private: /** If m_splatting is true, indicates the fourth splatting texture */ std::string m_splatting_texture_4; - std::string m_splatting_lightmap; - - std::vector m_shaders; - - /** Only used if bubble effect is enabled */ - std::map m_bubble_provider; - bool m_deprecated; void init (unsigned int index); diff --git a/src/graphics/material_manager.cpp b/src/graphics/material_manager.cpp index a8b85f69a..553ec4a22 100644 --- a/src/graphics/material_manager.cpp +++ b/src/graphics/material_manager.cpp @@ -60,10 +60,6 @@ MaterialManager::~MaterialManager() m_materials.clear(); } // ~MaterialManager -#if LIGHTMAP_VISUALISATION -std::set g_processed; -#endif - //----------------------------------------------------------------------------- Material* MaterialManager::getMaterialFor(video::ITexture* t, diff --git a/src/graphics/mlaa_areamap.hpp b/src/graphics/mlaa_areamap.hpp new file mode 100644 index 000000000..f5c636f68 --- /dev/null +++ b/src/graphics/mlaa_areamap.hpp @@ -0,0 +1,200 @@ +#ifndef AREAMAP_H +#define AREAMAP_H + +static const unsigned char AreaMap33[] = { +0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, +0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x00, 0x00, 0xa5, +0x08, 0x04, 0x00, 0x00, 0x00, 0x97, 0x22, 0xf5, 0x51, 0x00, 0x00, 0x00, +0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00, +0x00, 0x02, 0x62, 0x4b, 0x47, 0x44, 0x00, 0xff, 0x87, 0x8f, 0xcc, 0xbf, +0x00, 0x00, 0x00, 0x09, 0x70, 0x48, 0x59, 0x73, 0x00, 0x00, 0x0b, 0x13, +0x00, 0x00, 0x0b, 0x13, 0x01, 0x00, 0x9a, 0x9c, 0x18, 0x00, 0x00, 0x00, +0x07, 0x74, 0x49, 0x4d, 0x45, 0x07, 0xdb, 0x06, 0x07, 0x09, 0x17, 0x12, +0x5f, 0x51, 0x45, 0x04, 0x00, 0x00, 0x08, 0x87, 0x49, 0x44, 0x41, 0x54, +0x78, 0xda, 0xed, 0x5d, 0x4b, 0x6e, 0xdb, 0x48, 0x14, 0x2c, 0x89, 0x2f, +0x4e, 0xe2, 0x09, 0x30, 0x9b, 0x38, 0x5b, 0xf9, 0x24, 0x73, 0xbb, 0xb9, +0x49, 0x6e, 0x32, 0x67, 0x98, 0x7d, 0xbc, 0xf2, 0xc2, 0x18, 0x78, 0x31, +0x81, 0xc6, 0xa6, 0xc4, 0xd7, 0xc3, 0x8f, 0x1a, 0x0d, 0x9a, 0x92, 0x5b, +0x64, 0x91, 0x7a, 0x1d, 0xa1, 0xab, 0x41, 0x28, 0x41, 0x80, 0x42, 0xd5, +0x63, 0x93, 0x32, 0x9c, 0x42, 0x35, 0xc0, 0x63, 0x03, 0x73, 0x6c, 0x60, +0x8f, 0x35, 0x78, 0xdc, 0xe3, 0x16, 0x2c, 0x78, 0x09, 0x57, 0x31, 0xca, +0x6f, 0xb8, 0xc3, 0x0d, 0x68, 0xd8, 0x4b, 0xb0, 0x1f, 0xe5, 0x2d, 0xbe, +0xe2, 0x77, 0x08, 0x48, 0xd8, 0x4b, 0xb0, 0x1f, 0xe5, 0x47, 0x7c, 0xa9, +0x7d, 0xfc, 0x86, 0x02, 0x06, 0x48, 0x46, 0x02, 0x64, 0x16, 0x8e, 0xcf, +0xb5, 0x8b, 0x17, 0x54, 0xd8, 0x42, 0x41, 0xc0, 0x5a, 0x82, 0xfd, 0x28, +0x0b, 0x7c, 0xac, 0x9d, 0xdc, 0x62, 0x07, 0xc5, 0x0b, 0xe1, 0xc4, 0x5c, +0x82, 0xfd, 0x28, 0xd7, 0xb8, 0xa9, 0x9d, 0x7c, 0xc2, 0x2b, 0xf6, 0x70, +0x78, 0x25, 0x9c, 0x98, 0x4b, 0xb0, 0x1f, 0xa5, 0x1c, 0x9c, 0xec, 0xa1, +0x50, 0x94, 0x70, 0x20, 0x60, 0x27, 0xc1, 0x7e, 0x94, 0x2b, 0x14, 0xf8, +0x50, 0xaf, 0xda, 0x4b, 0xeb, 0x04, 0x84, 0x13, 0x73, 0x09, 0xf6, 0xa3, +0x94, 0xd6, 0x47, 0x59, 0x3b, 0x51, 0x28, 0x1c, 0x76, 0x84, 0x13, 0x53, +0x09, 0xf6, 0xa3, 0x2c, 0xf0, 0xa1, 0xf5, 0xb2, 0xc7, 0xbe, 0xf5, 0xe1, +0xb0, 0x27, 0x9c, 0x98, 0x49, 0xb0, 0x1f, 0xe5, 0x1a, 0xd2, 0x38, 0xf1, +0x3e, 0x5a, 0x27, 0x20, 0x9c, 0x18, 0x4a, 0xb0, 0x1f, 0x65, 0xe7, 0x44, +0xda, 0x6d, 0x51, 0x1d, 0x9c, 0x38, 0x54, 0x84, 0x13, 0x13, 0x09, 0xf6, +0xa3, 0xf4, 0x4e, 0xe4, 0xe0, 0xc4, 0xfb, 0x00, 0xe1, 0xc4, 0x40, 0x82, +0xfd, 0x28, 0x81, 0x35, 0x8a, 0x6e, 0xb5, 0x3e, 0x2a, 0x54, 0x84, 0x13, +0x53, 0x09, 0xf6, 0xbb, 0xd2, 0x6f, 0x8a, 0xe0, 0x26, 0xf8, 0x70, 0xa8, +0xc0, 0x23, 0x7d, 0x09, 0x90, 0x79, 0x9c, 0xf4, 0x3c, 0x54, 0xa8, 0xda, +0x4f, 0x77, 0x58, 0x20, 0x9c, 0x18, 0x48, 0xb0, 0x1f, 0xa5, 0xf7, 0xb2, +0x46, 0xd8, 0x14, 0xda, 0xba, 0x00, 0xe1, 0xc4, 0x48, 0x82, 0xfd, 0x03, +0x1e, 0x9e, 0x30, 0x6d, 0x2f, 0x47, 0x6c, 0x8b, 0x5f, 0x4d, 0x02, 0x64, +0x36, 0x27, 0xc1, 0xc5, 0x3a, 0xf8, 0x68, 0x2e, 0x62, 0x5b, 0x18, 0x48, +0xb0, 0x1e, 0x25, 0x06, 0x9b, 0xa2, 0x7a, 0xe3, 0xc3, 0x2d, 0xe8, 0x84, +0x90, 0x90, 0xe6, 0xbb, 0xb2, 0xbf, 0x0a, 0x68, 0xbb, 0x0a, 0xb8, 0x76, +0x15, 0x40, 0xfb, 0xa9, 0x60, 0xc1, 0x4b, 0x48, 0xfb, 0x5d, 0x39, 0xd8, +0x14, 0x55, 0xf7, 0x19, 0x5e, 0x54, 0x07, 0x2f, 0x20, 0x9c, 0x18, 0x48, +0xb0, 0x7a, 0xc0, 0xfb, 0x4e, 0xb4, 0x59, 0xfe, 0x6f, 0xde, 0x05, 0xe1, +0x24, 0x79, 0x09, 0x32, 0xf3, 0x17, 0xa8, 0x77, 0x12, 0xf4, 0xf7, 0x9f, +0x2e, 0x47, 0x38, 0x31, 0x90, 0x60, 0xbd, 0x2b, 0x87, 0xab, 0xf0, 0x5e, +0x88, 0x6d, 0x91, 0xbc, 0x04, 0x99, 0xd7, 0xc5, 0x09, 0x2f, 0xae, 0xbd, +0xc2, 0x96, 0x70, 0x11, 0x27, 0x56, 0x12, 0xec, 0xbf, 0x76, 0x06, 0x4f, +0x58, 0x70, 0x13, 0x5e, 0x53, 0x7d, 0x17, 0x88, 0x38, 0xb1, 0x95, 0x60, +0xbf, 0x2b, 0x7b, 0x0e, 0x56, 0x6f, 0x36, 0x44, 0xe1, 0x5d, 0xb4, 0x9f, +0x8e, 0x70, 0x62, 0x20, 0xc1, 0xe0, 0x5d, 0x79, 0xf4, 0x09, 0x53, 0xac, +0xfb, 0xef, 0x7d, 0x62, 0x5b, 0x24, 0x2c, 0x41, 0xe6, 0x77, 0x32, 0xfc, +0x1a, 0xed, 0xd4, 0x7b, 0x3f, 0xde, 0x07, 0xe1, 0x24, 0x49, 0x09, 0x32, +0xf7, 0x4f, 0xc8, 0xde, 0x4b, 0xcf, 0x55, 0x70, 0xb1, 0x1e, 0xf8, 0x70, +0x70, 0xd7, 0x21, 0x61, 0x85, 0x0d, 0xee, 0xf1, 0x0d, 0xb7, 0xf8, 0x08, +0x41, 0x71, 0xb8, 0x9d, 0x61, 0x85, 0x37, 0x3a, 0xfc, 0xe7, 0xe0, 0x4f, +0x7f, 0x61, 0x45, 0x32, 0xfc, 0x09, 0x12, 0x9b, 0xcd, 0x03, 0xe9, 0x62, +0xf3, 0x9d, 0x65, 0x10, 0x3c, 0xe0, 0x09, 0x77, 0xf8, 0x8a, 0x2f, 0xf8, +0x5c, 0xd3, 0xdc, 0xb4, 0x44, 0x85, 0x27, 0xeb, 0x11, 0x22, 0x90, 0xf6, +0xc8, 0x57, 0xf8, 0x1b, 0x3f, 0x28, 0x06, 0x1a, 0xf7, 0x0f, 0x4f, 0x4f, +0x5b, 0xca, 0x05, 0xcf, 0x20, 0x00, 0xb6, 0x78, 0xc4, 0xb6, 0xcd, 0xd4, +0x75, 0x24, 0x1f, 0x5a, 0x1a, 0x69, 0x49, 0x8a, 0x37, 0x3f, 0x5d, 0xe0, +0x08, 0xe1, 0x37, 0xfc, 0xc0, 0x3f, 0x0c, 0xc3, 0x2c, 0x51, 0xd5, 0xed, +0xe3, 0x63, 0x49, 0x68, 0xe0, 0x19, 0x04, 0x0d, 0x4a, 0x3c, 0xd7, 0xd7, +0x0b, 0x6e, 0xf1, 0xa9, 0x26, 0xf9, 0xd0, 0xae, 0x8e, 0xa4, 0xe8, 0xd1, +0xd4, 0xd7, 0x60, 0x75, 0x39, 0xd1, 0x2d, 0xfe, 0x25, 0x18, 0x38, 0x78, +0x09, 0xe5, 0xf3, 0xf3, 0x7e, 0xb2, 0x06, 0x9e, 0x41, 0xd0, 0x61, 0x8f, +0x9f, 0xa8, 0xb0, 0xc3, 0x6b, 0x4d, 0x72, 0x53, 0x2f, 0x39, 0x90, 0x14, +0x07, 0x1a, 0xbf, 0x56, 0x43, 0xa2, 0x43, 0x4e, 0xb4, 0xc4, 0x7f, 0x93, +0x19, 0x58, 0x78, 0x09, 0xfb, 0x9f, 0x3f, 0xab, 0x89, 0x1a, 0x78, 0x06, +0x81, 0x47, 0x97, 0xf2, 0xdc, 0xd7, 0xeb, 0x23, 0xca, 0xf6, 0x7e, 0x88, +0x7f, 0x5f, 0xf4, 0x49, 0x06, 0xf7, 0xe5, 0x16, 0x3e, 0x27, 0xfa, 0x32, +0x8d, 0x61, 0xc6, 0xa8, 0x6a, 0xb5, 0xdd, 0xea, 0x24, 0x0d, 0x73, 0x30, +0x04, 0x68, 0xcd, 0xe4, 0xa0, 0x0d, 0x49, 0x4b, 0x25, 0x81, 0x24, 0xdc, +0x93, 0x01, 0xc9, 0xba, 0x97, 0x13, 0x2d, 0xa7, 0x30, 0xcc, 0x1a, 0x55, +0xd5, 0x97, 0x17, 0x9d, 0xa0, 0x81, 0x67, 0x90, 0x9e, 0x22, 0xc5, 0x2b, +0xb4, 0x59, 0x2d, 0x85, 0xbf, 0x1f, 0xd2, 0x12, 0x78, 0x8a, 0x21, 0x4d, +0x3f, 0x27, 0xba, 0x1b, 0xcf, 0x30, 0x73, 0x54, 0x55, 0x5f, 0x5f, 0x75, +0xb4, 0x06, 0x9e, 0x41, 0xd0, 0x87, 0xa2, 0x04, 0xd0, 0xd1, 0x54, 0x6d, +0xf8, 0xa6, 0x40, 0xd5, 0x11, 0x9c, 0x22, 0x19, 0xe4, 0x44, 0x77, 0x63, +0x19, 0x66, 0x8f, 0xaa, 0x6a, 0x59, 0xba, 0x91, 0x1a, 0x78, 0x06, 0xc1, +0x5b, 0x38, 0x94, 0x70, 0xf5, 0xf2, 0x34, 0xd2, 0x50, 0x44, 0xb6, 0xf7, +0xdb, 0x9c, 0xe8, 0x6e, 0x1c, 0xc3, 0x02, 0x51, 0x55, 0x57, 0x8f, 0x62, +0x94, 0x06, 0x9e, 0x41, 0x30, 0x84, 0xeb, 0x8d, 0x42, 0xfd, 0xff, 0xce, +0xfb, 0x3b, 0xe3, 0x29, 0x02, 0xd1, 0x91, 0x9c, 0xe8, 0x7e, 0x0c, 0xc3, +0x22, 0x51, 0x55, 0xb7, 0xdb, 0xb9, 0x11, 0x1a, 0x78, 0x06, 0xc1, 0x11, +0xb4, 0xa3, 0x00, 0x3c, 0x8d, 0x34, 0x34, 0xed, 0x52, 0xac, 0x9b, 0xab, +0x4f, 0x72, 0x22, 0x27, 0x5a, 0xa1, 0x3c, 0x97, 0x61, 0xa1, 0xa8, 0xaa, +0xdb, 0xef, 0xdd, 0xd9, 0x2e, 0x78, 0x06, 0xc1, 0x71, 0x74, 0xfb, 0xca, +0x2f, 0xf5, 0x2b, 0x50, 0x04, 0xa2, 0x13, 0x39, 0xd1, 0x12, 0xd5, 0xb9, +0x0c, 0x8b, 0x45, 0x55, 0x9b, 0x51, 0x9c, 0xa9, 0x81, 0x67, 0x10, 0x1c, +0x87, 0x8f, 0x7f, 0x79, 0x0a, 0xff, 0x59, 0x34, 0x57, 0x9f, 0xe2, 0x9d, +0x9c, 0xa8, 0x62, 0x77, 0x0e, 0xc3, 0x82, 0x51, 0x55, 0x57, 0x55, 0xee, +0x2c, 0x17, 0x3c, 0x83, 0x00, 0x91, 0x61, 0x06, 0x1a, 0x45, 0xd1, 0xfd, +0x8e, 0xaa, 0x4f, 0xf1, 0x4e, 0x4e, 0x74, 0x07, 0xc5, 0x3e, 0xce, 0xb0, +0x68, 0x54, 0xb5, 0x1d, 0x45, 0x5c, 0x03, 0xcf, 0x20, 0x40, 0x64, 0x98, +0x2d, 0x1b, 0x9c, 0x5f, 0x03, 0x8a, 0xf5, 0xbb, 0x39, 0xd1, 0x3d, 0x14, +0xbb, 0x18, 0xc3, 0x62, 0x51, 0xd5, 0x30, 0x8a, 0xa8, 0x0b, 0x9e, 0x41, +0x80, 0xe8, 0x30, 0x71, 0x94, 0xa2, 0x80, 0xeb, 0xde, 0x17, 0x91, 0x9c, +0xe8, 0x2e, 0xce, 0xb0, 0x78, 0x54, 0xd5, 0x55, 0x51, 0x0d, 0x3c, 0x83, +0x20, 0x8e, 0xaa, 0xa5, 0xf0, 0x44, 0x45, 0x77, 0x75, 0x44, 0xed, 0x67, +0x3c, 0x27, 0x1a, 0x67, 0x58, 0x3e, 0xaa, 0x1a, 0xd1, 0xc0, 0x33, 0x08, +0xce, 0x81, 0xbf, 0x23, 0x05, 0xd0, 0x11, 0xf4, 0x62, 0x0f, 0x91, 0x9c, +0x68, 0x9c, 0xe1, 0x42, 0x51, 0xd5, 0x88, 0x06, 0x96, 0x41, 0x80, 0x11, +0xc3, 0x0c, 0x34, 0xcd, 0xea, 0x48, 0xf4, 0x9c, 0x9c, 0x68, 0x84, 0xe1, +0x52, 0x51, 0xd5, 0x77, 0x34, 0xf0, 0x0c, 0x82, 0x73, 0x51, 0xf5, 0xee, +0x87, 0x27, 0x51, 0xac, 0xe3, 0x39, 0xd1, 0x18, 0xc3, 0x05, 0xa3, 0xaa, +0x27, 0x35, 0xf0, 0x0c, 0x82, 0xf3, 0x51, 0x21, 0x44, 0x1b, 0x3c, 0xf7, +0x1a, 0xee, 0xbc, 0x9c, 0x68, 0x84, 0xe1, 0x72, 0x51, 0xd5, 0x13, 0x1a, +0x78, 0x06, 0xc1, 0x18, 0x28, 0x02, 0x05, 0x0e, 0x44, 0xeb, 0xb1, 0x39, +0xd1, 0x21, 0xc3, 0x85, 0xa3, 0xaa, 0x47, 0x35, 0xf0, 0x0c, 0x82, 0x71, +0x50, 0x04, 0xb8, 0x6e, 0x11, 0x39, 0x51, 0xff, 0xaf, 0x97, 0x8e, 0xaa, +0x1e, 0xd1, 0xc0, 0x33, 0x08, 0x30, 0x71, 0x98, 0xce, 0xaf, 0xa9, 0x39, +0xd1, 0xc0, 0x60, 0x10, 0x55, 0x1d, 0x68, 0xe0, 0x19, 0x04, 0x98, 0x34, +0x4c, 0x47, 0xe7, 0x44, 0x9d, 0x71, 0x54, 0xd5, 0x6b, 0x20, 0x19, 0xd8, +0xa0, 0x8b, 0xf6, 0xee, 0xc7, 0xc4, 0x9c, 0x68, 0x60, 0x30, 0x8a, 0xaa, +0xf6, 0x34, 0xf0, 0x0c, 0x82, 0x69, 0xd0, 0xab, 0x88, 0xaa, 0xea, 0x9c, +0x0c, 0x82, 0xa9, 0xd0, 0xab, 0x88, 0xaa, 0xea, 0x7c, 0x0c, 0x42, 0xe8, +0xd7, 0xab, 0x88, 0xaa, 0xea, 0x5c, 0x0c, 0x02, 0x06, 0x7a, 0x15, 0x51, +0x55, 0x9d, 0x87, 0x41, 0xc0, 0x41, 0xe1, 0xe8, 0x9c, 0xa8, 0x33, 0x8f, +0xaa, 0x2a, 0x1c, 0xcf, 0x20, 0x60, 0xe1, 0xae, 0x22, 0xaa, 0xea, 0xc0, +0x33, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, +0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, +0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, +0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x2c, 0x83, 0x15, 0x5f, 0xaa, +0xfa, 0x7d, 0x73, 0xff, 0x60, 0x5c, 0xaa, 0x8a, 0x3f, 0xb0, 0x32, 0x77, +0x21, 0x7c, 0xa9, 0xea, 0xc3, 0xfd, 0xd3, 0xd3, 0xdd, 0x96, 0x61, 0xe0, +0xc1, 0x17, 0xbb, 0xf2, 0x2e, 0x84, 0x2f, 0x55, 0xc5, 0xb7, 0xed, 0xdd, +0xe3, 0xe3, 0xb6, 0x24, 0x18, 0x68, 0xf0, 0xc5, 0xae, 0xbc, 0x0b, 0x61, +0x4b, 0x55, 0xbb, 0x4a, 0xd3, 0x72, 0xfb, 0xfc, 0x5c, 0xee, 0x8d, 0x4a, +0x55, 0x43, 0xb1, 0xab, 0xa9, 0x0b, 0xe1, 0x4b, 0x55, 0xbb, 0x4a, 0xd3, +0x7d, 0xd9, 0xf4, 0x91, 0x1a, 0x95, 0xaa, 0x86, 0x62, 0x57, 0x43, 0x17, +0xc2, 0x97, 0xaa, 0xfa, 0x4a, 0xd3, 0xae, 0x8f, 0x74, 0x0a, 0x03, 0x0f, +0xa1, 0x8b, 0x5d, 0x79, 0x17, 0xc2, 0x97, 0xaa, 0x86, 0x4a, 0xd3, 0xa6, +0x8f, 0xd4, 0xe9, 0x78, 0x06, 0x1e, 0x7c, 0xb1, 0x2b, 0xef, 0x42, 0xe8, +0x52, 0xd5, 0x5e, 0xa5, 0xa9, 0xba, 0xa6, 0x8f, 0x74, 0x2c, 0x03, 0x0f, +0xbe, 0xd8, 0x95, 0x77, 0x21, 0x7c, 0xa9, 0x6a, 0xbf, 0xd2, 0x54, 0xb5, +0x2c, 0xe1, 0xc6, 0x31, 0xf0, 0xe0, 0x8b, 0x5d, 0x79, 0x17, 0x42, 0x97, +0xaa, 0x0e, 0x2a, 0x4d, 0x1d, 0x9a, 0x3e, 0xd2, 0x31, 0x0c, 0x3c, 0xf8, +0x62, 0x57, 0xde, 0x85, 0xf0, 0xa5, 0xaa, 0xc3, 0x4a, 0xd3, 0xae, 0x8f, +0x74, 0x04, 0x03, 0x0d, 0xbe, 0xd8, 0x95, 0x77, 0x21, 0x74, 0xa9, 0xea, +0xd1, 0x4a, 0xd3, 0xa6, 0x8f, 0x14, 0xee, 0x5c, 0x06, 0x1e, 0x7c, 0xb1, +0x2b, 0xef, 0x42, 0xe8, 0x52, 0xd5, 0x13, 0x95, 0xa6, 0x0e, 0x4d, 0x1f, +0xe9, 0x79, 0x0c, 0x3c, 0xf8, 0x62, 0x57, 0xde, 0x85, 0xd0, 0xa5, 0xaa, +0x27, 0x2b, 0x4d, 0xdb, 0xa2, 0x42, 0x77, 0x0e, 0x03, 0x0f, 0xbe, 0xd8, +0x95, 0x77, 0x21, 0x7c, 0xa9, 0xea, 0xe9, 0x4a, 0x53, 0x87, 0x46, 0x46, +0x9c, 0x81, 0x07, 0x5f, 0xec, 0xca, 0xbb, 0x10, 0xba, 0x54, 0xf5, 0x44, +0xa5, 0x69, 0x90, 0xe1, 0x5c, 0x8c, 0x81, 0x07, 0x5f, 0xec, 0xca, 0xbb, +0x10, 0xba, 0x54, 0x35, 0x52, 0x69, 0xda, 0x3c, 0x20, 0x31, 0x06, 0x1e, +0x7c, 0xb1, 0x2b, 0xef, 0x42, 0xf8, 0x52, 0xd5, 0x78, 0xa5, 0x69, 0x55, +0x45, 0x18, 0x48, 0x44, 0x25, 0x5c, 0xc4, 0x85, 0xf0, 0xa5, 0xaa, 0xf1, +0x4a, 0xd3, 0x46, 0x46, 0x84, 0x81, 0x44, 0x5c, 0xc2, 0xf2, 0x2e, 0x84, +0x2e, 0x55, 0x3d, 0xb3, 0xd2, 0xb4, 0xaa, 0x4e, 0x32, 0xcc, 0xf5, 0x80, +0x47, 0x24, 0x2c, 0xed, 0x42, 0xe8, 0x52, 0xd5, 0xb3, 0x2b, 0x4d, 0xab, +0x6a, 0xb9, 0x52, 0xd5, 0xb8, 0x84, 0xe5, 0x5d, 0x08, 0x5d, 0xaa, 0x3a, +0xa2, 0xd2, 0xb4, 0xaa, 0x16, 0x2c, 0x55, 0x8d, 0x4b, 0x58, 0xd8, 0x85, +0xd0, 0xa5, 0xaa, 0xa3, 0x2a, 0x4d, 0x55, 0x8f, 0x31, 0x5c, 0xb4, 0x55, +0x75, 0x31, 0x17, 0x42, 0x97, 0xaa, 0x8e, 0xac, 0x34, 0x55, 0x1d, 0x32, +0x5c, 0xb0, 0x55, 0x75, 0x41, 0x17, 0x42, 0x97, 0xaa, 0x8e, 0xae, 0x34, +0x55, 0x7d, 0xcb, 0x60, 0xd0, 0xaa, 0xba, 0x80, 0x0b, 0x99, 0x58, 0xaa, +0x4a, 0x55, 0x9a, 0xaa, 0x7a, 0x06, 0x93, 0x56, 0xd5, 0x85, 0x5c, 0x08, +0x5d, 0xaa, 0x3a, 0xa9, 0xd2, 0x54, 0xb5, 0xc7, 0x60, 0xd0, 0xaa, 0x3a, +0xbf, 0x0b, 0xc1, 0x24, 0x28, 0x5d, 0x69, 0xaa, 0xd6, 0xad, 0xaa, 0xb3, +0xbb, 0x10, 0x4c, 0x84, 0xd2, 0x95, 0xa6, 0x6a, 0xd9, 0xaa, 0xba, 0x80, +0x0b, 0xc1, 0x64, 0x28, 0x5d, 0x69, 0xaa, 0xb6, 0xad, 0xaa, 0x33, 0xbb, +0x10, 0x10, 0x50, 0xba, 0xd2, 0x54, 0x2d, 0x5b, 0x55, 0x67, 0x76, 0x21, +0x74, 0xa9, 0x2a, 0x59, 0x69, 0xaa, 0xea, 0x60, 0xdd, 0xaa, 0xca, 0xbb, +0x68, 0x5c, 0x08, 0x48, 0x38, 0xba, 0xd2, 0xd4, 0xd9, 0xb7, 0xaa, 0xce, +0xe2, 0x62, 0x05, 0x1a, 0x1b, 0xc3, 0xa8, 0x6a, 0xd0, 0xf0, 0x70, 0x0f, +0x63, 0x11, 0x02, 0x16, 0x49, 0x44, 0x55, 0xf9, 0xe3, 0xfb, 0x01, 0xfb, +0x51, 0x26, 0x11, 0x55, 0xe5, 0x8f, 0xef, 0xb7, 0x1f, 0x65, 0x22, 0x51, +0x55, 0xfe, 0xf8, 0xfe, 0x14, 0x46, 0x99, 0x44, 0x54, 0x95, 0x3f, 0xbe, +0x3f, 0x85, 0x51, 0x26, 0x11, 0x55, 0x25, 0x8e, 0xef, 0x4f, 0x68, 0x94, +0x49, 0x44, 0x55, 0xf9, 0xe3, 0xfb, 0x53, 0x18, 0x65, 0x12, 0x51, 0x55, +0xfe, 0xf8, 0xfe, 0x44, 0x46, 0x69, 0x1f, 0x55, 0x65, 0x8f, 0xef, 0x4f, +0x63, 0x94, 0x49, 0x44, 0x55, 0x89, 0xe3, 0xfb, 0xd3, 0x1a, 0xa5, 0x7d, +0x54, 0x95, 0x3f, 0xbe, 0x3f, 0x91, 0x51, 0xda, 0x47, 0x55, 0xf9, 0xe3, +0xfb, 0x13, 0x19, 0xa5, 0x7d, 0x54, 0x95, 0x39, 0xbe, 0x3f, 0xa9, 0x51, +0xda, 0x47, 0x55, 0x89, 0xe3, 0xfb, 0x59, 0x11, 0xf3, 0x8e, 0xd2, 0x3e, +0xaa, 0x4a, 0x1c, 0xdf, 0x9f, 0xd0, 0x28, 0x93, 0x88, 0xaa, 0xf2, 0xc7, +0xf7, 0xa7, 0x30, 0xca, 0x24, 0xa2, 0xaa, 0xdc, 0xf1, 0xfd, 0xa9, 0x8c, +0x32, 0x89, 0xa8, 0x2a, 0x7f, 0x7c, 0x7f, 0x4a, 0xa3, 0x4c, 0x3f, 0xaa, +0x1a, 0xc9, 0xaa, 0xda, 0x8f, 0x32, 0x89, 0xa8, 0x2a, 0x7f, 0x7c, 0x7f, +0x0a, 0xa3, 0x4c, 0x22, 0xaa, 0xca, 0x1f, 0xdf, 0x9f, 0xc8, 0x28, 0xed, +0xa3, 0xaa, 0xfc, 0xf1, 0xfd, 0x89, 0x8c, 0xd2, 0x3e, 0xaa, 0xca, 0x1f, +0xdf, 0x9f, 0xc2, 0x28, 0x7f, 0xc5, 0xa8, 0xaa, 0x22, 0x20, 0xa1, 0xaf, +0x9d, 0x5f, 0x33, 0xaa, 0xaa, 0x7e, 0x90, 0xf6, 0xa3, 0xfc, 0xf5, 0xa3, +0xaa, 0x8a, 0x99, 0x45, 0xc8, 0xc5, 0x73, 0xa2, 0xe9, 0x44, 0x55, 0x35, +0x8d, 0x07, 0xfc, 0x3a, 0xa2, 0xaa, 0x9a, 0xc6, 0xae, 0xbc, 0x8e, 0xa8, +0xaa, 0xa6, 0x31, 0xca, 0xeb, 0x88, 0xaa, 0x6a, 0x1a, 0xa3, 0xbc, 0x8e, +0xa8, 0xaa, 0xa6, 0x32, 0xca, 0x6b, 0x88, 0xaa, 0x6a, 0x1a, 0x5f, 0x3b, +0xd7, 0x11, 0x55, 0x75, 0xa0, 0xf1, 0x3f, 0x19, 0xc2, 0xb4, 0x90, 0x78, +0xb9, 0x76, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, +0x42, 0x60, 0x82, +}; + +#endif diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp index 56fc1cbc3..46bcbadb1 100644 --- a/src/graphics/particle_emitter.cpp +++ b/src/graphics/particle_emitter.cpp @@ -22,9 +22,12 @@ #include "graphics/material_manager.hpp" #include "graphics/particle_kind.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/shaders.hpp" +#include "graphics/wind.hpp" #include "io/file_manager.hpp" #include "tracks/track.hpp" #include "utils/constants.hpp" +#include "utils/helpers.hpp" #include #include @@ -174,6 +177,46 @@ public: } }; +// ============================================================================ + +class WindAffector : public scene::IParticleAffector +{ + /** (Squared) distance from camera at which a particle is completely faded out */ + float m_speed; + float m_seed; + +public: + WindAffector(float speed): m_speed(speed) + { + m_seed = (rand() % 1000) - 500; + } + + // ------------------------------------------------------------------------ + + virtual void affect(u32 now, scene::SParticle* particlearray, u32 count) + { + const float time = irr_driver->getDevice()->getTimer()->getTime() / 10000.0f; + core::vector3df dir = irr_driver->getWind(); + dir *= m_speed * std::min(noise2d(time, m_seed), -0.2f); + + for (u32 n = 0; n < count; n++) + { + scene::SParticle& cur = particlearray[n]; + + cur.pos += dir; + } // for naddAffector(faa); faa->drop(); } - + if (type->hasScaleAffector()) { core::dimension2df factor = core::dimension2df(type->getScaleAffectorFactorX(), @@ -499,6 +542,21 @@ void ParticleEmitter::setParticleType(const ParticleKind* type) m_node->addAffector(scale_affector); scale_affector->drop(); } + + const float windspeed = type->getWindSpeed(); + if (windspeed > 0.01f) + { + WindAffector *waf = new WindAffector(windspeed); + m_node->addAffector(waf); + waf->drop(); + } + + const bool flips = type->getFlips(); + if (flips) + { + m_node->getMaterial(0).MaterialType = irr_driver->getShader(ES_SNOW); + m_node->getMaterial(0).BlendOperation = video::EBO_ADD; + } } } // setParticleType diff --git a/src/graphics/particle_kind.cpp b/src/graphics/particle_kind.cpp index d84b3374a..9d4370b18 100644 --- a/src/graphics/particle_kind.cpp +++ b/src/graphics/particle_kind.cpp @@ -54,6 +54,8 @@ ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,2 m_has_scale_affector = NULL; m_scale_affector_factor_x = 0.0f; m_scale_affector_factor_y = 0.0f; + m_wind_speed = 0; + m_flips = false; // ----- Read XML file @@ -219,6 +221,14 @@ ParticleKind::ParticleKind(const std::string file) : m_min_start_color(255,255,2 m_material_file = material_manager->getLatestMaterial()->getTexFname(); } + // ------------------------------------------------------------------------ + + const XMLNode* wind = xml->getNode("wind"); + if (wind != NULL) + { + wind->get("speed", &m_wind_speed); + wind->get("flips", &m_flips); + } // ------------------------------------------------------------------------ diff --git a/src/graphics/particle_kind.hpp b/src/graphics/particle_kind.hpp index af9718e6b..64c5c8f80 100644 --- a/src/graphics/particle_kind.hpp +++ b/src/graphics/particle_kind.hpp @@ -86,6 +86,11 @@ private: int m_emission_decay_rate; + /** Wind. < 0.01 if disabled. */ + float m_wind_speed; + + bool m_flips; + std::string m_name; std::string m_material_file; @@ -148,11 +153,14 @@ public: int getEmissionDecayRate() const { return m_emission_decay_rate; } - bool hasScaleAffector() const { return m_has_scale_affector; } float getScaleAffectorFactorX() const { return m_scale_affector_factor_x; } float getScaleAffectorFactorY() const { return m_scale_affector_factor_y; }; + float getWindSpeed() const { return m_wind_speed; } + + bool getFlips() const { return m_flips; } + std::string getName() const { return m_name; } }; diff --git a/src/graphics/per_camera_node.cpp b/src/graphics/per_camera_node.cpp index 010e0142e..6bccd7f41 100644 --- a/src/graphics/per_camera_node.cpp +++ b/src/graphics/per_camera_node.cpp @@ -24,7 +24,7 @@ #include PerCameraNode::PerCameraNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, - scene::ICameraSceneNode* camera, scene::IMesh* mesh) + scene::ICameraSceneNode* camera, scene::ISceneNode *node) : IDummyTransformationSceneNode(parent, mgr, id) { #ifdef DEBUG @@ -33,7 +33,10 @@ PerCameraNode::PerCameraNode(scene::ISceneNode* parent, scene::ISceneManager* mg #endif m_camera = camera; - m_child = mgr->addMeshSceneNode(mesh, this); + + node->setParent(this); + m_child = node; + //m_child = mgr->addCubeSceneNode(0.5f, this, -1, core::vector3df(0,0,0), core::vector3df(0,0,0), core::vector3df(3.0f,0.2f,3.0f)); //RelativeTransformationMatrix.setTranslation( core::vector3df(-0.5,-1,3) ); diff --git a/src/graphics/per_camera_node.hpp b/src/graphics/per_camera_node.hpp index 23455f727..1a7542af6 100644 --- a/src/graphics/per_camera_node.hpp +++ b/src/graphics/per_camera_node.hpp @@ -54,7 +54,7 @@ private: public: PerCameraNode(scene::ISceneNode* parent, scene::ISceneManager* mgr, s32 id, - scene::ICameraSceneNode* camera, scene::IMesh* mesh); + scene::ICameraSceneNode* camera, scene::ISceneNode* node); virtual ~PerCameraNode(); //! returns the axis aligned bounding box of this node diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp index d29b1c82b..789fd88a8 100644 --- a/src/graphics/post_processing.cpp +++ b/src/graphics/post_processing.cpp @@ -19,69 +19,46 @@ #include "config/user_config.hpp" #include "io/file_manager.hpp" +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/mlaa_areamap.hpp" +#include "graphics/shaders.hpp" +#include "karts/abstract_kart.hpp" +#include "karts/kart_model.hpp" +#include "modes/world.hpp" #include "race/race_manager.hpp" +#include "tracks/track.hpp" #include "utils/log.hpp" -#include -#include - -#define MOTION_BLUR_FACTOR (1.0f/15.0f) -#define MOTION_BLUR_OFFSET 20.0f +#include using namespace video; using namespace scene; -PostProcessing::PostProcessing(video::IVideoDriver* video_driver) +PostProcessing::PostProcessing(IVideoDriver* video_driver) { - // Check if post-processing is supported on this hardware - m_supported = false; - if( irr_driver->isGLSL() ) - { - m_supported = true; - } - - //Check which texture dimensions are supported on this hardware - bool nonsquare = video_driver->queryFeature(video::EVDF_TEXTURE_NSQUARE); - bool nonpower = video_driver->queryFeature(video::EVDF_TEXTURE_NPOT); - if (!nonpower) { - Log::warn("PostProcessing", - "Only power of two textures are supported."); - } - if (!nonsquare) { - Log::warn("PostProcessing", "Only square textures are supported."); - } // Initialization - if(m_supported) + m_material.Wireframe = false; + m_material.Lighting = false; + m_material.ZWriteEnable = false; + m_material.ZBuffer = ECFN_ALWAYS; + m_material.setFlag(EMF_TRILINEAR_FILTER, true); + + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) { - // Render target - core::dimension2du opt = video_driver->getScreenSize() - .getOptimalSize(!nonpower, !nonsquare); - m_render_target = - video_driver->addRenderTargetTexture(opt, "postprocess"); - if(!m_render_target) - { - Log::warn("PostProcessing", "Couldn't create the render target " - "for post-processing, disabling it."); - UserConfigParams::m_postprocess_enabled = false; + m_material.TextureLayer[i].TextureWrapU = + m_material.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; } - // Material and shaders - IGPUProgrammingServices* gpu = - video_driver->getGPUProgrammingServices(); - s32 material_type = gpu->addHighLevelShaderMaterialFromFiles( - (file_manager->getShaderDir() + "motion_blur.vert").c_str(), - "main", video::EVST_VS_2_0, - (file_manager->getShaderDir() + "motion_blur.frag").c_str(), - "main", video::EPST_PS_2_0, - this, video::EMT_SOLID); - m_blur_material.MaterialType = (E_MATERIAL_TYPE)material_type; - m_blur_material.setTexture(0, m_render_target); - m_blur_material.Wireframe = false; - m_blur_material.Lighting = false; - m_blur_material.ZWriteEnable = false; + // Load the MLAA area map + io::IReadFile *areamap = irr_driver->getDevice()->getFileSystem()-> + createMemoryReadFile((void *) AreaMap33, sizeof(AreaMap33), + "AreaMap33", false); + if (!areamap) Log::fatal("postprocessing", "Failed to load the areamap"); + m_areamap = irr_driver->getVideoDriver()->getTexture(areamap); + areamap->drop(); - } } // PostProcessing // ---------------------------------------------------------------------------- @@ -96,12 +73,15 @@ PostProcessing::~PostProcessing() */ void PostProcessing::reset() { - unsigned int n = Camera::getNumCameras(); + const u32 n = Camera::getNumCameras(); m_boost_time.resize(n); m_vertices.resize(n); m_center.resize(n); m_direction.resize(n); + MotionBlurProvider * const cb = (MotionBlurProvider *) irr_driver-> + getCallback(ES_MOTIONBLUR); + for(unsigned int i=0; isetDirection(i, m_direction[i].X, m_direction[i].Y); + cb->setMaxHeight(i, m_vertices[i].v1.TCoords.Y); } // for i + getCallback(ES_MOTIONBLUR); - bool any_boost = false; - for(unsigned int i=0; i0.0f; + const float tex_height = m_vertices[num].v1.TCoords.Y - m_vertices[num].v0.TCoords.Y; + m_center[num].Y = m_vertices[num].v0.TCoords.Y + y * tex_height; - // Don't capture the input when we have no post-processing to add - // it will be faster and this ay we won't lose anti-aliasing - if(!any_boost) - { - m_used_pp_this_frame = false; - return; - } - - m_used_pp_this_frame = true; - irr_driver->getVideoDriver()->setRenderTarget(m_render_target, true, true); -} // beginCapture + cb->setCenter(num, m_center[num].X, m_center[num].Y); +} // ---------------------------------------------------------------------------- -/** Restore the framebuffer render target. +/** Setup some PP data. */ -void PostProcessing::endCapture() +void PostProcessing::begin() { - if(!m_supported || !UserConfigParams::m_postprocess_enabled || - !m_used_pp_this_frame) - return; - - irr_driver->getVideoDriver()->setRenderTarget(video::ERT_FRAME_BUFFER, - true, true, 0); -} // endCapture + m_any_boost = false; + for (u32 i = 0; i < m_boost_time.size(); i++) + m_any_boost |= m_boost_time[i] > 0.01f; +} // beginCapture // ---------------------------------------------------------------------------- /** Set the boost amount according to the speed of the camera */ void PostProcessing::giveBoost(unsigned int camera_index) { m_boost_time[camera_index] = 0.75f; + + MotionBlurProvider * const cb = (MotionBlurProvider *) irr_driver-> + getCallback(ES_MOTIONBLUR); + cb->setBoostTime(camera_index, m_boost_time[camera_index]); } // giveBoost // ---------------------------------------------------------------------------- @@ -203,6 +174,9 @@ void PostProcessing::giveBoost(unsigned int camera_index) */ void PostProcessing::update(float dt) { + MotionBlurProvider * const cb = (MotionBlurProvider *) irr_driver-> + getCallback(ES_MOTIONBLUR); + for(unsigned int i=0; i 0.0f) @@ -210,68 +184,571 @@ void PostProcessing::update(float dt) m_boost_time[i] -= dt; if (m_boost_time[i] < 0.0f) m_boost_time[i] = 0.0f; } + + cb->setBoostTime(i, m_boost_time[i]); } } // update +// ---------------------------------------------------------------------------- +/** Render the post-processed scene, solids only, color to color, no stencil */ +void PostProcessing::renderSolid(const u32 cam) +{ + // Early out: do nothing if at all possible + if (UserConfigParams::m_ssao < 1 && !World::getWorld()->getTrack()->isFogEnabled()) + return; + + static u8 tick = 0; + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, core::IdentityMatrix); + drv->setTransform(ETS_VIEW, core::IdentityMatrix); + drv->setTransform(ETS_PROJECTION, core::IdentityMatrix); + + GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver-> + getCallback(ES_GAUSSIAN3H); + + const TypeRTT curssao = tick ? RTT_SSAO2 : RTT_SSAO1; + + if (World::getWorld()->getTrack()->isFogEnabled()) + { + m_material.MaterialType = irr_driver->getShader(ES_FOG); + m_material.setTexture(0, irr_driver->getRTT(RTT_DEPTH)); + + // Overlay + m_material.BlendOperation = EBO_ADD; + m_material.MaterialTypeParam = pack_textureBlendFunc(EBF_SRC_ALPHA, EBF_ONE_MINUS_SRC_ALPHA); + + drv->setRenderTarget(irr_driver->getRTT(RTT_COLOR), false, false); + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + m_material.MaterialTypeParam = 0; + } + + if (UserConfigParams::m_ssao == 1) // SSAO low + { + m_material.MaterialType = irr_driver->getShader(ES_SSAO); + m_material.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + m_material.setTexture(1, irr_driver->getRTT(tick ? RTT_SSAO1 : RTT_SSAO2)); + + drv->setRenderTarget(irr_driver->getRTT(curssao), true, false, + SColor(255, 255, 255, 255)); + + drawQuad(cam, m_material); + + // Blur it to reduce noise. + { + gacb->setResolution(UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + m_material.setTexture(0, irr_driver->getRTT(curssao)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(irr_driver->getRTT(curssao), false, false); + + drawQuad(cam, m_material); + } + + // Overlay + m_material.MaterialType = EMT_ONETEXTURE_BLEND; + m_material.setTexture(0, irr_driver->getRTT(curssao)); + m_material.setTexture(1, 0); + m_material.BlendOperation = EBO_ADD; + m_material.MaterialTypeParam = pack_textureBlendFunc(EBF_DST_COLOR, EBF_ZERO); + + drv->setRenderTarget(irr_driver->getRTT(RTT_COLOR), false, false); + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + m_material.MaterialTypeParam = 0; + + } else if (UserConfigParams::m_ssao == 2) // SSAO high + { + m_material.MaterialType = irr_driver->getShader(ES_SSAO); + m_material.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + m_material.setTexture(1, irr_driver->getRTT(tick ? RTT_SSAO1 : RTT_SSAO2)); + + drv->setRenderTarget(irr_driver->getRTT(curssao), true, false, + SColor(255, 255, 255, 255)); + + drawQuad(cam, m_material); + + // Blur it to reduce noise. + { + gacb->setResolution(UserConfigParams::m_width, + UserConfigParams::m_height); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6V); + m_material.setTexture(0, irr_driver->getRTT(curssao)); + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6H); + m_material.setTexture(0, irr_driver->getRTT(RTT_TMP3)); + drv->setRenderTarget(irr_driver->getRTT(curssao), false, false); + + drawQuad(cam, m_material); + } + + // Overlay + m_material.MaterialType = EMT_ONETEXTURE_BLEND; + m_material.setTexture(0, irr_driver->getRTT(curssao)); + m_material.setTexture(1, 0); + m_material.BlendOperation = EBO_ADD; + m_material.MaterialTypeParam = pack_textureBlendFunc(EBF_DST_COLOR, EBF_ZERO); + + drv->setRenderTarget(irr_driver->getRTT(RTT_COLOR), false, false); + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + m_material.MaterialTypeParam = 0; + } + + tick++; + tick %= 2; +} + // ---------------------------------------------------------------------------- /** Render the post-processed scene */ void PostProcessing::render() { - if(!m_supported || !UserConfigParams::m_postprocess_enabled) - return; + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, core::IdentityMatrix); + drv->setTransform(ETS_VIEW, core::IdentityMatrix); + drv->setTransform(ETS_PROJECTION, core::IdentityMatrix); - if (!m_used_pp_this_frame) + MotionBlurProvider * const mocb = (MotionBlurProvider *) irr_driver-> + getCallback(ES_MOTIONBLUR); + GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver-> + getCallback(ES_GAUSSIAN3H); + + const u32 cams = Camera::getNumCameras(); + for(u32 cam = 0; cam < cams; cam++) { - return; + scene::ICameraSceneNode * const camnode = + Camera::getCamera(cam)->getCameraSceneNode(); + mocb->setCurrentCamera(cam); + ITexture *in = irr_driver->getRTT(RTT_COLOR); + ITexture *out = irr_driver->getRTT(RTT_TMP1); + // Each effect uses these as named, and sets them up for the next effect. + // This allows chaining effects where some may be disabled. + + // As the original color shouldn't be touched, the first effect can't be disabled. + + if (1) // bloom + { + // Blit the base to tmp1 + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, in); + drv->setRenderTarget(out, true, false); + + drawQuad(cam, m_material); + + const bool globalbloom = World::getWorld()->getTrack()->getBloom(); + + BloomPowerProvider * const bloomcb = (BloomPowerProvider *) + irr_driver-> + getCallback(ES_BLOOM_POWER); + + if (globalbloom) + { + const float threshold = World::getWorld()->getTrack()->getBloomThreshold(); + ((BloomProvider *) irr_driver->getCallback(ES_BLOOM))->setThreshold(threshold); + + // Catch bright areas, and progressively minify + m_material.MaterialType = irr_driver->getShader(ES_BLOOM); + m_material.setTexture(0, in); + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + drawQuad(cam, m_material); + } + + // Do we have any forced bloom nodes? If so, draw them now + const std::vector &blooms = irr_driver->getForcedBloom(); + const u32 bloomsize = blooms.size(); + + if (!globalbloom && bloomsize) + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + + if (globalbloom || bloomsize) + { + // Clear the alpha to a suitable value, stencil + glClearColor(0, 0, 0, 0.1); + glColorMask(0, 0, 0, 1); + + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + + glClearColor(0, 0, 0, 0); + glColorMask(1, 1, 1, 1); + + // The forced-bloom objects are drawn again, to know which pixels to pick. + // While it's more drawcalls, there's a cost to using four MRTs over three, + // and there shouldn't be many such objects in a track. + // The stencil is already in use for the glow. The alpha channel is best + // reserved for other use (specular, etc). + // + // They are drawn with depth and color writes off, giving 4x-8x drawing speed. + if (bloomsize) + { + const core::aabbox3df &cambox = camnode-> + getViewFrustum()-> + getBoundingBox(); + + irr_driver->getSceneManager()->setCurrentRendertime(ESNRP_SOLID); + SOverrideMaterial &overridemat = drv->getOverrideMaterial(); + overridemat.EnablePasses = ESNRP_SOLID; + overridemat.EnableFlags = EMF_MATERIAL_TYPE | EMF_ZWRITE_ENABLE | EMF_COLOR_MASK; + overridemat.Enabled = true; + + overridemat.Material.MaterialType = irr_driver->getShader(ES_BLOOM_POWER); + overridemat.Material.ZWriteEnable = false; + overridemat.Material.ColorMask = ECP_ALPHA; + + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, ~0); + glEnable(GL_STENCIL_TEST); + + camnode->render(); + + for (u32 i = 0; i < bloomsize; i++) + { + scene::ISceneNode * const cur = blooms[i].node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + bloomcb->setPower(blooms[i].power); + + cur->render(); + } + + // Second pass for transparents. No-op for solids. + irr_driver->getSceneManager()->setCurrentRendertime(ESNRP_TRANSPARENT); + for (u32 i = 0; i < bloomsize; i++) + { + scene::ISceneNode * const cur = blooms[i].node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + bloomcb->setPower(blooms[i].power); + + cur->render(); + } + + overridemat.Enabled = 0; + overridemat.EnablePasses = 0; + + // Ok, we have the stencil; now use it to blit from color to bloom tex + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, ~0); + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_COLOR)); + + // Just in case. + glColorMask(1, 1, 1, 0); + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), false, false); + + m_material.ColorMask = ECP_RGB; + drawQuad(cam, m_material); + m_material.ColorMask = ECP_ALL; + + glColorMask(1, 1, 1, 1); + glDisable(GL_STENCIL_TEST); + } // end forced bloom + + // To half + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_TMP3)); + drv->setRenderTarget(irr_driver->getRTT(RTT_HALF1), true, false); + + drawQuad(cam, m_material); + + // To quarter + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_HALF1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), true, false); + + drawQuad(cam, m_material); + + // To eighth + m_material.MaterialType = EMT_SOLID; + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_EIGHTH1), true, false); + + drawQuad(cam, m_material); + + // Blur it for distribution. + { + gacb->setResolution(UserConfigParams::m_width / 8, + UserConfigParams::m_height / 8); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6V); + m_material.setTexture(0, irr_driver->getRTT(RTT_EIGHTH1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_EIGHTH2), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN6H); + m_material.setTexture(0, irr_driver->getRTT(RTT_EIGHTH2)); + drv->setRenderTarget(irr_driver->getRTT(RTT_EIGHTH1), false, false); + + drawQuad(cam, m_material); + } + + // Additively blend on top of tmp1 + m_material.BlendOperation = EBO_ADD; + m_material.MaterialType = irr_driver->getShader(ES_BLOOM_BLEND); + m_material.setTexture(0, irr_driver->getRTT(RTT_EIGHTH1)); + drv->setRenderTarget(out, false, false); + + drawQuad(cam, m_material); + + m_material.BlendOperation = EBO_NONE; + } // end if bloom + + in = irr_driver->getRTT(RTT_TMP1); + out = irr_driver->getRTT(RTT_TMP2); + } + + if (World::getWorld()->getTrack()->hasGodRays() && m_sunpixels > 30) // god rays + { + // Grab the sky + drv->setRenderTarget(out, true, false); + irr_driver->getSceneManager()->drawAll(ESNRP_SKY_BOX); + + // Set the sun's color + ColorizeProvider * const colcb = (ColorizeProvider *) irr_driver->getCallback(ES_COLORIZE); + const SColor col = World::getWorld()->getTrack()->getSunColor(); + colcb->setColor(col.getRed() / 255.0f, col.getGreen() / 255.0f, col.getBlue() / 255.0f); + + // The sun interposer + IMeshSceneNode * const sun = irr_driver->getSunInterposer(); + sun->getMaterial(0).ColorMask = ECP_ALL; + irr_driver->getSceneManager()->drawAll(ESNRP_CAMERA); + irr_driver->getSceneManager()->setCurrentRendertime(ESNRP_SOLID); + + sun->render(); + + sun->getMaterial(0).ColorMask = ECP_NONE; + + // Fade to quarter + m_material.MaterialType = irr_driver->getShader(ES_GODFADE); + m_material.setTexture(0, out); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), false, false); + + drawQuad(cam, m_material); + + // Blur + { + gacb->setResolution(UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), false, false); + + drawQuad(cam, m_material); + } + + // Calculate the sun's position in texcoords + const core::vector3df pos = sun->getPosition(); + float ndc[4]; + core::matrix4 trans = camnode->getProjectionMatrix(); + trans *= camnode->getViewMatrix(); + + trans.transformVect(ndc, pos); + + const float texh = m_vertices[cam].v1.TCoords.Y - m_vertices[cam].v0.TCoords.Y; + const float texw = m_vertices[cam].v3.TCoords.X - m_vertices[cam].v0.TCoords.X; + + const float sunx = ((ndc[0] / ndc[3]) * 0.5f + 0.5f) * texw; + const float suny = ((ndc[1] / ndc[3]) * 0.5f + 0.5f) * texh; + + ((GodRayProvider *) irr_driver->getCallback(ES_GODRAY))-> + setSunPosition(sunx, suny); + + // Rays please + m_material.MaterialType = irr_driver->getShader(ES_GODRAY); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), true, false); + + drawQuad(cam, m_material); + + // Blur + { + gacb->setResolution(UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER1), true, false); + + drawQuad(cam, m_material); + + m_material.MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER1)); + drv->setRenderTarget(irr_driver->getRTT(RTT_QUARTER2), false, false); + + drawQuad(cam, m_material); + } + + // Overlay + m_material.MaterialType = EMT_TRANSPARENT_ADD_COLOR; + m_material.setTexture(0, irr_driver->getRTT(RTT_QUARTER2)); + drv->setRenderTarget(in, false, false); + + drawQuad(cam, m_material); + } + + if (UserConfigParams::m_motionblur && m_any_boost) // motion blur + { + // Calculate the kart's Y position on screen + const core::vector3df pos = + Camera::getCamera(cam)->getKart()->getNode()->getPosition(); + float ndc[4]; + core::matrix4 trans = camnode->getProjectionMatrix(); + trans *= camnode->getViewMatrix(); + + trans.transformVect(ndc, pos); + const float karty = (ndc[1] / ndc[3]) * 0.5f + 0.5f; + setMotionBlurCenterY(cam, karty); + + + m_material.MaterialType = irr_driver->getShader(ES_MOTIONBLUR); + m_material.setTexture(0, in); + drv->setRenderTarget(out, true, false); + + drawQuad(cam, m_material); + + ITexture *tmp = in; + in = out; + out = tmp; + } + + if (irr_driver->getDisplacingNodes().size()) // Displacement + { + m_material.MaterialType = irr_driver->getShader(ES_PPDISPLACE); + m_material.setFlag(EMF_BILINEAR_FILTER, false); + m_material.setTexture(0, in); + m_material.setTexture(1, irr_driver->getRTT(RTT_DISPLACE)); + drv->setRenderTarget(out, true, false); + + drawQuad(cam, m_material); + + m_material.setTexture(1, 0); + m_material.setFlag(EMF_BILINEAR_FILTER, true); + + ITexture *tmp = in; + in = out; + out = tmp; + } + + if (UserConfigParams::m_mlaa) // MLAA. Must be the last pp filter. + { + drv->setRenderTarget(out, false, false); + + glEnable(GL_STENCIL_TEST); + glClearColor(0.0, 0.0, 0.0, 1.0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glStencilFunc(GL_ALWAYS, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + + // Pass 1: color edge detection + m_material.setFlag(EMF_BILINEAR_FILTER, false); + m_material.setFlag(EMF_TRILINEAR_FILTER, false); + m_material.MaterialType = irr_driver->getShader(ES_MLAA_COLOR1); + m_material.setTexture(0, in); + + drawQuad(cam, m_material); + m_material.setFlag(EMF_BILINEAR_FILTER, true); + m_material.setFlag(EMF_TRILINEAR_FILTER, true); + + glStencilFunc(GL_EQUAL, 1, ~0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + + // Pass 2: blend weights + drv->setRenderTarget(irr_driver->getRTT(RTT_TMP3), true, false); + + m_material.MaterialType = irr_driver->getShader(ES_MLAA_BLEND2); + m_material.setTexture(0, out); + m_material.setTexture(1, m_areamap); + m_material.TextureLayer[1].BilinearFilter = false; + m_material.TextureLayer[1].TrilinearFilter = false; + + drawQuad(cam, m_material); + + m_material.TextureLayer[1].BilinearFilter = true; + m_material.TextureLayer[1].TrilinearFilter = true; + m_material.setTexture(1, 0); + + // Pass 3: gather + drv->setRenderTarget(in, false, false); + + m_material.setFlag(EMF_BILINEAR_FILTER, false); + m_material.setFlag(EMF_TRILINEAR_FILTER, false); + m_material.MaterialType = irr_driver->getShader(ES_MLAA_NEIGH3); + m_material.setTexture(0, irr_driver->getRTT(RTT_TMP3)); + m_material.setTexture(1, irr_driver->getRTT(RTT_COLOR)); + + drawQuad(cam, m_material); + + m_material.setFlag(EMF_BILINEAR_FILTER, true); + m_material.setFlag(EMF_TRILINEAR_FILTER, true); + m_material.setTexture(1, 0); + + // Done. + glDisable(GL_STENCIL_TEST); + } + + // Final blit + + if (irr_driver->getNormals()) + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + } else if (irr_driver->getSSAOViz()) + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, irr_driver->getRTT(RTT_SSAO1)); + } else if (irr_driver->getShadowViz()) + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, irr_driver->getRTT(RTT_SHADOW)); + } else + { + m_material.MaterialType = irr_driver->getShader(ES_FLIP); + m_material.setTexture(0, in); + } + + drv->setRenderTarget(ERT_FRAME_BUFFER, false, false); + + drawQuad(cam, m_material); } - - u16 indices[6] = {0, 1, 2, 3, 0, 2}; - - for(m_current_camera=0; m_current_cameragetVideoDriver(); - video_driver->setMaterial(m_blur_material); - video_driver->drawIndexedTriangleList(&(m_vertices[m_current_camera].v0), - 4, &indices[0], 2); - } - } // render -// ---------------------------------------------------------------------------- -/** Implement IShaderConstantsSetCallback. Shader constants setter for - * post-processing */ -void PostProcessing::OnSetConstants(video::IMaterialRendererServices *services, - s32 user_data) +void PostProcessing::drawQuad(u32 cam, const SMaterial &mat) { - // We need the maximum texture coordinates: - float max_tex_height = m_vertices[m_current_camera].v1.TCoords.Y; - services->setPixelShaderConstant("max_tex_height", &max_tex_height, 1); + const u16 indices[6] = {0, 1, 2, 3, 0, 2}; + IVideoDriver * const drv = irr_driver->getVideoDriver(); - // Scale the boost time to get a usable boost amount: - float boost_amount = m_boost_time[m_current_camera] * 0.7f; + drv->setTransform(ETS_WORLD, core::IdentityMatrix); + drv->setTransform(ETS_VIEW, core::IdentityMatrix); + drv->setTransform(ETS_PROJECTION, core::IdentityMatrix); - // Especially for single screen the top of the screen is less blurred - // in the fragment shader by multiplying the blurr factor by - // (max_tex_height - texcoords.t), where max_tex_height is the maximum - // texture coordinate (1.0 or 0.5). In split screen this factor is too - // small (half the value compared with non-split screen), so we - // multiply this by 2. - if(m_boost_time.size()>1) - boost_amount *= 2.0f; - - services->setPixelShaderConstant("boost_amount", &boost_amount, 1); - services->setPixelShaderConstant("center", - &(m_center[m_current_camera].X), 2); - services->setPixelShaderConstant("direction", - &(m_direction[m_current_camera].X), 2); - - // Use a radius of 0.15 when showing a single kart, otherwise (2-4 karts - // on splitscreen) use only 0.75. - float radius = Camera::getNumCameras()==1 ? 0.15f : 0.075f; - services->setPixelShaderConstant("mask_radius", &radius, 1); - const int texunit = 0; - services->setPixelShaderConstant("color_buffer", &texunit, 1); -} // OnSetConstants + drv->setMaterial(mat); + drv->drawIndexedTriangleList(&(m_vertices[cam].v0), + 4, indices, 2); +} diff --git a/src/graphics/post_processing.hpp b/src/graphics/post_processing.hpp index e9145e865..020cb3ffc 100644 --- a/src/graphics/post_processing.hpp +++ b/src/graphics/post_processing.hpp @@ -33,61 +33,58 @@ using namespace irr; /** \brief Handles post processing, eg motion blur * \ingroup graphics */ -class PostProcessing : public video::IShaderConstantSetCallBack +class PostProcessing: public IReferenceCounted { private: - video::ITexture *m_render_target; - /** Material to be used when blurring is used. */ - video::SMaterial m_blur_material; - - bool m_supported; + video::SMaterial m_material; /** Boost time, how long the boost should be displayed. This also * affects the strength of the effect: longer boost time will * have a stronger effect. */ std::vector m_boost_time; + bool m_any_boost; + /** The center of blurring, in texture coordinates [0,1]).*/ std::vector m_center; /** The center to which the blurring is aimed at, in [0,1]. */ std::vector m_direction; - /** True if any of the cameras is using post processing. */ - bool m_used_pp_this_frame; - - /** Currently active camera during post-processing, needed in the - * OnSetConstants callback. */ - unsigned int m_current_camera; - - struct Quad { video::S3DVertex v0, v1, v2, v3; }; /** The vertices for the rectangle used for each camera. This includes * the vertex position, normal, and texture coordinate. */ std::vector m_vertices; + video::ITexture *m_areamap; + + u32 m_sunpixels; + + void setMotionBlurCenterY(const u32 num, const float y); + public: PostProcessing(video::IVideoDriver* video_driver); virtual ~PostProcessing(); void reset(); /** Those should be called around the part where we render the scene to be post-processed */ - void beginCapture(); - void endCapture(); + void begin(); void update(float dt); + /** Render the post-processed scene, solids only, color to color, no stencil */ + void renderSolid(const u32 cam); + /** Render the post-processed scene */ void render(); - /** Is the hardware able to use post-processing? */ - inline bool isSupported() const {return m_supported;} + /** Draw the quad for this camera */ + void drawQuad(u32 cam, const video::SMaterial &mat); /** Use motion blur for a short time */ void giveBoost(unsigned int cam_index); - /** Implement IShaderConstantsSetCallback. Shader constants setter for post-processing */ - virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 user_data); + void setSunPixels(const u32 in) { m_sunpixels = in; } }; #endif // HEADER_POST_PROCESSING_HPP diff --git a/src/graphics/rain.cpp b/src/graphics/rain.cpp index 27945a5c8..9d0127872 100644 --- a/src/graphics/rain.cpp +++ b/src/graphics/rain.cpp @@ -18,28 +18,117 @@ #include "audio/sfx_base.hpp" #include "audio/sfx_manager.hpp" +#include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" #include "graphics/material_manager.hpp" #include "graphics/material.hpp" #include "graphics/per_camera_node.hpp" #include "graphics/rain.hpp" +#include "graphics/shaders.hpp" #include "modes/world.hpp" #include "states_screens/race_gui.hpp" #include "utils/constants.hpp" #include "utils/random_generator.hpp" +#include #include -#include -const float RAIN_RADIUS[RAIN_RING_COUNT] = { 1.0f, 3.0f, 6.0f, 12.0f, 24.0f }; -const float RAIN_Y_TO = 25.0f; -const float RAIN_Y_FROM = -10.0f; -const float RAIN_DY = 2.5f; -const float RAIN_DX = 0.0f; +using namespace video; +using namespace scene; +using namespace core; -const float TEXTURE_X_TILES[RAIN_RING_COUNT] = { 2.0f, 2.5f, 3.5f, 5.0f, 8.0f }; -const float TEXTURE_Y_TILES[RAIN_RING_COUNT] = { 8.0f, 7.0f, 6.0f, 4.0f, 4.0f }; +// The actual rain node +class RainNode: public scene::ISceneNode +{ +public: + RainNode(scene::ISceneManager* mgr, ITexture *tex) + : scene::ISceneNode(0, mgr, -1) + { + mat.Lighting = false; + mat.ZWriteEnable = false; + mat.MaterialType = irr_driver->getShader(ES_RAIN); + mat.Thickness = 200; + mat.BlendOperation = EBO_ADD; + mat.setTexture(0, tex); + mat.TextureLayer[0].TextureWrapU = + mat.TextureLayer[0].TextureWrapV = ETC_CLAMP_TO_EDGE; + + count = 2500; + area = 3500; + + // Fill in the mesh buffer + buf.Vertices.clear(); + buf.Indices.clear(); + + buf.Vertices.set_used(count); + buf.Indices.set_used(count); + + buf.Primitive = EPT_POINT_SPRITES; + buf.setHardwareMappingHint(EHM_STATIC); + + u32 i; + float x, y, z; + for (i = 0; i < count; i++) + { + x = ((rand() % area) - area/2) / 100.0f; + y = ((rand() % 2400)) / 100.0f; + z = ((rand() % area) - area/2) / 100.0f; + + buf.Indices[i] = i; + buf.Vertices[i] = S3DVertex(x, y, z, 0, 0, 0, SColor(255, 255, 0, 0), 0, 0); + } + + box.addInternalPoint(vector3df(-area/2)); + box.addInternalPoint(vector3df(area/2)); + } + + ~RainNode() + { + } + + virtual void render() + { + glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(mat); + + drv->drawMeshBuffer(&buf); + + glDisable(GL_VERTEX_PROGRAM_POINT_SIZE); + } + + virtual const core::aabbox3d& getBoundingBox() const + { + return box; + } + + virtual void OnRegisterSceneNode() + { + if (IsVisible && + (irr_driver->getRenderPass() & ESNRP_TRANSPARENT) == ESNRP_TRANSPARENT) + { + SceneManager->registerNodeForRendering(this, ESNRP_TRANSPARENT); + } + + ISceneNode::OnRegisterSceneNode(); + } + + virtual u32 getMaterialCount() const { return 1; } + virtual video::SMaterial& getMaterial(u32 i) { return mat; } + +private: + video::SMaterial mat; + core::aabbox3d box; + u32 count; + s32 area; + + scene::SMeshBuffer buf; +}; + +// The rain manager Rain::Rain(Camera *camera, irr::scene::ISceneNode* parent) { @@ -54,80 +143,17 @@ Rain::Rain(Camera *camera, irr::scene::ISceneNode* parent) RandomGenerator g; m_next_lightning = (float)g.get(35); - for (int r=0; rMaterial.setTexture(0, m->getTexture()); - m->setMaterialProperties(&buffer->Material, NULL); - buffer->Material.ZWriteEnable = false; - buffer->Material.BackfaceCulling = false; - - m_materials.push_back(&buffer->Material); - - video::S3DVertex v; - v.Color.set(255,255,255,255); - - // create a cylinder mesh - const int VERTICES = 17; - - for (int vid=0; vidVertices.push_back(v); - - v.Pos.Y = RAIN_Y_FROM; - - v.TCoords.Y = 0.0f; - buffer->Vertices.push_back(v); - - if (vid > 0) - { - buffer->Indices.push_back(vid-2); - buffer->Indices.push_back(vid-1); - buffer->Indices.push_back(vid); - buffer->Indices.push_back(vid-1); - buffer->Indices.push_back(vid); - buffer->Indices.push_back(vid+1); - } - } - - scene::SMesh* mesh = new scene::SMesh(); - mesh->addMeshBuffer(buffer); - mesh->recalculateBoundingBox(); - - m_node[r] = irr_driver->addPerCameraMesh(mesh, - camera->getCameraSceneNode(), - parent); - m_node[r]->setAutomaticCulling(0); - mesh->drop(); - - buffer->drop(); - } + RainNode *node = new RainNode(irr_driver->getSceneManager(), m->getTexture()); + m_node = irr_driver->addPerCameraNode(node, camera->getCameraSceneNode(), parent); + m_node->setAutomaticCulling(0); } // Rain // ---------------------------------------------------------------------------- Rain::~Rain() { - for (int r=0; rdrop(); // drop STK's reference - m_node[r]->remove(); // Then remove it from the scene graph. - } + m_node->drop(); // drop STK's reference + m_node->remove(); // Then remove it from the scene graph. if (m_lightning && m_thunder_sound != NULL) sfx_manager->deleteSFX(m_thunder_sound); } @@ -136,19 +162,6 @@ Rain::~Rain() void Rain::update(float dt) { - //const int count = m_materials.size(); - for (int m=0; m 1.0f) m_x[m] = fmod(m_x[m], 1.0f); - if (m_y[m] > 1.0f) m_y[m] = fmod(m_y[m], 1.0f); - - core::matrix4& matrix = m_node[m]->getChild()->getMaterial(0).getTextureMatrix(0); - - matrix.setTextureTranslate(m_x[m], m_y[m]); - } - if (m_lightning) { m_next_lightning -= dt; @@ -173,15 +186,12 @@ void Rain::update(float dt) void Rain::setPosition(const core::vector3df& position) { - for (int m=0; mgetChild()->setPosition(position); - } + m_node->getChild()->setPosition(position); } // setPosition // ---------------------------------------------------------------------------- void Rain::setCamera(scene::ICameraSceneNode* camera) { - for (int n=0; nsetCamera(camera); + m_node->setCamera(camera); } diff --git a/src/graphics/rain.hpp b/src/graphics/rain.hpp index b05fa2c60..b8408c2a8 100644 --- a/src/graphics/rain.hpp +++ b/src/graphics/rain.hpp @@ -25,23 +25,16 @@ class PerCameraNode; #include namespace irr { - namespace video { class SMaterial; } + namespace video { class SMaterial; class ITexture; } namespace scene { class ICameraSceneNode; class ISceneNode; } } using namespace irr; -const int RAIN_RING_COUNT = 5; - class SFXBase; class Rain { - PerCameraNode* m_node[RAIN_RING_COUNT]; - - std::vector m_materials; - - float m_x[RAIN_RING_COUNT]; - float m_y[RAIN_RING_COUNT]; + PerCameraNode* m_node; float m_next_lightning; bool m_lightning; @@ -49,7 +42,7 @@ class Rain public: Rain(Camera* camera, irr::scene::ISceneNode* parent); - ~Rain(); + virtual ~Rain(); void update(float dt); void setPosition(const irr::core::vector3df& position); diff --git a/src/graphics/referee.cpp b/src/graphics/referee.cpp index 021925085..4579991cc 100644 --- a/src/graphics/referee.cpp +++ b/src/graphics/referee.cpp @@ -173,6 +173,8 @@ Referee::Referee() m_scene_node->setScale(m_st_scale.toIrrVector()); m_scene_node->setFrameLoop(m_st_first_start_frame, m_st_last_start_frame); + + irr_driver->applyObjectPassShader(m_scene_node); } // Referee // ---------------------------------------------------------------------------- @@ -197,6 +199,8 @@ Referee::Referee(const AbstractKart &kart) m_scene_node->setPosition(core::vector3df(0, kart.getKartHeight() + 0.4f, 0)); m_scene_node->setFrameLoop(m_st_first_rescue_frame, m_st_last_rescue_frame); + + irr_driver->applyObjectPassShader(m_scene_node); } // Referee // ---------------------------------------------------------------------------- diff --git a/src/graphics/referee.hpp b/src/graphics/referee.hpp index 98c10b17b..93941f051 100644 --- a/src/graphics/referee.hpp +++ b/src/graphics/referee.hpp @@ -19,7 +19,10 @@ #ifndef HEADER_REFEREE_HPP #define HEADER_REFEREE_HPP -#include "irrlicht.h" +#include +#include +#include +#include using namespace irr; #include "utils/vec3.hpp" diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp new file mode 100644 index 000000000..d4531d9b4 --- /dev/null +++ b/src/graphics/render.cpp @@ -0,0 +1,826 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2009 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 "graphics/irr_driver.hpp" + +#include "config/user_config.hpp" +#include "graphics/callbacks.hpp" +#include "graphics/camera.hpp" +#include "graphics/glow.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/lens_flare.hpp" +#include "graphics/light.hpp" +#include "graphics/lod_node.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/particle_kind_manager.hpp" +#include "graphics/per_camera_node.hpp" +#include "graphics/post_processing.hpp" +#include "graphics/referee.hpp" +#include "graphics/rtts.hpp" +#include "graphics/screenquad.hpp" +#include "graphics/shaders.hpp" +#include "graphics/shadow_importance.hpp" +#include "graphics/wind.hpp" +#include "io/file_manager.hpp" +#include "items/item.hpp" +#include "items/item_manager.hpp" +#include "modes/world.hpp" +#include "physics/physics.hpp" +#include "tracks/track.hpp" +#include "utils/constants.hpp" +#include "utils/helpers.hpp" +#include "utils/log.hpp" +#include "utils/profiler.hpp" + +void IrrDriver::renderGLSL(float dt) +{ + World *world = World::getWorld(); // Never NULL. + + // Overrides + video::SOverrideMaterial &overridemat = m_video_driver->getOverrideMaterial(); + overridemat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; + overridemat.EnableFlags = 0; + + if (m_wireframe) + { + overridemat.Material.Wireframe = 1; + overridemat.EnableFlags |= video::EMF_WIREFRAME; + } + if (m_mipviz) + { + overridemat.Material.MaterialType = m_shaders->getShader(ES_MIPVIZ); + overridemat.EnableFlags |= video::EMF_MATERIAL_TYPE; + overridemat.EnablePasses = scene::ESNRP_SOLID; + } + + // Get a list of all glowing things. The driver's list contains the static ones, + // here we add items, as they may disappear each frame. + std::vector glows = m_glowing; + std::vector transparent_glow_nodes; + + ItemManager * const items = ItemManager::get(); + const u32 itemcount = items->getNumberOfItems(); + u32 i; + + // For each static node, give it a glow representation + const u32 staticglows = glows.size(); + for (i = 0; i < staticglows; i++) + { + scene::ISceneNode * const node = glows[i].node; + + const float radius = (node->getBoundingBox().getExtent().getLength() / 2) * 2.0f; + GlowNode * const repnode = new GlowNode(irr_driver->getSceneManager(), radius); + repnode->setPosition(node->getTransformedBoundingBox().getCenter()); + transparent_glow_nodes.push_back(repnode); + } + + for (i = 0; i < itemcount; i++) + { + Item * const item = items->getItem(i); + if (!item) continue; + const Item::ItemType type = item->getType(); + + if (type != Item::ITEM_NITRO_BIG && type != Item::ITEM_NITRO_SMALL && + type != Item::ITEM_BONUS_BOX) + continue; + + LODNode * const lod = (LODNode *) item->getSceneNode(); + if (!lod->isVisible()) continue; + + const int level = lod->getLevel(); + if (level < 0) continue; + + scene::ISceneNode * const node = lod->getAllNodes()[level]; + node->updateAbsolutePosition(); + + GlowData dat; + dat.node = node; + + dat.r = 1.0f; + dat.g = 1.0f; + dat.b = 1.0f; + + // Item colors + switch (type) + { + case Item::ITEM_NITRO_BIG: + case Item::ITEM_NITRO_SMALL: + dat.r = stk_config->m_nitro_glow_color[0]; + dat.g = stk_config->m_nitro_glow_color[1]; + dat.b = stk_config->m_nitro_glow_color[2]; + break; + case Item::ITEM_BONUS_BOX: + dat.r = stk_config->m_box_glow_color[0]; + dat.g = stk_config->m_box_glow_color[1]; + dat.b = stk_config->m_box_glow_color[2]; + break; + default: + Log::fatal("render", "Unknown item type got through"); + break; + } + + glows.push_back(dat); + + // Push back its representation too + const float radius = (node->getBoundingBox().getExtent().getLength() / 2) * 2.0f; + GlowNode * const repnode = new GlowNode(irr_driver->getSceneManager(), radius); + repnode->setPosition(node->getTransformedBoundingBox().getCenter()); + transparent_glow_nodes.push_back(repnode); + } + + // Start the RTT for post-processing. + // We do this before beginScene() because we want to capture the glClear() + // because of tracks that do not have skyboxes (generally add-on tracks) + m_post_processing->begin(); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + + m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, + world->getClearColor()); + + // Clear normal and depth to zero + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_NORMAL), true, false, video::SColor(0,0,0,0)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_DEPTH), true, false, video::SColor(0,0,0,0)); + + irr_driver->getVideoDriver()->enableMaterial2D(); + RaceGUIBase *rg = world->getRaceGUI(); + if (rg) rg->update(dt); + + + for(unsigned int cam = 0; cam < Camera::getNumCameras(); cam++) + { + Camera * const camera = Camera::getCamera(cam); + scene::ICameraSceneNode * const camnode = camera->getCameraSceneNode(); + +#ifdef ENABLE_PROFILER + std::ostringstream oss; + oss << "drawAll() for kart " << cam << std::flush; + PROFILER_PUSH_CPU_MARKER(oss.str().c_str(), (cam+1)*60, + 0x00, 0x00); +#endif + camera->activate(); + rg->preRenderCallback(camera); // adjusts start referee + + const u32 bgnodes = m_background.size(); + if (bgnodes) + { + // If there are background nodes (3d skybox), draw them now. + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + + m_renderpass = scene::ESNRP_SKY_BOX; + m_scene_manager->drawAll(m_renderpass); + + const video::SOverrideMaterial prev = overridemat; + overridemat.Enabled = 1; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; + overridemat.Material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; + + for (i = 0; i < bgnodes; i++) + { + m_background[i]->setPosition(camnode->getPosition() * 0.97f); + m_background[i]->updateAbsolutePosition(); + m_background[i]->render(); + } + + overridemat = prev; + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, true); + } + + // Fire up the MRT + m_video_driver->setRenderTarget(m_mrt, false, false); + + m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_SOLID; + m_scene_manager->drawAll(m_renderpass); + + ShadowImportanceProvider * const sicb = (ShadowImportanceProvider *) + irr_driver->getCallback(ES_SHADOW_IMPORTANCE); + sicb->updateIPVMatrix(); + + // Used to cull glowing items & lights + const core::aabbox3df cambox = camnode->getViewFrustum()->getBoundingBox(); + + // Render anything glowing. + if (!m_mipviz && !m_wireframe) + { + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_TMP1), false, false); + glClearColor(0, 0, 0, 0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + const u32 glowcount = glows.size(); + ColorizeProvider * const cb = (ColorizeProvider *) m_shaders->m_callbacks[ES_COLORIZE]; + + GlowProvider * const glowcb = (GlowProvider *) m_shaders->m_callbacks[ES_GLOW]; + glowcb->setResolution(UserConfigParams::m_width, + UserConfigParams::m_height); + + overridemat.Material.MaterialType = m_shaders->getShader(ES_COLORIZE); + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE; + overridemat.EnablePasses = scene::ESNRP_SOLID; + overridemat.Enabled = true; + + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, ~0); + glEnable(GL_STENCIL_TEST); + + for (u32 i = 0; i < glowcount; i++) + { + const GlowData &dat = glows[i]; + scene::ISceneNode * const cur = dat.node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + cb->setColor(dat.r, dat.g, dat.b); + cur->render(); + } + + // Second round for transparents; it's a no-op for solids + m_scene_manager->setCurrentRendertime(scene::ESNRP_TRANSPARENT); + overridemat.Material.MaterialType = m_shaders->getShader(ES_COLORIZE_REF); + for (u32 i = 0; i < glowcount; i++) + { + const GlowData &dat = glows[i]; + scene::ISceneNode * const cur = dat.node; + + // Quick box-based culling + const core::aabbox3df nodebox = cur->getTransformedBoundingBox(); + if (!nodebox.intersectsWithBox(cambox)) + continue; + + cb->setColor(dat.r, dat.g, dat.b); + cur->render(); + } + overridemat.Enabled = false; + overridemat.EnablePasses = 0; + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDisable(GL_STENCIL_TEST); + + // Cool, now we have the colors set up. Progressively minify. + video::SMaterial minimat; + minimat.Lighting = false; + minimat.ZWriteEnable = false; + minimat.ZBuffer = video::ECFN_ALWAYS; + minimat.setFlag(video::EMF_TRILINEAR_FILTER, true); + + minimat.TextureLayer[0].TextureWrapU = + minimat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + + // To half + minimat.setTexture(0, m_rtts->getRTT(RTT_TMP1)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_HALF1), false, false); + m_post_processing->drawQuad(cam, minimat); + + // To quarter + minimat.setTexture(0, m_rtts->getRTT(RTT_HALF1)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_QUARTER1), false, false); + m_post_processing->drawQuad(cam, minimat); + + // Blur it + ((GaussianBlurProvider *) m_shaders->m_callbacks[ES_GAUSSIAN3H])->setResolution( + UserConfigParams::m_width / 4, + UserConfigParams::m_height / 4); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN6H); + minimat.setTexture(0, m_rtts->getRTT(RTT_QUARTER1)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_QUARTER2), false, false); + m_post_processing->drawQuad(cam, minimat); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN6V); + minimat.setTexture(0, m_rtts->getRTT(RTT_QUARTER2)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_QUARTER1), false, false); + m_post_processing->drawQuad(cam, minimat); + + // The glows will be rendered in the transparent phase + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glDisable(GL_STENCIL_TEST); + } // end glow + + // Shadows + if (!m_mipviz && !m_wireframe && UserConfigParams::m_shadows && + World::getWorld()->getTrack()->hasShadows()) + { + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + static u8 tick = 0; + + const Vec3 *vmin, *vmax; + World::getWorld()->getTrack()->getAABB(&vmin, &vmax); + core::aabbox3df trackbox(vmin->toIrrVector(), vmax->toIrrVector() - + core::vector3df(0, 30, 0)); + + const float oldfar = camnode->getFarValue(); + camnode->setFarValue(std::min(100.0f, oldfar)); + camnode->render(); + const core::aabbox3df smallcambox = camnode-> + getViewFrustum()->getBoundingBox(); + camnode->setFarValue(oldfar); + camnode->render(); + + // Set up a nice ortho projection that contains our camera frustum + core::matrix4 ortho; + core::aabbox3df box = smallcambox; + box = box.intersect(trackbox); + + m_suncam->getViewMatrix().transformBoxEx(box); + m_suncam->getViewMatrix().transformBoxEx(trackbox); + + core::vector3df extent = trackbox.getExtent(); + const float w = fabsf(extent.X); + const float h = fabsf(extent.Y); + float z = box.MaxEdge.Z; + + // Snap to texels + const float units_per_w = w / m_rtts->getRTT(RTT_SHADOW)->getSize().Width; + const float units_per_h = h / m_rtts->getRTT(RTT_SHADOW)->getSize().Height; + + float left = box.MinEdge.X; + float right = box.MaxEdge.X; + float up = box.MaxEdge.Y; + float down = box.MinEdge.Y; + + left -= fmodf(left, units_per_w); + right -= fmodf(right, units_per_w); + up -= fmodf(up, units_per_h); + down -= fmodf(down, units_per_h); + z -= fmodf(z, 0.5f); + + // FIXME: quick and dirt (and wrong) workaround to avoid division by zero + if (left == right) right += 0.1f; + if (up == down) down += 0.1f; + if (z == 30) z += 0.1f; + + ortho.buildProjectionMatrixOrthoLH(left, right, + up, down, + 30, z); + + m_suncam->setProjectionMatrix(ortho, true); + m_scene_manager->setActiveCamera(m_suncam); + m_suncam->render(); + + ortho *= m_suncam->getViewMatrix(); + ((SunLightProvider *) m_shaders->m_callbacks[ES_SUNLIGHT])->setShadowMatrix(ortho); + sicb->setShadowMatrix(ortho); + + overridemat.Enabled = 0; + + // Render the importance map + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLLAPSE), true, true); + + m_shadow_importance->render(); + + CollapseProvider * const colcb = (CollapseProvider *) + m_shaders-> + m_callbacks[ES_COLLAPSE]; + ScreenQuad sq(m_video_driver); + sq.setMaterialType(m_shaders->getShader(ES_COLLAPSE)); + sq.setTexture(m_rtts->getRTT(RTT_COLLAPSE)); + sq.getMaterial().setFlag(EMF_BILINEAR_FILTER, false); + + const TypeRTT oldh = tick ? RTT_COLLAPSEH : RTT_COLLAPSEH2; + const TypeRTT oldv = tick ? RTT_COLLAPSEV : RTT_COLLAPSEV2; + const TypeRTT curh = tick ? RTT_COLLAPSEH2 : RTT_COLLAPSEH; + const TypeRTT curv = tick ? RTT_COLLAPSEV2 : RTT_COLLAPSEV; + + colcb->setResolution(1, m_rtts->getRTT(RTT_WARPV)->getSize().Height); + sq.setTexture(m_rtts->getRTT(oldh), 1); + sq.render(m_rtts->getRTT(RTT_WARPH)); + + colcb->setResolution(m_rtts->getRTT(RTT_WARPV)->getSize().Height, 1); + sq.setTexture(m_rtts->getRTT(oldv), 1); + sq.render(m_rtts->getRTT(RTT_WARPV)); + + sq.setTexture(0, 1); + ((GaussianBlurProvider *) m_shaders->m_callbacks[ES_GAUSSIAN3H])->setResolution( + m_rtts->getRTT(RTT_WARPV)->getSize().Height, + m_rtts->getRTT(RTT_WARPV)->getSize().Height); + + sq.setMaterialType(m_shaders->getShader(ES_GAUSSIAN6H)); + sq.setTexture(m_rtts->getRTT(RTT_WARPH)); + sq.render(m_rtts->getRTT(curh)); + + sq.setMaterialType(m_shaders->getShader(ES_GAUSSIAN6V)); + sq.setTexture(m_rtts->getRTT(RTT_WARPV)); + sq.render(m_rtts->getRTT(curv)); + + // Convert importance maps to warp maps + // + // It should be noted that while they do repeated work + // calculating the min, max, and total, it's several hundred us + // faster to do that than to do it once in a separate shader + // (shader switch overhead, measured). + colcb->setResolution(m_rtts->getRTT(RTT_WARPV)->getSize().Height, + m_rtts->getRTT(RTT_WARPV)->getSize().Height); + + sq.setMaterialType(m_shaders->getShader(ES_SHADOW_WARPH)); + sq.setTexture(m_rtts->getRTT(curh)); + sq.render(m_rtts->getRTT(RTT_WARPH)); + + sq.setMaterialType(m_shaders->getShader(ES_SHADOW_WARPV)); + sq.setTexture(m_rtts->getRTT(curv)); + sq.render(m_rtts->getRTT(RTT_WARPV)); + + // Actual shadow map + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_SHADOW), true, true); + overridemat.Material.MaterialType = m_shaders->getShader(ES_SHADOWPASS); + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE | video::EMF_TEXTURE1 | + video::EMF_TEXTURE2; + overridemat.EnablePasses = scene::ESNRP_SOLID; + overridemat.Material.setTexture(1, m_rtts->getRTT(RTT_WARPH)); + overridemat.Material.setTexture(2, m_rtts->getRTT(RTT_WARPV)); + overridemat.Material.TextureLayer[1].TextureWrapU = + overridemat.Material.TextureLayer[1].TextureWrapV = + overridemat.Material.TextureLayer[2].TextureWrapU = + overridemat.Material.TextureLayer[2].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + overridemat.Material.TextureLayer[1].BilinearFilter = + overridemat.Material.TextureLayer[2].BilinearFilter = true; + overridemat.Material.TextureLayer[1].TrilinearFilter = + overridemat.Material.TextureLayer[2].TrilinearFilter = false; + overridemat.Material.TextureLayer[1].AnisotropicFilter = + overridemat.Material.TextureLayer[2].AnisotropicFilter = 0; + overridemat.Material.Wireframe = 1; + overridemat.Enabled = true; + + m_scene_manager->drawAll(scene::ESNRP_SOLID); + + if (m_shadowviz) + { + overridemat.EnableFlags |= video::EMF_WIREFRAME; + m_scene_manager->drawAll(scene::ESNRP_SOLID); + } + + overridemat.EnablePasses = 0; + overridemat.Enabled = false; + camera->activate(); + + tick++; + tick %= 2; + } + + // Lights + if (!m_lightviz) + { + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_TMP1), true, false, + video::SColor(1, 0, 0, 0)); + } else + { + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + } + + const vector3df camcenter = cambox.getCenter(); + const float camradius = cambox.getExtent().getLength() / 2; + const vector3df campos = camnode->getPosition(); + const float camnear = camnode->getNearValue(); + + m_scene_manager->drawAll(scene::ESNRP_CAMERA); + PointLightProvider * const pcb = (PointLightProvider *) irr_driver-> + getCallback(ES_POINTLIGHT); + pcb->updateIPVMatrix(); + SunLightProvider * const scb = (SunLightProvider *) irr_driver-> + getCallback(ES_SUNLIGHT); + scb->updateIPVMatrix(); + FogProvider * const fogcb = (FogProvider *) irr_driver-> + getCallback(ES_FOG); + fogcb->updateIPVMatrix(); + + const u32 lightcount = m_lights.size(); + for (i = 0; i < lightcount; i++) + { + // Sphere culling + const float distance_sq = (m_lights[i]->getPosition() - camcenter).getLengthSQ(); + float radius_sum = camradius + m_lights[i]->getRadius(); + radius_sum *= radius_sum; + if (radius_sum < distance_sq) + continue; + + bool inside = false; + + const float camdistance_sq = (m_lights[i]->getPosition() - campos).getLengthSQ(); + float adjusted_radius = m_lights[i]->getRadius() + camnear; + adjusted_radius *= adjusted_radius; + + // Camera inside the light's radius? Needs adjustment for the near plane. + if (camdistance_sq < adjusted_radius) + { + inside = true; + + video::SMaterial &m = m_lights[i]->getMaterial(0); + m.FrontfaceCulling = true; + m.BackfaceCulling = false; + m.ZBuffer = video::ECFN_GREATER; + } + + if (m_lightviz) + { + overridemat.Enabled = true; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE | video::EMF_WIREFRAME | + video::EMF_FRONT_FACE_CULLING | + video::EMF_BACK_FACE_CULLING | + video::EMF_ZBUFFER; + overridemat.Material.MaterialType = m_shaders->getShader(ES_COLORIZE); + overridemat.Material.Wireframe = true; + overridemat.Material.BackfaceCulling = false; + overridemat.Material.FrontfaceCulling = false; + overridemat.Material.ZBuffer = video::ECFN_LESSEQUAL; + + + ColorizeProvider * const cb = (ColorizeProvider *) m_shaders->m_callbacks[ES_COLORIZE]; + float col[3]; + m_lights[i]->getColor(col); + cb->setColor(col[0], col[1], col[2]); + } + + // Action + m_lights[i]->render(); + + // Reset the inside change + if (inside) + { + video::SMaterial &m = m_lights[i]->getMaterial(0); + m.FrontfaceCulling = false; + m.BackfaceCulling = true; + m.ZBuffer = video::ECFN_LESSEQUAL; + } + + if (m_lightviz) + { + overridemat.Enabled = false; + } + + } // for i in lights + + // Blend lights to the image + video::SMaterial lightmat; + lightmat.Lighting = false; + lightmat.ZWriteEnable = false; + lightmat.ZBuffer = video::ECFN_ALWAYS; + lightmat.setFlag(video::EMF_BILINEAR_FILTER, false); + lightmat.setTexture(0, m_rtts->getRTT(RTT_TMP1)); + // Specular mapping + lightmat.setTexture(1, m_rtts->getRTT(RTT_COLOR)); + lightmat.MaterialType = m_shaders->getShader(ES_LIGHTBLEND); + lightmat.MaterialTypeParam = video::pack_textureBlendFunc(video::EBF_DST_COLOR, video::EBF_ZERO); + lightmat.BlendOperation = video::EBO_ADD; + + lightmat.TextureLayer[0].TextureWrapU = + lightmat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + if (!m_mipviz) + m_post_processing->drawQuad(cam, lightmat); + + if (!bgnodes) + { + // If there are no BG nodes, it's more efficient to do the skybox here. + m_renderpass = scene::ESNRP_SKY_BOX; + m_scene_manager->drawAll(m_renderpass); + } + + // Is the lens flare enabled & visible? Check last frame's query. + const bool hasflare = World::getWorld()->getTrack()->hasLensFlare(); + const bool hasgodrays = World::getWorld()->getTrack()->hasGodRays(); + if (hasflare | hasgodrays) + { + irr::video::COpenGLDriver* gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver(); + + GLuint res; + gl_driver->extGlGetQueryObjectuiv(m_lensflare_query, GL_QUERY_RESULT, &res); + m_post_processing->setSunPixels(res); + + // Prepare the query for the next frame. + gl_driver->extGlBeginQuery(GL_SAMPLES_PASSED_ARB, m_lensflare_query); + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + m_scene_manager->drawAll(scene::ESNRP_CAMERA); + m_sun_interposer->render(); + gl_driver->extGlEndQuery(GL_SAMPLES_PASSED_ARB); + + m_lensflare->setStrength(res / 4000.0f); + + if (hasflare) + m_lensflare->OnRegisterSceneNode(); + + // Make sure the color mask is reset + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + + // Render the post-processed scene for solids + m_post_processing->renderSolid(cam); + + // We need to re-render camera due to the per-cam-node hack. + m_renderpass = scene::ESNRP_CAMERA | scene::ESNRP_TRANSPARENT | + scene::ESNRP_TRANSPARENT_EFFECT; + m_scene_manager->drawAll(m_renderpass); + + // Handle displacing nodes, if any + const u32 displacingcount = m_displacing.size(); + if (displacingcount) + { + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_DISPLACE), false, false); + glClearColor(0, 0, 0, 0); + glClear(GL_STENCIL_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + glStencilFunc(GL_ALWAYS, 1, ~0); + glEnable(GL_STENCIL_TEST); + + overridemat.Enabled = 1; + overridemat.EnableFlags = video::EMF_MATERIAL_TYPE | video::EMF_TEXTURE0; + overridemat.Material.MaterialType = m_shaders->getShader(ES_DISPLACE); + + overridemat.Material.TextureLayer[0].Texture = + irr_driver->getTexture((file_manager->getTextureDir() + "displace.png").c_str()); + overridemat.Material.TextureLayer[0].BilinearFilter = + overridemat.Material.TextureLayer[0].TrilinearFilter = true; + overridemat.Material.TextureLayer[0].AnisotropicFilter = 0; + overridemat.Material.TextureLayer[0].TextureWrapU = + overridemat.Material.TextureLayer[0].TextureWrapV = video::ETC_REPEAT; + + for (i = 0; i < displacingcount; i++) + { + m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID); + m_displacing[i]->render(); + + m_scene_manager->setCurrentRendertime(scene::ESNRP_TRANSPARENT); + m_displacing[i]->render(); + } + + overridemat.Enabled = 0; + + // Blur it + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilFunc(GL_EQUAL, 1, ~0); + + video::SMaterial minimat; + minimat.Lighting = false; + minimat.ZWriteEnable = false; + minimat.ZBuffer = video::ECFN_ALWAYS; + minimat.setFlag(video::EMF_TRILINEAR_FILTER, true); + + minimat.TextureLayer[0].TextureWrapU = + minimat.TextureLayer[0].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + + ((GaussianBlurProvider *) m_shaders->m_callbacks[ES_GAUSSIAN3H])->setResolution( + UserConfigParams::m_width, + UserConfigParams::m_height); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN3H); + minimat.setTexture(0, m_rtts->getRTT(RTT_DISPLACE)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_TMP2), true, false); + m_post_processing->drawQuad(cam, minimat); + + minimat.MaterialType = m_shaders->getShader(ES_GAUSSIAN3V); + minimat.setTexture(0, m_rtts->getRTT(RTT_TMP2)); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_DISPLACE), true, false); + m_post_processing->drawQuad(cam, minimat); + + glDisable(GL_STENCIL_TEST); + m_video_driver->setRenderTarget(m_rtts->getRTT(RTT_COLOR), false, false); + } + + // Drawing for this cam done, cleanup + const u32 glowrepcount = transparent_glow_nodes.size(); + for (i = 0; i < glowrepcount; i++) + { + transparent_glow_nodes[i]->remove(); + transparent_glow_nodes[i]->drop(); + } + + PROFILER_POP_CPU_MARKER(); + + // Note that drawAll must be called before rendering + // the bullet debug view, since otherwise the camera + // is not set up properly. This is only used for + // the bullet debug view. + if (UserConfigParams::m_artist_debug_mode) + World::getWorld()->getPhysics()->draw(); + } // for igetNumKarts() + + // Render the post-processed scene + m_post_processing->render(); + + // Set the viewport back to the full screen for race gui + m_video_driver->setViewPort(core::recti(0, 0, + UserConfigParams::m_width, + UserConfigParams::m_height)); + + for(unsigned int i=0; irenderPlayerView(camera, dt); + + PROFILER_POP_CPU_MARKER(); + } // for iendScene(); + + getPostProcessing()->update(dt); +} + +// -------------------------------------------- + +void IrrDriver::renderFixed(float dt) +{ + World *world = World::getWorld(); // Never NULL. + + m_video_driver->beginScene(/*backBuffer clear*/ true, /*zBuffer*/ true, + world->getClearColor()); + + irr_driver->getVideoDriver()->enableMaterial2D(); + + RaceGUIBase *rg = world->getRaceGUI(); + if (rg) rg->update(dt); + + + for(unsigned int i=0; iactivate(); + rg->preRenderCallback(camera); // adjusts start referee + + m_renderpass = ~0; + m_scene_manager->drawAll(); + + PROFILER_POP_CPU_MARKER(); + + // Note that drawAll must be called before rendering + // the bullet debug view, since otherwise the camera + // is not set up properly. This is only used for + // the bullet debug view. + if (UserConfigParams::m_artist_debug_mode) + World::getWorld()->getPhysics()->draw(); + } // for igetNumKarts() + + // Set the viewport back to the full screen for race gui + m_video_driver->setViewPort(core::recti(0, 0, + UserConfigParams::m_width, + UserConfigParams::m_height)); + + for(unsigned int i=0; irenderPlayerView(camera, dt); + PROFILER_POP_CPU_MARKER(); + + } // for iendScene(); +} diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp new file mode 100644 index 000000000..2fa92f575 --- /dev/null +++ b/src/graphics/rtts.cpp @@ -0,0 +1,141 @@ +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/rtts.hpp" + +#include "config/user_config.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "utils/log.hpp" + +RTT::RTT() +{ + using namespace video; + using namespace core; + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + const dimension2du res(UserConfigParams::m_width, UserConfigParams::m_height); + const dimension2du half = res/2; + const dimension2du quarter = res/4; + const dimension2du eighth = res/8; + const dimension2du sixteenth = res/16; + + const dimension2du ssaosize = UserConfigParams::m_ssao == 2 ? res : quarter; + + const u16 shadowside = UserConfigParams::m_shadows == 2 ? 2048 : 512; + const dimension2du shadowsize(shadowside, shadowside); + const dimension2du warpvsize(1, 512); + const dimension2du warphsize(512, 1); + + // The last parameter stands for "has stencil". The name is used in the texture + // cache, and when saving textures to files as the default name. + // + // All RTTs are currently RGBA8 with stencil. The four tmp RTTs are the same size + // as the screen, for use in post-processing. + // + // Optionally, the collapse ones use a smaller format. + + rtts[RTT_TMP1] = drv->addRenderTargetTexture(res, "rtt.tmp1", ECF_A8R8G8B8, true); + rtts[RTT_TMP2] = drv->addRenderTargetTexture(res, "rtt.tmp2", ECF_A8R8G8B8, true); + rtts[RTT_TMP3] = drv->addRenderTargetTexture(res, "rtt.tmp3", ECF_A8R8G8B8, true); + rtts[RTT_TMP4] = drv->addRenderTargetTexture(res, "rtt.tmp4", ECF_A8R8G8B8, true); + rtts[RTT_DEPTH] = drv->addRenderTargetTexture(res, "rtt.depth", ECF_A8R8G8B8, true); + rtts[RTT_NORMAL] = drv->addRenderTargetTexture(res, "rtt.normal", ECF_A8R8G8B8, true); + rtts[RTT_COLOR] = drv->addRenderTargetTexture(res, "rtt.color", ECF_A8R8G8B8, true); + + rtts[RTT_HALF1] = drv->addRenderTargetTexture(half, "rtt.half1", ECF_A8R8G8B8, true); + rtts[RTT_HALF2] = drv->addRenderTargetTexture(half, "rtt.half2", ECF_A8R8G8B8, true); + + rtts[RTT_QUARTER1] = drv->addRenderTargetTexture(quarter, "rtt.q1", ECF_A8R8G8B8, true); + rtts[RTT_QUARTER2] = drv->addRenderTargetTexture(quarter, "rtt.q2", ECF_A8R8G8B8, true); + rtts[RTT_QUARTER3] = drv->addRenderTargetTexture(quarter, "rtt.q3", ECF_A8R8G8B8, true); + + rtts[RTT_EIGHTH1] = drv->addRenderTargetTexture(eighth, "rtt.e1", ECF_A8R8G8B8, true); + rtts[RTT_EIGHTH2] = drv->addRenderTargetTexture(eighth, "rtt.e2", ECF_A8R8G8B8, true); + + rtts[RTT_SIXTEENTH1] = drv->addRenderTargetTexture(sixteenth, "rtt.s1", ECF_A8R8G8B8, true); + rtts[RTT_SIXTEENTH2] = drv->addRenderTargetTexture(sixteenth, "rtt.s2", ECF_A8R8G8B8, true); + + rtts[RTT_SSAO1] = drv->addRenderTargetTexture(ssaosize, "rtt.ssao1", ECF_A8R8G8B8, true); + rtts[RTT_SSAO2] = drv->addRenderTargetTexture(ssaosize, "rtt.ssao2", ECF_A8R8G8B8, true); + + rtts[RTT_SHADOW] = drv->addRenderTargetTexture(shadowsize, "rtt.shadow", ECF_A8R8G8B8, true); + rtts[RTT_WARPV] = drv->addRenderTargetTexture(warpvsize, "rtt.warpv", ECF_A8R8G8B8, true); + rtts[RTT_WARPH] = drv->addRenderTargetTexture(warphsize, "rtt.warph", ECF_A8R8G8B8, true); + + rtts[RTT_DISPLACE] = drv->addRenderTargetTexture(res, "rtt.displace", ECF_A8R8G8B8, true); + + if (((COpenGLDriver *) drv)->queryOpenGLFeature(COpenGLDriver::IRR_ARB_texture_rg)) + { + // Use optimized formats if supported + rtts[RTT_COLLAPSE] = drv->addRenderTargetTexture(shadowsize, "rtt.collapse", ECF_R8, true); + + rtts[RTT_COLLAPSEV] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev", ECF_R8, true); + rtts[RTT_COLLAPSEH] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh", ECF_R8, true); + rtts[RTT_COLLAPSEV2] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev2", ECF_R8, true); + rtts[RTT_COLLAPSEH2] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh2", ECF_R8, true); + + rtts[RTT_HALF_SOFT] = drv->addRenderTargetTexture(half, "rtt.halfsoft", ECF_R8, true); + } else + { + rtts[RTT_COLLAPSE] = drv->addRenderTargetTexture(shadowsize, "rtt.collapse", ECF_A8R8G8B8, true); + + rtts[RTT_COLLAPSEV] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev", ECF_A8R8G8B8, true); + rtts[RTT_COLLAPSEH] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh", ECF_A8R8G8B8, true); + rtts[RTT_COLLAPSEV2] = drv->addRenderTargetTexture(warpvsize, "rtt.collapsev2", ECF_A8R8G8B8, true); + rtts[RTT_COLLAPSEH2] = drv->addRenderTargetTexture(warphsize, "rtt.collapseh2", ECF_A8R8G8B8, true); + + rtts[RTT_HALF_SOFT] = drv->addRenderTargetTexture(half, "rtt.halfsoft", ECF_A8R8G8B8, true); + } + + u32 i; + for (i = 0; i < RTT_COUNT; i++) + { + if (!rtts[i]) + Log::fatal("RTT", "Failed to create a RTT"); + } + + // Clear those that should be cleared + drv->beginScene(false, false); + + drv->setRenderTarget(rtts[RTT_SSAO1], true, false, SColor(255, 255, 255, 255)); + drv->setRenderTarget(rtts[RTT_SSAO2], true, false, SColor(255, 255, 255, 255)); + + drv->setRenderTarget(rtts[RTT_COLLAPSEV], true, false); + drv->setRenderTarget(rtts[RTT_COLLAPSEH], true, false); + drv->setRenderTarget(rtts[RTT_COLLAPSEV2], true, false); + drv->setRenderTarget(rtts[RTT_COLLAPSEH2], true, false); + + drv->setRenderTarget(0, false, false); + + drv->endScene(); +} + +RTT::~RTT() +{ + u32 i; + for (i = 0; i < RTT_COUNT; i++) + { + irr_driver->removeTexture(rtts[i]); + } +} + +ITexture *RTT::getRTT(TypeRTT which) +{ + assert(which < RTT_COUNT); + return rtts[which]; +} diff --git a/src/graphics/rtts.hpp b/src/graphics/rtts.hpp new file mode 100644 index 000000000..8000c02b9 --- /dev/null +++ b/src/graphics/rtts.hpp @@ -0,0 +1,83 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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_RTTS_HPP +#define HEADER_RTTS_HPP + +namespace irr { + namespace video { + class ITexture; + }; +}; + +using irr::video::ITexture; + +enum TypeRTT +{ + RTT_TMP1 = 0, + RTT_TMP2, + RTT_TMP3, + RTT_TMP4, + RTT_DEPTH, + RTT_NORMAL, + RTT_COLOR, + + RTT_HALF1, + RTT_HALF2, + + RTT_QUARTER1, + RTT_QUARTER2, + RTT_QUARTER3, + + RTT_EIGHTH1, + RTT_EIGHTH2, + + RTT_SIXTEENTH1, + RTT_SIXTEENTH2, + + RTT_SSAO1, + RTT_SSAO2, + + RTT_SHADOW, + RTT_COLLAPSE, + RTT_COLLAPSEH, + RTT_COLLAPSEV, + RTT_COLLAPSEH2, + RTT_COLLAPSEV2, + RTT_WARPH, + RTT_WARPV, + + RTT_HALF_SOFT, + + RTT_DISPLACE, + + RTT_COUNT +}; + +class RTT +{ +public: + RTT(); + ~RTT(); + + ITexture *getRTT(TypeRTT which); + +private: + ITexture *rtts[RTT_COUNT]; +}; + +#endif + diff --git a/src/graphics/screenquad.cpp b/src/graphics/screenquad.cpp new file mode 100644 index 000000000..da2a8a176 --- /dev/null +++ b/src/graphics/screenquad.cpp @@ -0,0 +1,30 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 "graphics/screenquad.hpp" + +// Just the static parts to our screenquad + +const u16 ScreenQuad::indices[4] = {0, 1, 2, 3}; + +static const SColor white(255, 255, 255, 255); + +const S3DVertex ScreenQuad::vertices[4] = { + S3DVertex(-1, 1, 0, 0, 1, 0, white, 0, 1), + S3DVertex(1, 1, 0, 0, 1, 0, white, 1, 1), + S3DVertex(-1, -1, 0, 0, 1, 0, white, 0, 0), + S3DVertex(1, -1, 0, 0, 1, 0, white, 1, 0), + }; diff --git a/src/graphics/screenquad.hpp b/src/graphics/screenquad.hpp new file mode 100644 index 000000000..478500946 --- /dev/null +++ b/src/graphics/screenquad.hpp @@ -0,0 +1,91 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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_SCREENQUAD_H +#define HEADER_SCREENQUAD_H + +#include "graphics/glwrap.hpp" + +#include +#include + +using namespace irr; +using namespace video; + +class ScreenQuad { + +public: + ScreenQuad(IVideoDriver *xy) + { + vd = xy; + + mat.Lighting = false; + mat.ZBuffer = video::ECFN_ALWAYS; + mat.ZWriteEnable = false; + + for(u32 c = 0; c < MATERIAL_MAX_TEXTURES; c++) + { + mat.TextureLayer[c].TextureWrapU = video::ETC_CLAMP_TO_EDGE; + mat.TextureLayer[c].TextureWrapV = video::ETC_CLAMP_TO_EDGE; + } + } + + SMaterial& getMaterial() { return mat; } + + //Set the texture to render with the quad + void setTexture(ITexture* tex, u32 layer = 0) + { + mat.TextureLayer[layer].Texture = tex; + } + + ITexture* getTexture(u32 layer = 0) const { return mat.TextureLayer[layer].Texture; } + + void setMaterialType(E_MATERIAL_TYPE mt) { mat.MaterialType = mt; } + + void render(bool setRTToFrameBuff = true) const + { + if(setRTToFrameBuff) + vd->setRenderTarget(video::ERT_FRAME_BUFFER); + + sq_dorender(); + } + + void render(ITexture* rt) const + { + vd->setRenderTarget(rt); + + sq_dorender(); + } + +protected: + static const S3DVertex vertices[4]; + static const u16 indices[4]; + SMaterial mat; + + IVideoDriver* vd; + + void sq_dorender() const + { + vd->setMaterial(mat); + vd->setTransform(ETS_WORLD, core::IdentityMatrix); + vd->setTransform(ETS_VIEW, core::IdentityMatrix); + vd->setTransform(ETS_PROJECTION, core::IdentityMatrix); + vd->drawVertexPrimitiveList(vertices, 4, indices, 2, EVT_STANDARD, + scene::EPT_TRIANGLE_STRIP); + } +}; + +#endif diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp new file mode 100644 index 000000000..8a26cab4b --- /dev/null +++ b/src/graphics/shaders.cpp @@ -0,0 +1,260 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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. + +#define SHADER_NAMES + +#include "graphics/callbacks.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/shaders.hpp" +#include "io/file_manager.hpp" +#include "utils/log.hpp" + +#include +#include + +using namespace video; + +Shaders::Shaders() +{ + const std::string &dir = file_manager->getShaderDir(); + + IGPUProgrammingServices * const gpu = irr_driver->getVideoDriver()->getGPUProgrammingServices(); + + #define glsl(a, b, c) gpu->addHighLevelShaderMaterialFromFiles((a).c_str(), (b).c_str(), (IShaderConstantSetCallBack*) c) + #define glslmat(a, b, c, d) gpu->addHighLevelShaderMaterialFromFiles((a).c_str(), (b).c_str(), (IShaderConstantSetCallBack*) c, d) + #define glsl_noinput(a, b) gpu->addHighLevelShaderMaterialFromFiles((a).c_str(), (b).c_str(), (IShaderConstantSetCallBack*) 0) + + // Callbacks + memset(m_callbacks, 0, sizeof(m_callbacks)); + + m_callbacks[ES_NORMAL_MAP_LIGHTMAP] = new NormalMapProvider(true); + m_callbacks[ES_NORMAL_MAP] = new NormalMapProvider(false); + m_callbacks[ES_SPLATTING] = new SplattingProvider(); + m_callbacks[ES_WATER] = new WaterShaderProvider(); + m_callbacks[ES_GRASS] = new GrassShaderProvider(); + m_callbacks[ES_BUBBLES] = new BubbleEffectProvider(); + m_callbacks[ES_RAIN] = new RainEffectProvider(); + m_callbacks[ES_SNOW] = new SnowEffectProvider(); + m_callbacks[ES_MOTIONBLUR] = new MotionBlurProvider(); + m_callbacks[ES_GAUSSIAN3V] = m_callbacks[ES_GAUSSIAN3H] = new GaussianBlurProvider(); + m_callbacks[ES_MIPVIZ] = new MipVizProvider(); + m_callbacks[ES_COLORIZE] = new ColorizeProvider(); + m_callbacks[ES_GLOW] = new GlowProvider(); + m_callbacks[ES_OBJECTPASS] = new ObjectPassProvider(); + m_callbacks[ES_LIGHTBLEND] = new LightBlendProvider(); + m_callbacks[ES_POINTLIGHT] = new PointLightProvider(); + m_callbacks[ES_SUNLIGHT] = new SunLightProvider(); + m_callbacks[ES_BLOOM] = new BloomProvider(); + m_callbacks[ES_MLAA_COLOR1] = new MLAAColor1Provider(); + m_callbacks[ES_MLAA_BLEND2] = new MLAABlend2Provider(); + m_callbacks[ES_MLAA_NEIGH3] = new MLAANeigh3Provider(); + m_callbacks[ES_SSAO] = new SSAOProvider(); + m_callbacks[ES_GODRAY] = new GodRayProvider(); + m_callbacks[ES_SHADOWPASS] = new ShadowPassProvider(); + m_callbacks[ES_SHADOW_IMPORTANCE] = new ShadowImportanceProvider(); + m_callbacks[ES_COLLAPSE] = new CollapseProvider(); + m_callbacks[ES_BLOOM_POWER] = new BloomPowerProvider(); + m_callbacks[ES_MULTIPLY_ADD] = new MultiplyProvider(); + m_callbacks[ES_SHADOWGEN] = new ShadowGenProvider(); + m_callbacks[ES_CAUSTICS] = new CausticsProvider(); + m_callbacks[ES_DISPLACE] = new DisplaceProvider(); + m_callbacks[ES_PPDISPLACE] = new PPDisplaceProvider(); + m_callbacks[ES_FOG] = new FogProvider(); + + // Ok, go + m_shaders[ES_NORMAL_MAP] = glslmat(dir + "normalmap.vert", dir + "normalmap.frag", + m_callbacks[ES_NORMAL_MAP], EMT_SOLID_2_LAYER); + + m_shaders[ES_NORMAL_MAP_LIGHTMAP] = glslmat(dir + "normalmap.vert", dir + "normalmap.frag", + m_callbacks[ES_NORMAL_MAP_LIGHTMAP], EMT_SOLID_2_LAYER); + + m_shaders[ES_SPLATTING] = glsl(dir + "objectpass.vert", dir + "splatting.frag", + m_callbacks[ES_SPLATTING]); + + m_shaders[ES_WATER] = glslmat(dir + "water.vert", dir + "water.frag", + m_callbacks[ES_WATER], EMT_TRANSPARENT_ALPHA_CHANNEL); + m_shaders[ES_WATER_SURFACE] = glsl(dir + "water.vert", dir + "pass.frag", + m_callbacks[ES_WATER]); + + m_shaders[ES_SPHERE_MAP] = glslmat(dir + "objectpass_rimlit.vert", dir + "objectpass_spheremap.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + + m_shaders[ES_GRASS] = glslmat(dir + "grass.vert", dir + "grass.frag", + m_callbacks[ES_GRASS], EMT_TRANSPARENT_ALPHA_CHANNEL); + m_shaders[ES_GRASS_REF] = glslmat(dir + "grass.vert", dir + "grass.frag", + m_callbacks[ES_GRASS], EMT_TRANSPARENT_ALPHA_CHANNEL_REF); + + m_shaders[ES_BUBBLES] = glslmat(dir + "bubble.vert", dir + "bubble.frag", + m_callbacks[ES_BUBBLES], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_RAIN] = glslmat(dir + "rain.vert", dir + "rain.frag", + m_callbacks[ES_RAIN], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_SNOW] = glslmat(dir + "snow.vert", dir + "snow.frag", + m_callbacks[ES_SNOW], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_MOTIONBLUR] = glsl(std::string(""), dir + "motion_blur.frag", + m_callbacks[ES_MOTIONBLUR]); + + m_shaders[ES_GAUSSIAN3H] = glslmat(std::string(""), dir + "gaussian3h.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_GAUSSIAN3V] = glslmat(std::string(""), dir + "gaussian3v.frag", + m_callbacks[ES_GAUSSIAN3V], EMT_SOLID); + + m_shaders[ES_GAUSSIAN6H] = glslmat(std::string(""), dir + "gaussian6h.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_GAUSSIAN6V] = glslmat(std::string(""), dir + "gaussian6v.frag", + m_callbacks[ES_GAUSSIAN3V], EMT_SOLID); + + m_shaders[ES_MIPVIZ] = glslmat(std::string(""), dir + "mipviz.frag", + m_callbacks[ES_MIPVIZ], EMT_SOLID); + + m_shaders[ES_FLIP] = glslmat(std::string(""), dir + "flip.frag", + 0, EMT_SOLID); + m_shaders[ES_FLIP_ADDITIVE] = glslmat(std::string(""), dir + "flip.frag", + 0, EMT_TRANSPARENT_ADD_COLOR); + + m_shaders[ES_BLOOM] = glslmat(std::string(""), dir + "bloom.frag", + m_callbacks[ES_BLOOM], EMT_SOLID); + + m_shaders[ES_COLORIZE] = glslmat(std::string(""), dir + "colorize.frag", + m_callbacks[ES_COLORIZE], EMT_SOLID); + m_shaders[ES_COLORIZE_REF] = glslmat(std::string(""), dir + "colorize_ref.frag", + m_callbacks[ES_COLORIZE], EMT_SOLID); + + m_shaders[ES_PASS] = glslmat(std::string(""), dir + "pass.frag", + 0, EMT_SOLID); + m_shaders[ES_PASS_ADDITIVE] = glslmat(std::string(""), dir + "pass.frag", + 0, EMT_TRANSPARENT_ADD_COLOR); + + m_shaders[ES_GLOW] = glslmat(std::string(""), dir + "glow.frag", + m_callbacks[ES_GLOW], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_OBJECTPASS] = glslmat(dir + "objectpass.vert", dir + "objectpass.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + m_shaders[ES_OBJECTPASS_REF] = glslmat(dir + "objectpass.vert", dir + "objectpass_ref.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + m_shaders[ES_OBJECTPASS_RIMLIT] = glslmat(dir + "objectpass_rimlit.vert", dir + "objectpass_rimlit.frag", + m_callbacks[ES_OBJECTPASS], EMT_SOLID); + + m_shaders[ES_LIGHTBLEND] = glslmat(std::string(""), dir + "lightblend.frag", + m_callbacks[ES_LIGHTBLEND], EMT_ONETEXTURE_BLEND); + + m_shaders[ES_POINTLIGHT] = glslmat(std::string(""), dir + "pointlight.frag", + m_callbacks[ES_POINTLIGHT], EMT_ONETEXTURE_BLEND); + + m_shaders[ES_SUNLIGHT] = glslmat(std::string(""), dir + "sunlight.frag", + m_callbacks[ES_SUNLIGHT], EMT_SOLID); + m_shaders[ES_SUNLIGHT_SHADOW] = glslmat(dir + "pass.vert", dir + "sunlightshadow.frag", + m_callbacks[ES_SUNLIGHT], EMT_SOLID); + + m_shaders[ES_MLAA_COLOR1] = glsl(dir + "mlaa_offset.vert", dir + "mlaa_color1.frag", + m_callbacks[ES_MLAA_COLOR1]); + m_shaders[ES_MLAA_BLEND2] = glsl(dir + "pass.vert", dir + "mlaa_blend2.frag", + m_callbacks[ES_MLAA_BLEND2]); + m_shaders[ES_MLAA_NEIGH3] = glsl(dir + "mlaa_offset.vert", dir + "mlaa_neigh3.frag", + m_callbacks[ES_MLAA_NEIGH3]); + + m_shaders[ES_SSAO] = glsl(std::string(""), dir + "ssao.frag", m_callbacks[ES_SSAO]); + + m_shaders[ES_GODFADE] = glsl(std::string(""), dir + "godfade.frag", m_callbacks[ES_COLORIZE]); + m_shaders[ES_GODRAY] = glsl(std::string(""), dir + "godray.frag", m_callbacks[ES_GODRAY]); + + m_shaders[ES_SHADOWPASS] = glsl(dir + "shadowpass.vert", dir + "shadowpass.frag", + m_callbacks[ES_SHADOWPASS]); + + m_shaders[ES_SHADOW_IMPORTANCE] = glsl(dir + "shadowimportance.vert", + dir + "shadowimportance.frag", + m_callbacks[ES_SHADOW_IMPORTANCE]); + + m_shaders[ES_COLLAPSE] = glsl(std::string(""), dir + "collapse.frag", + m_callbacks[ES_COLLAPSE]); + m_shaders[ES_SHADOW_WARPH] = glsl(std::string(""), dir + "shadowwarph.frag", + m_callbacks[ES_COLLAPSE]); + m_shaders[ES_SHADOW_WARPV] = glsl(std::string(""), dir + "shadowwarpv.frag", + m_callbacks[ES_COLLAPSE]); + + m_shaders[ES_BLOOM_POWER] = glsl(std::string(""), dir + "bloompower.frag", + m_callbacks[ES_BLOOM_POWER]); + m_shaders[ES_BLOOM_BLEND] = glslmat(std::string(""), dir + "bloomblend.frag", + 0, EMT_TRANSPARENT_ADD_COLOR); + + m_shaders[ES_MULTIPLY_ADD] = glslmat(std::string(""), dir + "multiply.frag", + m_callbacks[ES_MULTIPLY_ADD], EMT_ONETEXTURE_BLEND); + + m_shaders[ES_PENUMBRAH] = glslmat(std::string(""), dir + "penumbrah.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_PENUMBRAV] = glslmat(std::string(""), dir + "penumbrav.frag", + m_callbacks[ES_GAUSSIAN3H], EMT_SOLID); + m_shaders[ES_SHADOWGEN] = glslmat(std::string(""), dir + "shadowgen.frag", + m_callbacks[ES_SHADOWGEN], EMT_SOLID); + + m_shaders[ES_CAUSTICS] = glslmat(std::string(""), dir + "caustics.frag", + m_callbacks[ES_CAUSTICS], EMT_TRANSPARENT_ALPHA_CHANNEL); + + m_shaders[ES_DISPLACE] = glsl(dir + "displace.vert", dir + "displace.frag", + m_callbacks[ES_DISPLACE]); + m_shaders[ES_PPDISPLACE] = glsl(std::string(""), dir + "ppdisplace.frag", + m_callbacks[ES_PPDISPLACE]); + + m_shaders[ES_PASSFAR] = glsl(dir + "farplane.vert", dir + "colorize.frag", + m_callbacks[ES_COLORIZE]); + + m_shaders[ES_FOG] = glslmat(std::string(""), dir + "fog.frag", + m_callbacks[ES_FOG], EMT_ONETEXTURE_BLEND); + + // Check that all successfully loaded + u32 i; + for (i = 0; i < ES_COUNT; i++) { + + // Old Intel Windows drivers fail here. + // It's an artist option, so not necessary to play. + if (i == ES_MIPVIZ) + continue; + + check(i); + } + + #undef glsl + #undef glslmat + #undef glsl_noinput +} + +Shaders::~Shaders() +{ + u32 i; + for (i = 0; i < ES_COUNT; i++) + { + if (i == ES_GAUSSIAN3V || !m_callbacks[i]) continue; + delete m_callbacks[i]; + } +} + +E_MATERIAL_TYPE Shaders::getShader(const ShaderType num) const +{ + assert(num < ES_COUNT); + + return (E_MATERIAL_TYPE) m_shaders[num]; +} + +void Shaders::check(const int num) const +{ + if (m_shaders[num] == -1) + { + Log::fatal("shaders", "Shader %s failed to load. Update your drivers, if the issue " + "persists, report a bug to us.", shader_names[num] + 3); + } +} diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp new file mode 100644 index 000000000..80794a95f --- /dev/null +++ b/src/graphics/shaders.hpp @@ -0,0 +1,117 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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_SHADERS_HPP +#define HEADER_SHADERS_HPP + +#include +#include +#include +using namespace irr; + +#define FOREACH_SHADER(ACT) \ + ACT(ES_NORMAL_MAP) \ + ACT(ES_NORMAL_MAP_LIGHTMAP) \ + ACT(ES_SPLATTING) \ + ACT(ES_WATER) \ + ACT(ES_WATER_SURFACE) \ + ACT(ES_SPHERE_MAP) \ + ACT(ES_GRASS) \ + ACT(ES_GRASS_REF) \ + ACT(ES_BUBBLES) \ + ACT(ES_RAIN) \ + ACT(ES_SNOW) \ + ACT(ES_MOTIONBLUR) \ + ACT(ES_GAUSSIAN3H) \ + ACT(ES_GAUSSIAN3V) \ + ACT(ES_MIPVIZ) \ + ACT(ES_FLIP) \ + ACT(ES_FLIP_ADDITIVE) \ + ACT(ES_BLOOM) \ + ACT(ES_GAUSSIAN6H) \ + ACT(ES_GAUSSIAN6V) \ + ACT(ES_COLORIZE) \ + ACT(ES_COLORIZE_REF) \ + ACT(ES_PASS) \ + ACT(ES_PASS_ADDITIVE) \ + ACT(ES_GLOW) \ + ACT(ES_OBJECTPASS) \ + ACT(ES_OBJECTPASS_REF) \ + ACT(ES_LIGHTBLEND) \ + ACT(ES_POINTLIGHT) \ + ACT(ES_SUNLIGHT) \ + ACT(ES_SUNLIGHT_SHADOW) \ + ACT(ES_OBJECTPASS_RIMLIT) \ + ACT(ES_MLAA_COLOR1) \ + ACT(ES_MLAA_BLEND2) \ + ACT(ES_MLAA_NEIGH3) \ + ACT(ES_SSAO) \ + ACT(ES_GODFADE) \ + ACT(ES_GODRAY) \ + ACT(ES_SHADOWPASS) \ + ACT(ES_SHADOW_IMPORTANCE) \ + ACT(ES_COLLAPSE) \ + ACT(ES_SHADOW_WARPH) \ + ACT(ES_SHADOW_WARPV) \ + ACT(ES_BLOOM_POWER) \ + ACT(ES_BLOOM_BLEND) \ + ACT(ES_MULTIPLY_ADD) \ + ACT(ES_PENUMBRAH) \ + ACT(ES_PENUMBRAV) \ + ACT(ES_SHADOWGEN) \ + ACT(ES_CAUSTICS) \ + ACT(ES_DISPLACE) \ + ACT(ES_PPDISPLACE) \ + ACT(ES_PASSFAR) \ + ACT(ES_FOG) + +#define ENUM(a) a, +#define STR(a) #a, + +enum ShaderType +{ + FOREACH_SHADER(ENUM) + + ES_COUNT +}; + +#ifdef SHADER_NAMES +static const char *shader_names[] = { + FOREACH_SHADER(STR) +}; +#endif + +class Shaders +{ +public: + Shaders(); + ~Shaders(); + + video::E_MATERIAL_TYPE getShader(const ShaderType num) const; + + video::IShaderConstantSetCallBack * m_callbacks[ES_COUNT]; + +private: + void check(const int num) const; + + int m_shaders[ES_COUNT]; +}; + +#undef ENUM +#undef STR +#undef FOREACH_SHADER + +#endif diff --git a/src/graphics/shadow_importance.cpp b/src/graphics/shadow_importance.cpp new file mode 100644 index 000000000..2e09c6a59 --- /dev/null +++ b/src/graphics/shadow_importance.cpp @@ -0,0 +1,169 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "config/user_config.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/large_mesh_buffer.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/per_camera_node.hpp" +#include "graphics/shadow_importance.hpp" +#include "graphics/shaders.hpp" +#include "graphics/rtts.hpp" +#include "utils/vs.hpp" + +#include + +using namespace video; +using namespace scene; +using namespace core; + +// The actual ShadowImportance node +class ShadowImportanceNode: public scene::ISceneNode +{ +public: + ShadowImportanceNode(scene::ISceneManager* mgr) + : scene::ISceneNode(0, mgr, -1) + { + mat.Lighting = false; + mat.ZWriteEnable = false; + mat.MaterialType = irr_driver->getShader(ES_SHADOW_IMPORTANCE); + + mat.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + mat.setTexture(1, irr_driver->getRTT(RTT_DEPTH)); + mat.setTexture(2, irr_driver->getRTT(RTT_COLOR)); + + mat.setFlag(EMF_BILINEAR_FILTER, false); + + u32 i; + for (i = 0; i < MATERIAL_MAX_TEXTURES; i++) + { + mat.TextureLayer[i].TextureWrapU = + mat.TextureLayer[i].TextureWrapV = ETC_CLAMP_TO_EDGE; + } + + // Low shadows only back-project every other pixel + const u32 incr = UserConfigParams::m_shadows < 2 ? 2 : 1; + + count = (UserConfigParams::m_width * UserConfigParams::m_height) / (incr * incr); + + // Fill in the mesh buffer + buf.Vertices.clear(); + buf.Indices.clear(); + + buf.Vertices.set_used(count); + buf.Indices.set_used(count); + + buf.Primitive = EPT_POINTS; + buf.setHardwareMappingHint(EHM_STATIC); + + const float halfx = 0.5f / UserConfigParams::m_width; + const float halfy = 0.5f / UserConfigParams::m_height; + + list = glGenLists(1); + + s32 x, y; + i = 0; + glNewList(list, GL_COMPILE); + glBegin(GL_POINTS); + for (x = 0; x < UserConfigParams::m_width; x += incr) + { + const float xpos = ((float) x) / UserConfigParams::m_width + halfx; + + for (y = 0; y < UserConfigParams::m_height; y += incr) + { + const float ypos = ((float) y) / UserConfigParams::m_height + halfy; + + buf.Indices[i] = i; + buf.Vertices[i] = S3DVertex(xpos, ypos, 0, 0, 0, 0, + SColor(255, 255, 255, 255), 0, 0); + + glVertex2s(roundf(xpos * 32767), roundf(ypos * 32767)); + i++; + } + } + glEnd(); + glEndList(); + + box.addInternalPoint(vector3df(-1)); + box.addInternalPoint(vector3df(1)); + } + + ~ShadowImportanceNode() + { + } + + virtual void render() + { + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setMaterial(mat); + + drv->setTransform(ETS_WORLD, IdentityMatrix); + +// drv->drawMeshBuffer(&buf); + // Setup the env for drawing our list by drawing one point + drv->drawVertexPrimitiveList(buf.getVertices(), 1, buf.getIndices(), 1, + EVT_STANDARD, EPT_POINTS); + glCallList(list); + } + + virtual const core::aabbox3d& getBoundingBox() const + { + return box; + } + + virtual void OnRegisterSceneNode() + { + ISceneNode::OnRegisterSceneNode(); + } + + virtual u32 getMaterialCount() const { return 1; } + virtual video::SMaterial& getMaterial(u32 i) { return mat; } + +private: + video::SMaterial mat; + core::aabbox3d box; + u32 count; + GLuint list; + + scene::LargeMeshBuffer buf; +}; + +// The ShadowImportance manager + +ShadowImportance::ShadowImportance() +{ + m_node = new ShadowImportanceNode(irr_driver->getSceneManager()); + m_node->setAutomaticCulling(0); +} // ShadowImportance + +// ---------------------------------------------------------------------------- + +ShadowImportance::~ShadowImportance() +{ + m_node->drop(); // drop STK's reference + m_node->remove(); // Then remove it from the scene graph. +} + +// ---------------------------------------------------------------------------- + +void ShadowImportance::render() +{ + m_node->render(); +} diff --git a/src/graphics/shadow_importance.hpp b/src/graphics/shadow_importance.hpp new file mode 100644 index 000000000..3d5c2dba0 --- /dev/null +++ b/src/graphics/shadow_importance.hpp @@ -0,0 +1,41 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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_SHADOW_IMPORTANCE_HPP +#define HEADER_SHADOW_IMPORTANCE_HPP + +#include +namespace irr +{ + namespace video { class SMaterial; class ITexture; } + namespace scene { class ICameraSceneNode; class ISceneNode; } +} +using namespace irr; + +class ShadowImportance +{ + scene::ISceneNode *m_node; + +public: + ShadowImportance(); + ~ShadowImportance(); + + void render(); +}; + +#endif diff --git a/src/graphics/sun.cpp b/src/graphics/sun.cpp new file mode 100644 index 000000000..e9c029822 --- /dev/null +++ b/src/graphics/sun.cpp @@ -0,0 +1,161 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/sun.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/screenquad.hpp" +#include "graphics/shaders.hpp" +#include "io/file_manager.hpp" +#include "modes/world.hpp" +#include "tracks/track.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +SunNode::SunNode(scene::ISceneManager* mgr, float r, float g, float b): + LightNode(mgr, 10000, r, g, b) +{ + + sq = new ScreenQuad(irr_driver->getVideoDriver()); + + SMaterial &m = sq->getMaterial(); + + m.MaterialType = irr_driver->getShader(ES_SUNLIGHT); + m.setTexture(0, irr_driver->getRTT(RTT_NORMAL)); + m.setTexture(1, irr_driver->getRTT(RTT_DEPTH)); + m.setTexture(2, irr_driver->getTexture((file_manager->getTextureDir() + "cloudshadow.png").c_str())); + m.setFlag(EMF_BILINEAR_FILTER, false); + + if (UserConfigParams::m_shadows) + { + m.setTexture(3, irr_driver->getRTT(RTT_SHADOW)); + m.setTexture(4, irr_driver->getRTT(RTT_WARPH)); + m.setTexture(5, irr_driver->getRTT(RTT_WARPV)); + + m.TextureLayer[4].BilinearFilter = + m.TextureLayer[5].BilinearFilter = true; + + m.MaterialType = irr_driver->getShader(ES_SUNLIGHT_SHADOW); + } + + for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; i++) + { + m.TextureLayer[i].TextureWrapU = m.TextureLayer[i].TextureWrapV = + ETC_CLAMP_TO_EDGE; + } + + m.TextureLayer[2].TextureWrapU = m.TextureLayer[2].TextureWrapV = ETC_REPEAT; + + m.TextureLayer[2].TrilinearFilter = true; + + m_color[0] = r; + m_color[1] = g; + m_color[2] = b; +} + +SunNode::~SunNode() +{ + delete sq; +} + +void SunNode::render() +{ + SunLightProvider * const cb = (SunLightProvider *) irr_driver->getCallback(ES_SUNLIGHT); + cb->setColor(m_color[0], m_color[1], m_color[2]); + + vector3df pos = getPosition(); + pos.normalize(); + cb->setPosition(pos.X, pos.Y, pos.Z); + + if (!UserConfigParams::m_shadows || !World::getWorld()->getTrack()->hasShadows()) + { + sq->render(false); + return; + } + + array mrt; + mrt.reallocate(2); + mrt.push_back(irr_driver->getRTT(RTT_TMP2)); + mrt.push_back(irr_driver->getRTT(RTT_TMP3)); + irr_driver->getVideoDriver()->setRenderTarget(mrt, true, false); + + // Render the sun lighting to tmp2, shadow map to tmp3 + sq->render(false); + + // Filter the shadow map for soft shadows + // Note that quarter1 is reserved for glow during this time. + // Having a separate RTT for glow would work, but be wasted VRAM due to less reuse. + ScreenQuad tmpsq(irr_driver->getVideoDriver()); + GaussianBlurProvider * const gcb = (GaussianBlurProvider *) irr_driver->getCallback(ES_GAUSSIAN3H); + + gcb->setResolution(UserConfigParams::m_width / 2, UserConfigParams::m_height / 2); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAH)); + tmpsq.setTexture(irr_driver->getRTT(RTT_TMP3)); + tmpsq.render(irr_driver->getRTT(RTT_HALF1)); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAV)); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF1)); + tmpsq.render(irr_driver->getRTT(RTT_HALF2)); + + gcb->setResolution(UserConfigParams::m_width / 4, UserConfigParams::m_height / 4); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAH)); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF2)); + tmpsq.render(irr_driver->getRTT(RTT_QUARTER2)); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAV)); + tmpsq.setTexture(irr_driver->getRTT(RTT_QUARTER2)); + tmpsq.render(irr_driver->getRTT(RTT_QUARTER3)); + + gcb->setResolution(UserConfigParams::m_width / 8, UserConfigParams::m_height / 8); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAH)); + tmpsq.setTexture(irr_driver->getRTT(RTT_QUARTER3)); + tmpsq.render(irr_driver->getRTT(RTT_EIGHTH1)); + tmpsq.setMaterialType(irr_driver->getShader(ES_PENUMBRAV)); + tmpsq.setTexture(irr_driver->getRTT(RTT_EIGHTH1)); + tmpsq.render(irr_driver->getRTT(RTT_EIGHTH2)); + + // Use these to generate a new soft shadow map + tmpsq.setMaterialType(irr_driver->getShader(ES_SHADOWGEN)); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF2), 0); + tmpsq.setTexture(irr_driver->getRTT(RTT_QUARTER3), 1); + tmpsq.setTexture(irr_driver->getRTT(RTT_EIGHTH2), 2); + + irr_driver->getVideoDriver()->setRenderTarget(irr_driver->getRTT(RTT_HALF_SOFT), true, false); + tmpsq.render(false); + + tmpsq.setTexture(0, 0); + tmpsq.setTexture(0, 1); + tmpsq.setTexture(0, 2); + tmpsq.setTexture(0, 3); + + // Combine them back to the lighting RTT + tmpsq.setMaterialType(irr_driver->getShader(ES_MULTIPLY_ADD)); + tmpsq.setTexture(irr_driver->getRTT(RTT_TMP2), 0); + tmpsq.setTexture(irr_driver->getRTT(RTT_HALF_SOFT), 1); + + tmpsq.getMaterial().MaterialTypeParam = pack_textureBlendFunc(EBF_ONE, EBF_ONE); + tmpsq.getMaterial().BlendOperation = EBO_ADD; + + irr_driver->getVideoDriver()->setRenderTarget(irr_driver->getRTT(RTT_TMP1), false, false); + tmpsq.render(false); +} diff --git a/src/graphics/sun.hpp b/src/graphics/sun.hpp new file mode 100644 index 000000000..387444c30 --- /dev/null +++ b/src/graphics/sun.hpp @@ -0,0 +1,40 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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_SUN_HPP +#define HEADER_SUN_HPP + +#include "graphics/light.hpp" +#include "utils/cpp2011.h" + +class ScreenQuad; + +// The actual rain node +class SunNode: public LightNode +{ +public: + SunNode(scene::ISceneManager* mgr, float r, float g, float b); + virtual ~SunNode(); + + virtual void render() OVERRIDE; + +private: + ScreenQuad *sq; +}; + +#endif diff --git a/src/graphics/water.cpp b/src/graphics/water.cpp new file mode 100644 index 000000000..3ac11d981 --- /dev/null +++ b/src/graphics/water.cpp @@ -0,0 +1,94 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/water.hpp" + +#include "graphics/callbacks.hpp" +#include "graphics/glwrap.hpp" +#include "graphics/irr_driver.hpp" +#include "graphics/material_manager.hpp" +#include "graphics/material.hpp" +#include "graphics/rtts.hpp" +#include "graphics/shaders.hpp" + +using namespace video; +using namespace scene; +using namespace core; + +WaterNode::WaterNode(scene::ISceneManager* mgr, IMesh *mesh, float height, float speed, + float length): + IMeshSceneNode(mgr->getRootSceneNode(), mgr, -1) +{ + m_mat = mesh->getMeshBuffer(0)->getMaterial(); + + m_mat.Lighting = false; + + if (m_mat.MaterialType != irr_driver->getShader(ES_WATER)) + { + m_mat.MaterialType = irr_driver->getShader(ES_WATER_SURFACE); + } else + { + m_mat.BlendOperation = EBO_ADD; + } + + m_mat.setFlag(EMF_ZWRITE_ENABLE, false); + + m_mesh = mesh; + mesh->grab(); + mesh->setHardwareMappingHint(EHM_STATIC); + m_box = mesh->getBoundingBox(); + + m_height = height; + m_speed = speed; + m_length = length; +} + +WaterNode::~WaterNode() +{ + m_mesh->drop(); +} + +void WaterNode::render() +{ + if (SceneManager->getSceneNodeRenderPass() != scene::ESNRP_TRANSPARENT) + return; + + WaterShaderProvider * const cb = (WaterShaderProvider *) irr_driver->getCallback(ES_WATER); + cb->setSpeed(m_speed); + cb->setHeight(m_height); + cb->setLength(m_length); + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + drv->setTransform(ETS_WORLD, AbsoluteTransformation); + drv->setMaterial(m_mat); + + const u32 max = m_mesh->getMeshBufferCount(); + for (u32 i = 0; i < max; i++) + { + drv->drawMeshBuffer(m_mesh->getMeshBuffer(i)); + } +} + +void WaterNode::OnRegisterSceneNode() +{ + if (IsVisible) + { + SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT); + ISceneNode::OnRegisterSceneNode(); + } +} diff --git a/src/graphics/water.hpp b/src/graphics/water.hpp new file mode 100644 index 000000000..15acacb6f --- /dev/null +++ b/src/graphics/water.hpp @@ -0,0 +1,72 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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_WATER_HPP +#define HEADER_WATER_HPP + +#include +#include + +using namespace irr; + +namespace irr +{ + namespace scene { class IMesh; } +} + +// The actual node +class WaterNode: public scene::IMeshSceneNode +{ +public: + WaterNode(scene::ISceneManager* mgr, scene::IMesh *mesh, float height, float speed, + float length); + virtual ~WaterNode(); + + virtual void render() OVERRIDE; + + virtual const core::aabbox3d& getBoundingBox() const OVERRIDE + { + return m_box; + } + + virtual void OnRegisterSceneNode() OVERRIDE; + + virtual u32 getMaterialCount() const OVERRIDE { return 1; } + virtual video::SMaterial& getMaterial(u32 i) OVERRIDE { return m_mat; } + + virtual scene::ESCENE_NODE_TYPE getType() const OVERRIDE { return scene::ESNT_MESH; } + + virtual void setMesh(scene::IMesh *) OVERRIDE {} + virtual scene::IMesh *getMesh() OVERRIDE { return m_mesh; } + virtual void setReadOnlyMaterials(bool) OVERRIDE {} + virtual bool isReadOnlyMaterials() const OVERRIDE { return false; } + virtual scene::IShadowVolumeSceneNode* addShadowVolumeSceneNode + (const scene::IMesh*, int, bool, f32) OVERRIDE { return NULL; } + +protected: + video::SMaterial m_mat; + core::aabbox3df m_box; + + scene::IMesh *m_mesh; + + float m_height; + float m_speed; + float m_length; +}; + +#endif diff --git a/src/graphics/wind.cpp b/src/graphics/wind.cpp new file mode 100644 index 000000000..cab30b15a --- /dev/null +++ b/src/graphics/wind.cpp @@ -0,0 +1,40 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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 "graphics/irr_driver.hpp" +#include "graphics/wind.hpp" +#include "utils/helpers.hpp" + +Wind::Wind() +{ + m_seed = (rand() % 1000) - 500; +} + +vector3df Wind::getWind() const +{ + return m_wind; +} + +void Wind::update() +{ + vector3df dir(0, 0, 1); + const u32 now = irr_driver->getDevice()->getTimer()->getTime(); + + const float rotation = asinf(noise2d(m_seed, now / 100000.0f)) * RAD_TO_DEGREE; + dir.rotateXZBy(rotation); + + m_wind = dir; +} diff --git a/src/graphics/wind.hpp b/src/graphics/wind.hpp new file mode 100644 index 000000000..651258f2b --- /dev/null +++ b/src/graphics/wind.hpp @@ -0,0 +1,38 @@ +// SuperTuxKart - a fun racing game with go-kart +// +// 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_WIND_HPP +#define HEADER_WIND_HPP + +#include +using namespace irr; +using core::vector3df; + +class Wind +{ +public: + Wind(); + + vector3df getWind() const; + void update(); + +private: + vector3df m_wind; + float m_seed; +}; + +#endif + diff --git a/src/guiengine/layout_manager.cpp b/src/guiengine/layout_manager.cpp index e05ce354e..b5cb4634c 100644 --- a/src/guiengine/layout_manager.cpp +++ b/src/guiengine/layout_manager.cpp @@ -33,13 +33,10 @@ using namespace video; #include "io/file_manager.hpp" #include "utils/ptr_vector.hpp" #include "utils/string_utils.hpp" +#include "utils/vs.hpp" using namespace GUIEngine; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - /** Like atoi, but on error prints an error message to stderr */ int atoi_p(const char* val) { diff --git a/src/guiengine/widget.cpp b/src/guiengine/widget.cpp index eb60910b6..734b98cbd 100644 --- a/src/guiengine/widget.cpp +++ b/src/guiengine/widget.cpp @@ -34,11 +34,7 @@ using namespace gui; #include "io/file_manager.hpp" #include "utils/string_utils.hpp" #include "utils/translation.hpp" - - -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif +#include "utils/vs.hpp" namespace GUIEngine { diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.cpp b/src/guiengine/widgets/dynamic_ribbon_widget.cpp index d40464cfc..ddaae8673 100644 --- a/src/guiengine/widgets/dynamic_ribbon_widget.cpp +++ b/src/guiengine/widgets/dynamic_ribbon_widget.cpp @@ -19,6 +19,7 @@ #include "guiengine/widgets/dynamic_ribbon_widget.hpp" #include "io/file_manager.hpp" #include "states_screens/state_manager.hpp" +#include "utils/vs.hpp" #include #include @@ -29,10 +30,6 @@ using namespace GUIEngine; using namespace irr::core; using namespace irr::gui; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - DynamicRibbonWidget::DynamicRibbonWidget(const bool combo, const bool multi_row) : Widget(WTYPE_DYNAMIC_RIBBON) { m_scroll_offset = 0; diff --git a/src/guiengine/widgets/ribbon_widget.cpp b/src/guiengine/widgets/ribbon_widget.cpp index 4ed5efc9a..40c2be3a0 100644 --- a/src/guiengine/widgets/ribbon_widget.cpp +++ b/src/guiengine/widgets/ribbon_widget.cpp @@ -28,6 +28,7 @@ #include "io/file_manager.hpp" #include "states_screens/state_manager.hpp" #include "utils/string_utils.hpp" +#include "utils/vs.hpp" #include #include @@ -37,10 +38,6 @@ using namespace GUIEngine; using namespace irr::core; using namespace irr::gui; -#ifndef round -# define round(x) (floor(x+0.5f)) -#endif - const char RibbonWidget::NO_ITEM_ID[] = "?"; diff --git a/src/input/input_manager.cpp b/src/input/input_manager.cpp index be9979bed..5933b4cc7 100644 --- a/src/input/input_manager.cpp +++ b/src/input/input_manager.cpp @@ -290,15 +290,51 @@ void InputManager::handleStaticAction(int key, int value) UserConfigParams::m_profiler_enabled = !UserConfigParams::m_profiler_enabled; break; + + // Debug views + // These should be available in normal (non-artist) mode + // to enable easier bug reports (go there, press this key, screenshot) + // without requiring editing the UTF-32 config files (which is inconvenient). case KEY_HOME: if (value) { - video::SOverrideMaterial &mat = - irr_driver->getVideoDriver()->getOverrideMaterial(); - - mat.Material.Wireframe ^= 1; - mat.EnableFlags = video::EMF_WIREFRAME; - mat.EnablePasses = scene::ESNRP_SOLID | scene::ESNRP_TRANSPARENT; + irr_driver->toggleWireframe(); + } + break; + case KEY_END: + if (value) + { + irr_driver->toggleMipVisualization(); + } + break; + case KEY_DELETE: + if (value) + { + irr_driver->toggleNormals(); + } + break; + case KEY_NEXT: // pgdown + if (value) + { + irr_driver->toggleSSAOViz(); + } + break; + case KEY_PRIOR: // pgup + if (value) + { + irr_driver->toggleLightViz(); + } + break; + case KEY_INSERT: + if (value) + { + irr_driver->toggleShadowViz(); + } + break; + case KEY_SCROLL: + if (value) + { + irr_driver->toggleDistortViz(); } break; default: diff --git a/src/items/attachment.cpp b/src/items/attachment.cpp index 36ff1512c..ebfd86e03 100644 --- a/src/items/attachment.cpp +++ b/src/items/attachment.cpp @@ -177,6 +177,8 @@ void Attachment::set(AttachmentType type, float time, } } m_node->setVisible(true); + + irr_driver->applyObjectPassShader(m_node); } // set // ----------------------------------------------------------------------------- diff --git a/src/items/flyable.cpp b/src/items/flyable.cpp index d6bbed85f..813b87cdc 100644 --- a/src/items/flyable.cpp +++ b/src/items/flyable.cpp @@ -21,9 +21,6 @@ #include "items/flyable.hpp" -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#endif #include #include @@ -43,6 +40,7 @@ #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/string_utils.hpp" +#include "utils/vs.hpp" // static variables: float Flyable::m_st_speed [PowerupManager::POWERUP_MAX]; @@ -78,6 +76,7 @@ Flyable::Flyable(AbstractKart *kart, PowerupManager::PowerupType type, // Add the graphical model setNode(irr_driver->addMesh(m_st_model[type])); + irr_driver->applyObjectPassShader(getNode()); #ifdef DEBUG std::string debug_name("flyable: "); debug_name += type; diff --git a/src/items/item.cpp b/src/items/item.cpp index f08550002..2a15d4399 100644 --- a/src/items/item.cpp +++ b/src/items/item.cpp @@ -186,8 +186,11 @@ void Item::switchTo(ItemType type, scene::IMesh *mesh, scene::IMesh *lowmesh) { node = m_node->getAllNodes()[1]; ((scene::IMeshSceneNode*)node)->setMesh(lowmesh); + irr_driver->applyObjectPassShader(m_node->getAllNodes()[1]); } + irr_driver->applyObjectPassShader(m_node->getAllNodes()[0]); + World::getWorld()->getTrack()->adjustForFog(m_node); } // switchTo diff --git a/src/items/rubber_band.cpp b/src/items/rubber_band.cpp index a9291924c..99ad6e16a 100644 --- a/src/items/rubber_band.cpp +++ b/src/items/rubber_band.cpp @@ -67,7 +67,7 @@ const wchar_t* getPlungerString() RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) : m_plunger(plunger), m_owner(kart) { - video::SColor color(77, 179, 0, 0); + const video::SColor color(77, 179, 0, 0); video::SMaterial m; m.AmbientColor = color; m.DiffuseColor = color; @@ -78,8 +78,19 @@ RubberBand::RubberBand(Plunger *plunger, AbstractKart *kart) m_attached_state = RB_TO_PLUNGER; assert(m_buffer->getVertexType()==video::EVT_STANDARD); + // Set the vertex colors properly, as the new pipeline doesn't use the old light values + u32 i; + scene::IMeshBuffer * const mb = m_mesh->getMeshBuffer(0); + video::S3DVertex * const verts = (video::S3DVertex *) mb->getVertices(); + const u32 max = mb->getVertexCount(); + for (i = 0; i < max; i++) + { + verts[i].Color = color; + } + updatePosition(); m_node = irr_driver->addMesh(m_mesh); + irr_driver->applyObjectPassShader(m_node); #ifdef DEBUG std::string debug_name = m_owner->getIdent()+" (rubber-band)"; m_node->setName(debug_name.c_str()); diff --git a/src/karts/controller/skidding_ai.cpp b/src/karts/controller/skidding_ai.cpp index 0592299f7..53738399f 100644 --- a/src/karts/controller/skidding_ai.cpp +++ b/src/karts/controller/skidding_ai.cpp @@ -61,18 +61,14 @@ #include "tracks/track.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" +#include "utils/vs.hpp" #ifdef AI_DEBUG # include "irrlicht.h" using namespace irr; #endif -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif - +#include #include #include #include diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp index d12d0864b..a9d2ba0ed 100644 --- a/src/karts/kart.cpp +++ b/src/karts/kart.cpp @@ -70,6 +70,7 @@ #include "tracks/track_manager.hpp" #include "utils/constants.hpp" #include "utils/log.hpp" //TODO: remove after debugging is done +#include "utils/vs.hpp" @@ -78,12 +79,6 @@ # pragma warning(disable:4355) #endif -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif - /** The kart constructor. * \param ident The identifier for the kart model to use. * \param position The position (or rank) for this kart (between 1 and @@ -1325,13 +1320,18 @@ void Kart::update(float dt) m_kart_model->setAnimation(KartModel::AF_DEFAULT); m_jump_time = 0; } - - if( (!isOnGround() || emergency) && m_shadow_enabled) + + const bool dyn_shadows = World::getWorld()->getTrack()->hasShadows() && + UserConfigParams::m_shadows && + irr_driver->isGLSL(); + + // Disable the fake shadow if real ones are in use, or if we're flying + if( ((!isOnGround() || emergency) && m_shadow_enabled) || dyn_shadows) { m_shadow_enabled = false; m_shadow->disableShadow(); } - if(!m_shadow_enabled && isOnGround() && !emergency) + if(!m_shadow_enabled && isOnGround() && !emergency && !dyn_shadows) { m_shadow->enableShadow(); m_shadow_enabled = true; diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp index de120ecad..20419c581 100644 --- a/src/karts/kart_model.cpp +++ b/src/karts/kart_model.cpp @@ -373,6 +373,15 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) if(!m_speed_weighted_objects[i].m_node) continue; m_speed_weighted_objects[i].m_node->setParent(lod_node); } + + // Enable rim lighting for the kart + irr_driver->applyObjectPassShader(lod_node, true); + std::vector &lodnodes = lod_node->getAllNodes(); + const u32 max = lodnodes.size(); + for (u32 i = 0; i < max; i++) + { + irr_driver->applyObjectPassShader(lodnodes[i], true); + } } else { @@ -401,6 +410,7 @@ scene::ISceneNode* KartModel::attachModel(bool animated_models) m_wheel_graphics_radius[i] = 0.5f*(wheel_max.getY() - wheel_min.getY()); m_wheel_node[i]->grab(); + ((scene::IMeshSceneNode *) m_wheel_node[i])->setReadOnlyMaterials(true); #ifdef DEBUG std::string debug_name = m_wheel_filename[i]+" (wheel)"; m_wheel_node[i]->setName(debug_name.c_str()); diff --git a/src/karts/moveable.cpp b/src/karts/moveable.cpp index b985020a3..afeb56e4c 100644 --- a/src/karts/moveable.cpp +++ b/src/karts/moveable.cpp @@ -17,6 +17,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +#include #include "karts/moveable.hpp" #include "graphics/irr_driver.hpp" diff --git a/src/modes/world.cpp b/src/modes/world.cpp index 626f32e87..1cbed703c 100644 --- a/src/modes/world.cpp +++ b/src/modes/world.cpp @@ -183,6 +183,9 @@ void World::init() } // for i + // Now that all models are loaded, apply the overrides + irr_driver->applyObjectPassShader(); + // Must be called after all karts are created m_race_gui->init(); diff --git a/src/physics/kart_motion_state.hpp b/src/physics/kart_motion_state.hpp index df58bcaf3..8e0fa80e0 100644 --- a/src/physics/kart_motion_state.hpp +++ b/src/physics/kart_motion_state.hpp @@ -19,11 +19,8 @@ #ifndef HEADER_KART_MOTION_STATE_HPP #define HEADER_KART_MOTION_STATE_HPP -#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__) -# define isnan _isnan -#else -# include -#endif +#include +#include "utils/vs.hpp" #include "LinearMath/btMotionState.h" diff --git a/src/states_screens/dialogs/custom_video_settings.cpp b/src/states_screens/dialogs/custom_video_settings.cpp index 1966ac8af..aef6328af 100644 --- a/src/states_screens/dialogs/custom_video_settings.cpp +++ b/src/states_screens/dialogs/custom_video_settings.cpp @@ -83,7 +83,20 @@ void CustomVideoSettingsialog::beforeAddingWidgets() antialias->addLabel( L"x8" ); // 3 antialias->setValue( UserConfigParams::m_antialiasing ); - getWidget("postprocessing")->setState( UserConfigParams::m_postprocess_enabled ); + SpinnerWidget* ssao = getWidget("ssao"); + ssao->addLabel( _("Disabled") ); // 0 + ssao->addLabel( _("low") ); // 1 + ssao->addLabel( _("high") ); // 2 + ssao->setValue( UserConfigParams::m_ssao ); + + SpinnerWidget* shadows = getWidget("shadows"); + shadows->addLabel( _("Disabled") ); // 0 + shadows->addLabel( _("low") ); // 1 + shadows->addLabel( _("high") ); // 2 + shadows->setValue( UserConfigParams::m_shadows ); + + getWidget("motionblur")->setState( UserConfigParams::m_motionblur ); + getWidget("mlaa")->setState( UserConfigParams::m_mlaa ); getWidget("pixelshaders")->setState( UserConfigParams::m_pixel_shaders ); } @@ -99,12 +112,18 @@ GUIEngine::EventPropagation CustomVideoSettingsialog::processEvent(const std::st getWidget("weather_gfx")->getState(); UserConfigParams::m_antialiasing = getWidget("antialiasing")->getValue(); - UserConfigParams::m_postprocess_enabled = - getWidget("postprocessing")->getState(); + UserConfigParams::m_motionblur = + getWidget("motionblur")->getState(); UserConfigParams::m_show_steering_animations = getWidget("steering_animations")->getValue(); UserConfigParams::m_pixel_shaders = getWidget("pixelshaders")->getState(); + UserConfigParams::m_mlaa = + getWidget("mlaa")->getState(); + UserConfigParams::m_ssao = + getWidget("ssao")->getValue(); + UserConfigParams::m_shadows = + getWidget("shadows")->getValue(); switch (getWidget("filtering")->getValue()) { diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp index a1b898434..dcb56b6ce 100644 --- a/src/states_screens/feature_unlocked.cpp +++ b/src/states_screens/feature_unlocked.cpp @@ -300,15 +300,16 @@ void FeatureUnlockedCutScene::init() 120, 120)); const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f ); - m_light = - irr_driver->getSceneManager()->addLightSceneNode(NULL, sun_pos, - video::SColorf(1.0f,1.0f,1.0f), - 10000.0f /* radius */); + m_light = irr_driver->addLight(sun_pos, 10000.0f, 1, 1, 1); #ifdef DEBUG m_light->setName("light"); #endif - m_light->getLightData().DiffuseColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); - m_light->getLightData().SpecularColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + + if (!irr_driver->isGLSL()) + { + ((scene::ILightSceneNode *) m_light)->getLightData().DiffuseColor = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + ((scene::ILightSceneNode *) m_light)->getLightData().SpecularColor = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + } const int unlockedStuffCount = m_unlocked_stuff.size(); diff --git a/src/states_screens/feature_unlocked.hpp b/src/states_screens/feature_unlocked.hpp index eeb3a34eb..867825442 100644 --- a/src/states_screens/feature_unlocked.hpp +++ b/src/states_screens/feature_unlocked.hpp @@ -113,7 +113,7 @@ class FeatureUnlockedCutScene : public GUIEngine::Screen, public GUIEngine::Scre irr::scene::IAnimatedMeshSceneNode *m_chest; /** The scene node for the light. */ - irr::scene::ILightSceneNode* m_light; + irr::scene::ISceneNode* m_light; //#define USE_IRRLICHT_BUG_WORKAROUND diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp index e1e08e6ca..449da86d6 100644 --- a/src/states_screens/grand_prix_lose.cpp +++ b/src/states_screens/grand_prix_lose.cpp @@ -155,11 +155,14 @@ void GrandPrixLose::init() sceneManager->setAmbientLight(video::SColor(255, 120, 120, 120)); const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f ); - m_light = irr_driver->getSceneManager()->addLightSceneNode(NULL, sun_pos, - video::SColorf(1.0f,1.0f,1.0f), - 300.0f /* radius */); - m_light->getLightData().DiffuseColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); - m_light->getLightData().SpecularColor = irr::video::SColorf(1.0f, 0.0f, 0.0f, 0.0f); + m_light = irr_driver->addLight(sun_pos, 300.0f, 1, 1, 1); + + if (!irr_driver->isGLSL()) + { + scene::ILightSceneNode *lnode = (scene::ILightSceneNode *) m_light; + lnode->getLightData().DiffuseColor = irr::video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); + lnode->getLightData().SpecularColor = irr::video::SColorf(1.0f, 0.0f, 0.0f, 0.0f); + } } // init // ------------------------------------------------------------------------------------- diff --git a/src/states_screens/grand_prix_lose.hpp b/src/states_screens/grand_prix_lose.hpp index e20097682..7395e5ea0 100644 --- a/src/states_screens/grand_prix_lose.hpp +++ b/src/states_screens/grand_prix_lose.hpp @@ -53,7 +53,7 @@ class GrandPrixLose : public GUIEngine::Screen, public GUIEngine::ScreenSingleto irr::scene::ISceneNode* m_sky; irr::scene::ICameraSceneNode* m_camera; - irr::scene::ILightSceneNode* m_light; + irr::scene::ISceneNode* m_light; /** A copy of the kart model for each kart used. */ std::vector m_all_kart_models; diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp index 5e29da0bd..1aed4ba40 100644 --- a/src/states_screens/grand_prix_win.cpp +++ b/src/states_screens/grand_prix_win.cpp @@ -210,14 +210,18 @@ void GrandPrixWin::init() sceneManager->setAmbientLight(video::SColor(255, 95, 95, 95)); const core::vector3df &sun_pos = core::vector3df( 0, 200, 100.0f ); - m_light = irr_driver->getSceneManager()->addLightSceneNode(NULL, sun_pos, - video::SColorf(0.25f,0.25f,0.25f), - 300.0f /* radius */); - m_light->getLightData().DiffuseColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); - m_light->getLightData().AmbientColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); - m_light->getLightData().SpecularColor = irr::video::SColorf(0.0f, 0.0f, 0.0f, 1.0f); + m_light = irr_driver->addLight(sun_pos, 300.0f, 0.25f, 0.25f, 0.25f); m_finish_sound = sfx_manager->quickSound("gp_end"); + if (!irr_driver->isGLSL()) + { + scene::ILightSceneNode *lnode = (scene::ILightSceneNode *) m_light; + lnode->getLightData().DiffuseColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); + lnode->getLightData().AmbientColor = irr::video::SColorf(0.25f, 0.25f, 0.25f, 1.0f); + lnode->getLightData().SpecularColor = irr::video::SColorf(0.0f, 0.0f, 0.0f, 1.0f); + } + + sfx_manager->quickSound("gp_end"); } // init // ------------------------------------------------------------------------------------- diff --git a/src/states_screens/grand_prix_win.hpp b/src/states_screens/grand_prix_win.hpp index a2316220b..8a5ca7e0c 100644 --- a/src/states_screens/grand_prix_win.hpp +++ b/src/states_screens/grand_prix_win.hpp @@ -54,7 +54,7 @@ class GrandPrixWin : public GUIEngine::Screen, public GUIEngine::ScreenSingleton irr::scene::ISceneNode* m_sky; irr::scene::ICameraSceneNode* m_camera; - irr::scene::ILightSceneNode* m_light; + irr::scene::ISceneNode* m_light; GUIEngine::LabelWidget* m_unlocked_label; diff --git a/src/states_screens/options_screen_video.cpp b/src/states_screens/options_screen_video.cpp index bd760f60d..d4a98094e 100644 --- a/src/states_screens/options_screen_video.cpp +++ b/src/states_screens/options_screen_video.cpp @@ -45,15 +45,20 @@ using namespace GUIEngine; DEFINE_SCREEN_SINGLETON( OptionsScreenVideo ); // Look-up table for GFX levels -const bool GFX [] = {false, true, true, true, true, true, true, true}; -const int GFX_ANIM_KARTS[] = {0, 0, 1, 2, 2, 2, 2, 2}; -const bool GFX_WEATHER [] = {false, false, false, false, true, true, true, true}; -const int GFX_ANTIALIAS [] = {0, 0, 0, 0, 0, 2, 2, 3}; -const bool GFX_POSTPROCESSING[] = - {false, false, false, false, false, false, true, true}; -const bool GFX_PIXEL_SHADERS[] = +static const bool GFX [] = {false, true, true, true, true, true, true, true}; +static const int GFX_ANIM_KARTS[] = {0, 1, 2, 2, 2, 2, 2, 2}; +static const bool GFX_WEATHER [] = {false, false, true, true, true, true, true, true}; +static const int GFX_ANTIALIAS [] = {0, 0, 0, 0, 0, 2, 2, 3}; +static const bool GFX_MOTIONBLUR[] = {false, false, false, false, true, true, true, true}; -const int GFX_LEVEL_AMOUNT = 8; +static const bool GFX_PIXEL_SHADERS[] = + {false, false, true, true, true, true, true, true}; +static const bool GFX_MLAA[] = {false, false, false, false, false, true, true, true}; +static const int GFX_SSAO[] = {0, 0, 0, 1, 1, 1, 2, 2}; +static const int GFX_SHADOWS[] = {0, 0, 0, 1, 1, 2, 2, 2}; + + +static const int GFX_LEVEL_AMOUNT = 8; // ---------------------------------------------------------------------------- @@ -102,11 +107,6 @@ void OptionsScreenVideo::init() assert( vsync != NULL ); vsync->setState( UserConfigParams::m_vsync ); - GUIEngine::CheckBoxWidget* fbos = - getWidget("fbos"); - assert( fbos != NULL ); - fbos->setState( UserConfigParams::m_fbo ); - // ---- video modes DynamicRibbonWidget* res = getWidget("resolutions"); @@ -315,7 +315,10 @@ void OptionsScreenVideo::updateGfxSlider() UserConfigParams::m_graphical_effects == GFX[l] && UserConfigParams::m_weather_effects == GFX_WEATHER[l] && UserConfigParams::m_antialiasing == GFX_ANTIALIAS[l] && - UserConfigParams::m_postprocess_enabled == GFX_POSTPROCESSING[l] && + UserConfigParams::m_motionblur == GFX_MOTIONBLUR[l] && + UserConfigParams::m_mlaa == GFX_MLAA[l] && + UserConfigParams::m_ssao == GFX_SSAO[l] && + UserConfigParams::m_shadows == GFX_SHADOWS[l] && UserConfigParams::m_pixel_shaders == GFX_PIXEL_SHADERS[l]) { gfx->setValue(l+1); @@ -345,16 +348,16 @@ void OptionsScreenVideo::updateTooltip() //I18N: in the graphical options tooltip; // indicates a graphical feature is enabled - core::stringw enabled = _LTR("Enabled"); + const core::stringw enabled = _LTR("Enabled"); //I18N: in the graphical options tooltip; // indicates a graphical feature is disabled - core::stringw disabled = _LTR("Disabled"); + const core::stringw disabled = _LTR("Disabled"); //I18N: if all kart animations are enabled - core::stringw all = _LTR("All"); + const core::stringw all = _LTR("All"); //I18N: if some kart animations are enabled - core::stringw me = _LTR("Me Only"); + const core::stringw me = _LTR("Me Only"); //I18N: if no kart animations are enabled - core::stringw none = _LTR("None"); + const core::stringw none = _LTR("None"); core::stringw antialias_label; switch ((int)UserConfigParams::m_antialiasing) @@ -369,6 +372,9 @@ void OptionsScreenVideo::updateTooltip() antialias_label = L"x8"; break; } + //I18N: in graphical options + tooltip = tooltip + L"\n" + _("Pixel shaders : %s", + UserConfigParams::m_pixel_shaders ? enabled : disabled); //I18N: in graphical options tooltip = _("Animated Scenery : %s", UserConfigParams::m_graphical_effects ? enabled : disabled); @@ -384,11 +390,22 @@ void OptionsScreenVideo::updateTooltip() tooltip = tooltip + L"\n" + _("Anti-aliasing (requires restart) : %s", antialias_label); //I18N: in graphical options - tooltip = tooltip + L"\n" + _("Pixel shaders : %s", - UserConfigParams::m_pixel_shaders ? enabled : disabled); + tooltip = tooltip + L"\n" + _("Motion blur: %s", + UserConfigParams::m_motionblur ? enabled : disabled); //I18N: in graphical options - tooltip = tooltip + L"\n" + _("Post-processing (motion blur) : %s", - UserConfigParams::m_postprocess_enabled ? enabled : disabled); + tooltip = tooltip + L"\n" + _("MLAA: %s", + UserConfigParams::m_mlaa ? enabled : disabled); + //I18N: in graphical options + tooltip = tooltip + L"\n" + _("SSAO: %s", + UserConfigParams::m_ssao == 1 ? "low" : UserConfigParams::m_ssao == 2 ? + "high" : disabled); + //I18N: in graphical options + tooltip = tooltip + L"\n" + _("Shadows: %s", + UserConfigParams::m_shadows == 1 ? "low" : UserConfigParams::m_shadows == 2 ? + "high" : disabled); + + + gfx->setTooltip(tooltip); } // updateTooltip @@ -461,8 +478,11 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name, UserConfigParams::m_graphical_effects = GFX[level-1]; UserConfigParams::m_weather_effects = GFX_WEATHER[level-1]; UserConfigParams::m_antialiasing = GFX_ANTIALIAS[level-1]; - UserConfigParams::m_postprocess_enabled = GFX_POSTPROCESSING[level-1]; + UserConfigParams::m_motionblur = GFX_MOTIONBLUR[level-1]; UserConfigParams::m_pixel_shaders = GFX_PIXEL_SHADERS[level-1]; + UserConfigParams::m_mlaa = GFX_MLAA[level-1]; + UserConfigParams::m_ssao = GFX_SSAO[level-1]; + UserConfigParams::m_shadows = GFX_SHADOWS[level-1]; updateGfxSlider(); } @@ -473,13 +493,6 @@ void OptionsScreenVideo::eventCallback(Widget* widget, const std::string& name, assert( vsync != NULL ); UserConfigParams::m_vsync = vsync->getState(); } - else if (name == "fbos") - { - GUIEngine::CheckBoxWidget* fbos = - getWidget("fbos"); - assert( fbos != NULL ); - UserConfigParams::m_fbo = fbos->getState(); - } else if (name == "rememberWinpos") { CheckBoxWidget* rememberWinpos = getWidget("rememberWinpos"); diff --git a/src/states_screens/race_gui_base.cpp b/src/states_screens/race_gui_base.cpp index 881f2e398..846024fe7 100644 --- a/src/states_screens/race_gui_base.cpp +++ b/src/states_screens/race_gui_base.cpp @@ -20,21 +20,8 @@ #include "states_screens/race_gui_base.hpp" -#ifdef __APPLE__ -# include -#else -# define _WINSOCKAPI_ -# ifdef WIN32 -# include -# endif -# ifdef ANDROID -# include -# else -# include -# endif -#endif - #include "audio/music_manager.hpp" +#include "graphics/glwrap.hpp" #include "graphics/irr_driver.hpp" #include "graphics/material.hpp" #include "graphics/material_manager.hpp" diff --git a/src/tracks/quad_graph.cpp b/src/tracks/quad_graph.cpp index bdae209ee..e68041a84 100644 --- a/src/tracks/quad_graph.cpp +++ b/src/tracks/quad_graph.cpp @@ -24,7 +24,11 @@ #include #include "config/user_config.hpp" +#include "graphics/callbacks.hpp" #include "graphics/irr_driver.hpp" +#include "graphics/screenquad.hpp" +#include "graphics/shaders.hpp" +#include "graphics/rtts.hpp" #include "io/file_manager.hpp" #include "io/xml_node.hpp" #include "modes/world.hpp" @@ -966,10 +970,12 @@ int QuadGraph::findOutOfRoadSector(const Vec3& xyz, //----------------------------------------------------------------------------- /** Takes a snapshot of the driveline quads so they can be used as minimap. */ -video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &dimension, +video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &origdimension, const std::string &name, const video::SColor &fill_color) { + const core::dimension2du dimension = origdimension * 2; + IrrDriver::RTTProvider rttProvider(dimension, name, true); video::SColor red(128, 255, 0, 0); createMesh(/*show_invisible part of the track*/ false, @@ -1047,8 +1053,28 @@ video::ITexture *QuadGraph::makeMiniMap(const core::dimension2du &dimension, { Log::error("Quad Graph", "[makeMiniMap] WARNING: RTT does not appear to work," "mini-map will not be available."); + return NULL; } + if (!irr_driver->isGLSL()) + return texture; + + GaussianBlurProvider * const gacb = (GaussianBlurProvider *) irr_driver->getCallback(ES_GAUSSIAN3H); + gacb->setResolution(UserConfigParams::m_width, UserConfigParams::m_height); + + ScreenQuad sq(irr_driver->getVideoDriver()); + sq.getMaterial().MaterialType = irr_driver->getShader(ES_GAUSSIAN3H); + sq.setTexture(texture); + + // Horizontal pass + sq.render(irr_driver->getRTT(RTT_TMP1)); + + // Vertical pass + sq.getMaterial().MaterialType = irr_driver->getShader(ES_GAUSSIAN3V); + sq.setTexture(irr_driver->getRTT(RTT_TMP1)); + + sq.render(texture); + return texture; } // makeMiniMap diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp index 9df42d10c..71bdec41f 100644 --- a/src/tracks/track.cpp +++ b/src/tracks/track.cpp @@ -112,6 +112,14 @@ Track::Track(const std::string &filename) m_is_cutscene = false; m_camera_far = 1000.0f; m_mini_map = NULL; + m_bloom = true; + m_bloom_threshold = 0.75f; + m_clouds = false; + m_lensflare = false; + m_godrays = false; + m_displacement_speed = 1.0f; + m_caustics_speed = 1.0f; + m_shadows = true; m_sky_particles = NULL; m_sky_dx = 0.05f; m_sky_dy = 0.0f; @@ -258,6 +266,12 @@ void Track::cleanup() material_manager->popTempMaterial(); } + irr_driver->clearGlowingNodes(); + irr_driver->clearLights(); + irr_driver->clearForcedBloom(); + irr_driver->clearDisplacingNodes(); + irr_driver->clearBackgroundNodes(); + if(UserConfigParams::logMemory()) { Log::debug("track", @@ -309,9 +323,11 @@ void Track::loadTrackInfo() { // Default values m_use_fog = false; - m_fog_density = 1.0f/100.0f; + m_fog_max = 1.0f; m_fog_start = 0.0f; m_fog_end = 1000.0f; + m_fog_height_start = 0.0f; + m_fog_height_end = 100.0f; m_gravity = 9.80665f; m_smooth_normals = false; /* ARGB */ @@ -347,6 +363,14 @@ void Track::loadTrackInfo() root->get("internal", &m_internal); root->get("reverse", &m_reverse_available); root->get("push-back", &m_enable_push_back); + root->get("clouds", &m_clouds); + root->get("bloom", &m_bloom); + root->get("bloom-threshold", &m_bloom_threshold); + root->get("lens-flare", &m_lensflare); + root->get("shadows", &m_shadows); + root->get("god-rays", &m_godrays); + root->get("displacement-speed", &m_displacement_speed); + root->get("caustics-speed", &m_caustics_speed); // Make the default for auto-rescue in battle mode and soccer mode to be false if(m_is_arena || m_is_soccer) @@ -1297,17 +1321,17 @@ void Track::createWater(const XMLNode &node) if (UserConfigParams::m_graphical_effects) { - scene_node = irr_driver->addWaterNode(mesh, + scene::IMesh *welded; + scene_node = irr_driver->addWaterNode(mesh, &welded, wave_height, wave_speed, wave_length); - // 'addWaterNode' welds the mesh so keep both the original and the welded copy mesh->grab(); irr_driver->grabAllTextures(mesh); m_all_cached_meshes.push_back(mesh); - mesh = ((scene::IMeshSceneNode*)scene_node)->getMesh(); + mesh = welded; } else { @@ -1473,9 +1497,11 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) node->get("sun-diffuse", &m_sun_diffuse_color); node->get("fog", &m_use_fog); node->get("fog-color", &m_fog_color); - node->get("fog-density", &m_fog_density); + node->get("fog-max", &m_fog_max); node->get("fog-start", &m_fog_start); node->get("fog-end", &m_fog_end); + node->get("fog-start-height", &m_fog_height_start); + node->get("fog-end-height", &m_fog_height_end); } loadMainTrack(*root); @@ -1555,14 +1581,21 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) video::SColor color; node->get("color", &color); + const video::SColorf colorf(color); float distance = 25.0f; node->get("distance", &distance); + if (irr_driver->isGLSL()) + { + irr_driver->addLight(pos, distance, colorf.r, colorf.g, colorf.b); + } else + { scene::ILightSceneNode* node = irr_driver->getSceneManager()->addLightSceneNode(NULL, pos, color, distance); node->setLightType(video::ELT_POINT); node->enableCastShadow(true); } + } else if(name=="weather") { std::string weather_particles; @@ -1650,14 +1683,14 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) // It's important to execute this BEFORE the code that creates the skycube, // otherwise the skycube node could be modified to have fog enabled, which // we don't want - if (m_use_fog && !UserConfigParams::m_camera_debug) + if (m_use_fog && !UserConfigParams::m_camera_debug && !irr_driver->isGLSL()) { /* NOTE: if LINEAR type, density does not matter, if EXP or EXP2, start and end do not matter */ irr_driver->getVideoDriver()->setFog(m_fog_color, video::EFT_FOG_LINEAR, m_fog_start, m_fog_end, - m_fog_density); + 1.0f); } // Enable for for all track nodes if fog is used @@ -1712,21 +1745,30 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id) irr_driver->getSceneManager()->setAmbientLight(m_ambient_color); // ---- Create sun (non-ambient directional light) - m_sun = irr_driver->getSceneManager()->addLightSceneNode(NULL, - m_sun_position, - m_sun_diffuse_color); - m_sun->setLightType(video::ELT_DIRECTIONAL); + if (m_sun_position.getLengthSQ() < 0.03f) + { + m_sun_position = core::vector3df(500, 250, 250); + } + + const video::SColorf tmpf(m_sun_diffuse_color); + m_sun = irr_driver->addLight(m_sun_position, 10000.0f, tmpf.r, tmpf.g, tmpf.b, true); + + if (!irr_driver->isGLSL()) + { + scene::ILightSceneNode *sun = (scene::ILightSceneNode *) m_sun; + sun->setLightType(video::ELT_DIRECTIONAL); // The angle of the light is rather important - let the sun // point towards (0,0,0). - if(m_sun_position.getLengthSQ()==0) + if(m_sun_position.getLengthSQ() < 0.03f) // Backward compatibility: if no sun is specified, use the // old hardcoded default angle m_sun->setRotation( core::vector3df(180, 45, 45) ); else m_sun->setRotation((-m_sun_position).getHorizontalAngle()); - m_sun->getLightData().SpecularColor = m_sun_specular_color; + sun->getLightData().SpecularColor = m_sun_specular_color; + } createPhysicsModel(main_track_count); diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp index eb0b2f27d..01db425f5 100644 --- a/src/tracks/track.hpp +++ b/src/tracks/track.hpp @@ -221,7 +221,7 @@ private: #endif PtrVector m_all_emitters; - scene::ILightSceneNode *m_sun; + scene::ISceneNode *m_sun; /** Used to collect the triangles for the bullet mesh. */ TriangleMesh* m_track_mesh; /** Used to collect the triangles which do not have a physical @@ -350,9 +350,11 @@ private: /** True if this track supports using smoothed normals. */ bool m_smooth_normals; - float m_fog_density; + float m_fog_max; float m_fog_start; float m_fog_end; + float m_fog_height_start; + float m_fog_height_end; core::vector3df m_sun_position; /** The current ambient color for each kart. */ video::SColor m_ambient_color; @@ -367,6 +369,18 @@ private: float m_minimap_x_scale; float m_minimap_y_scale; + bool m_clouds; + + bool m_bloom; + float m_bloom_threshold; + + bool m_lensflare; + bool m_godrays; + bool m_shadows; + + float m_displacement_speed; + float m_caustics_speed; + /** List of all bezier curves in the track - for e.g. camera, ... */ std::vector m_all_curves; @@ -534,8 +548,16 @@ public: // ------------------------------------------------------------------------ float getFogEnd() const { return m_fog_end; } // ------------------------------------------------------------------------ + float getFogStartHeight() const { return m_fog_height_start; } + // ------------------------------------------------------------------------ + float getFogEndHeight() const { return m_fog_height_end; } + // ------------------------------------------------------------------------ + float getFogMax() const { return m_fog_max; } + // ------------------------------------------------------------------------ video::SColor getFogColor() const { return m_fog_color; } // ------------------------------------------------------------------------ + video::SColor getSunColor() const { return m_sun_diffuse_color; } + // ------------------------------------------------------------------------ /** Whether this is an "internal" track. If so it won't be offered * in the track selection screen. */ bool isInternal() const { return m_internal; } @@ -557,6 +579,17 @@ public: const std::vector& getSubtitles() const { return m_subtitles; } + bool hasClouds() const { return m_clouds; } + + bool getBloom() const { return m_bloom; } + float getBloomThreshold() const { return m_bloom_threshold; } + + bool hasLensFlare() const { return m_lensflare; } + bool hasGodRays() const { return m_godrays; } + bool hasShadows() const { return m_shadows; } + + float getDisplacementSpeed() const { return m_displacement_speed; } + float getCausticsSpeed() const { return m_caustics_speed; } }; // class Track #endif diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp index fe101baf7..9d21e413f 100644 --- a/src/tracks/track_object.cpp +++ b/src/tracks/track_object.cpp @@ -26,6 +26,7 @@ #include "items/item_manager.hpp" #include "physics/physical_object.hpp" #include "race/race_manager.hpp" +#include "utils/helpers.hpp" /** A track object: any additional object on the track. This object implements @@ -133,24 +134,59 @@ void TrackObject::init(const XMLNode &xml_node, LODNode* lod_node) } else { + scene::ISceneNode *glownode = NULL; + if (lod_node != NULL) { m_type = "lod"; m_presentation = new TrackObjectPresentationLOD(xml_node, lod_node); + + glownode = lod_node->getAllNodes()[0]; } else { m_type = "mesh"; m_presentation = new TrackObjectPresentationMesh(xml_node, m_enabled); + glownode = ((TrackObjectPresentationMesh *) m_presentation)->getNode(); } - if (m_interaction != "ghost" && m_interaction != "none") + std::string render_pass; + xml_node.get("renderpass", &render_pass); + + if (m_interaction != "ghost" && m_interaction != "none" && render_pass != "skybox") { m_rigid_body = PhysicalObject::fromXML(type == "movable", xml_node, this); } + + video::SColor glow; + if (xml_node.get("glow", &glow) && glownode) + { + float r, g, b; + r = glow.getRed() / 255.0f; + g = glow.getGreen() / 255.0f; + b = glow.getBlue() / 255.0f; + + irr_driver->addGlowingNode(glownode, r, g, b); + } + + bool forcedbloom = false; + if (xml_node.get("forcedbloom", &forcedbloom) && forcedbloom && glownode) + { + float power = 1; + xml_node.get("bloompower", &power); + power = clampf(power, 0.5f, 10); + + irr_driver->addForcedBloomNode(glownode, power); + } + + bool displacing = false; + if (xml_node.get("displacing", &displacing) && displacing && glownode) + { + irr_driver->addDisplacingNode(glownode); + } } diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp index 9f0b7f5a2..d5f20d0f6 100644 --- a/src/tracks/track_object_presentation.cpp +++ b/src/tracks/track_object_presentation.cpp @@ -150,6 +150,15 @@ TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node std::string model_name; xml_node.get("model", &model_name ); + m_is_in_skybox = false; + std::string render_pass; + xml_node.get("renderpass", &render_pass); + + if(render_pass == "skybox") + { + m_is_in_skybox = true; + } + std::string full_path = World::getWorld()->getTrack()->getTrackFile(model_name); @@ -225,7 +234,19 @@ void TrackObjectPresentationMesh::init(const XMLNode* xml_node, bool enabled) m_mesh->grab(); irr_driver->grabAllTextures(m_mesh); - if (animated) + if (m_is_in_skybox) + { + // Tell the driver that this mesh is a part of the background + scene::IMeshSceneNode * const node = + irr_driver->getSceneManager()->addMeshSceneNode(m_mesh); + node->grab(); + node->setParent(NULL); + + irr_driver->addBackgroundNode(node); + + m_node = node; + } + else if (animated) { scene::IAnimatedMeshSceneNode *node = irr_driver->addAnimatedMesh((scene::IAnimatedMesh*)m_mesh); @@ -576,7 +597,7 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const xml_node.get("action", &m_action); m_action_active = true; - + if (m_action.size() == 0) { fprintf(stderr, "[TrackObject] WARNING: action-trigger has no action defined\n"); @@ -588,7 +609,7 @@ TrackObjectPresentationActionTrigger::TrackObjectPresentationActionTrigger(const void TrackObjectPresentationActionTrigger::onTriggerItemApproached(Item* who) { if (!m_action_active) return; - + if (m_action == "garage") { new RacePausedDialog(0.8f, 0.6f); diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp index 8e779752c..7894748cc 100644 --- a/src/tracks/track_object_presentation.hpp +++ b/src/tracks/track_object_presentation.hpp @@ -165,6 +165,9 @@ private: /** True if it is a looped animation. */ bool m_is_looped; + /** True if the object is in the skybox */ + bool m_is_in_skybox; + /** Start frame of the animation to be played. */ unsigned int m_frame_start; @@ -281,7 +284,7 @@ public: virtual ~TrackObjectPresentationActionTrigger() {} virtual void onTriggerItemApproached(Item* who) OVERRIDE; - + virtual void reset() OVERRIDE { m_action_active = true; } }; diff --git a/src/utils/helpers.cpp b/src/utils/helpers.cpp new file mode 100644 index 000000000..ed105b023 --- /dev/null +++ b/src/utils/helpers.cpp @@ -0,0 +1,220 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 "graphics/irr_driver.hpp" +#include "utils/helpers.hpp" + +#include +#include + +float clampf(float in, float low, float high) { + return in > high ? high : in < low ? low : in; +} + +float smootherstep(float e0, float e1, float x) { + x = clampf((x - e0)/(e1 - e0), 0, 1); + + return x*x*x*(x*(x*6 - 15) + 10); +} + +void savetex(ITexture *tex, const char *name) { + + using namespace core; + using namespace video; + + IVideoDriver * const drv = irr_driver->getVideoDriver(); + + IImage * const tmp = drv->createImage(tex, position2di(0,0), tex->getSize()); + + if (!name) + { + stringc namec = tex->getName().getPath(); + namec += ".png"; + drv->writeImageToFile(tmp, namec.c_str()); + } + else + { + drv->writeImageToFile(tmp, name); + } + + tmp->drop(); +} + +float mix(float x, float y, float a) { + return x * (1 - a) + y * a; +} + +unsigned ispow(const unsigned in) { + + if (in < 2) return 0; + + return !(in & (in - 1)); +} + +unsigned npow(unsigned in) { + + if (ispow(in)) return in; + + in |= in >> 1; + in |= in >> 2; + in |= in >> 4; + in |= in >> 8; + in |= in >> 16; + + return in + 1; +} + +// Simple 8-bit hash +u8 shash8(const u8 * const data, const u16 size) { + + u32 out = data[0], i; + + for (i = 1; i < size; i++) { + out += data[i] * ((i + 1) ^ data[i - 1]); + } + + out %= 307; // prime + out %= 256; // eight bits + + return out; +} + + +/* + +Copyright (C) 2011 by Ashima Arts (Simplex noise) +Copyright (C) 2011 by Lauri Kasanen (cpu port) + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +*/ + +static inline float mod289(float x) { +// return x - floorf(x * (1.0 / 289.0)) * 289.0; + return fmodf(x, 289); +} + +static inline float permute(float x) { + return mod289(((x*34.0f)+1.0f)*x); +} + +// Vectorized 2d simplex noise. +float noise2d(float v1, float v2) { + + const float C[] = { + 0.211324865405187, + 0.366025403784439, + -0.577350269189626, + 0.024390243902439 }; + + // First corner + float i[2]; + i[0] = floorf(v1 + v1*C[1] + v2*C[1]); + i[1] = floorf(v2 + v1*C[1] + v2*C[1]); + + float x0[2]; + x0[0] = v1 - i[0] + i[0]*C[0] + i[1]*C[0]; + x0[1] = v2 - i[1] + i[0]*C[0] + i[1]*C[0]; + + // Other corners + float i1[2]; + if (x0[0] > x0[1]) { + i1[0] = 1; + i1[1] = 0; + } else { + i1[0] = 0; + i1[1] = 1; + } + + float x12[4]; + x12[0] = x0[0] + C[0] - i1[0]; + x12[1] = x0[1] + C[0] - i1[1]; + x12[2] = x0[0] + C[2]; + x12[3] = x0[1] + C[2]; + + // Permutations + i[0] = mod289(i[0]); + i[1] = mod289(i[1]); + + float p[3]; + p[0] = permute(permute(i[1]) + i[0]); + p[1] = permute(permute(i[1] + i1[1]) + i[0] + i1[0]); + p[2] = permute(permute(i[1] + 1) + i[0] + 1); + + float m[3]; + m[0] = std::max(0.5 - x0[0]*x0[0] - x0[1]*x0[1], 0); + m[1] = std::max(0.5 - x12[0]*x12[0] - x12[1]*x12[1], 0); + m[2] = std::max(0.5 - x12[2]*x12[2] - x12[3]*x12[3], 0); + + m[0] = m[0] * m[0] * m[0] * m[0]; + m[1] = m[1] * m[1] * m[1] * m[1]; + m[2] = m[2] * m[2] * m[2] * m[2]; + + // Gradients + float tmp; + + float x[3]; + x[0] = 2 * modff(p[0] * C[3], &tmp) - 1; + x[1] = 2 * modff(p[1] * C[3], &tmp) - 1; + x[2] = 2 * modff(p[2] * C[3], &tmp) - 1; + + float h[3]; + h[0] = fabsf(x[0]) - 0.5f; + h[1] = fabsf(x[1]) - 0.5f; + h[2] = fabsf(x[2]) - 0.5f; + + float ox[3]; + ox[0] = floorf(x[0] + 0.5f); + ox[1] = floorf(x[1] + 0.5f); + ox[2] = floorf(x[2] + 0.5f); + + float a0[3]; + a0[0] = x[0] - ox[0]; + a0[1] = x[1] - ox[1]; + a0[2] = x[2] - ox[2]; + + // Normalize + m[0] *= 1.79284291400159f - 0.85373472095314f * (a0[0]*a0[0] + h[0]*h[0]); + m[1] *= 1.79284291400159f - 0.85373472095314f * (a0[1]*a0[1] + h[1]*h[1]); + m[2] *= 1.79284291400159f - 0.85373472095314f * (a0[2]*a0[2] + h[2]*h[2]); + + // Compute final value + float g[3]; + g[0] = a0[0] * x0[0] + h[0] * x0[1]; + g[1] = a0[1] * x12[0] + h[1] * x12[1]; + g[2] = a0[2] * x12[2] + h[2] * x12[3]; + + return 130 * (m[0] * g[0] + m[1] * g[1] + m[2] * g[2]); +} diff --git a/src/utils/helpers.hpp b/src/utils/helpers.hpp new file mode 100644 index 000000000..c13028666 --- /dev/null +++ b/src/utils/helpers.hpp @@ -0,0 +1,40 @@ +// +// SuperTuxKart - a fun racing game with go-kart +// Copyright (C) 2013 Lauri Kasanen +// +// 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 HELPERS_H +#define HELPERS_H + +#include + +using irr::video::ITexture; + +float smootherstep(float, float, float); +float clampf(float, float, float); + +float mix(float x, float y, float a); + +unsigned ispow(const unsigned in); +unsigned npow(unsigned in); + +void savetex(ITexture *tex, const char *name = NULL); + +float noise2d(float v1, float v2 = 0); + +u8 shash8(const u8 * const data, const u16 size); + +#endif diff --git a/src/utils/vs.hpp b/src/utils/vs.hpp new file mode 100644 index 000000000..68045956b --- /dev/null +++ b/src/utils/vs.hpp @@ -0,0 +1,12 @@ +// Visual studio workarounds in one place + +#if defined(_MSC_VER) +# include +# include +# include + +# define isnan _isnan +//# define round(x) (floor(x + 0.5f)) +# define roundf(x) (floor(x + 0.5f)) +#endif +