diff --git a/.travis.yml b/.travis.yml
index a6bd4732b..b7f46447e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -14,13 +14,13 @@ before_install:
   # Update repos
   - sudo apt-get update -qq
   # Install dependencies
-  - sudo apt-get install build-essential cmake libogg-dev libvorbis-dev libopenal-dev libxxf86vm-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev
+  - sudo apt-get install build-essential libogg-dev libvorbis-dev libopenal-dev libxxf86vm-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev
   # Install mesa from an other repo (a newer version is required). Quantal is not supported anymore, saucy is only supported till July 2014,
   # so we try to use trusty (precise which is what traiv uses a too old mesa version which doesn't link)
   - sudo apt-add-repository "deb http://archive.ubuntu.com/ubuntu trusty main restricted"
   - sudo apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 3B4FE6ACC0B21F32
   - sudo apt-get update -qq
-  - sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev
+  - sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev libglew-dev cmake
 script:
   # First a debug build: 
   - mkdir build-debug
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e7a47d353..f44e15003 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -15,12 +15,12 @@ option(USE_WIIUSE "Support for wiimote input devices" ON)
 option(USE_FRIBIDI "Support for right-to-left languages" ON)
 option(CHECK_ASSETS "Check if assets are installed in ../stk-assets" ON)
 
-if(UNIX)
+if(UNIX OR MINGW)
     option(USE_CPP2011 "Activate C++ 2011 mode (GCC only)" OFF)
 endif()
-if(MSVC)
+if(MSVC OR MINGW)
     # Normally hide the option to build wiiuse on VS, since it depends
-    # on the installation of the Windows DDK (Driver Developer Kit), 
+    # on the installation of the Windows DDK (Driver Developer Kit),
     # which also needs an absolute path :(
     option(WIIUSE_BUILD "Build wiiuse lib (only for developers)" OFF)
     mark_as_advanced(WIIUSE_BUILD)
@@ -28,6 +28,10 @@ else()
     set(WIIUSE_BUILD ON)
 endif()
 
+if(MINGW)
+    set(USE_WIIUSE OFF)
+endif()
+
 if(UNIX AND NOT APPLE)
     option(USE_XRANDR "Use xrandr instead of vidmode" ON)
     option(USE_ASAN   "Build with Leak/Address sanitizer" OFF)
@@ -69,6 +73,7 @@ endif()
 # Build the angelscript library
 add_subdirectory("${PROJECT_SOURCE_DIR}/lib/angelscript/projects/cmake")
 include_directories("${PROJECT_SOURCE_DIR}/lib/angelscript/include")
+
 # Set include paths
 include_directories(${STK_SOURCE_DIR})
 
@@ -80,6 +85,11 @@ if(MSVC)
     add_definitions(/D_IRR_STATIC_LIB_)
 endif()
 
+if(MINGW)
+    set(ENV{OPENALDIR} ${PROJECT_SOURCE_DIR}/dependencies)
+    add_definitions(-D_IRR_STATIC_LIB_)
+endif()
+
 if(APPLE)
     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch i386")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch i386 -F/Library/Frameworks")
@@ -124,8 +134,15 @@ if(USE_FRIBIDI)
     endif()
 endif()
 
+# OpenMP
+find_package(OpenMP)
+if (OPENMP_FOUND)
+    set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
+    set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
+endif()
 
-if(UNIX)
+
+if(UNIX OR MINGW)
 #    if(USE_CPP2011)
         add_definitions("-std=gnu++0x")
 #    endif()
@@ -135,6 +152,10 @@ endif()
 find_package(OpenGL REQUIRED)
 include_directories(${OPENGL_INCLUDE_DIR})
 
+# Glew
+find_package(GLEW REQUIRED)
+include_directories(${GLEW_INCLUDE_DIR})
+
 if(UNIX AND NOT APPLE)
     if(USE_XRANDR)
         find_package(Xrandr REQUIRED)
@@ -147,7 +168,7 @@ if(UNIX AND NOT APPLE)
     endif()
 endif()
 
-    
+
 # Set some compiler options
 if(UNIX)
     add_definitions(-Wall)
@@ -175,6 +196,9 @@ endif()
 # TODO: remove this switch
 add_definitions(-DHAVE_OGGVORBIS)
 
+if(WIN32 AND NOT MINGW)
+    configure_file("${STK_SOURCE_DIR}/windows_installer/icon_rc.template" "${PROJECT_BINARY_DIR}/tmp/icon.rc")
+endif()
 
 # Provides list of source and header files (STK_SOURCES and STK_HEADERS)
 include(sources.cmake)
@@ -230,7 +254,7 @@ else()
     endif()
 
     # Build the final executable
-    add_executable(supertuxkart ${STK_SOURCES} ${STK_HEADERS})
+    add_executable(supertuxkart ${STK_SOURCES} ${STK_RESOURCES} ${STK_HEADERS})
     target_link_libraries(supertuxkart ${PTHREAD_LIBRARY})
 endif()
 
@@ -253,8 +277,9 @@ target_link_libraries(supertuxkart
     ${CURL_LIBRARIES}
     ${OGGVORBIS_LIBRARIES}
     ${OPENAL_LIBRARY}
-    ${OPENGL_LIBRARIES})
-    
+    ${OPENGL_LIBRARIES}
+    ${GLEW_LIBRARIES})
+
 if(UNIX AND NOT APPLE)
     if(USE_XRANDR)
         target_link_libraries(supertuxkart ${XRANDR_LIBRARIES})
@@ -288,8 +313,12 @@ if(USE_WIIUSE)
     if(APPLE)
         find_library(BLUETOOTH_LIBRARY NAMES IOBluetooth PATHS /Developer/Library/Frameworks/IOBluetooth.framework)
         target_link_libraries(supertuxkart wiiuse ${BLUETOOTH_LIBRARY})
-    elseif(MSVC)
-        add_definitions("/DWIIUSE_STATIC")
+    elseif(WIN32)
+        if(MSVC)
+            add_definitions("/DWIIUSE_STATIC")
+        else()
+            add_definitions("-DWIIUSE_STATIC")
+        endif()
         if(WIIUSE_BUILD)
             target_link_libraries(supertuxkart wiiuse)
         else()
@@ -302,12 +331,12 @@ if(USE_WIIUSE)
 
 endif()
 
-if(MSVC)
+if(MSVC OR MINGW)
   target_link_libraries(supertuxkart iphlpapi.lib)
   add_custom_command(TARGET supertuxkart POST_BUILD
     COMMAND ${CMAKE_COMMAND} -E copy_directory
         "${PROJECT_SOURCE_DIR}/dependencies/dll"
-        $<TARGET_FILE_DIR:supertuxkart>) 
+        $<TARGET_FILE_DIR:supertuxkart>)
   add_custom_target(stkshaders SOURCES ${STK_SHADERS})
 endif()
 
@@ -316,7 +345,7 @@ add_subdirectory(tools/font_tool)
 
 
 # ==== Make dist target ====
-if(MSVC)
+if(MSVC OR MINGW)
   # Don't create a dist target for VS
 else()
   add_custom_target(dist
@@ -340,9 +369,9 @@ endif()
 
 # ==== Checking if stk-assets folder exists ====
 if(CHECK_ASSETS)
-  if((IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/karts) AND 
-       (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/library) AND 
-       (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/music) AND 
+  if((IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/karts) AND
+       (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/library) AND
+       (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/music) AND
        (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/sfx) AND
        (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/textures) AND
        (IS_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/data/tracks))
@@ -368,11 +397,8 @@ install(DIRECTORY ${STK_DATA_DIR} DESTINATION ${STK_INSTALL_DATA_DIR} PATTERN ".
 if(STK_ASSETS_DIR AND CHECK_ASSETS)
   install(DIRECTORY ${STK_ASSETS_DIR} DESTINATION ${STK_INSTALL_DATA_DIR}/data PATTERN ".svn" EXCLUDE PATTERN ".git" EXCLUDE)
 endif()
-install(FILES ${PROJECT_BINARY_DIR}/supertuxkart.desktop DESTINATION share/applications)
+install(FILES ${STK_DATA_DIR}/supertuxkart.desktop DESTINATION share/applications)
+install(FILES data/supertuxkart_32.png DESTINATION share/icons/hicolor/32x32 RENAME supertuxkart.png)
+install(FILES data/supertuxkart_128.png DESTINATION share/icons/hicolor/128x128 RENAME supertuxkart.png)
 install(FILES data/supertuxkart_32.png data/supertuxkart_128.png DESTINATION share/pixmaps)
 install(FILES data/supertuxkart.appdata DESTINATION share/appdata)
-
-
-set(PREFIX ${CMAKE_INSTALL_PREFIX})
-configure_file(data/supertuxkart_desktop.template supertuxkart.desktop)
-add_dependencies(supertuxkart supertuxkart.desktop)
\ No newline at end of file
diff --git a/INSTALL.md b/INSTALL.md
index 9e1a4f976..47ce60feb 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -23,7 +23,7 @@ Ubuntu command:
 
 ```
 sudo apt-get install autoconf automake build-essential cmake libogg-dev libvorbis-dev libopenal-dev libxxf86vm-dev \
-libgl1-mesa-dev libglu1-mesa-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev
+libgl1-mesa-dev libglu1-mesa-dev libcurl4-openssl-dev libfribidi-dev libbluetooth-dev libxrandr-dev
 ```
   
 Unpack the files from the tarball like this:
diff --git a/data/gui/gp_info.stkgui b/data/gui/gp_info.stkgui
new file mode 100644
index 000000000..a1018587c
--- /dev/null
+++ b/data/gui/gp_info.stkgui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<stkgui>
+    <icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
+
+    <div x="2%" y="2%" width="96%" height="96%" layout="vertical-row">
+
+        <header id="name" height="5%" width="80%" align="center" text_align="center"/>
+
+        <spacer width="1" height="5%"/>
+
+        <box width="95%" height="40%" padding="10" layout="horizontal-row">
+            <spacer width="10" height="100%"/>
+            <!-- Left pane -->
+            <div width="45%" height="95%" align="center" layout="vertical-row">
+                <placeholder proportion="1" width="100%" height="100%" id="screenshot_div">
+                </placeholder>
+            </div>
+
+            <spacer width="5%" height="100%"/>
+
+            <!-- Right pane -->
+            <div width="45%" height="95%" align="center" layout="vertical-row">
+                <list id="tracks" width="100%" height="100%"/>
+            </div>
+
+        </box>
+        <spacer width="1" height="5%"/>
+        <box width="95%" height="25%" padding="10" layout="horizontal-row">
+            <spacer width="1" height="5%"/>
+            <div width="50%" height="100%" layout="vertical-row">
+
+                <div width="100%" height="20%" layout="horizontal-row" >
+                    <label id="ai-text" height="100%" width="50%" I18N="Number of AI karts" text="AI karts"/>
+                    <spinner id="ai-spinner" width="50%" min_value="1" max_value="20" align="center"
+                             wrap_around="true" />
+                </div>
+                <spacer height="5%" />
+                <div width="100%" height="20%" layout="horizontal-row" >
+                    <label id="reverse-text"      width="50%" height="100%" I18N="Drive the track reverse" text="Reverse"/>
+                    <spinner id="reverse-spinner" width="50%" align="center" wrap_around="true" />
+                </div>
+                <spacer height="5%" />
+                <div width="100%" height="20%" layout="horizontal-row">
+                    <label id="track-text" width="50%" height="100%" text="Tracks"
+                           I18N="Number of tracks to pick in a random Grand Prix."/>
+                    <spinner id="track-spinner" width="50%" min_value="1" max_value="20" align="center"
+                             wrap_around="true" />
+                    <spacer height="1%" />
+                </div>
+                <spacer height="5%" />
+                <div width="100%" height="20%" layout="horizontal-row" >
+                    <label   id="group-text"    width="50%" height="100%" I18N="Number of AI karts" text="Track group"/>
+                    <spinner id="group-spinner" width="50%" align="center" wrap_around="true" />
+                    <spacer proportion="1" height="2" />
+                </div>
+            </div>
+        </box>
+        <spacer width="1" height="5%"/>
+        <buttonbar id="buttons" height="15%" width="100%" align="center">
+
+            <icon-button id="start" width="64" height="64" icon="gui/green_check.png"
+                    I18N="Start race" text="Start Race"/>
+
+            <icon-button id="continue" width="64" height="64" icon="gui/green_check.png"
+                    I18N="Continue saved GP" text="Continue saved GP"/>
+        </buttonbar>
+
+    </div>
+</stkgui>
diff --git a/data/gui/grand_prix_lose.stkgui b/data/gui/grand_prix_lose.stkgui
index 743196dae..e08bb49f4 100644
--- a/data/gui/grand_prix_lose.stkgui
+++ b/data/gui/grand_prix_lose.stkgui
@@ -1,7 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <stkgui>
-    <div x="2%" y="2%" width="96%" height="96%" layout="vertical-row">
-        <spacer width="10" proportion="1"/>
-        <button id="continue" x="20" width="250" align="left" text="Continue"/>
+    <div x="2%" y="2%" width="96%" height="96%" layout="horizontal-row">
+        <button id="continue" width="250" align="bottom" text="Continue"/>
+        <spacer width="20"/>
+        <button id="save"     width="250" align="bottom" text="Save Grand Prix"/>
     </div>
 </stkgui>
diff --git a/data/gui/grand_prix_win.stkgui b/data/gui/grand_prix_win.stkgui
index 743196dae..05d24c048 100644
--- a/data/gui/grand_prix_win.stkgui
+++ b/data/gui/grand_prix_win.stkgui
@@ -1,7 +1,9 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <stkgui>
-    <div x="2%" y="2%" width="96%" height="96%" layout="vertical-row">
-        <spacer width="10" proportion="1"/>
-        <button id="continue" x="20" width="250" align="left" text="Continue"/>
+    <div x="2%" y="2%" width="96%" height="96%" layout="horizontal-row">
+        <button id="continue" width="250" align="bottom" text="Continue"/>
+        <spacer width="20"/>
+        <button id="save"     width="250" align="bottom" text="Save Grand Prix"/>
     </div>
 </stkgui>
+
diff --git a/data/gui/racesetup.stkgui b/data/gui/racesetup.stkgui
index fe940123f..dd52aa0ea 100644
--- a/data/gui/racesetup.stkgui
+++ b/data/gui/racesetup.stkgui
@@ -1,41 +1,33 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <stkgui>
-    <div x="5%" y="5%" width="90%" height="90%" layout="vertical-row" >
+    <div x="2%" y="2%" width="96%" height="96%" layout="vertical-row" >
 
-        <header width="80%" text="Race Setup" align="center" text_align="center" />
+        <header height="5%" width="80%" text="Race Setup" align="center" text_align="center" />
 
-        <spacer proportion="1" width="25"/>
+        <spacer height="5%" width="25"/>
+        <box width="95%" height="40%" padding="10" layout="vertical-row">
+            <bright width="100%" text="Select a difficulty" align="center" text_align="left" />
 
-        <div layout="horizontal-row" width="100%" height="50" align="center">
-            <bright proportion="1" height="100%"
-                    I18N="In race setup menu" text="Number of AI karts" text_align="right" />
-            <spacer width="50" height="25"/>
-            <!--
-            <spinner id="aikartamount" proportion="3" height="100%" min_value="0" max_value="8" icon="gui/karts%i.png"/> -->
-            <!-- The maximum is set by stk from data/stk_config.xml. -->
-            <spinner id="aikartamount" proportion="1" height="100%" min_value="0" wrap_around="true"/>
-        </div>
+            <ribbon id="difficulty" height="135" width="100%" align="center">
+                <icon-button id="novice" width="128" height="128" icon="gui/difficulty_easy.png"
+                        I18N="Difficulty" text="Novice"/>
+                <icon-button id="intermediate" width="128" height="128" icon="gui/difficulty_medium.png"
+                        I18N="Difficulty" text="Intermediate"/>
+                <icon-button id="expert" width="128" height="128" icon="gui/difficulty_hard.png"
+                        I18N="Difficulty"  text="Expert"/>
+                <icon-button id="best" width="128" height="128" icon="gui/difficulty_best.png"
+                        I18N="Difficulty"  text="SuperTux"/>
+            </ribbon>
+        </box>
+        <spacer height="5%"" width="25"/>
 
-        <spacer proportion="1" width="25"/>
+        <box width="95%" height="40%" padding="10" layout="vertical-row">
+            <bright width="100%" text="Select a game mode" align="center" text_align="left" />
 
-        <ribbon id="difficulty" height="135" width="65%" align="center">
-            <icon-button id="novice" width="128" height="128" icon="gui/difficulty_easy.png"
-                    I18N="Difficulty" text="Novice"/>
-            <icon-button id="intermediate" width="128" height="128" icon="gui/difficulty_medium.png"
-                    I18N="Difficulty" text="Intermediate"/>
-            <icon-button id="expert" width="128" height="128" icon="gui/difficulty_hard.png"
-                    I18N="Difficulty"  text="Expert"/>
-            <icon-button id="best" width="128" height="128" icon="gui/difficulty_best.png"
-                    I18N="Difficulty"  text="SuperTux"/>
-        </ribbon>
-
-        <spacer proportion="1" width="25"/>
-
-        <bright width="100%" text="Select a game mode" align="center" text_align="left" />
-
-        <scrollable_toolbar id="gamemode" height="135" width="85%" label_location="bottom" align="center"
-                            child_width="135" child_height="135" />
-        <spacer proportion="1" width="25" />
+            <scrollable_toolbar id="gamemode" height="135" width="85%" label_location="bottom" align="center"
+                                child_width="135" child_height="135" />
+            <spacer proportion="1" width="25" />
+        </box>
     </div>
 
     <icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
diff --git a/data/gui/track_info.stkgui b/data/gui/track_info.stkgui
new file mode 100644
index 000000000..32ab2e52d
--- /dev/null
+++ b/data/gui/track_info.stkgui
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<stkgui>
+    <icon-button id="back" x="0" y="0" height="8%" icon="gui/back.png"/>
+
+    <div x="2%" y="2%" width="96%" height="96%" layout="vertical-row">
+
+        <header id="name" height="5%" width="80%" align="center" text_align="center"/>
+
+        <spacer width="1" height="5%"/>
+
+        <box width="95%" height="40%" padding="10" layout="horizontal-row">
+            <!-- Left pane -->
+            <div proportion="1" height="100%" layout="vertical-row">
+                <placeholder proportion="1" width="100%" height="100%" id="screenshot_div">
+                </placeholder>
+
+            </div>
+            <!-- Right pane -->
+            <div proportion="1" height="100%" layout="vertical-row">
+                <label id="highscores" width="100%" text_align="center" text="= Highscores ="/>
+
+                <spacer width="1" height="2%"/>
+
+                <div width="95%" height="fit" layout="horizontal-row">
+                    <icon id="iconscore1" icon="gui/random_kart.png" width="font" height="font"/>
+                    <spacer width="2%" height="1"/>
+                    <label id="highscore1" proportion="1" text="(Empty)"/>
+                </div>
+
+                <spacer width="1" height="2%"/>
+
+                <div width="95%" height="fit" layout="horizontal-row">
+                    <icon id="iconscore2" icon="gui/random_kart.png" width="font" height="font"/>
+                    <spacer width="2%" height="1"/>
+                    <label id="highscore2" proportion="1" text="(Empty)"/>
+                </div>
+
+                <spacer width="1" height="2%"/>
+
+                <div width="95%" height="fit" layout="horizontal-row">
+                    <icon id="iconscore3" icon="gui/random_kart.png" width="font" height="font"/>
+                    <spacer width="2%" height="1"/>
+                    <label id="highscore3" proportion="1" text="(Empty)"/>
+                </div>
+
+                <spacer width="1" height="10%"/>
+
+                <label id="author" width="100%" text_align="center" word_wrap="true"/>
+            </div>
+
+        </box>
+        <spacer width="1" height="5%"/>
+        <box width="95%" height="25%" padding="10" layout="vertical-row">
+            <spacer width="1" height="5%"/>
+
+            <div width="75%" height="fit" layout="horizontal-row" >
+                <spacer width="40" height="2" />
+                <label id="lap-text" height="100%" width="40%" I18N="Number of laps" text="Number of laps"/>
+                <spacer witdh="40" height="2" />
+                <spinner id="lap-spinner" width="20%" min_value="1" max_value="20" align="center"
+                         wrap_around="true" />
+                <spacer proportion="1" height="2" />
+            </div>
+            <div width="75%" height="fit" layout="horizontal-row" >
+                <spacer width="40" height="2" />
+                <label id="ai-text" height="100%" width="40%" I18N="Number of AI karts" text="Number of AI karts"/>
+                <spacer with="40" height="2" />
+                <spinner id="ai-spinner" width="20%" min_value="1" max_value="20" align="center"
+                         wrap_around="true" />
+                <spacer proportion="1" height="2" />
+            </div>
+            <div width="75%" height="fit" layout="horizontal-row" >
+                <spacer width="40" height="2" />
+                <label id="reverse-text" height="100%" width="40%" I18N="Drive the track reverse" text="Drive in reverse"/>
+                <spacer width="40" height="2" />
+                <checkbox id="reverse"/>
+                <spacer proportion="1" height="2" />
+            </div>
+            <spacer width="1" height="2%"/>
+        </box>
+        <spacer width="1" height="5%"/>
+        <buttonbar id="buttons" height="15%" width="100%" align="center">
+
+            <icon-button id="start" width="64" height="64" icon="gui/green_check.png"
+                    I18N="Start race" text="Start Race"/>
+
+        </buttonbar>>
+
+    </div>
+</stkgui>
diff --git a/data/gui/track_info_dialog.stkgui b/data/gui/track_info_dialog.stkgui
deleted file mode 100644
index 1cd67f681..000000000
--- a/data/gui/track_info_dialog.stkgui
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<stkgui>
-    <div x="1%" y="1%" width="99%" height="99%">
-        <div x="2%" y="2%" width="96%" height="96%" layout="vertical-row">
-            <label id="name" width="100%" text_align="center"/>
-
-            <spacer width="1" height="5%"/>
-
-            <div width="95%" proportion="5" layout="horizontal-row">
-
-            <!-- Left pane -->
-            <div proportion="1" height="100%" layout="vertical-row">
-                <label id="highscores" width="100%" text_align="center" text="= Highscores ="/>
-
-                <spacer width="1" height="2%"/>
-
-                <div width="95%" height="fit" layout="horizontal-row">
-                <icon id="iconscore1" icon="gui/random_kart.png" width="font" height="font"/>
-                <spacer width="2%" height="1"/>
-                <label id="highscore1" proportion="1" text="(Empty)"/>
-                </div>
-
-                <spacer width="1" height="2%"/>
-
-                <div width="95%" height="fit" layout="horizontal-row">
-                <icon id="iconscore2" icon="gui/random_kart.png" width="font" height="font"/>
-                <spacer width="2%" height="1"/>
-                <label id="highscore2" proportion="1" text="(Empty)"/>
-                </div>
-
-                <spacer width="1" height="2%"/>
-
-                <div width="95%" height="fit" layout="horizontal-row">
-                <icon id="iconscore3" icon="gui/random_kart.png" width="font" height="font"/>
-                <spacer width="2%" height="1"/>
-                <label id="highscore3" proportion="1" text="(Empty)"/>
-                </div>
-
-                <spacer width="1" proportion="1"/>
-
-                <label id="author" width="100%" text_align="center" word_wrap="true"/>
-            </div>
-
-            <!-- Right pane -->
-            <div proportion="1" height="100%" layout="vertical-row">
-                <placeholder proportion="1" width="100%" height="100%" id="screenshot_div">
-                </placeholder>
-                <div width="75%" height="fit" layout="horizontal-row" >
-                    <spacer proportion="1" height="2" />
-                    <checkbox id="reverse"/>
-                    <spacer width="20" height="2" />
-                    <label id="reverse-text" height="100%" I18N="Drive the track reverse" text="Reverse"/>
-                    <spacer proportion="1" height="2" />
-                </div>
-
-            </div>
-            </div>
-
-            <spacer width="1" height="5%"/>
-
-            <spinner id="lapcountspinner" width="50%" min_value="1" max_value="20" align="center" wrap_around="true"
-                I18N="In the track setup screen (number of laps choice, where %i is the number)" text="%i laps"/>
-
-            <spacer width="1" height="5%"/>
-
-            <button id="start" text="Start Race" align="center"/>
-
-            <spacer width="1" height="2%"/>
-        </div>
-
-        <icon-button id="closePopup" x="0" y="0" width="8%" height="8%" icon="gui/back.png"/>
-    </div>
-</stkgui>
diff --git a/data/shaders/detailledobject_pass2.frag b/data/shaders/detailledobject_pass2.frag
index 6d9a94c04..a426b6935 100644
--- a/data/shaders/detailledobject_pass2.frag
+++ b/data/shaders/detailledobject_pass2.frag
@@ -1,5 +1,10 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D Albedo;
+layout(bindless_sampler) uniform sampler2D Detail;
+#else
 uniform sampler2D Albedo;
 uniform sampler2D Detail;
+#endif
 
 #if __VERSION__ >= 130
 in vec2 uv;
@@ -16,6 +21,11 @@ vec3 getLightFactor(float specMapValue);
 void main(void)
 {
     vec4 color = texture(Albedo, uv);
+#ifdef GL_ARB_bindless_texture
+#ifdef SRGBBindlessFix
+    color.xyz = pow(color.xyz, vec3(2.2));
+#endif
+#endif
     vec4 detail = texture(Detail, uv_bis);
     color *= detail;
     vec3 LightFactor = getLightFactor(1. - color.a);
diff --git a/data/shaders/glow_object.frag b/data/shaders/glow_object.frag
new file mode 100644
index 000000000..a811b40f8
--- /dev/null
+++ b/data/shaders/glow_object.frag
@@ -0,0 +1,8 @@
+flat in vec4 glowColor;
+
+out vec4 FragColor;
+
+void main()
+{
+    FragColor = vec4(glowColor.rgb, 1.0);
+}
diff --git a/data/shaders/glow_object.vert b/data/shaders/glow_object.vert
new file mode 100644
index 000000000..703529317
--- /dev/null
+++ b/data/shaders/glow_object.vert
@@ -0,0 +1,24 @@
+layout(location = 0) in vec3 Position;
+layout(location = 1) in vec3 Normal;
+layout(location = 2) in vec4 Color;
+layout(location = 3) in vec2 Texcoord;
+layout(location = 5) in vec3 Tangent;
+layout(location = 6) in vec3 Bitangent;
+
+layout(location = 7) in vec3 Origin;
+layout(location = 8) in vec3 Orientation;
+layout(location = 9) in vec3 Scale;
+layout(location = 12) in vec4 GlowColor;
+
+flat out vec4 glowColor;
+
+mat4 getWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
+mat4 getInverseWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
+
+void main(void)
+{
+    mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale);
+    mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin, Orientation, Scale) * InverseViewMatrix);
+    gl_Position = ProjectionMatrix * ViewMatrix *  ModelMatrix * vec4(Position, 1.);
+    glowColor = GlowColor;
+}
diff --git a/data/shaders/grass_pass2.frag b/data/shaders/grass_pass2.frag
index 663232a72..408e78365 100644
--- a/data/shaders/grass_pass2.frag
+++ b/data/shaders/grass_pass2.frag
@@ -1,6 +1,12 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D Albedo;
+layout(bindless_sampler) uniform sampler2D dtex;
+#else
 uniform sampler2D Albedo;
-uniform vec3 SunDir;
 uniform sampler2D dtex;
+#endif
+
+uniform vec3 SunDir;
 
 in vec3 nor;
 in vec2 uv;
@@ -26,6 +32,11 @@ void main(void)
     float scattering = mix(fPowEdotL, fLdotNBack, .5);
 
     vec4 color = texture(Albedo, uv);
+#ifdef GL_ARB_bindless_texture
+#ifdef SRGBBindlessFix
+    color.xyz = pow(color.xyz, vec3(2.2));
+#endif
+#endif
     if (color.a < 0.5) discard;
     vec3 LightFactor = (scattering * 0.3) + getLightFactor(1.);
     FragColor = vec4(color.xyz * LightFactor, 1.);
diff --git a/data/shaders/header.txt b/data/shaders/header.txt
index 74a2cb81b..98e73f6a9 100644
--- a/data/shaders/header.txt
+++ b/data/shaders/header.txt
@@ -3,6 +3,7 @@ uniform mat4 ViewMatrix;
 uniform mat4 ProjectionMatrix;
 uniform mat4 InverseViewMatrix;
 uniform mat4 InverseProjectionMatrix;
+uniform mat4 ProjectionViewMatrix;
 uniform vec2 screen;
 #else
 layout (std140) uniform MatrixesData
@@ -11,7 +12,8 @@ layout (std140) uniform MatrixesData
     mat4 ProjectionMatrix;
     mat4 InverseViewMatrix;
     mat4 InverseProjectionMatrix;
+    mat4 ProjectionViewMatrix;
     mat4 ShadowViewProjMatrixes[4];
     vec2 screen;
 };
-#endif
+#endif
\ No newline at end of file
diff --git a/data/shaders/instanced_detailledobject_pass2.frag b/data/shaders/instanced_detailledobject_pass2.frag
new file mode 100644
index 000000000..18ff5aa20
--- /dev/null
+++ b/data/shaders/instanced_detailledobject_pass2.frag
@@ -0,0 +1,31 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D Albedo;
+uniform sampler2D Detail;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+flat in sampler2D secondhandle;
+#endif
+in vec2 uv;
+in vec2 uv_bis;
+out vec4 FragColor;
+
+vec3 getLightFactor(float specMapValue);
+
+void main(void)
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 color = texture(handle, uv);
+#ifdef SRGBBindlessFix
+    color.xyz = pow(color.xyz, vec3(2.2));
+#endif
+    vec4 detail = texture(secondhandle, uv_bis);
+#else
+    vec4 color = texture(Albedo, uv);
+    vec4 detail = texture(Detail, uv_bis);
+#endif
+    color *= detail;
+    vec3 LightFactor = getLightFactor(1. - color.a);
+    FragColor = vec4(color.xyz * LightFactor, 1.);
+}
diff --git a/data/shaders/instanced_grass.vert b/data/shaders/instanced_grass.vert
index 803fa7f92..5a9d11769 100644
--- a/data/shaders/instanced_grass.vert
+++ b/data/shaders/instanced_grass.vert
@@ -9,6 +9,10 @@ layout(location = 3) in vec2 Texcoord;
 layout(location = 7) in vec3 Origin;
 layout(location = 8) in vec3 Orientation;
 layout(location = 9) in vec3 Scale;
+#ifdef GL_ARB_bindless_texture
+layout(location = 10) in sampler2D Handle;
+#endif
+
 #else
 in vec3 Position;
 in vec3 Normal;
@@ -22,6 +26,9 @@ in vec3 Scale;
 
 out vec3 nor;
 out vec2 uv;
+#ifdef GL_ARB_bindless_texture
+flat out sampler2D handle;
+#endif
 
 mat4 getWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
 mat4 getInverseWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
@@ -30,7 +37,10 @@ void main()
 {
     mat4 ModelMatrix = getWorldMatrix(Origin + windDir * Color.r, Orientation, Scale);
     mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin + windDir * Color.r, Orientation, Scale) * InverseViewMatrix);
-    gl_Position = ProjectionMatrix * ViewMatrix *  ModelMatrix * vec4(Position, 1.);
+    gl_Position = ProjectionViewMatrix *  ModelMatrix * vec4(Position, 1.);
     nor = (TransposeInverseModelView * vec4(Normal, 0.)).xyz;
     uv = Texcoord;
+#ifdef GL_ARB_bindless_texture
+    handle = Handle;
+#endif
 }
diff --git a/data/shaders/instanced_grass_pass2.frag b/data/shaders/instanced_grass_pass2.frag
new file mode 100644
index 000000000..b297a4c88
--- /dev/null
+++ b/data/shaders/instanced_grass_pass2.frag
@@ -0,0 +1,47 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D dtex;
+#else
+uniform sampler2D Albedo;
+uniform sampler2D dtex;
+#endif
+
+uniform vec3 SunDir;
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+in vec3 nor;
+in vec2 uv;
+out vec4 FragColor;
+
+vec3 getLightFactor(float specMapValue);
+
+void main(void)
+{
+    vec2 texc = gl_FragCoord.xy / screen;
+    float z = texture(dtex, texc).x;
+
+    vec4 xpos = 2.0 * vec4(texc, z, 1.0) - 1.0f;
+    xpos = InverseProjectionMatrix * xpos;
+    xpos /= xpos.w;
+    vec3 eyedir = normalize(xpos.xyz);
+
+    // Inspired from http://http.developer.nvidia.com/GPUGems3/gpugems3_ch16.html
+    float fEdotL = max(0., dot(SunDir, eyedir));
+    float fPowEdotL = pow(fEdotL, 4.);
+
+    float fLdotNBack  = max(0., - dot(nor, SunDir) * 0.6 + 0.4);
+    float scattering = mix(fPowEdotL, fLdotNBack, .5);
+
+#ifdef GL_ARB_bindless_texture
+    vec4 color = texture(handle, uv);
+#ifdef SRGBBindlessFix
+    color.xyz = pow(color.xyz, vec3(2.2));
+#endif
+#else
+    vec4 color = texture(Albedo, uv);
+#endif
+    if (color.a < 0.5) discard;
+    vec3 LightFactor = (scattering * 0.3) + getLightFactor(1.);
+    FragColor = vec4(color.xyz * LightFactor, 1.);
+}
diff --git a/data/shaders/instanced_normalmap.frag b/data/shaders/instanced_normalmap.frag
new file mode 100644
index 000000000..4b4ecadcd
--- /dev/null
+++ b/data/shaders/instanced_normalmap.frag
@@ -0,0 +1,35 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D normalMap;
+uniform sampler2D DiffuseForAlpha;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+flat in sampler2D secondhandle;
+#endif
+in vec3 tangent;
+in vec3 bitangent;
+in vec2 uv;
+out vec3 EncodedNormal;
+
+vec2 EncodeNormal(vec3 n);
+
+void main()
+{
+    // normal in Tangent Space
+#ifdef GL_ARB_bindless_texture
+    vec3 TS_normal = 2.0 * texture(secondhandle, uv).rgb - 1.0;
+    float alpha = texture(handle, uv).a;
+#else
+    vec3 TS_normal = 2.0 * texture(normalMap, uv).rgb - 1.0;
+    float alpha = texture(DiffuseForAlpha, uv).a;
+#endif
+    // Because of interpolation, we need to renormalize
+    vec3 Frag_tangent = normalize(tangent);
+    vec3 Frag_normal = normalize(cross(Frag_tangent, bitangent));
+    vec3 Frag_bitangent = cross(Frag_normal, Frag_tangent);
+
+    vec3 FragmentNormal = TS_normal.x * Frag_tangent + TS_normal.y * Frag_bitangent - TS_normal.z * Frag_normal;
+    EncodedNormal.xy = 0.5 * EncodeNormal(normalize(FragmentNormal)) + 0.5;
+    EncodedNormal.z = exp2(10. * (1. - alpha) + 1.);
+}
diff --git a/data/shaders/instanced_object_pass.vert b/data/shaders/instanced_object_pass.vert
index f83c7f7b5..ab0fce5b2 100644
--- a/data/shaders/instanced_object_pass.vert
+++ b/data/shaders/instanced_object_pass.vert
@@ -9,6 +9,11 @@ layout(location = 6) in vec3 Bitangent;
 layout(location = 7) in vec3 Origin;
 layout(location = 8) in vec3 Orientation;
 layout(location = 9) in vec3 Scale;
+#ifdef GL_ARB_bindless_texture
+layout(location = 10) in sampler2D Handle;
+layout(location = 11) in sampler2D SecondHandle;
+#endif
+
 #else
 in vec3 Position;
 in vec3 Normal;
@@ -27,6 +32,10 @@ out vec3 tangent;
 out vec3 bitangent;
 out vec2 uv;
 out vec4 color;
+#ifdef GL_ARB_bindless_texture
+flat out sampler2D handle;
+flat out sampler2D secondhandle;
+#endif
 
 mat4 getWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
 mat4 getInverseWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
@@ -35,10 +44,14 @@ void main(void)
 {
     mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale);
     mat4 TransposeInverseModelView = transpose(getInverseWorldMatrix(Origin, Orientation, Scale) * InverseViewMatrix);
-    gl_Position = ProjectionMatrix * ViewMatrix *  ModelMatrix * vec4(Position, 1.);
+    gl_Position = ProjectionViewMatrix *  ModelMatrix * vec4(Position, 1.);
     nor = (TransposeInverseModelView * vec4(Normal, 0.)).xyz;
     tangent = (TransposeInverseModelView * vec4(Tangent, 1.)).xyz;
     bitangent = (TransposeInverseModelView * vec4(Bitangent, 1.)).xyz;
     uv = Texcoord;
     color = Color.zyxw;
+#ifdef GL_ARB_bindless_texture
+    handle = Handle;
+    secondhandle = SecondHandle;
+#endif
 }
diff --git a/data/shaders/instanced_object_pass1.frag b/data/shaders/instanced_object_pass1.frag
new file mode 100644
index 000000000..fc04aa63f
--- /dev/null
+++ b/data/shaders/instanced_object_pass1.frag
@@ -0,0 +1,23 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D tex;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+in vec3 nor;
+in vec2 uv;
+out vec3 EncodedNormal;
+
+vec2 EncodeNormal(vec3 n);
+
+void main(void)
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(handle, uv);
+#else
+    vec4 col = texture(tex, uv);
+#endif
+    EncodedNormal.xy = 0.5 * EncodeNormal(normalize(nor)) + 0.5;
+    EncodedNormal.z = exp2(10. * (1. - col.a) + 1.);
+}
diff --git a/data/shaders/instanced_object_pass2.frag b/data/shaders/instanced_object_pass2.frag
new file mode 100644
index 000000000..76176ba37
--- /dev/null
+++ b/data/shaders/instanced_object_pass2.frag
@@ -0,0 +1,27 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D Albedo;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+in vec2 uv;
+in vec4 color;
+out vec4 FragColor;
+
+vec3 getLightFactor(float specMapValue);
+
+void main(void)
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(handle, uv);
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#else
+    vec4 col = texture(Albedo, uv);
+#endif
+    col.xyz *= pow(color.xyz, vec3(2.2));
+    vec3 LightFactor = getLightFactor(1.);
+    FragColor = vec4(col.xyz * LightFactor, 1.);
+}
diff --git a/data/shaders/instanced_object_unlit.frag b/data/shaders/instanced_object_unlit.frag
new file mode 100644
index 000000000..4e90c0fc4
--- /dev/null
+++ b/data/shaders/instanced_object_unlit.frag
@@ -0,0 +1,26 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D tex;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+
+in vec2 uv;
+in vec4 color;
+out vec4 FragColor;
+
+void main(void)
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(handle, uv);
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#else
+    vec4 col = texture(tex, uv);
+#endif
+    col.xyz *= pow(color.xyz, vec3(2.2));
+    if (col.a < 0.5) discard;
+    FragColor = vec4(col.xyz, 1.);
+}
diff --git a/data/shaders/instanced_objectpass_spheremap.frag b/data/shaders/instanced_objectpass_spheremap.frag
new file mode 100644
index 000000000..5365dd7ef
--- /dev/null
+++ b/data/shaders/instanced_objectpass_spheremap.frag
@@ -0,0 +1,35 @@
+// See http://www.ozone3d.net/tutorials/glsl_texturing_p04.php for ref
+
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D tex;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+in vec3 nor;
+out vec4 FragColor;
+
+
+vec4 getPosFromUVDepth(vec3 uvDepth, mat4 InverseProjectionMatrix);
+vec3 getLightFactor(float specMapValue);
+
+void main() {
+    vec3 texc = gl_FragCoord.xyz / vec3(screen, 1.);
+    vec3 u = getPosFromUVDepth(texc, InverseProjectionMatrix).xyz;
+    vec3 r = reflect(u, nor);
+
+    float m = 2.0 * sqrt(r.x * r.x + r.y * r.y + (r.z + 1.0) * (r.z + 1.0));
+    r.y = - r.y;
+#ifdef GL_ARB_bindless_texture
+    vec4 detail0 = texture(handle, r.xy / m + .5);
+#ifdef SRGBBindlessFix
+    detail0.xyz = pow(detail0.xyz, vec3(2.2));
+#endif
+#else
+    vec4 detail0 = texture(tex, r.xy / m + .5);
+#endif
+    vec3 LightFactor = getLightFactor(1.);
+
+    FragColor = vec4(detail0.xyz * LightFactor, 1.);
+}
diff --git a/data/shaders/instanced_objectref_pass1.frag b/data/shaders/instanced_objectref_pass1.frag
new file mode 100644
index 000000000..c24fcddf7
--- /dev/null
+++ b/data/shaders/instanced_objectref_pass1.frag
@@ -0,0 +1,25 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D tex;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+in vec3 nor;
+in vec2 uv;
+out vec3 EncodedNormal;
+
+vec2 EncodeNormal(vec3 n);
+
+void main() {
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(handle, uv);
+#else
+    vec4 col = texture(tex, uv);
+#endif
+    if (col.a < 0.5)
+        discard;
+    EncodedNormal.xy = 0.5 * EncodeNormal(normalize(nor)) + 0.5;
+    EncodedNormal.z = 1.;
+}
+
diff --git a/data/shaders/instanced_objectref_pass2.frag b/data/shaders/instanced_objectref_pass2.frag
new file mode 100644
index 000000000..201f610a1
--- /dev/null
+++ b/data/shaders/instanced_objectref_pass2.frag
@@ -0,0 +1,28 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D Albedo;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in sampler2D handle;
+#endif
+in vec2 uv;
+in vec4 color;
+out vec4 FragColor;
+
+vec3 getLightFactor(float specMapValue);
+
+void main(void)
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(handle, uv);
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#else
+    vec4 col = texture(Albedo, uv);
+#endif
+    col.xyz *= pow(color.xyz, vec3(2.2));
+    if (col.a * color.a < 0.5) discard;
+    vec3 LightFactor = getLightFactor(1.);
+    FragColor = vec4(col.xyz * LightFactor, 1.);
+}
diff --git a/data/shaders/instanced_rsm.frag b/data/shaders/instanced_rsm.frag
new file mode 100644
index 000000000..a5f82d259
--- /dev/null
+++ b/data/shaders/instanced_rsm.frag
@@ -0,0 +1,27 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D tex;
+#endif
+
+in vec2 uv;
+in vec3 nor;
+in vec4 color;
+#ifdef GL_ARB_bindless_texture
+flat in uvec2 handle;
+#endif
+layout (location = 0) out vec3 RSMColor;
+layout (location = 1) out vec3 RSMNormals;
+
+void main()
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(sampler2D(handle), uv);
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#else
+    vec4 col = texture(tex, uv);
+#endif
+    if (col.a < .5) discard;
+    RSMColor = col.xyz * color.rgb;
+    RSMNormals = .5 * normalize(nor) + .5;
+}
diff --git a/data/shaders/instanced_rsm.vert b/data/shaders/instanced_rsm.vert
new file mode 100644
index 000000000..fc84a79ed
--- /dev/null
+++ b/data/shaders/instanced_rsm.vert
@@ -0,0 +1,39 @@
+uniform mat4 RSMMatrix;
+
+layout(location = 0) in vec3 Position;
+layout(location = 1) in vec3 Normal;
+layout(location = 2) in vec4 Color;
+layout(location = 3) in vec2 Texcoord;
+layout(location = 4) in vec2 SecondTexcoord;
+layout(location = 7) in vec3 Origin;
+layout(location = 8) in vec3 Orientation;
+layout(location = 9) in vec3 Scale;
+#ifdef GL_ARB_bindless_texture
+layout(location = 10) in uvec2 Handle;
+#endif
+
+out vec3 nor;
+out vec2 uv;
+out vec2 uv_bis;
+out vec4 color;
+#ifdef GL_ARB_bindless_texture
+flat out uvec2 handle;
+#endif
+
+
+mat4 getWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
+mat4 getInverseWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
+
+void main(void)
+{
+    mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale);
+    mat4 ModelViewProjectionMatrix = RSMMatrix * ModelMatrix;
+    mat4 TransposeInverseModel = transpose(inverse(ModelMatrix));
+    gl_Position = ModelViewProjectionMatrix * vec4(Position, 1.);
+    nor = (TransposeInverseModel * vec4(Normal, 0.)).xyz;
+    uv = Texcoord;
+    color = Color.zyxw;
+#ifdef GL_ARB_bindless_texture
+    handle = Handle;
+#endif
+}
diff --git a/data/shaders/instanced_shadow.geom b/data/shaders/instanced_shadow.geom
new file mode 100644
index 000000000..a1a851e65
--- /dev/null
+++ b/data/shaders/instanced_shadow.geom
@@ -0,0 +1,28 @@
+layout(triangles) in;
+layout(triangle_strip, max_vertices=3) out;
+
+#ifdef GL_ARB_bindless_texture
+flat in uvec2 hdle[3];
+#endif
+in vec2 tc[3];
+in int layerId[3];
+
+out vec2 uv;
+#ifdef GL_ARB_bindless_texture
+out flat uvec2 handle;
+#endif
+
+void main(void)
+{
+  gl_Layer = layerId[0];
+#ifdef GL_ARB_bindless_texture
+  handle = hdle[0];
+#endif
+  for(int i=0; i<3; i++)
+  {
+    uv = tc[i];
+    gl_Position = gl_in[i].gl_Position;
+    EmitVertex();
+  }
+  EndPrimitive();
+}
diff --git a/data/shaders/instanced_shadowref.frag b/data/shaders/instanced_shadowref.frag
new file mode 100644
index 000000000..0706edb1d
--- /dev/null
+++ b/data/shaders/instanced_shadowref.frag
@@ -0,0 +1,21 @@
+#ifndef GL_ARB_bindless_texture
+uniform sampler2D tex;
+#endif
+
+#ifdef GL_ARB_bindless_texture
+flat in uvec2 handle;
+#endif
+in vec2 uv;
+in vec4 color;
+out vec4 FragColor;
+
+void main(void)
+{
+#ifdef GL_ARB_bindless_texture
+    vec4 col = texture(sampler2D(handle), uv);
+#else
+    vec4 col = texture(tex, uv);
+#endif
+    if (col.a < 0.5) discard;
+    FragColor = vec4(1.);
+}
diff --git a/data/shaders/instanciedgrassshadow.vert b/data/shaders/instanciedgrassshadow.vert
index d27f2e808..e78f020eb 100644
--- a/data/shaders/instanciedgrassshadow.vert
+++ b/data/shaders/instanciedgrassshadow.vert
@@ -1,3 +1,5 @@
+uniform int layer;
+
 uniform vec3 windDir;
 #if __VERSION__ >= 330
 layout(location = 0) in vec3 Position;
@@ -7,6 +9,10 @@ layout(location = 3) in vec2 Texcoord;
 layout(location = 7) in vec3 Origin;
 layout(location = 8) in vec3 Orientation;
 layout(location = 9) in vec3 Scale;
+#ifdef GL_ARB_bindless_texture
+layout(location = 10) in uvec2 Handle;
+#endif
+
 #else
 in vec3 Position;
 in vec4 Color;
@@ -19,9 +25,15 @@ in vec3 Scale;
 
 #ifdef VSLayer
 out vec2 uv;
+#ifdef GL_ARB_bindless_texture
+flat out uvec2 handle;
+#endif
 #else
 out vec2 tc;
 out int layerId;
+#ifdef GL_ARB_bindless_texture
+flat out uvec2 hdle;
+#endif
 #endif
 
 mat4 getWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
@@ -31,12 +43,18 @@ void main(void)
 {
     mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale);
 #ifdef VSLayer
-    gl_Layer = gl_InstanceID & 3;
+    gl_Layer = layer;
     gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + windDir * Color.r, 1.);
     uv = Texcoord;
+#ifdef GL_ARB_bindless_texture
+    handle = Handle;
+#endif
 #else
-    layerId = gl_InstanceID & 3;
+    layerId = layer;
     gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + windDir * Color.r, 1.);
     tc = Texcoord;
+#ifdef GL_ARB_bindless_texture
+    hdle = Handle;
+#endif
 #endif
 }
\ No newline at end of file
diff --git a/data/shaders/instanciedshadow.vert b/data/shaders/instanciedshadow.vert
index a9f5fc34d..20b9d87f1 100644
--- a/data/shaders/instanciedshadow.vert
+++ b/data/shaders/instanciedshadow.vert
@@ -1,3 +1,5 @@
+uniform int layer;
+
 #if __VERSION__ >= 330
 layout(location = 0) in vec3 Position;
 layout(location = 3) in vec2 Texcoord;
@@ -5,6 +7,10 @@ layout(location = 3) in vec2 Texcoord;
 layout(location = 7) in vec3 Origin;
 layout(location = 8) in vec3 Orientation;
 layout(location = 9) in vec3 Scale;
+#ifdef GL_ARB_bindless_texture
+layout(location = 10) in uvec2 Handle;
+#endif
+
 #else
 in vec3 Position;
 in vec2 Texcoord;
@@ -16,9 +22,15 @@ in vec3 Scale;
 
 #ifdef VSLayer
 out vec2 uv;
+#ifdef GL_ARB_bindless_texture
+flat out uvec2 handle;
+#endif
 #else
 out vec2 tc;
 out int layerId;
+#ifdef GL_ARB_bindless_texture
+flat out uvec2 hdle;
+#endif
 #endif
 
 mat4 getWorldMatrix(vec3 translation, vec3 rotation, vec3 scale);
@@ -28,12 +40,18 @@ void main(void)
 {
     mat4 ModelMatrix = getWorldMatrix(Origin, Orientation, Scale);
 #ifdef VSLayer
-    gl_Layer = gl_InstanceID & 3;
+    gl_Layer = layer;
     gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position, 1.);
     uv = Texcoord;
+#ifdef GL_ARB_bindless_texture
+    handle = Handle;
+#endif
 #else
-    layerId = gl_InstanceID & 3;
+    layerId = layer;
     gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position, 1.);
     tc = Texcoord;
+#ifdef GL_ARB_bindless_texture
+    hdle = Handle;
+#endif
 #endif
 }
\ No newline at end of file
diff --git a/data/shaders/normalmap.frag b/data/shaders/normalmap.frag
index 255942efd..e7ba8efe4 100644
--- a/data/shaders/normalmap.frag
+++ b/data/shaders/normalmap.frag
@@ -1,5 +1,10 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D normalMap;
+layout(bindless_sampler) uniform sampler2D DiffuseForAlpha;
+#else
 uniform sampler2D normalMap;
 uniform sampler2D DiffuseForAlpha;
+#endif
 
 in vec3 tangent;
 in vec3 bitangent;
diff --git a/data/shaders/object_pass1.frag b/data/shaders/object_pass1.frag
index e8e0df6ba..cd193b5ec 100644
--- a/data/shaders/object_pass1.frag
+++ b/data/shaders/object_pass1.frag
@@ -1,4 +1,8 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex;
+#else
 uniform sampler2D tex;
+#endif
 
 #if __VERSION__ >= 130
 in vec3 nor;
diff --git a/data/shaders/object_pass2.frag b/data/shaders/object_pass2.frag
index d45ca01fb..047f0a192 100644
--- a/data/shaders/object_pass2.frag
+++ b/data/shaders/object_pass2.frag
@@ -1,4 +1,8 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D Albedo;
+#else
 uniform sampler2D Albedo;
+#endif
 
 in vec2 uv;
 in vec4 color;
@@ -8,7 +12,14 @@ vec3 getLightFactor(float specMapValue);
 
 void main(void)
 {
+#ifdef GL_ARB_bindless_texture
     vec4 col = texture(Albedo, uv);
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#else
+    vec4 col = texture(Albedo, uv);
+#endif
     col.xyz *= pow(color.xyz, vec3(2.2));
     vec3 LightFactor = getLightFactor(1.);
     FragColor = vec4(col.xyz * LightFactor, 1.);
diff --git a/data/shaders/object_unlit.frag b/data/shaders/object_unlit.frag
index bf5c5f3c9..6f38a27be 100644
--- a/data/shaders/object_unlit.frag
+++ b/data/shaders/object_unlit.frag
@@ -1,4 +1,8 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex;
+#else
 uniform sampler2D tex;
+#endif
 
 in vec2 uv;
 in vec4 color;
@@ -7,6 +11,11 @@ out vec4 FragColor;
 void main(void)
 {
     vec4 col = texture(tex, uv);
+#ifdef GL_ARB_bindless_texture
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#endif
     col.xyz *= pow(color.xyz, vec3(2.2));
     if (col.a < 0.5) discard;
     FragColor = vec4(col.xyz, 1.);
diff --git a/data/shaders/objectpass_spheremap.frag b/data/shaders/objectpass_spheremap.frag
index 138936d23..8e4e0f4fc 100644
--- a/data/shaders/objectpass_spheremap.frag
+++ b/data/shaders/objectpass_spheremap.frag
@@ -1,6 +1,10 @@
 // See http://www.ozone3d.net/tutorials/glsl_texturing_p04.php for ref
 
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex;
+#else
 uniform sampler2D tex;
+#endif
 
 #if __VERSION__ >= 130
 in vec3 nor;
@@ -21,6 +25,11 @@ void main() {
     float m = 2.0 * sqrt(r.x * r.x + r.y * r.y + (r.z + 1.0) * (r.z + 1.0));
     r.y = - r.y;
     vec4 detail0 = texture(tex, r.xy / m + .5);
+#ifdef GL_ARB_bindless_texture
+#ifdef SRGBBindlessFix
+    detail0.xyz = pow(detail0.xyz, vec3(2.2));
+#endif
+#endif
     vec3 LightFactor = getLightFactor(1.);
 
     FragColor = vec4(detail0.xyz * LightFactor, 1.);
diff --git a/data/shaders/objectref_pass1.frag b/data/shaders/objectref_pass1.frag
index f4cf55e29..7738e82b8 100644
--- a/data/shaders/objectref_pass1.frag
+++ b/data/shaders/objectref_pass1.frag
@@ -1,4 +1,8 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex;
+#else
 uniform sampler2D tex;
+#endif
 
 #if __VERSION__ >= 130
 in vec3 nor;
diff --git a/data/shaders/objectref_pass2.frag b/data/shaders/objectref_pass2.frag
index c20b7e82e..13ff676f8 100644
--- a/data/shaders/objectref_pass2.frag
+++ b/data/shaders/objectref_pass2.frag
@@ -1,4 +1,8 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D Albedo;
+#else
 uniform sampler2D Albedo;
+#endif
 
 in vec2 uv;
 in vec4 color;
@@ -9,6 +13,11 @@ vec3 getLightFactor(float specMapValue);
 void main(void)
 {
     vec4 col = texture(Albedo, uv);
+#ifdef GL_ARB_bindless_texture
+#ifdef SRGBBindlessFix
+    col.xyz = pow(col.xyz, vec3(2.2));
+#endif
+#endif
     col.xyz *= pow(color.xyz, vec3(2.2));
     if (col.a * color.a < 0.5) discard;
     vec3 LightFactor = getLightFactor(1.);
diff --git a/data/shaders/pointlight.frag b/data/shaders/pointlight.frag
index c8913fd50..c479ec7ce 100644
--- a/data/shaders/pointlight.frag
+++ b/data/shaders/pointlight.frag
@@ -1,6 +1,5 @@
 uniform sampler2D ntex;
 uniform sampler2D dtex;
-uniform float spec;
 
 flat in vec3 center;
 flat in float energy;
diff --git a/data/shaders/shadow.vert b/data/shaders/shadow.vert
index 7fe9b6fa8..3bbc6c6a5 100644
--- a/data/shaders/shadow.vert
+++ b/data/shaders/shadow.vert
@@ -1,3 +1,4 @@
+uniform int layer;
 uniform mat4 ModelMatrix;
 
 #if __VERSION__ >= 330
@@ -18,11 +19,11 @@ out int layerId;
 void main(void)
 {
 #ifdef VSLayer
-    gl_Layer = gl_InstanceID & 3;
+    gl_Layer = layer;
     uv = Texcoord;
     gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position, 1.);
 #else
-    layerId = gl_InstanceID & 3;
+    layerId = layer;
     tc = Texcoord;
     gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position, 1.);
 #endif
diff --git a/data/shaders/shadow_grass.vert b/data/shaders/shadow_grass.vert
index 39ae2ea5c..bf6d7435b 100644
--- a/data/shaders/shadow_grass.vert
+++ b/data/shaders/shadow_grass.vert
@@ -1,3 +1,4 @@
+uniform int layer;
 uniform mat4 ModelMatrix;
 uniform vec3 windDir;
 
@@ -21,11 +22,11 @@ out int layerId;
 void main(void)
 {
 #ifdef VSLayer
-    gl_Layer = gl_InstanceID & 3;
+    gl_Layer = layer;
     uv = Texcoord;
     gl_Position = ShadowViewProjMatrixes[gl_Layer] * ModelMatrix * vec4(Position + windDir * Color.r, 1.);
 #else
-    layerId = gl_InstanceID & 3;
+    layerId = layer;
     tc = Texcoord;
     gl_Position = ShadowViewProjMatrixes[layerId] * ModelMatrix * vec4(Position + windDir * Color.r, 1.);
 #endif
diff --git a/data/shaders/splatting.frag b/data/shaders/splatting.frag
index d8bdc0555..d302908d2 100644
--- a/data/shaders/splatting.frag
+++ b/data/shaders/splatting.frag
@@ -1,8 +1,16 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex_layout;
+layout(bindless_sampler) uniform sampler2D tex_detail0;
+layout(bindless_sampler) uniform sampler2D tex_detail1;
+layout(bindless_sampler) uniform sampler2D tex_detail2;
+layout(bindless_sampler) uniform sampler2D tex_detail3;
+#else
 uniform sampler2D tex_layout;
 uniform sampler2D tex_detail0;
 uniform sampler2D tex_detail1;
 uniform sampler2D tex_detail2;
 uniform sampler2D tex_detail3;
+#endif
 
 #if __VERSION__ >= 130
 in vec2 uv;
@@ -24,6 +32,14 @@ void main() {
     vec4 detail2 = texture(tex_detail2, uv);
     vec4 detail3 = texture(tex_detail3, uv);
     vec4 detail4 = vec4(0.0);
+#ifdef GL_ARB_bindless_texture
+#ifdef SRGBBindlessFix
+    detail0.xyz = pow(detail0.xyz, vec3(2.2));
+    detail1.xyz = pow(detail1.xyz, vec3(2.2));
+    detail2.xyz = pow(detail2.xyz, vec3(2.2));
+    detail3.xyz = pow(detail3.xyz, vec3(2.2));
+#endif
+#endif
 
     vec4 splatted = splatting.r * detail0 +
         splatting.g * detail1 +
diff --git a/data/shaders/transparent.frag b/data/shaders/transparent.frag
index aaeba0601..5c84fee03 100644
--- a/data/shaders/transparent.frag
+++ b/data/shaders/transparent.frag
@@ -1,4 +1,8 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex;
+#else
 uniform sampler2D tex;
+#endif
 
 in vec2 uv;
 in vec4 color;
@@ -8,6 +12,9 @@ out vec4 FragColor;
 void main()
 {
     vec4 Color = texture(tex, uv);
+#ifdef GL_ARB_bindless_texture
+    Color.xyz = pow(Color.xyz, vec3(2.2));
+#endif
     Color.xyz *= pow(color.xyz, vec3(2.2));
     Color.a *= color.a;
     // Premultiply alpha
diff --git a/data/shaders/transparentfog.frag b/data/shaders/transparentfog.frag
index d6f0f3344..b73a06fdf 100644
--- a/data/shaders/transparentfog.frag
+++ b/data/shaders/transparentfog.frag
@@ -1,4 +1,9 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D tex;
+#else
 uniform sampler2D tex;
+#endif
+
 
 uniform float fogmax;
 uniform float startH;
@@ -15,6 +20,9 @@ out vec4 FragColor;
 void main()
 {
     vec4 diffusecolor = texture(tex, uv);
+#ifdef GL_ARB_bindless_texture
+    diffusecolor.xyz = pow(diffusecolor.xyz, vec3(2.2));
+#endif
     diffusecolor.xyz *= pow(color.xyz, vec3(2.2));
     diffusecolor.a *= color.a;
     vec3 tmp = vec3(gl_FragCoord.xy / screen, gl_FragCoord.z);
diff --git a/data/shaders/utils/getLightFactor.frag b/data/shaders/utils/getLightFactor.frag
index ec7fa5725..9bf4cdf58 100644
--- a/data/shaders/utils/getLightFactor.frag
+++ b/data/shaders/utils/getLightFactor.frag
@@ -1,6 +1,12 @@
+#ifdef GL_ARB_bindless_texture
+layout(bindless_sampler) uniform sampler2D DiffuseMap;
+layout(bindless_sampler) uniform sampler2D SpecularMap;
+layout(bindless_sampler) uniform sampler2D SSAO;
+#else
 uniform sampler2D DiffuseMap;
 uniform sampler2D SpecularMap;
 uniform sampler2D SSAO;
+#endif
 
 vec3 getLightFactor(float specMapValue)
 {
diff --git a/data/shaders/utils/getworldmatrix.vert b/data/shaders/utils/getworldmatrix.vert
index 3bff563f4..c5883ae35 100644
--- a/data/shaders/utils/getworldmatrix.vert
+++ b/data/shaders/utils/getworldmatrix.vert
@@ -45,6 +45,6 @@ mat4 getInverseWorldMatrix(vec3 translation, vec3 rotation, vec3 scale)
 {
     mat4 result = transpose(getMatrixFromRotation(rotation));
     // FIXME: it's wrong but the fourth column is not used
-    result[3].xyz -= translation;
+    // result[3].xyz -= translation;
     return getScaleMatrix(1. / scale) * result;
 }
\ No newline at end of file
diff --git a/data/stk_config.xml b/data/stk_config.xml
index f6b5c9223..5e0fdfbe0 100644
--- a/data/stk_config.xml
+++ b/data/stk_config.xml
@@ -155,12 +155,13 @@
                  otherwise obstricts too much of the view.  -->
     <camera distance="1.5" forward-up-angle="15"
             backward-up-angle="30"/>
+    <!-- Additional offset to move graphical chassis with regards to the physics. -->
+    <graphics y-offset="0.0"/>
 
-   <!-- Jump animation related values:
-        animation-time: only if the estimated time for a jump is larger
-                        than this value will the jump animation being
-                        shown. -->
-   <jump animation-time="0.5" />
+    <!-- Jump animation related values:
+         animation-time: only if the estimated time for a jump is larger
+                         than this value will the jump animation being shown. -->
+    <jump animation-time="0.5" />
 
     <!-- Skidding: increase: multiplicative increase of skidding factor in each frame.
          decrease: multiplicative decrease of skidding factor in each frame.
@@ -415,11 +416,14 @@
          physical-wheel-position: Defines where the 'physical' (raycast)
            wheel will be located. It's a weight factor with 0 = being
            at the widest side of the bevel, 1 = at the front and
-           narrowest part of the kart.  -->
+           narrowest part of the kart. If the value is less than 0, the old
+           physics settings are used which places the raycast wheels
+           outside of the chassis and results in more stable physical
+           behaviour of the karts. -->
     <collision impulse-type="normal" 
-               impulse="3000" impulse-time="0.1" terrain-impulse="8000"
+               impulse="3000" impulse-time="0.1" terrain-impulse="1600"
                restitution="1.0" bevel-factor="0.5 0.0 0.7"
-               physical-wheel-position="0.5" />
+               physical-wheel-position="-1" />
 			 
      <!-- If a kart starts within the specified time after 'go',
           it receives the corresponding bonus from 'boost'. Those
diff --git a/data/supertuxkart_desktop.template b/data/supertuxkart.desktop
similarity index 66%
rename from data/supertuxkart_desktop.template
rename to data/supertuxkart.desktop
index 789b21442..034f20da6 100644
--- a/data/supertuxkart_desktop.template
+++ b/data/supertuxkart.desktop
@@ -1,15 +1,14 @@
 [Desktop Entry]
 Name=SuperTuxKart
-Icon=@PREFIX@/share/pixmaps/supertuxkart_128.png
+Icon=supertuxkart
 GenericName=A kart racing game
 GenericName[de]=Ein Kart-Rennspiel
 GenericName[fr]=Un jeu de karting
 GenericName[gl]=Xogo de carreiras con karts
 GenericName[pl]=Wyścigi gokartów
 GenericName[ro]=Un joc de curse cu carturi
-Exec=@PREFIX@/@STK_INSTALL_BINARY_DIR@/supertuxkart --no-console
+Exec=supertuxkart
 Terminal=false
 StartupNotify=false
 Type=Application
-TryExec=@PREFIX@/@STK_INSTALL_BINARY_DIR@/supertuxkart
 Categories=Game;ArcadeGame;
diff --git a/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h b/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h
index 6140458e4..6e76828d4 100644
--- a/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h
+++ b/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h
@@ -4,8 +4,8 @@ Copyright (c) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
 
 This software is provided 'as-is', without any express or implied warranty.
 In no event will the authors be held liable for any damages arising from the use of this software.
-Permission is granted to anyone to use this software for any purpose, 
-including commercial applications, and to alter it and redistribute it freely, 
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it freely,
 subject to the following restrictions:
 
 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
@@ -24,6 +24,12 @@ subject to the following restrictions:
 #  include <math.h>
 #endif
 
+#if defined(__MINGW32__) && __cplusplus >= 201103
+	#include <cmath>
+	using std::isinf;
+	using std::isnan;
+#endif
+
 #include "LinearMath/btTransform.h"
 
 //island management, m_activationState1
@@ -51,7 +57,7 @@ typedef btAlignedObjectArray<class btCollisionObject*> btCollisionObjectArray;
 #endif
 
 
-/// btCollisionObject can be used to manage collision detection objects. 
+/// btCollisionObject can be used to manage collision detection objects.
 /// btCollisionObject maintains all information that is needed for a collision detection: Shape, Transform and AABB proxy.
 /// They can be added to the btCollisionWorld.
 ATTRIBUTE_ALIGNED16(class)	btCollisionObject
@@ -64,20 +70,20 @@ protected:
 	///m_interpolationWorldTransform is used for CCD and interpolation
 	///it can be either previous or future (predicted) transform
 	btTransform	m_interpolationWorldTransform;
-	//those two are experimental: just added for bullet time effect, so you can still apply impulses (directly modifying velocities) 
+	//those two are experimental: just added for bullet time effect, so you can still apply impulses (directly modifying velocities)
 	//without destroying the continuous interpolated motion (which uses this interpolation velocities)
 	btVector3	m_interpolationLinearVelocity;
 	btVector3	m_interpolationAngularVelocity;
-	
+
 	btVector3	m_anisotropicFriction;
 	int			m_hasAnisotropicFriction;
-	btScalar	m_contactProcessingThreshold;	
+	btScalar	m_contactProcessingThreshold;
 
 	btBroadphaseProxy*		m_broadphaseHandle;
 	btCollisionShape*		m_collisionShape;
 	///m_extensionPointer is used by some internal low-level Bullet extensions.
 	void*					m_extensionPointer;
-	
+
 	///m_rootCollisionShape is temporarily used to store the original collision shape
 	///The m_collisionShape might be temporarily replaced by a child collision shape during collision detection purposes
 	///If it is NULL, the m_collisionShape is not temporarily replaced.
@@ -102,14 +108,14 @@ protected:
 	void*			m_userObjectPointer;
 
 	///time of impact calculation
-	btScalar		m_hitFraction; 
-	
+	btScalar		m_hitFraction;
+
 	///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm::
 	btScalar		m_ccdSweptSphereRadius;
 
 	/// Don't do continuous collision detection if the motion (in one step) is less then m_ccdMotionThreshold
 	btScalar		m_ccdMotionThreshold;
-	
+
 	/// If some object should have elaborate collision filtering by sub-classes
 	int			m_checkCollideWith;
 
@@ -194,7 +200,7 @@ public:
 		return (m_collisionFlags & CF_NO_CONTACT_RESPONSE)==0;
 	}
 
-	
+
 	btCollisionObject();
 
 	virtual ~btCollisionObject();
@@ -232,7 +238,7 @@ public:
 		m_collisionShape = collisionShape;
 	}
 
-	///Avoid using this internal API call, the extension pointer is used by some Bullet extensions. 
+	///Avoid using this internal API call, the extension pointer is used by some Bullet extensions.
 	///If you need to store your own user pointer, use 'setUserPointer/getUserPointer' instead.
 	void*		internalGetExtensionPointer() const
 	{
@@ -246,7 +252,7 @@ public:
 	}
 
 	SIMD_FORCE_INLINE	int	getActivationState() const { return m_activationState1;}
-	
+
 	void setActivationState(int newState);
 
 	void	setDeactivationTime(btScalar time)
@@ -385,7 +391,7 @@ public:
 
 	SIMD_FORCE_INLINE btScalar			getHitFraction() const
 	{
-		return m_hitFraction; 
+		return m_hitFraction;
 	}
 
 	void	setHitFraction(btScalar hitFraction)
@@ -393,7 +399,7 @@ public:
 		m_hitFraction = hitFraction;
 	}
 
-	
+
 	SIMD_FORCE_INLINE int	getCollisionFlags() const
 	{
 		return m_collisionFlags;
@@ -403,7 +409,7 @@ public:
 	{
 		m_collisionFlags = flags;
 	}
-	
+
 	///Swept sphere radius (0.0 by default), see btConvexConvexAlgorithm::
 	btScalar			getCcdSweptSphereRadius() const
 	{
@@ -439,7 +445,7 @@ public:
 	{
 		return m_userObjectPointer;
 	}
-	
+
 	///users can point to their objects, userPointer is not used by Bullet
 	void	setUserPointer(void* userPointer)
 	{
@@ -477,11 +483,11 @@ struct	btCollisionObjectDoubleData
 	btVector3DoubleData		m_interpolationLinearVelocity;
 	btVector3DoubleData		m_interpolationAngularVelocity;
 	btVector3DoubleData		m_anisotropicFriction;
-	double					m_contactProcessingThreshold;	
+	double					m_contactProcessingThreshold;
 	double					m_deactivationTime;
 	double					m_friction;
 	double					m_restitution;
-	double					m_hitFraction; 
+	double					m_hitFraction;
 	double					m_ccdSweptSphereRadius;
 	double					m_ccdMotionThreshold;
 
@@ -509,11 +515,11 @@ struct	btCollisionObjectFloatData
 	btVector3FloatData		m_interpolationLinearVelocity;
 	btVector3FloatData		m_interpolationAngularVelocity;
 	btVector3FloatData		m_anisotropicFriction;
-	float					m_contactProcessingThreshold;	
+	float					m_contactProcessingThreshold;
 	float					m_deactivationTime;
 	float					m_friction;
 	float					m_restitution;
-	float					m_hitFraction; 
+	float					m_hitFraction;
 	float					m_ccdSweptSphereRadius;
 	float					m_ccdMotionThreshold;
 
diff --git a/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h b/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h
index 6028a19cb..1036c576c 100644
--- a/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h
+++ b/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h
@@ -4,8 +4,8 @@ Copyright (c) 2003-2006 Erwin Coumans  http://continuousphysics.com/Bullet/
 
 This software is provided 'as-is', without any express or implied warranty.
 In no event will the authors be held liable for any damages arising from the use of this software.
-Permission is granted to anyone to use this software for any purpose, 
-including commercial applications, and to alter it and redistribute it freely, 
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it freely,
 subject to the following restrictions:
 
 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
@@ -26,6 +26,11 @@ subject to the following restrictions:
 #endif
 #include <math.h>
 
+#if defined(__MINGW32__) && __cplusplus >= 201103
+	#include <cmath>
+	using std::isnan;
+#endif
+
 class btCollisionShape;
 class btMotionState;
 class btTypedConstraint;
@@ -51,7 +56,7 @@ enum	btRigidBodyFlags
 
 ///The btRigidBody is the main class for rigid body objects. It is derived from btCollisionObject, so it keeps a pointer to a btCollisionShape.
 ///It is recommended for performance and memory use to share btCollisionShape objects whenever possible.
-///There are 3 types of rigid bodies: 
+///There are 3 types of rigid bodies:
 ///- A) Dynamic rigid bodies, with positive mass. Motion is controlled by rigid body dynamics.
 ///- B) Fixed objects with zero mass. They are not moving (basically collision objects)
 ///- C) Kinematic objects, which are objects without mass, but the user can move them. There is on-way interaction, and Bullet calculates a velocity based on the timestep and previous and current world transform.
@@ -66,12 +71,12 @@ class btRigidBody  : public btCollisionObject
 	btScalar		m_inverseMass;
 	btVector3		m_linearFactor;
 
-	btVector3		m_gravity;	
+	btVector3		m_gravity;
 	btVector3		m_gravity_acceleration;
 	btVector3		m_invInertiaLocal;
 	btVector3		m_totalForce;
 	btVector3		m_totalTorque;
-	
+
 	btScalar		m_linearDamping;
 	btScalar		m_angularDamping;
 
@@ -92,9 +97,9 @@ class btRigidBody  : public btCollisionObject
 	btAlignedObjectArray<btTypedConstraint*> m_constraintRefs;
 
 	int				m_rigidbodyFlags;
-	
+
 	int				m_debugBodyId;
-	
+
 
 protected:
 
@@ -111,7 +116,7 @@ public:
 
 	///The btRigidBodyConstructionInfo structure provides information to create a rigid body. Setting mass to zero creates a fixed (non-dynamic) rigid body.
 	///For dynamic objects, you can use the collision shape to approximate the local inertia tensor, otherwise use the zero vector (default argument)
-	///You can use the motion state to synchronize the world transform between physics and graphics objects. 
+	///You can use the motion state to synchronize the world transform between physics and graphics objects.
 	///And if the motion state is provided, the rigid body will initialize its initial world transform from the motion state,
 	///m_startWorldTransform is only used when you don't provide a motion state.
 	struct	btRigidBodyConstructionInfo
@@ -168,16 +173,16 @@ public:
 	///btRigidBody constructor using construction info
 	btRigidBody(	const btRigidBodyConstructionInfo& constructionInfo);
 
-	///btRigidBody constructor for backwards compatibility. 
+	///btRigidBody constructor for backwards compatibility.
 	///To specify friction (etc) during rigid body construction, please use the other constructor (using btRigidBodyConstructionInfo)
 	btRigidBody(	btScalar mass, btMotionState* motionState, btCollisionShape* collisionShape, const btVector3& localInertia=btVector3(0,0,0));
 
 
 	virtual ~btRigidBody()
-        { 
+        {
                 //No constraints should point to this rigidbody
-		//Remove constraints from the dynamics world before you delete the related rigidbodies. 
-                btAssert(m_constraintRefs.size()==0); 
+		//Remove constraints from the dynamics world before you delete the related rigidbodies.
+                btAssert(m_constraintRefs.size()==0);
         }
 
 protected:
@@ -187,8 +192,8 @@ protected:
 
 public:
 
-	void			proceedToTransform(const btTransform& newTrans); 
-	
+	void			proceedToTransform(const btTransform& newTrans);
+
 	///to keep collision detection and dynamics separate we don't store a rigidbody pointer
 	///but a rigidbody is derived from btCollisionObject, so we can safely perform an upcast
 	static const btRigidBody*	upcast(const btCollisionObject* colObj)
@@ -203,15 +208,15 @@ public:
 			return (btRigidBody*)colObj;
 		return 0;
 	}
-	
+
 	/// continuous collision detection needs prediction
 	void			predictIntegratedTransform(btScalar step, btTransform& predictedTransform) ;
-	
+
 	void			saveKinematicState(btScalar step);
-	
+
 	void			applyGravity();
-	
-	void			setGravity(const btVector3& acceleration);  
+
+	void			setGravity(const btVector3& acceleration);
 
 	const btVector3&	getGravity() const
 	{
@@ -249,9 +254,9 @@ public:
 	SIMD_FORCE_INLINE btCollisionShape*	getCollisionShape() {
 			return m_collisionShape;
 	}
-	
+
 	void			setMassProps(btScalar mass, const btVector3& inertia);
-	
+
 	const btVector3& getLinearFactor() const
 	{
 		return m_linearFactor;
@@ -265,10 +270,10 @@ public:
 		m_invMass = m_linearFactor*m_inverseMass;
 	}
 	btScalar		getInvMass() const { return m_inverseMass; }
-	const btMatrix3x3& getInvInertiaTensorWorld() const { 
-		return m_invInertiaTensorWorld; 
+	const btMatrix3x3& getInvInertiaTensorWorld() const {
+		return m_invInertiaTensorWorld;
 	}
-		
+
 	void			integrateVelocities(btScalar step);
 
 	void			setCenterOfMassTransform(const btTransform& xform);
@@ -290,7 +295,7 @@ public:
 	{
 		return m_totalTorque;
 	};
-    
+
 	const btVector3& getInvInertiaDiagLocal() const
 	{
 		return m_invInertiaLocal;
@@ -314,8 +319,8 @@ public:
         btAssert(!isnan(torque.getZ()));
 		m_totalTorque += torque*m_angularFactor;
 	}
-	
-	void	applyForce(const btVector3& force, const btVector3& rel_pos) 
+
+	void	applyForce(const btVector3& force, const btVector3& rel_pos)
 	{
         btAssert(!isnan(force.getX()));
         btAssert(!isnan(force.getY()));
@@ -326,7 +331,7 @@ public:
 		applyCentralForce(force);
 		applyTorque(rel_pos.cross(force*m_linearFactor));
 	}
-	
+
 	void applyCentralImpulse(const btVector3& impulse)
 	{
         btAssert(!isnan(impulse.getX()));
@@ -334,7 +339,7 @@ public:
         btAssert(!isnan(impulse.getZ()));
 		m_linearVelocity += impulse *m_linearFactor * m_inverseMass;
 	}
-	
+
   	void applyTorqueImpulse(const btVector3& torque)
 	{
             btAssert(!isnan(torque.getX()));
@@ -342,8 +347,8 @@ public:
             btAssert(!isnan(torque.getZ()));
 			m_angularVelocity += m_invInertiaTensorWorld * torque * m_angularFactor;
 	}
-	
-	void applyImpulse(const btVector3& impulse, const btVector3& rel_pos) 
+
+	void applyImpulse(const btVector3& impulse, const btVector3& rel_pos)
 	{
         btAssert(!isnan(impulse.getX()));
         btAssert(!isnan(impulse.getY()));
@@ -361,44 +366,44 @@ public:
 		}
 	}
 
-	void clearForces() 
+	void clearForces()
 	{
 		m_totalForce.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0));
 		m_totalTorque.setValue(btScalar(0.0), btScalar(0.0), btScalar(0.0));
 	}
-	
-	void updateInertiaTensor();    
-	
-	const btVector3&     getCenterOfMassPosition() const { 
-		return m_worldTransform.getOrigin(); 
+
+	void updateInertiaTensor();
+
+	const btVector3&     getCenterOfMassPosition() const {
+		return m_worldTransform.getOrigin();
 	}
 	btQuaternion getOrientation() const;
-	
-	const btTransform&  getCenterOfMassTransform() const { 
-		return m_worldTransform; 
+
+	const btTransform&  getCenterOfMassTransform() const {
+		return m_worldTransform;
 	}
-	const btVector3&   getLinearVelocity() const { 
-		return m_linearVelocity; 
+	const btVector3&   getLinearVelocity() const {
+		return m_linearVelocity;
 	}
-	const btVector3&    getAngularVelocity() const { 
-		return m_angularVelocity; 
+	const btVector3&    getAngularVelocity() const {
+		return m_angularVelocity;
 	}
-	
+
 
 	inline void setLinearVelocity(const btVector3& lin_vel)
-	{ 
+	{
         btAssert(!isnan(lin_vel.getX()));
         btAssert(!isnan(lin_vel.getY()));
         btAssert(!isnan(lin_vel.getZ()));
-		m_linearVelocity = lin_vel; 
+		m_linearVelocity = lin_vel;
 	}
 
-	inline void setAngularVelocity(const btVector3& ang_vel) 
-	{ 
+	inline void setAngularVelocity(const btVector3& ang_vel)
+	{
         btAssert(!isnan(ang_vel.getX()));
         btAssert(!isnan(ang_vel.getY()));
         btAssert(!isnan(ang_vel.getZ()));
-		m_angularVelocity = ang_vel; 
+		m_angularVelocity = ang_vel;
 	}
 
 	btVector3 getVelocityInLocalPoint(const btVector3& rel_pos) const
@@ -410,18 +415,18 @@ public:
 		//		return 	(m_worldTransform(rel_pos) - m_interpolationWorldTransform(rel_pos)) / m_kinematicTimeStep;
 	}
 
-	void translate(const btVector3& v) 
+	void translate(const btVector3& v)
 	{
-		m_worldTransform.getOrigin() += v; 
+		m_worldTransform.getOrigin() += v;
 	}
 
-	
+
 	void	getAabb(btVector3& aabbMin,btVector3& aabbMax) const;
 
 
 
 
-	
+
 	SIMD_FORCE_INLINE btScalar computeImpulseDenominator(const btVector3& pos, const btVector3& normal) const
 	{
 		btVector3 r0 = pos - getCenterOfMassPosition();
@@ -478,12 +483,12 @@ public:
 	}
 
 
-	
+
 	const btBroadphaseProxy*	getBroadphaseProxy() const
 	{
 		return m_broadphaseHandle;
 	}
-	btBroadphaseProxy*	getBroadphaseProxy() 
+	btBroadphaseProxy*	getBroadphaseProxy()
 	{
 		return m_broadphaseHandle;
 	}
@@ -567,12 +572,12 @@ public:
 		return m_deltaAngularVelocity;
 	}
 
-	const btVector3& getPushVelocity() const 
+	const btVector3& getPushVelocity() const
 	{
 		return m_pushVelocity;
 	}
 
-	const btVector3& getTurnVelocity() const 
+	const btVector3& getTurnVelocity() const
 	{
 		return m_turnVelocity;
 	}
@@ -580,7 +585,7 @@ public:
 
 	////////////////////////////////////////////////
 	///some internal methods, don't use them
-		
+
 	btVector3& internalGetDeltaLinearVelocity()
 	{
 		return m_deltaLinearVelocity;
@@ -600,7 +605,7 @@ public:
 	{
 		return m_invMass;
 	}
-	
+
 	btVector3& internalGetPushVelocity()
 	{
 		return m_pushVelocity;
@@ -640,7 +645,7 @@ public:
 			m_turnVelocity += angularComponent*(impulseMagnitude*m_angularFactor);
 		}
 	}
-	
+
 	void	internalWritebackVelocity()
 	{
 		if (m_inverseMass)
@@ -656,7 +661,7 @@ public:
 
 	void	internalWritebackVelocity(btScalar timeStep);
 
-	
+
 
 	///////////////////////////////////////////////
 
@@ -679,7 +684,7 @@ struct	btRigidBodyFloatData
 	btVector3FloatData		m_angularVelocity;
 	btVector3FloatData		m_angularFactor;
 	btVector3FloatData		m_linearFactor;
-	btVector3FloatData		m_gravity;	
+	btVector3FloatData		m_gravity;
 	btVector3FloatData		m_gravity_acceleration;
 	btVector3FloatData		m_invInertiaLocal;
 	btVector3FloatData		m_totalForce;
@@ -705,7 +710,7 @@ struct	btRigidBodyDoubleData
 	btVector3DoubleData		m_angularVelocity;
 	btVector3DoubleData		m_angularFactor;
 	btVector3DoubleData		m_linearFactor;
-	btVector3DoubleData		m_gravity;	
+	btVector3DoubleData		m_gravity;
 	btVector3DoubleData		m_gravity_acceleration;
 	btVector3DoubleData		m_invInertiaLocal;
 	btVector3DoubleData		m_totalForce;
diff --git a/lib/enet/CMakeLists.txt b/lib/enet/CMakeLists.txt
index 9cae95a6b..719d2b9ea 100644
--- a/lib/enet/CMakeLists.txt
+++ b/lib/enet/CMakeLists.txt
@@ -21,6 +21,10 @@ add_library(enet STATIC
 	win32.c
 )
 
+if(MINGW)
+  target_link_libraries(enet wsock32 ws2_32 Winmm)
+endif()
+
 #if(WIN32)
 #  find_library(WS2_LIBRARY NAMES "ws2_32" PATHS "C:/Windows/System32")
 #  target_link_libraries(enet ${WS2_LIBRARY})
diff --git a/lib/irrlicht/CMakeLists.txt b/lib/irrlicht/CMakeLists.txt
index 17b96e62e..8b5b2132a 100644
--- a/lib/irrlicht/CMakeLists.txt
+++ b/lib/irrlicht/CMakeLists.txt
@@ -16,6 +16,9 @@ add_definitions(-DNDEBUG=1 -DIRRLICHT_EXPORTS=1 -DPNG_THREAD_UNSAFE_OK -DPNG_NO_
 if(MSVC)
   add_definitions(/D_IRR_STATIC_LIB_)
   add_definitions(/D_CRT_SECURE_NO_WARNINGS) # Shut up about unsafe stuff
+elseif(MINGW)
+  add_definitions(-D_IRR_STATIC_LIB_)
+  add_definitions(-D_CRT_SECURE_NO_WARNINGS) # Shut up about unsafe stuff
 else()
   set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -Wall -pipe -O3  -fno-exceptions  -fstrict-aliasing -fexpensive-optimizations -I/usr/X11R6/include")
   set(CMAKE_C_FLAGS   "${CMAKE_C_FLAGS} -Wall -pipe -O3  -fno-exceptions  -fstrict-aliasing -fexpensive-optimizations -I/usr/X11R6/include")
@@ -766,16 +769,16 @@ if(APPLE)
         source/Irrlicht/MacOSX/AppDelegate.mm
         source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm
         source/Irrlicht/MacOSX/OSXClipboard.mm)
-    
+
     #list(APPEND CMAKE_C_SOURCE_FILE_EXTENSIONS mm)
     #set(CMAKE_CXX_SOURCE_FILE_EXTENSIONS cpp)
-    
+
     set_source_files_properties(source/Irrlicht/MacOSX/AppDelegate.mm PROPERTIES COMPILE_FLAGS "-x objective-c++ -O3  -fno-rtti")
     set_source_files_properties(source/Irrlicht/MacOSX/AppDelegate.mm PROPERTIES LANGUAGE C)
-    
+
     set_source_files_properties(source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm PROPERTIES COMPILE_FLAGS "-x objective-c++ -O3  -fno-rtti")
     set_source_files_properties(source/Irrlicht/MacOSX/CIrrDeviceMacOSX.mm PROPERTIES LANGUAGE C)
-    
+
     set_source_files_properties(source/Irrlicht/MacOSX/OSXClipboard.mm PROPERTIES COMPILE_FLAGS "-x objective-c++ -O3 -fno-rtti")
     set_source_files_properties(source/Irrlicht/MacOSX/OSXClipboard.mm PROPERTIES LANGUAGE C)
 endif()
diff --git a/lib/irrlicht/include/matrix4.h b/lib/irrlicht/include/matrix4.h
index 45fe98374..6c04a8308 100644
--- a/lib/irrlicht/include/matrix4.h
+++ b/lib/irrlicht/include/matrix4.h
@@ -12,6 +12,9 @@
 #include "aabbox3d.h"
 #include "rect.h"
 #include "irrString.h"
+#if defined(WIN32) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
+#include <intrin.h>
+#endif
 
 // enable this to keep track of changes to the matrix
 // and make simpler identity check for seldomly changing matrices
@@ -44,6 +47,10 @@ namespace core
 	template <class T>
 	class CMatrix4
 	{
+    private:
+#if defined(WIN32) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
+        float M_raw[24];
+#endif
 		public:
 
 			//! Constructor Flags
@@ -402,8 +409,12 @@ namespace core
 			bool equals(const core::CMatrix4<T>& other, const T tolerance=(T)ROUNDING_ERROR_f64) const;
 
 		private:
+#if defined(WIN32) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
 			//! Matrix data, stored in row-major order
-			T M[16];
+            T* M = (T*)((uintptr_t)&M_raw[4] & ~0xF);
+#else
+            T M[16];
+#endif
 #if defined ( USE_MATRIX_TEST )
 			//! Flag is this matrix is identity matrix
 			mutable u32 definitelyIdentityMatrix;
@@ -658,6 +669,58 @@ namespace core
 		const T *m1 = other_a.M;
 		const T *m2 = other_b.M;
 
+#if defined(WIN32) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
+        // From http://drrobsjournal.blogspot.fr/2012/10/fast-simd-4x4-matrix-multiplication.html
+        // Use unaligned load/store
+
+        const float *matA = other_a.pointer();
+
+        const __m128 a = _mm_load_ps(matA); // First row
+        const __m128 b = _mm_load_ps(&matA[4]); // Second row
+        const __m128 c = _mm_load_ps(&matA[8]); // Third row
+        const __m128 d = _mm_load_ps(&matA[12]); // Fourth row
+
+         __m128 t1 = _mm_set1_ps(m2[0]);
+         __m128 t2 = _mm_mul_ps(a, t1);
+        t1 = _mm_set1_ps(m2[1]);
+        t2 = _mm_add_ps(_mm_mul_ps(b, t1), t2);
+        t1 = _mm_set1_ps(m2[2]);
+        t2 = _mm_add_ps(_mm_mul_ps(c, t1), t2);
+        t1 = _mm_set1_ps(m2[3]);
+        t2 = _mm_add_ps(_mm_mul_ps(d, t1), t2);
+        _mm_store_ps(&M[0], t2);
+
+        t1 = _mm_set1_ps(m2[4]);
+        t2 = _mm_mul_ps(a, t1);
+        t1 = _mm_set1_ps(m2[5]);
+        t2 = _mm_add_ps(_mm_mul_ps(b, t1), t2);
+        t1 = _mm_set1_ps(m2[6]);
+        t2 = _mm_add_ps(_mm_mul_ps(c, t1), t2);
+        t1 = _mm_set1_ps(m2[7]);
+        t2 = _mm_add_ps(_mm_mul_ps(d, t1), t2);
+        _mm_store_ps(&M[4], t2);
+
+        t1 = _mm_set1_ps(m2[8]);
+        t2 = _mm_mul_ps(a, t1);
+        t1 = _mm_set1_ps(m2[9]);
+        t2 = _mm_add_ps(_mm_mul_ps(b, t1), t2);
+        t1 = _mm_set1_ps(m2[10]);
+        t2 = _mm_add_ps(_mm_mul_ps(c, t1), t2);
+        t1 = _mm_set1_ps(m2[11]);
+        t2 = _mm_add_ps(_mm_mul_ps(d, t1), t2);
+        _mm_store_ps(&M[8], t2);
+
+        t1 = _mm_set1_ps(m2[12]);
+        t2 = _mm_mul_ps(a, t1);
+        t1 = _mm_set1_ps(m2[13]);
+        t2 = _mm_add_ps(_mm_mul_ps(b, t1), t2);
+        t1 = _mm_set1_ps(m2[14]);
+        t2 = _mm_add_ps(_mm_mul_ps(c, t1), t2);
+        t1 = _mm_set1_ps(m2[15]);
+        t2 = _mm_add_ps(_mm_mul_ps(d, t1), t2);
+        _mm_store_ps(&M[12], t2);
+#else
+
 		M[0] = m1[0]*m2[0] + m1[4]*m2[1] + m1[8]*m2[2] + m1[12]*m2[3];
 		M[1] = m1[1]*m2[0] + m1[5]*m2[1] + m1[9]*m2[2] + m1[13]*m2[3];
 		M[2] = m1[2]*m2[0] + m1[6]*m2[1] + m1[10]*m2[2] + m1[14]*m2[3];
@@ -679,6 +742,7 @@ namespace core
 		M[15] = m1[3]*m2[12] + m1[7]*m2[13] + m1[11]*m2[14] + m1[15]*m2[15];
 #if defined ( USE_MATRIX_TEST )
 		definitelyIdentityMatrix=false;
+#endif
 #endif
 		return *this;
 	}
@@ -1307,20 +1371,110 @@ namespace core
 
 
 	template <class T>
-	inline bool CMatrix4<T>::getInverse(CMatrix4<T>& out) const
-	{
-		/// Calculates the inverse of this Matrix
-		/// The inverse is calculated using Cramers rule.
-		/// If no inverse exists then 'false' is returned.
+    inline bool CMatrix4<T>::getInverse(CMatrix4<T>& out) const
+    {
+        /// Calculates the inverse of this Matrix
+        /// The inverse is calculated using Cramers rule.
+        /// If no inverse exists then 'false' is returned.
 
 #if defined ( USE_MATRIX_TEST )
-		if ( this->isIdentity() )
-		{
-			out=*this;
-			return true;
-		}
+        if ( this->isIdentity() )
+        {
+            out=*this;
+            return true;
+        }
 #endif
-		const CMatrix4<T> &m = *this;
+        const CMatrix4<T> &m = *this;
+#if defined(WIN32) && (defined(__x86_64__) || defined(_M_X64) || defined(__i386) || defined(_M_IX86))
+        float *src = (float*)m.pointer();
+        float *dst = (float*)out.pointer();
+        // from http://www.intel.com/design/pentiumiii/sml/245043.htm
+        {
+            __m128 minor0 = {}, minor1 = {}, minor2 = {}, minor3 = {};
+            __m128 row0 = {}, row1 = {}, row2 = {}, row3 = {};
+            __m128 det = {}, tmp1 = {};
+            tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src)), (__m64*)(src + 4));
+            row1 = _mm_loadh_pi(_mm_loadl_pi(row1, (__m64*)(src + 8)), (__m64*)(src + 12));
+            row0 = _mm_shuffle_ps(tmp1, row1, 0x88);
+            row1 = _mm_shuffle_ps(row1, tmp1, 0xDD);
+            tmp1 = _mm_loadh_pi(_mm_loadl_pi(tmp1, (__m64*)(src + 2)), (__m64*)(src + 6));
+            row3 = _mm_loadh_pi(_mm_loadl_pi(row3, (__m64*)(src + 10)), (__m64*)(src + 14));
+            row2 = _mm_shuffle_ps(tmp1, row3, 0x88);
+            row3 = _mm_shuffle_ps(row3, tmp1, 0xDD);
+            // -----------------------------------------------
+            tmp1 = _mm_mul_ps(row2, row3);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+            minor0 = _mm_mul_ps(row1, tmp1);
+            minor1 = _mm_mul_ps(row0, tmp1);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+            minor0 = _mm_sub_ps(_mm_mul_ps(row1, tmp1), minor0);
+            minor1 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor1);
+            minor1 = _mm_shuffle_ps(minor1, minor1, 0x4E);
+            // -----------------------------------------------
+            tmp1 = _mm_mul_ps(row1, row2);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+            minor0 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor0);
+            minor3 = _mm_mul_ps(row0, tmp1);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+            minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row3, tmp1));
+            minor3 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor3);
+            minor3 = _mm_shuffle_ps(minor3, minor3, 0x4E);
+            // -----------------------------------------------
+            tmp1 = _mm_mul_ps(_mm_shuffle_ps(row1, row1, 0x4E), row3);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+            row2 = _mm_shuffle_ps(row2, row2, 0x4E);
+            minor0 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor0);
+            minor2 = _mm_mul_ps(row0, tmp1);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+            minor0 = _mm_sub_ps(minor0, _mm_mul_ps(row2, tmp1));
+            minor2 = _mm_sub_ps(_mm_mul_ps(row0, tmp1), minor2);
+            minor2 = _mm_shuffle_ps(minor2, minor2, 0x4E);
+            // -----------------------------------------------
+            tmp1 = _mm_mul_ps(row0, row1);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+            minor2 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor2);
+            minor3 = _mm_sub_ps(_mm_mul_ps(row2, tmp1), minor3);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+            minor2 = _mm_sub_ps(_mm_mul_ps(row3, tmp1), minor2);
+            minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row2, tmp1));
+            // -----------------------------------------------
+            tmp1 = _mm_mul_ps(row0, row3);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+            minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row2, tmp1));
+            minor2 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor2);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+            minor1 = _mm_add_ps(_mm_mul_ps(row2, tmp1), minor1);
+            minor2 = _mm_sub_ps(minor2, _mm_mul_ps(row1, tmp1));
+            // -----------------------------------------------
+            tmp1 = _mm_mul_ps(row0, row2);
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0xB1);
+            minor1 = _mm_add_ps(_mm_mul_ps(row3, tmp1), minor1);
+            minor3 = _mm_sub_ps(minor3, _mm_mul_ps(row1, tmp1));
+            tmp1 = _mm_shuffle_ps(tmp1, tmp1, 0x4E);
+            minor1 = _mm_sub_ps(minor1, _mm_mul_ps(row3, tmp1));
+            minor3 = _mm_add_ps(_mm_mul_ps(row1, tmp1), minor3);
+            // -----------------------------------------------
+            det = _mm_mul_ps(row0, minor0);
+            det = _mm_add_ps(_mm_shuffle_ps(det, det, 0x4E), det);
+            det = _mm_add_ss(_mm_shuffle_ps(det, det, 0xB1), det);
+            tmp1 = _mm_rcp_ss(det);
+            det = _mm_sub_ss(_mm_add_ss(tmp1, tmp1), _mm_mul_ss(det, _mm_mul_ss(tmp1, tmp1)));
+            det = _mm_shuffle_ps(det, det, 0x00);
+            minor0 = _mm_mul_ps(det, minor0);
+            _mm_storel_pi((__m64*)(dst), minor0);
+            _mm_storeh_pi((__m64*)(dst + 2), minor0);
+            minor1 = _mm_mul_ps(det, minor1);
+            _mm_storel_pi((__m64*)(dst + 4), minor1);
+            _mm_storeh_pi((__m64*)(dst + 6), minor1);
+            minor2 = _mm_mul_ps(det, minor2);
+            _mm_storel_pi((__m64*)(dst + 8), minor2);
+            _mm_storeh_pi((__m64*)(dst + 10), minor2);
+            minor3 = _mm_mul_ps(det, minor3);
+            _mm_storel_pi((__m64*)(dst + 12), minor3);
+            _mm_storeh_pi((__m64*)(dst + 14), minor3);
+        }
+        return true;
+#else
 
 		f32 d = (m(0, 0) * m(1, 1) - m(0, 1) * m(1, 0)) * (m(2, 2) * m(3, 3) - m(2, 3) * m(3, 2)) -
 			(m(0, 0) * m(1, 2) - m(0, 2) * m(1, 0)) * (m(2, 1) * m(3, 3) - m(2, 3) * m(3, 1)) +
@@ -1387,6 +1541,7 @@ namespace core
 		out.definitelyIdentityMatrix = definitelyIdentityMatrix;
 #endif
 		return true;
+#endif
 	}
 
 
diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp
index f780938da..b5423ca57 100644
--- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp
+++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.cpp
@@ -194,7 +194,7 @@ CIrrDeviceLinux::~CIrrDeviceLinux()
 		}
 		
 		// Reset fullscreen resolution change
-		switchToFullscreen(true);		
+		restoreResolution();		
 		
 		if (!ExternalWindow)
 		{
@@ -235,43 +235,62 @@ int IrrPrintXError(Display *display, XErrorEvent *event)
 }
 #endif
 
-
-bool CIrrDeviceLinux::switchToFullscreen(bool reset)
+bool CIrrDeviceLinux::restoreResolution()
 {
 	if (!CreationParams.Fullscreen)
 		return true;
-	if (reset)
+
+	#ifdef _IRR_LINUX_X11_VIDMODE_
+	if (UseXVidMode && CreationParams.Fullscreen)
 	{
-		#ifdef _IRR_LINUX_X11_VIDMODE_
-		if (UseXVidMode && CreationParams.Fullscreen)
-		{
-			XF86VidModeSwitchToMode(display, screennr, &oldVideoMode);
-			XF86VidModeSetViewPort(display, screennr, 0, 0);
-		}
-		#endif
-		#ifdef _IRR_LINUX_X11_RANDR_
-		if (UseXRandR && CreationParams.Fullscreen)
-		{
-			XRRScreenResources* res = XRRGetScreenResources(display, DefaultRootWindow(display));
-			XRROutputInfo* output = XRRGetOutputInfo(display, res, output_id);
-			XRRCrtcInfo* crtc = XRRGetCrtcInfo(display, res, output->crtc);
-
-			Status s = XRRSetCrtcConfig(display, res, output->crtc, CurrentTime,
-										crtc->x, crtc->y, old_mode,
-										crtc->rotation, &output_id, 1);
-
-			XRRFreeOutputInfo(output);
-			XRRFreeCrtcInfo(crtc);
-			XRRFreeScreenResources(res);
-
-			if (s != Success)
-				return false;
-		}
-		#endif
-		return true;
+		XF86VidModeSwitchToMode(display, screennr, &oldVideoMode);
+		XF86VidModeSetViewPort(display, screennr, 0, 0);
 	}
+	#endif
+	#ifdef _IRR_LINUX_X11_RANDR_
+	if (UseXRandR && CreationParams.Fullscreen && old_mode != BadRRMode)
+	{
+		XRRScreenResources* res = XRRGetScreenResources(display, DefaultRootWindow(display));
+		if (!res)
+			return false;
+
+		XRROutputInfo* output = XRRGetOutputInfo(display, res, output_id);
+		if (!output || !output->crtc || output->connection == RR_Disconnected) 
+		{
+			XRRFreeOutputInfo(output);
+			return false;
+		}
+
+		XRRCrtcInfo* crtc = XRRGetCrtcInfo(display, res, output->crtc);
+		if (!crtc) 
+		{
+			XRRFreeOutputInfo(output);
+			return false;
+		}
+
+		Status s = XRRSetCrtcConfig(display, res, output->crtc, CurrentTime,
+									crtc->x, crtc->y, old_mode,
+									crtc->rotation, &output_id, 1);
+
+		XRRFreeOutputInfo(output);
+		XRRFreeCrtcInfo(crtc);
+		XRRFreeScreenResources(res);
+
+		if (s != Success)
+			return false;
+	}
+	#endif
+	return true;
+}
+
+
+bool CIrrDeviceLinux::changeResolution()
+{
+	if (!CreationParams.Fullscreen)
+		return true;
 
 	getVideoModeList();
+
 	#if defined(_IRR_LINUX_X11_VIDMODE_) || defined(_IRR_LINUX_X11_RANDR_)
 	s32 eventbase, errorbase;
 	s32 bestMode = -1;
@@ -333,47 +352,56 @@ bool CIrrDeviceLinux::switchToFullscreen(bool reset)
 
 		XFree(modes);
 	}
-	else
 	#endif
 	#ifdef _IRR_LINUX_X11_RANDR_
-	if (XRRQueryExtension(display, &eventbase, &errorbase))
+	while (XRRQueryExtension(display, &eventbase, &errorbase))
 	{
-		XRRScreenResources* res = XRRGetScreenResources(display, DefaultRootWindow(display));
+		if (output_id == BadRROutput)
+			break;
 
+		XRRScreenResources* res = XRRGetScreenResources(display, DefaultRootWindow(display));
 		if (!res)
-		{
-			CreationParams.Fullscreen = false;
-			return CreationParams.Fullscreen;
-		}
+			break;
 
 		XRROutputInfo* output = XRRGetOutputInfo(display, res, output_id);
+		if (!output || !output->crtc || output->connection == RR_Disconnected)
+		{
+			XRRFreeOutputInfo(output);
+			XRRFreeScreenResources(res);
+			break;
+		}
+		
 		XRRCrtcInfo* crtc = XRRGetCrtcInfo(display, res, output->crtc);
+		if (!crtc)
+		{
+			XRRFreeOutputInfo(output);
+			XRRFreeScreenResources(res);
+			break;
+		}
+
 		float refresh_rate, refresh_rate_new;
-		unsigned int mode0_width = -1, mode0_height = -1;
+		core::dimension2d<u32> mode0_size = core::dimension2d<u32>(0, 0);
 
 		for (int i = 0; i < res->nmode; i++)
 		{
 			const XRRModeInfo* mode = &res->modes[i];
-			unsigned int w, h;
-			
+			core::dimension2d<u32> size;
+
 			if (crtc->rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT))
 			{
-				w = mode->height;
-				h = mode->width;
+				size = core::dimension2d<u32>(mode->height, mode->width);
 			} 
 			else 
 			{
-				w = mode->width;
-				h = mode->height;
+				size = core::dimension2d<u32>(mode->width, mode->height);
 			}
 			
 			if (bestMode == -1 && mode->id == output->modes[0])
 			{
-				mode0_width = w;
-				mode0_height = h;
+				mode0_size = size;
 			}
 
-			if (bestMode == -1 && w == Width && h == Height)
+			if (bestMode == -1 && size.Width == Width && size.Height == Height)
 			{
 				for (int j = 0; j < output->nmode; j++)
 				{
@@ -385,7 +413,7 @@ bool CIrrDeviceLinux::switchToFullscreen(bool reset)
 					}
 				}
 			}
-			else if (bestMode != -1 && w == Width && h == Height)
+			else if (bestMode != -1 && size.Width == Width && size.Height == Height)
 			{
 				refresh_rate_new = (mode->dotClock * 1000.0) / (mode->hTotal * mode->vTotal);
 
@@ -408,33 +436,29 @@ bool CIrrDeviceLinux::switchToFullscreen(bool reset)
 		if (bestMode == -1)
 		{
 			bestMode = 0;
-			Width = mode0_width;
-			Height = mode0_height;
+			Width = mode0_size.Width;
+			Height = mode0_size.Height;
 		}
 
 		Status s = XRRSetCrtcConfig(display, res, output->crtc, CurrentTime,
 									crtc->x, crtc->y, output->modes[bestMode],
 									crtc->rotation, &output_id, 1);
+		
+		if (s == Success)
+			UseXRandR = true;
 
 		XRRFreeCrtcInfo(crtc);
 		XRRFreeOutputInfo(output);
 		XRRFreeScreenResources(res);
-
-		if (s != Success)
-		{
-			CreationParams.Fullscreen = false;
-			return CreationParams.Fullscreen;
-		}
-
-		UseXRandR=true;
+		break;
 	}
-	else
-	#endif
+	
+	if (UseXRandR == false)
 	{
-		os::Printer::log("VidMode or RandR extension must be installed to allow Irrlicht "
-		"to switch to fullscreen mode. Running in windowed mode instead.", ELL_WARNING);
+		os::Printer::log("Could not get video output. Try to run in windowed mode.", ELL_WARNING);
 		CreationParams.Fullscreen = false;
 	}
+	#endif
 
 	return CreationParams.Fullscreen;
 }
@@ -549,7 +573,7 @@ bool CIrrDeviceLinux::createWindow()
 
 	screennr = DefaultScreen(display);
 
-	switchToFullscreen();
+	changeResolution();
 
 #ifdef _IRR_COMPILE_WITH_OPENGL_
 
@@ -1592,89 +1616,88 @@ video::IVideoModeList* CIrrDeviceLinux::getVideoModeList()
 				}
 				XFree(modes);
 			}
-			else
 			#endif
 			#ifdef _IRR_LINUX_X11_RANDR_
-			if (XRRQueryExtension(display, &eventbase, &errorbase))
+			output_id = BadRROutput;
+			old_mode = BadRRMode;
+			
+			while (XRRQueryExtension(display, &eventbase, &errorbase))
 			{
-				XRRScreenResources* res = XRRGetScreenResources(display, DefaultRootWindow(display));
-
-				if (!res)
-					return &VideoModeList;
-
-				XRROutputInfo *output = NULL;
+				XRROutputInfo* output = NULL;
 				XRRCrtcInfo* crtc = NULL;
 				crtc_x = crtc_y = -1;
+
+				XRRScreenResources* res = XRRGetScreenResources(display, DefaultRootWindow(display));
+				if (!res)
+					break;
+				
+				RROutput primary_id = XRRGetOutputPrimary(display, DefaultRootWindow(display));
 		
 				for (int i = 0; i < res->noutput; i++) 
 				{
-					output = XRRGetOutputInfo(display, res, res->outputs[i]);
-					
-					if (!output || !output->crtc || output->connection == RR_Disconnected) 
+					XRROutputInfo* output_tmp = XRRGetOutputInfo(display, res, res->outputs[i]);
+					if (!output_tmp || !output_tmp->crtc || output_tmp->connection == RR_Disconnected) 
 					{
-						XRRFreeOutputInfo(output);
+						XRRFreeOutputInfo(output_tmp);
 						continue;
 					}
 		
-					crtc = XRRGetCrtcInfo(display, res, output->crtc);
-		
-					if (!crtc) 
+					XRRCrtcInfo* crtc_tmp = XRRGetCrtcInfo(display, res, output_tmp->crtc);
+					if (!crtc_tmp) 
+					{
+						XRRFreeOutputInfo(output_tmp);
+						continue;
+					}
+					
+					if (res->outputs[i] == primary_id ||
+						output_id == BadRROutput || crtc_tmp->x < crtc->x ||
+						(crtc_tmp->x == crtc->x && crtc_tmp->y < crtc->y))
 					{
 						XRRFreeCrtcInfo(crtc);
-						XRRFreeOutputInfo(output);
-						continue;
-					}
-					
-					if (crtc_x == -1 || crtc->x < crtc_x)
-					{
-						crtc_x = crtc->x;
-						crtc_y = crtc->y;						
+						XRRFreeOutputInfo(output);		
+						
+						output = output_tmp;
+						crtc = crtc_tmp;					
 						output_id = res->outputs[i];
 					}
-					else if (crtc_x == crtc->x && crtc->y < crtc_y)
+					else
 					{
-						crtc_x = crtc->x;
-						crtc_y = crtc->y;
-						output_id = res->outputs[i];			
+						XRRFreeCrtcInfo(crtc_tmp);
+						XRRFreeOutputInfo(output_tmp);			
 					}
 					
-					XRRFreeCrtcInfo(crtc);
-					XRRFreeOutputInfo(output);
+					if (res->outputs[i] == primary_id)
+						break;
 				}
 				
-				output = XRRGetOutputInfo(display, res, output_id);
-				crtc = XRRGetCrtcInfo(display, res, output->crtc);
-
-				if (crtc == NULL)
+				if (output_id == BadRROutput)
 				{
-					XRRFreeCrtcInfo(crtc);
-					XRRFreeOutputInfo(output);
-					XRRFreeScreenResources(res);
-					return &VideoModeList;
+					os::Printer::log("Could not get video output.", ELL_WARNING);
+					break;
 				}
+				
+				crtc_x = crtc->x;
+				crtc_y = crtc->y;
 
 				for (int i = 0; i < res->nmode; i++)
 				{
 					const XRRModeInfo* mode = &res->modes[i];
-					unsigned int w, h;
+					core::dimension2d<u32> size;
 
 					if (crtc->rotation & (XRANDR_ROTATION_LEFT|XRANDR_ROTATION_RIGHT))
 					{
-						w = mode->height;
-						h = mode->width;
+						size = core::dimension2d<u32>(mode->height, mode->width);
 					} 
 					else 
 					{
-						w = mode->width;
-						h = mode->height;
+						size = core::dimension2d<u32>(mode->width, mode->height);
 					}
 
 					for (int j = 0; j < output->nmode; j++)
 					{            
 						if (mode->id == output->modes[j])
 						{
-							VideoModeList.addMode(core::dimension2d<u32>(
-									w, h), defaultDepth);
+							VideoModeList.addMode(size, defaultDepth);
 							break;
 						}
 					}
@@ -1682,21 +1705,18 @@ video::IVideoModeList* CIrrDeviceLinux::getVideoModeList()
 					if (mode->id == crtc->mode)
 					{
 						old_mode = crtc->mode;
-						VideoModeList.setDesktop(defaultDepth,
-							core::dimension2d<u32>(w, h));
+						VideoModeList.setDesktop(defaultDepth, size);
 					}
 				}
-
+				
 				XRRFreeCrtcInfo(crtc);
 				XRRFreeOutputInfo(output);
-				XRRFreeScreenResources(res);
+				XRRFreeScreenResources(res);						
+				break;
 			}
-			else
 			#endif
-			{
-				os::Printer::log("VidMode or RandR X11 extension requireed for VideoModeList." , ELL_WARNING);
-			}
 		}
+	
 		if (display && temporaryDisplay)
 		{
 			XCloseDisplay(display);
diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h
index e071724de..c808b8ebd 100644
--- a/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h
+++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceLinux.h
@@ -150,7 +150,8 @@ namespace irr
 
 		void initXAtoms();
 
-		bool switchToFullscreen(bool reset=false);
+		bool restoreResolution();
+		bool changeResolution();
 
 		//! Implementation of the linux cursor control
 		class CCursorControl : public gui::ICursorControl
diff --git a/sources.cmake b/sources.cmake
index 317927706..574d5aeb7 100644
--- a/sources.cmake
+++ b/sources.cmake
@@ -1,5 +1,6 @@
-# Modify this file to change the last-modified date when you add/remove a file.
-# This will then trigger a new cmake run automatically.
+# Modify this file to change the last-modified date when you add/remove a file. 
+# This will then trigger a new cmake run automatically. 
 file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
 file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
 file(GLOB_RECURSE STK_SHADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "data/shaders/*")
+file(GLOB_RECURSE STK_RESOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${PROJECT_BINARY_DIR}/tmp/*.rc")
\ No newline at end of file
diff --git a/src/addons/addons_manager.cpp b/src/addons/addons_manager.cpp
index e4d24e11f..c83a10dbc 100644
--- a/src/addons/addons_manager.cpp
+++ b/src/addons/addons_manager.cpp
@@ -114,13 +114,13 @@ void AddonsManager::init(const XMLNode *xml,
     
     if (download)
     {
-        Log::info("NetworkHttp", "Downloading updated addons.xml");
+        Log::info("addons", "Downloading updated addons.xml.");
         Online::HTTPRequest *download_request = new Online::HTTPRequest("addons.xml");
         download_request->setURL(addon_list_url);
         download_request->executeNow();
         if(download_request->hadDownloadError())
         {
-            Log::error("addons", "Error on download addons.xml: %s\n",
+            Log::error("addons", "Error on download addons.xml: %s.",
                        download_request->getDownloadErrorMessage());
             delete download_request;
             return;
@@ -129,12 +129,12 @@ void AddonsManager::init(const XMLNode *xml,
         UserConfigParams::m_addons_last_updated=StkTime::getTimeSinceEpoch();
     }
     else
-        Log::info("NetworkHttp", "Using cached addons.xml");
+        Log::info("addons", "Using cached addons.xml.");
         
     const XMLNode *xml_addons = new XMLNode(filename);
     addons_manager->initAddons(xml_addons);   // will free xml_addons
     if(UserConfigParams::logAddons())
-        Log::info("addons", "Addons manager list downloaded");
+        Log::info("addons", "Addons manager list downloaded.");
 }   // init
 
 // ----------------------------------------------------------------------------
@@ -195,7 +195,7 @@ void AddonsManager::initAddons(const XMLNode *xml)
                 if(file_manager->fileExists(full_path))
                 {
                     if(UserConfigParams::logAddons())
-                        Log::warn("[AddonsManager] Removing cached icon '%s'.\n",
+                        Log::warn("addons", "Removing cached icon '%s'.",
                                addon.getIconBasename().c_str());
                     file_manager->removeFile(full_path);
                 }
@@ -226,9 +226,9 @@ void AddonsManager::initAddons(const XMLNode *xml)
         }
         else
         {
-            Log::error("[AddonsManager]", "Found invalid node '%s' while downloading addons.",
+            Log::error("addons", "Found invalid node '%s' while downloading addons.",
                     node->getName().c_str());
-            Log::error("[AddonsManager]", "Ignored.");
+            Log::error("addons", "Ignored.");
         }
     }   // for i<xml->getNumNodes
     delete xml;
@@ -254,7 +254,7 @@ void AddonsManager::initAddons(const XMLNode *xml)
         // it from the list.
         if(UserConfigParams::logAddons())
             Log::warn(
-                "[AddonsManager] Removing '%s' which is not on the server anymore.\n",
+                "addons", "Removing '%s' which is not on the server anymore.",
                 m_addons_list.getData()[i].getId().c_str() );
         const std::string &icon = m_addons_list.getData()[i].getIconBasename();
         std::string icon_file =file_manager->getAddonsFile("icons/"+icon);
@@ -311,7 +311,7 @@ void AddonsManager::checkInstalledAddons()
         if(n<0) continue;
         if(!m_addons_list.getData()[n].isInstalled())
         {
-            Log::info("[AddonsManager] Marking '%s' as being installed.",
+            Log::info("addons", "Marking '%s' as being installed.",
                    kp->getIdent().c_str());
             m_addons_list.getData()[n].setInstalled(true);
             something_was_changed = true;
@@ -330,7 +330,7 @@ void AddonsManager::checkInstalledAddons()
         if(n<0) continue;
         if(!m_addons_list.getData()[n].isInstalled())
         {
-            Log::info("[AddonsManager] Marking '%s' as being installed.",
+            Log::info("addons", "Marking '%s' as being installed.",
                    track->getIdent().c_str());
             m_addons_list.getData()[n].setInstalled(true);
             something_was_changed = true;
@@ -361,7 +361,7 @@ void AddonsManager::downloadIcons()
             if(icon=="")
             {
                 if(UserConfigParams::logAddons())
-                    Log::error("[AddonsManager]", "No icon or image specified for '%s'.",
+                    Log::error("addons", "No icon or image specified for '%s'.",
                                 addon.getId().c_str());
                 continue;
             }
@@ -400,7 +400,7 @@ void AddonsManager::loadInstalledAddons()
     /* checking for installed addons */
     if(UserConfigParams::logAddons())
     {
-        Log::info("[AddonsManager]", "Loading an xml file for installed addons: %s",
+        Log::info("addons", "Loading an xml file for installed addons: %s.",
                     m_file_installed.c_str());
     }
     const XMLNode *xml = file_manager->createXMLTree(m_file_installed);
@@ -478,15 +478,15 @@ bool AddonsManager::install(const Addon &addon)
     if (!success)
     {
         // TODO: show a message in the interface
-        Log::error("[AddonsManager]", "Failed to unzip '%s' to '%s'",
+        Log::error("addons", "Failed to unzip '%s' to '%s'.",
                     from.c_str(), to.c_str());
-        Log::error("[AddonsManager]", "Zip file will not be removed.");
+        Log::error("addons", "Zip file will not be removed.");
         return false;
     }
 
     if(!file_manager->removeFile(from))
     {
-        Log::error("[AddonsManager]", "Problems removing temporary file '%s'",
+        Log::error("addons", "Problems removing temporary file '%s'.",
                     from.c_str());
     }
 
@@ -520,7 +520,7 @@ bool AddonsManager::install(const Addon &addon)
         }
         catch (std::exception& e)
         {
-            Log::error("[AddonsManager]", "ERROR: Cannot load track <%s> : %s",
+            Log::error("addons", "Cannot load track '%s' : %s.",
                         addon.getDataDir().c_str(), e.what());
         }
     }
@@ -535,8 +535,8 @@ bool AddonsManager::install(const Addon &addon)
  */
 bool AddonsManager::uninstall(const Addon &addon)
 {
-    Log::info("[AddonsManager]", "Uninstalling <%s>",
-                core::stringc(addon.getName()).c_str());
+    Log::info("addons", "Uninstalling '%s'.",
+               core::stringc(addon.getName()).c_str());
 
     // addon is a const reference, and to avoid removing the const, we
     // find the proper index again to modify the installed state
diff --git a/src/challenges/challenge_data.cpp b/src/challenges/challenge_data.cpp
index a65131611..6d0d0514a 100644
--- a/src/challenges/challenge_data.cpp
+++ b/src/challenges/challenge_data.cpp
@@ -376,7 +376,7 @@ void ChallengeData::setRace(RaceManager::Difficulty d) const
     else if(m_mode==CM_GRAND_PRIX)
     {
         race_manager->setMinorMode(m_minor);
-        race_manager->setGrandPrix(grand_prix_manager->getGrandPrix(m_gp_id));
+        race_manager->setGrandPrix(*grand_prix_manager->getGrandPrix(m_gp_id));
         race_manager->setDifficulty(d);
         race_manager->setNumKarts(m_num_karts[d]);
         race_manager->setNumLocalPlayers(1);
diff --git a/src/config/user_config.hpp b/src/config/user_config.hpp
index ae91e0b4d..c2f787b42 100644
--- a/src/config/user_config.hpp
+++ b/src/config/user_config.hpp
@@ -464,6 +464,9 @@ namespace UserConfigParams
     PARAM_PREFIX BoolUserConfigParam        m_gi
         PARAM_DEFAULT(BoolUserConfigParam(false, "enable_gi",
         &m_video_group, "Enable Global Illumination"));
+    PARAM_PREFIX BoolUserConfigParam        m_azdo
+        PARAM_DEFAULT(BoolUserConfigParam(false, "enable_azdo",
+        &m_video_group, "Enable 'Approaching Zero Driver Overhead' mode (very experimental !)"));
 
     // ---- Debug - not saved to config file
     /** If gamepad debugging is enabled. */
diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp
index 547add4f9..9648c0372 100644
--- a/src/graphics/camera.cpp
+++ b/src/graphics/camera.cpp
@@ -24,7 +24,6 @@
 #include "audio/music_manager.hpp"
 #include "config/user_config.hpp"
 #include "graphics/irr_driver.hpp"
-#include "graphics/rain.hpp"
 #include "io/xml_node.hpp"
 #include "karts/abstract_kart.hpp"
 #include "karts/explosion_animation.hpp"
@@ -52,7 +51,6 @@ Camera::Camera(int camera_index, AbstractKart* kart) : m_kart(NULL)
 {
     m_mode          = CM_NORMAL;
     m_index         = camera_index;
-    m_rain          = NULL;
     m_original_kart = kart;
     m_camera        = irr_driver->addCameraSceneNode();
 
@@ -92,7 +90,6 @@ Camera::Camera(int camera_index, AbstractKart* kart) : m_kart(NULL)
  */
 Camera::~Camera()
 {
-    if(m_rain) delete m_rain;
     irr_driver->removeCameraSceneNode(m_camera);
 
     if (s_active_camera == this)
@@ -222,13 +219,6 @@ void Camera::setupCamera()
     m_camera->setFOV(m_fov);
     m_camera->setAspectRatio(m_aspect);
     m_camera->setFarValue(World::getWorld()->getTrack()->getCameraFar());
-
-    if (UserConfigParams::m_weather_effects &&
-        World::getWorld()->getTrack()->getWeatherType() == WEATHER_RAIN)
-    {
-        m_rain = new Rain(this, NULL);
-    }
-
 }   // setupCamera
 
 // ----------------------------------------------------------------------------
@@ -530,12 +520,6 @@ void Camera::update(float dt)
         getCameraSettings(&above_kart, &cam_angle, &side_way, &distance, &smoothing);
         positionCamera(dt, above_kart, cam_angle, side_way, distance, smoothing);
     }
-
-    if (UserConfigParams::m_graphical_effects && m_rain)
-    {
-        m_rain->setPosition( getCameraSceneNode()->getPosition() );
-        m_rain->update(dt);
-    }  // UserConfigParams::m_graphical_effects
 }   // update
 
 // ----------------------------------------------------------------------------
diff --git a/src/graphics/camera.hpp b/src/graphics/camera.hpp
index 7cf5b15a9..db5937a89 100644
--- a/src/graphics/camera.hpp
+++ b/src/graphics/camera.hpp
@@ -41,7 +41,6 @@ namespace irr
 using namespace irr;
 
 class AbstractKart;
-class Rain;
 
 /**
   * \brief Handles the game camera
@@ -120,10 +119,6 @@ private:
     /** List of all cameras. */
     static std::vector<Camera*> m_all_cameras;
 
-    /** Used to show rain graphical effects. */
-    Rain *m_rain;
-
-
     /** A class that stores information about the different end cameras
      *  which can be specified in the scene.xml file. */
     class EndCameraInformation
diff --git a/src/graphics/gl_headers.hpp b/src/graphics/gl_headers.hpp
new file mode 100644
index 000000000..50069f23f
--- /dev/null
+++ b/src/graphics/gl_headers.hpp
@@ -0,0 +1,46 @@
+#ifndef GL_HEADER_HPP
+#define GL_HEADER_HPP
+
+#define GLEW_STATIC
+extern "C" {
+#include <GL/glew.h>
+}
+#include <cinttypes>
+
+#if defined(__APPLE__)
+#    include <OpenGL/gl.h>
+#    include <OpenGL/gl3.h>
+#    define OGL32CTX
+#    ifdef GL_ARB_instanced_arrays
+#        define glVertexAttribDivisor glVertexAttribDivisorARB
+#    endif
+#    ifndef GL_TEXTURE_SWIZZLE_RGBA
+#        define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#    endif
+#elif defined(ANDROID)
+#    include <GLES/gl.h>
+#elif defined(WIN32)
+#    define _WINSOCKAPI_
+#    include <windows.h>
+#else
+#define GL_GLEXT_PROTOTYPES
+#define DEBUG_OUTPUT_DECLARED
+#    include <GL/gl.h>
+#    include <GL/glext.h>
+#endif
+
+#define Bindless_Texture_Support
+#define Base_Instance_Support
+#define Buffer_Storage
+#define Multi_Draw_Indirect
+#define Draw_Indirect
+
+struct DrawElementsIndirectCommand{
+    GLuint count;
+    GLuint instanceCount;
+    GLuint firstIndex;
+    GLuint baseVertex;
+    GLuint baseInstance;
+};
+
+#endif
\ No newline at end of file
diff --git a/src/graphics/glwrap.cpp b/src/graphics/glwrap.cpp
index 7387b9d8f..7c7975fbd 100644
--- a/src/graphics/glwrap.cpp
+++ b/src/graphics/glwrap.cpp
@@ -3,92 +3,16 @@
 #include <string>
 #include "config/user_config.hpp"
 #include "utils/profiler.hpp"
+#include "utils/cpp2011.hpp"
+#include "graphics/stkmesh.hpp"
 
-#ifdef _IRR_WINDOWS_API_
-#define IRR_OGL_LOAD_EXTENSION(X) wglGetProcAddress(reinterpret_cast<const char*>(X))
-PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks;
-PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback;
-PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback;
-PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
-PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
-PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
-PFNGLBINDBUFFERBASEPROC glBindBufferBase;
-PFNGLGENBUFFERSPROC glGenBuffers;
-PFNGLBINDBUFFERPROC glBindBuffer;
-PFNGLBUFFERDATAPROC glBufferData;
-PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
-PFNGLCREATESHADERPROC glCreateShader;
-PFNGLCOMPILESHADERPROC glCompileShader;
-PFNGLSHADERSOURCEPROC glShaderSource;
-PFNGLCREATEPROGRAMPROC glCreateProgram;
-PFNGLATTACHSHADERPROC glAttachShader;
-PFNGLLINKPROGRAMPROC glLinkProgram;
-PFNGLUSEPROGRAMPROC glUseProgram;
-PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
-PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
-PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
-PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
-PFNGLUNIFORM1FPROC glUniform1f;
-PFNGLUNIFORM3FPROC glUniform3f;
-PFNGLDELETESHADERPROC glDeleteShader;
-PFNGLGETSHADERIVPROC glGetShaderiv;
-PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
-PFNGLACTIVETEXTUREPROC glActiveTexture;
-PFNGLUNIFORM2FPROC glUniform2f;
-PFNGLUNIFORM1IPROC glUniform1i;
-PFNGLUNIFORM3IPROC glUniform3i;
-PFNGLUNIFORM4IPROC glUniform4i;
-PFNGLUNIFORM1FVPROC glUniform1fv;
-PFNGLUNIFORM4FVPROC glUniform4fv;
-PFNGLGETPROGRAMIVPROC glGetProgramiv;
-PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
-PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
-PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
-PFNGLBLENDEQUATIONPROC glBlendEquation;
-PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor;
-PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced;
-PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex;
-PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced;
-PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertex;
-PFNGLDELETEBUFFERSPROC glDeleteBuffers;
-PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
-PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
-PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
-PFNGLTEXBUFFERPROC glTexBuffer;
-PFNGLBUFFERSUBDATAPROC glBufferSubData;
-PFNGLMAPBUFFERPROC glMapBuffer;
-PFNGLMAPBUFFERRANGEPROC glMapBufferRange;
-PFNGLUNMAPBUFFERPROC glUnmapBuffer;
-PFNGLFENCESYNCPROC glFenceSync;
-PFNGLCLIENTWAITSYNCPROC glClientWaitSync;
-PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer;
-PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB;
-PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
-PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
-PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
-PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
-PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture;
-PFNGLTEXIMAGE3DPROC glTexImage3D;
-PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
-PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
-PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample;
-PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
-PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
-PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
-PFNGLBLENDCOLORPROC glBlendColor;
-PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
-PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage;
-PFNGLTEXSTORAGE1DPROC glTexStorage1D;
-PFNGLTEXSTORAGE2DPROC glTexStorage2D;
-PFNGLTEXSTORAGE3DPROC glTexStorage3D;
-PFNGLBINDIMAGETEXTUREPROC glBindImageTexture;
-PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
-#endif
+
+#include "../../lib/irrlicht/source/Irrlicht/COpenGLTexture.h"
 
 static bool is_gl_init = false;
 
 #ifdef DEBUG
-#ifdef WIN32
+#if !defined(__APPLE__)
 #define ARB_DEBUG_OUTPUT
 #endif
 #endif
@@ -101,9 +25,11 @@ CALLBACK
 debugCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
               const GLchar* msg, const void *userparam)
 {
+#ifdef GL_DEBUG_SEVERITY_NOTIFICATION
     // ignore minor notifications sent by some drivers (notably the nvidia one)
     if (severity == GL_DEBUG_SEVERITY_NOTIFICATION)
         return;
+#endif
 
     switch(source)
     {
@@ -172,87 +98,10 @@ void initGL()
     if (is_gl_init)
         return;
     is_gl_init = true;
-#ifdef _IRR_WINDOWS_API_
-    glGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC)IRR_OGL_LOAD_EXTENSION("glGenTransformFeedbacks");
-    glBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glBindTransformFeedback");
-    glDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glDrawTransformFeedback");
-    glBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glBeginTransformFeedback");
-    glEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC)IRR_OGL_LOAD_EXTENSION("glEndTransformFeedback");
-    glBindBufferBase = (PFNGLBINDBUFFERBASEPROC)IRR_OGL_LOAD_EXTENSION("glBindBufferBase");
-    glGenBuffers = (PFNGLGENBUFFERSPROC)IRR_OGL_LOAD_EXTENSION("glGenBuffers");
-    glBindBuffer = (PFNGLBINDBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glBindBuffer");
-    glBufferData = (PFNGLBUFFERDATAPROC)IRR_OGL_LOAD_EXTENSION("glBufferData");
-    glMapBuffer = (PFNGLMAPBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glMapBuffer");
-    glMapBufferRange = (PFNGLMAPBUFFERRANGEPROC)IRR_OGL_LOAD_EXTENSION("glMapBufferRange");
-    glUnmapBuffer = (PFNGLUNMAPBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glUnmapBuffer");
-    glFenceSync = (PFNGLFENCESYNCPROC)IRR_OGL_LOAD_EXTENSION("glFenceSync");
-    glClientWaitSync = (PFNGLCLIENTWAITSYNCPROC)IRR_OGL_LOAD_EXTENSION("glClientWaitSync");
-    glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)IRR_OGL_LOAD_EXTENSION("glVertexAttribPointer");
-    glCreateShader = (PFNGLCREATESHADERPROC)IRR_OGL_LOAD_EXTENSION("glCreateShader");
-    glCompileShader = (PFNGLCOMPILESHADERPROC)IRR_OGL_LOAD_EXTENSION("glCompileShader");
-    glShaderSource = (PFNGLSHADERSOURCEPROC)IRR_OGL_LOAD_EXTENSION("glShaderSource");
-    glCreateProgram = (PFNGLCREATEPROGRAMPROC)IRR_OGL_LOAD_EXTENSION("glCreateProgram");
-    glAttachShader = (PFNGLATTACHSHADERPROC)IRR_OGL_LOAD_EXTENSION("glAttachShader");
-    glLinkProgram = (PFNGLLINKPROGRAMPROC)IRR_OGL_LOAD_EXTENSION("glLinkProgram");
-    glUseProgram = (PFNGLUSEPROGRAMPROC)IRR_OGL_LOAD_EXTENSION("glUseProgram");
-    glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)IRR_OGL_LOAD_EXTENSION("glEnableVertexAttribArray");
-    glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC)IRR_OGL_LOAD_EXTENSION("glGetUniformLocation");
-    glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)IRR_OGL_LOAD_EXTENSION("glUniformMatrix4fv");
-    glUniform1f = (PFNGLUNIFORM1FPROC)IRR_OGL_LOAD_EXTENSION("glUniform1f");
-    glUniform3f = (PFNGLUNIFORM3FPROC)IRR_OGL_LOAD_EXTENSION("glUniform3f");
-    glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)IRR_OGL_LOAD_EXTENSION("glDisableVertexAttribArray");
-    glDeleteShader = (PFNGLDELETESHADERPROC)IRR_OGL_LOAD_EXTENSION("glDeleteShader");
-    glGetShaderiv = (PFNGLGETSHADERIVPROC)IRR_OGL_LOAD_EXTENSION("glGetShaderiv");
-    glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)IRR_OGL_LOAD_EXTENSION("glGetShaderInfoLog");
-    glActiveTexture = (PFNGLACTIVETEXTUREPROC)IRR_OGL_LOAD_EXTENSION("glActiveTexture");
-    glUniform2f = (PFNGLUNIFORM2FPROC)IRR_OGL_LOAD_EXTENSION("glUniform2f");
-    glUniform4i = (PFNGLUNIFORM4IPROC)IRR_OGL_LOAD_EXTENSION("glUniform4i");
-    glUniform3i = (PFNGLUNIFORM3IPROC)IRR_OGL_LOAD_EXTENSION("glUniform3i");
-    glUniform1i = (PFNGLUNIFORM1IPROC)IRR_OGL_LOAD_EXTENSION("glUniform1i");
-    glGetProgramiv = (PFNGLGETPROGRAMIVPROC)IRR_OGL_LOAD_EXTENSION("glGetProgramiv");
-    glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)IRR_OGL_LOAD_EXTENSION("glGetProgramInfoLog");
-    glTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC)IRR_OGL_LOAD_EXTENSION("glTransformFeedbackVaryings");
-    glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)IRR_OGL_LOAD_EXTENSION("glGetAttribLocation");
-    glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)IRR_OGL_LOAD_EXTENSION("glBindAttribLocation");
-    glBlendEquation = (PFNGLBLENDEQUATIONPROC)IRR_OGL_LOAD_EXTENSION("glBlendEquation");
-    glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)IRR_OGL_LOAD_EXTENSION("glVertexAttribDivisor");
-    glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)IRR_OGL_LOAD_EXTENSION("glDrawArraysInstanced");
-    glDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC)IRR_OGL_LOAD_EXTENSION("glDrawElementsBaseVertex");
-    glDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC)IRR_OGL_LOAD_EXTENSION("glDrawElementsInstanced");
-    glDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC)IRR_OGL_LOAD_EXTENSION("glDrawElementsInstancedBaseVertex");
-    glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)IRR_OGL_LOAD_EXTENSION("glDeleteBuffers");
-    glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)IRR_OGL_LOAD_EXTENSION("glGenVertexArrays");
-    glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)IRR_OGL_LOAD_EXTENSION("glBindVertexArray");
-    glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)IRR_OGL_LOAD_EXTENSION("glDeleteVertexArrays");
-    glTexBuffer = (PFNGLTEXBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glTexBuffer");
-    glUniform1fv = (PFNGLUNIFORM1FVPROC)IRR_OGL_LOAD_EXTENSION("glUniform1fv");
-    glUniform4fv = (PFNGLUNIFORM4FVPROC)IRR_OGL_LOAD_EXTENSION("glUniform4fv");
-    glBufferSubData = (PFNGLBUFFERSUBDATAPROC)IRR_OGL_LOAD_EXTENSION("glBufferSubData");
-    glVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC)IRR_OGL_LOAD_EXTENSION("glVertexAttribIPointer");
-    glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)IRR_OGL_LOAD_EXTENSION("glGenFramebuffers");
-    glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC)IRR_OGL_LOAD_EXTENSION("glDeleteFramebuffers");
-    glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glBindFramebuffer");
-    glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC)IRR_OGL_LOAD_EXTENSION("glFramebufferTexture2D");
-    glFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC)IRR_OGL_LOAD_EXTENSION("glFramebufferTexture");
-    glTexImage3D = (PFNGLTEXIMAGE3DPROC)IRR_OGL_LOAD_EXTENSION("glTexImage3D");
-    glGenerateMipmap = (PFNGLGENERATEMIPMAPPROC)IRR_OGL_LOAD_EXTENSION("glGenerateMipmap");
-    glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)IRR_OGL_LOAD_EXTENSION("glCheckFramebufferStatus");
-    glTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC)IRR_OGL_LOAD_EXTENSION("glTexImage2DMultisample");
-    glBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC)IRR_OGL_LOAD_EXTENSION("glBlitFramebuffer");
-    glGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC)IRR_OGL_LOAD_EXTENSION("glGetUniformBlockIndex");
-    glUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC)IRR_OGL_LOAD_EXTENSION("glUniformBlockBinding");
-    glBlendColor = (PFNGLBLENDCOLORPROC)IRR_OGL_LOAD_EXTENSION("glBlendColor");
-    glCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC)IRR_OGL_LOAD_EXTENSION("glCompressedTexImage2D");
-    glGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC)IRR_OGL_LOAD_EXTENSION("glGetCompressedTexImage");
-    glTexStorage1D = (PFNGLTEXSTORAGE1DPROC)IRR_OGL_LOAD_EXTENSION("glTexStorage1D");
-    glTexStorage2D = (PFNGLTEXSTORAGE2DPROC)IRR_OGL_LOAD_EXTENSION("glTexStorage2D");
-    glTexStorage3D = (PFNGLTEXSTORAGE3DPROC)IRR_OGL_LOAD_EXTENSION("glTexStorage3D");
-    glBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC)IRR_OGL_LOAD_EXTENSION("glBindImageTexture");
-    glDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC)IRR_OGL_LOAD_EXTENSION("glDispatchCompute");
-#ifdef DEBUG
-    glDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC)IRR_OGL_LOAD_EXTENSION("glDebugMessageCallbackARB");
-#endif
-#endif
+    glewExperimental = GL_TRUE;
+    GLenum err = glewInit();
+    if (GLEW_OK != err)
+        Log::fatal("GLEW", "Glew initialisation failed with error %s", glewGetErrorString(err));
 #ifdef ARB_DEBUG_OUTPUT
     if (glDebugMessageCallbackARB)
         glDebugMessageCallbackARB((GLDEBUGPROCARB)debugCallback, NULL);
@@ -282,12 +131,21 @@ GLuint LoadShader(const char * file, unsigned type)
     char versionString[20];
     sprintf(versionString, "#version %d\n", irr_driver->getGLSLVersion());
     std::string Code = versionString;
+    if (UserConfigParams::m_azdo)
+        Code += "#extension GL_ARB_bindless_texture : enable\n";
+    else
+    {
+        Code += "#extension GL_ARB_bindless_texture : disable\n";
+        Code += "#undef GL_ARB_bindless_texture\n";
+    }
     std::ifstream Stream(file, std::ios::in);
     Code += "//" + std::string(file) + "\n";
     if (irr_driver->needUBOWorkaround())
         Code += "#define UBO_DISABLED\n";
     if (irr_driver->hasVSLayerExtension())
         Code += "#define VSLayer\n";
+    if (irr_driver->needsRGBBindlessWorkaround())
+        Code += "#define SRGBBindlessFix\n";
     Code += LoadHeader();
     if (Stream.is_open())
     {
@@ -539,40 +397,7 @@ void setTexture(unsigned TextureUnit, GLuint TextureId, GLenum MagFilter, GLenum
     glGetError();
 }
 
-class VBOGatherer
-{
-    enum VTXTYPE { VTXTYPE_STANDARD, VTXTYPE_TCOORD, VTXTYPE_TANGENT, VTXTYPE_COUNT };
-    GLuint vbo[VTXTYPE_COUNT], ibo[VTXTYPE_COUNT], vao[VTXTYPE_COUNT];
-    std::vector<scene::IMeshBuffer *> storedCPUBuffer[VTXTYPE_COUNT];
-    void *vtx_mirror[VTXTYPE_COUNT], *idx_mirror[VTXTYPE_COUNT];
-    size_t vtx_cnt[VTXTYPE_COUNT], idx_cnt[VTXTYPE_COUNT];
-    std::map<scene::IMeshBuffer*, unsigned> mappedBaseVertex[VTXTYPE_COUNT], mappedBaseIndex[VTXTYPE_COUNT];
-
-    void regenerateBuffer(enum VTXTYPE);
-    void regenerateVAO(enum VTXTYPE);
-    size_t getVertexPitch(enum VTXTYPE) const;
-    VTXTYPE getVTXTYPE(video::E_VERTEX_TYPE type);
-    void append(scene::IMeshBuffer *, VBOGatherer::VTXTYPE tp);
-public:
-    VBOGatherer();
-    std::pair<unsigned, unsigned> getBase(scene::IMeshBuffer *);
-    unsigned getVBO(video::E_VERTEX_TYPE type) { return vbo[getVTXTYPE(type)]; }
-    unsigned getVAO(video::E_VERTEX_TYPE type) { return vao[getVTXTYPE(type)]; }
-    ~VBOGatherer()
-    {
-        for (unsigned i = 0; i < VTXTYPE_COUNT; i++)
-        {
-            if (vbo[i])
-                glDeleteBuffers(1, &vbo[i]);
-            if (ibo[i])
-                glDeleteBuffers(1, &ibo[i]);
-            if (vao[i])
-                glDeleteVertexArrays(1, &vao[i]);
-        }
-    }
-};
-
-VBOGatherer::VBOGatherer()
+VAOManager::VAOManager()
 {
     vao[0] = vao[1] = vao[2] = 0;
     vbo[0] = vbo[1] = vbo[2] = 0;
@@ -581,16 +406,81 @@ VBOGatherer::VBOGatherer()
     idx_cnt[0] = idx_cnt[1] = idx_cnt[2] = 0;
     vtx_mirror[0] = vtx_mirror[1] = vtx_mirror[2] = NULL;
     idx_mirror[0] = idx_mirror[1] = idx_mirror[2] = NULL;
+    instance_count[0] = 0;
+
+    for (unsigned i = 0; i < InstanceTypeCount; i++)
+    {
+        glGenBuffers(1, &instance_vbo[i]);
+        glBindBuffer(GL_ARRAY_BUFFER, instance_vbo[i]);
+#ifdef Buffer_Storage
+        if (irr_driver->hasBufferStorageExtension())
+        {
+            glBufferStorage(GL_ARRAY_BUFFER, 10000 * sizeof(InstanceData), 0, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
+            Ptr[i] = glMapBufferRange(GL_ARRAY_BUFFER, 0, 10000 * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
+        }
+        else
+#endif
+        {
+            glBufferData(GL_ARRAY_BUFFER, 10000 * sizeof(InstanceData), 0, GL_STREAM_DRAW);
+        }
+    }
 }
 
-void VBOGatherer::regenerateBuffer(enum VTXTYPE tp)
+static void cleanVAOMap(std::map<std::pair<video::E_VERTEX_TYPE, InstanceType>, GLuint> Map)
+{
+    std::map<std::pair<video::E_VERTEX_TYPE, InstanceType>, GLuint>::iterator It = Map.begin(), E = Map.end();
+    for (; It != E; It++)
+    {
+        glDeleteVertexArrays(1, &(It->second));
+    }
+}
+
+void VAOManager::cleanInstanceVAOs()
+{
+    cleanVAOMap(InstanceVAO);
+    InstanceVAO.clear();
+}
+
+VAOManager::~VAOManager()
+{
+    cleanInstanceVAOs();
+    for (unsigned i = 0; i < 3; i++)
+    {
+        if (vtx_mirror[i])
+            free(vtx_mirror[i]);
+        if (idx_mirror[i])
+            free(idx_mirror[i]);
+        if (vbo[i])
+            glDeleteBuffers(1, &vbo[i]);
+        if (ibo[i])
+            glDeleteBuffers(1, &ibo[i]);
+        if (vao[i])
+            glDeleteVertexArrays(1, &vao[i]);
+    }
+    for (unsigned i = 0; i < InstanceTypeCount; i++)
+    {
+        glDeleteBuffers(1, &instance_vbo[i]);
+    }
+
+}
+
+void VAOManager::regenerateBuffer(enum VTXTYPE tp)
 {
     glBindVertexArray(0);
     if (vbo[tp])
         glDeleteBuffers(1, &vbo[tp]);
     glGenBuffers(1, &vbo[tp]);
     glBindBuffer(GL_ARRAY_BUFFER, vbo[tp]);
-    glBufferData(GL_ARRAY_BUFFER, vtx_cnt[tp] * getVertexPitch(tp), vtx_mirror[tp], GL_DYNAMIC_DRAW);
+#ifdef Buffer_Storage
+    if (irr_driver->hasBufferStorageExtension())
+    {
+        glBufferStorage(GL_ARRAY_BUFFER, vtx_cnt[tp] * getVertexPitch(tp), vtx_mirror[tp], GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
+        VBOPtr[tp] = glMapBufferRange(GL_ARRAY_BUFFER, 0, vtx_cnt[tp] * getVertexPitch(tp), GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
+    }
+    else
+#endif
+        glBufferData(GL_ARRAY_BUFFER, vtx_cnt[tp] * getVertexPitch(tp), vtx_mirror[tp], GL_DYNAMIC_DRAW);
+
 
     if (ibo[tp])
         glDeleteBuffers(1, &ibo[tp]);
@@ -602,7 +492,7 @@ void VBOGatherer::regenerateBuffer(enum VTXTYPE tp)
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
 }
 
-void VBOGatherer::regenerateVAO(enum VTXTYPE tp)
+void VAOManager::regenerateVAO(enum VTXTYPE tp)
 {
     if (vao[tp])
         glDeleteVertexArrays(1, &vao[tp]);
@@ -668,7 +558,100 @@ void VBOGatherer::regenerateVAO(enum VTXTYPE tp)
     glBindVertexArray(0);
 }
 
-size_t VBOGatherer::getVertexPitch(enum VTXTYPE tp) const
+void VAOManager::regenerateInstancedVAO()
+{
+    cleanInstanceVAOs();
+
+    enum video::E_VERTEX_TYPE IrrVT[] = { video::EVT_STANDARD, video::EVT_2TCOORDS, video::EVT_TANGENTS };
+    for (unsigned i = 0; i < VTXTYPE_COUNT; i++)
+    {
+            video::E_VERTEX_TYPE tp = IrrVT[i];
+            if (!vbo[tp] || !ibo[tp])
+                continue;
+            GLuint vao = createVAO(vbo[tp], ibo[tp], tp);
+            glBindBuffer(GL_ARRAY_BUFFER, instance_vbo[InstanceTypeDefault]);
+
+            glEnableVertexAttribArray(7);
+            glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), 0);
+            glVertexAttribDivisor(7, 1);
+            glEnableVertexAttribArray(8);
+            glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (GLvoid*)(3 * sizeof(float)));
+            glVertexAttribDivisor(8, 1);
+            glEnableVertexAttribArray(9);
+            glVertexAttribPointer(9, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (GLvoid*)(6 * sizeof(float)));
+            glVertexAttribDivisor(9, 1);
+            glEnableVertexAttribArray(10);
+            glVertexAttribIPointer(10, 2, GL_UNSIGNED_INT, sizeof(InstanceData), (GLvoid*)(9 * sizeof(float)));
+            glVertexAttribDivisor(10, 1);
+            glEnableVertexAttribArray(11);
+            glVertexAttribIPointer(11, 2, GL_UNSIGNED_INT, sizeof(InstanceData), (GLvoid*)(9 * sizeof(float) + 2 * sizeof(unsigned)));
+            glVertexAttribDivisor(11, 1);
+            InstanceVAO[std::pair<video::E_VERTEX_TYPE, InstanceType>(tp, InstanceTypeDefault)] = vao;
+
+            vao = createVAO(vbo[tp], ibo[tp], tp);
+            glBindBuffer(GL_ARRAY_BUFFER, instance_vbo[InstanceTypeShadow]);
+
+            glEnableVertexAttribArray(7);
+            glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), 0);
+            glVertexAttribDivisor(7, 1);
+            glEnableVertexAttribArray(8);
+            glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (GLvoid*)(3 * sizeof(float)));
+            glVertexAttribDivisor(8, 1);
+            glEnableVertexAttribArray(9);
+            glVertexAttribPointer(9, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (GLvoid*)(6 * sizeof(float)));
+            glVertexAttribDivisor(9, 1);
+            glEnableVertexAttribArray(10);
+            glVertexAttribIPointer(10, 2, GL_UNSIGNED_INT, sizeof(InstanceData), (GLvoid*)(9 * sizeof(float)));
+            glVertexAttribDivisor(10, 1);
+            glEnableVertexAttribArray(11);
+            glVertexAttribIPointer(11, 2, GL_UNSIGNED_INT, sizeof(InstanceData), (GLvoid*)(9 * sizeof(float) + 2 * sizeof(unsigned)));
+            glVertexAttribDivisor(11, 1);
+            InstanceVAO[std::pair<video::E_VERTEX_TYPE, InstanceType>(tp, InstanceTypeShadow)] = vao;
+
+            vao = createVAO(vbo[tp], ibo[tp], tp);
+            glBindBuffer(GL_ARRAY_BUFFER, instance_vbo[InstanceTypeRSM]);
+
+            glEnableVertexAttribArray(7);
+            glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), 0);
+            glVertexAttribDivisor(7, 1);
+            glEnableVertexAttribArray(8);
+            glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (GLvoid*)(3 * sizeof(float)));
+            glVertexAttribDivisor(8, 1);
+            glEnableVertexAttribArray(9);
+            glVertexAttribPointer(9, 3, GL_FLOAT, GL_FALSE, sizeof(InstanceData), (GLvoid*)(6 * sizeof(float)));
+            glVertexAttribDivisor(9, 1);
+            glEnableVertexAttribArray(10);
+            glVertexAttribIPointer(10, 2, GL_UNSIGNED_INT, sizeof(InstanceData), (GLvoid*)(9 * sizeof(float)));
+            glVertexAttribDivisor(10, 1);
+            glEnableVertexAttribArray(11);
+            glVertexAttribIPointer(11, 2, GL_UNSIGNED_INT, sizeof(InstanceData), (GLvoid*)(9 * sizeof(float) + 2 * sizeof(unsigned)));
+            glVertexAttribDivisor(11, 1);
+            InstanceVAO[std::pair<video::E_VERTEX_TYPE, InstanceType>(tp, InstanceTypeRSM)] = vao;
+
+            vao = createVAO(vbo[tp], ibo[tp], tp);
+            glBindBuffer(GL_ARRAY_BUFFER, instance_vbo[InstanceTypeGlow]);
+
+            glEnableVertexAttribArray(7);
+            glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, sizeof(GlowInstanceData), 0);
+            glVertexAttribDivisor(7, 1);
+            glEnableVertexAttribArray(8);
+            glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, sizeof(GlowInstanceData), (GLvoid*)(3 * sizeof(float)));
+            glVertexAttribDivisor(8, 1);
+            glEnableVertexAttribArray(9);
+            glVertexAttribPointer(9, 3, GL_FLOAT, GL_FALSE, sizeof(GlowInstanceData), (GLvoid*)(6 * sizeof(float)));
+            glVertexAttribDivisor(9, 1);
+            glEnableVertexAttribArray(12);
+            glVertexAttribPointer(12, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(GlowInstanceData), (GLvoid*)(9 * sizeof(float)));
+            glVertexAttribDivisor(12, 1);
+            InstanceVAO[std::pair<video::E_VERTEX_TYPE, InstanceType>(tp, InstanceTypeGlow)] = vao;
+            glBindVertexArray(0);
+    }
+
+
+
+}
+
+size_t VAOManager::getVertexPitch(enum VTXTYPE tp) const
 {
     switch (tp)
     {
@@ -684,22 +667,22 @@ size_t VBOGatherer::getVertexPitch(enum VTXTYPE tp) const
     }
 }
 
-VBOGatherer::VTXTYPE VBOGatherer::getVTXTYPE(video::E_VERTEX_TYPE type)
+VAOManager::VTXTYPE VAOManager::getVTXTYPE(video::E_VERTEX_TYPE type)
 {
     switch (type)
     {
+    default:
+        assert(0 && "Wrong vtxtype");
     case video::EVT_STANDARD:
         return VTXTYPE_STANDARD;
     case video::EVT_2TCOORDS:
         return VTXTYPE_TCOORD;
     case video::EVT_TANGENTS:
         return VTXTYPE_TANGENT;
-    default:
-        assert(0 && "Wrong vtxtype");
     }
 };
 
-void VBOGatherer::append(scene::IMeshBuffer *mb, VBOGatherer::VTXTYPE tp)
+void VAOManager::append(scene::IMeshBuffer *mb, VTXTYPE tp)
 {
     size_t old_vtx_cnt = vtx_cnt[tp];
     vtx_cnt[tp] += mb->getVertexCount();
@@ -717,7 +700,7 @@ void VBOGatherer::append(scene::IMeshBuffer *mb, VBOGatherer::VTXTYPE tp)
     mappedBaseIndex[tp][mb] = old_idx_cnt * sizeof(u16);
 }
 
-std::pair<unsigned, unsigned> VBOGatherer::getBase(scene::IMeshBuffer *mb)
+std::pair<unsigned, unsigned> VAOManager::getBase(scene::IMeshBuffer *mb)
 {
     VTXTYPE tp = getVTXTYPE(mb->getVertexType());
     if (mappedBaseVertex[tp].find(mb) == mappedBaseVertex[tp].end())
@@ -727,6 +710,7 @@ std::pair<unsigned, unsigned> VBOGatherer::getBase(scene::IMeshBuffer *mb)
         append(mb, tp);
         regenerateBuffer(tp);
         regenerateVAO(tp);
+        regenerateInstancedVAO();
     }
 
     std::map<scene::IMeshBuffer*, unsigned>::iterator It;
@@ -738,63 +722,41 @@ std::pair<unsigned, unsigned> VBOGatherer::getBase(scene::IMeshBuffer *mb)
     return std::pair<unsigned, unsigned>(vtx, It->second);
 }
 
-static VBOGatherer *gatherersingleton = 0;
-
-std::pair<unsigned, unsigned> getVAOOffsetAndBase(scene::IMeshBuffer *mb)
+size_t VAOManager::appendInstance(enum InstanceType, const std::vector<InstanceData> &instance_data)
 {
-    if (!gatherersingleton)
-        gatherersingleton = new VBOGatherer();
-    return gatherersingleton->getBase(mb);
+    glBindBuffer(GL_ARRAY_BUFFER, instance_vbo[0]);
+    glBufferSubData(GL_ARRAY_BUFFER, instance_count[0] * sizeof(InstanceData), instance_data.size() * sizeof(InstanceData), instance_data.data());
+    size_t result = instance_count[0];
+    instance_count[0] += instance_data.size();
+    return result;
 }
 
-unsigned getVBO(video::E_VERTEX_TYPE type)
-{
-    if (gatherersingleton)
-        return gatherersingleton->getVBO(type);
-    return 0;
-}
-
-unsigned getVAO(video::E_VERTEX_TYPE type)
-{
-    if (gatherersingleton)
-        return gatherersingleton->getVAO(type);
-    return 0;
-}
-
-void resetVAO()
-{
-    if (gatherersingleton)
-        delete gatherersingleton;
-    gatherersingleton = 0;
-}
-
-ScopedGPUTimer::ScopedGPUTimer(GPUTimer &timer)
+ScopedGPUTimer::ScopedGPUTimer(GPUTimer &t) : timer(t)
 {
     if (!UserConfigParams::m_profiler_enabled) return;
     if (profiler.isFrozen()) return;
-
+    if (!timer.canSubmitQuery) return;
 #ifdef GL_TIME_ELAPSED
-    irr::video::COpenGLDriver *gl_driver = (irr::video::COpenGLDriver *)irr_driver->getDevice()->getVideoDriver();
     if (!timer.initialised)
     {
-        gl_driver->extGlGenQueries(1, &timer.query);
+        glGenQueries(1, &timer.query);
         timer.initialised = true;
     }
-    gl_driver->extGlBeginQuery(GL_TIME_ELAPSED, timer.query);
+    glBeginQuery(GL_TIME_ELAPSED, timer.query);
 #endif
 }
 ScopedGPUTimer::~ScopedGPUTimer()
 {
     if (!UserConfigParams::m_profiler_enabled) return;
     if (profiler.isFrozen()) return;
-    
+    if (!timer.canSubmitQuery) return;
 #ifdef GL_TIME_ELAPSED
-    irr::video::COpenGLDriver *gl_driver = (irr::video::COpenGLDriver *)irr_driver->getDevice()->getVideoDriver();
-    gl_driver->extGlEndQuery(GL_TIME_ELAPSED);
+    glEndQuery(GL_TIME_ELAPSED);
+    timer.canSubmitQuery = false;
 #endif
 }
 
-GPUTimer::GPUTimer() : initialised(false)
+GPUTimer::GPUTimer() : initialised(false), lastResult(0), canSubmitQuery(true)
 {
 }
 
@@ -803,8 +765,12 @@ unsigned GPUTimer::elapsedTimeus()
     if (!initialised)
         return 0;
     GLuint result;
-    irr::video::COpenGLDriver *gl_driver = (irr::video::COpenGLDriver *)irr_driver->getDevice()->getVideoDriver();
-    gl_driver->extGlGetQueryObjectuiv(query, GL_QUERY_RESULT, &result);
+    glGetQueryObjectuiv(query, GL_QUERY_RESULT_AVAILABLE, &result);
+    if (result == GL_FALSE)
+        return lastResult;
+    glGetQueryObjectuiv(query, GL_QUERY_RESULT, &result);
+    lastResult = result / 1000;
+    canSubmitQuery = true;
     return result / 1000;
 }
 
@@ -861,7 +827,7 @@ void FrameBuffer::Bind()
     glViewport(0, 0, width, height);
     irr::video::COpenGLDriver *gl_driver = (irr::video::COpenGLDriver*)irr_driver->getDevice()->getVideoDriver();
     GLenum bufs[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
-    gl_driver->extGlDrawBuffers(RenderTargets.size(), bufs);
+    glDrawBuffers(RenderTargets.size(), bufs);
 }
 
 void FrameBuffer::Blit(const FrameBuffer &Src, FrameBuffer &Dst, GLbitfield mask, GLenum filter)
@@ -924,7 +890,7 @@ static void drawTexColoredQuad(const video::ITexture *texture, const video::SCol
     glUseProgram(UIShader::ColoredTextureRectShader::getInstance()->Program);
     glBindVertexArray(UIShader::ColoredTextureRectShader::getInstance()->vao);
 
-    setTexture(UIShader::ColoredTextureRectShader::getInstance()->TU_tex, static_cast<const irr::video::COpenGLTexture*>(texture)->getOpenGLTextureName(), GL_LINEAR, GL_LINEAR);
+    UIShader::ColoredTextureRectShader::getInstance()->SetTextureUnits(createVector<GLuint>(static_cast<const irr::video::COpenGLTexture*>(texture)->getOpenGLTextureName()));
     UIShader::ColoredTextureRectShader::getInstance()->setUniforms(
         core::vector2df(center_pos_x, center_pos_y), core::vector2df(width, height),
         core::vector2df(tex_center_pos_x, tex_center_pos_y), core::vector2df(tex_width, tex_height));
@@ -944,7 +910,7 @@ void drawTexQuad(GLuint texture, float width, float height,
     glUseProgram(UIShader::TextureRectShader::getInstance()->Program);
     glBindVertexArray(SharedObject::UIVAO);
 
-    setTexture(UIShader::TextureRectShader::getInstance()->TU_tex, texture, GL_LINEAR, GL_LINEAR);
+    UIShader::TextureRectShader::getInstance()->SetTextureUnits(createVector<GLuint>(texture));
     UIShader::TextureRectShader::getInstance()->setUniforms(
         core::vector2df(center_pos_x, center_pos_y), core::vector2df(width, height),
         core::vector2df(tex_center_pos_x, tex_center_pos_y),
@@ -1047,7 +1013,7 @@ void draw2DImage(const video::ITexture* texture, const core::rect<s32>& destRect
     glUseProgram(UIShader::UniformColoredTextureRectShader::getInstance()->Program);
     glBindVertexArray(SharedObject::UIVAO);
 
-    setTexture(UIShader::UniformColoredTextureRectShader::getInstance()->TU_tex, static_cast<const irr::video::COpenGLTexture*>(texture)->getOpenGLTextureName(), GL_LINEAR, GL_LINEAR);
+    UIShader::UniformColoredTextureRectShader::getInstance()->SetTextureUnits(createVector<GLuint>(static_cast<const irr::video::COpenGLTexture*>(texture)->getOpenGLTextureName()));
     UIShader::UniformColoredTextureRectShader::getInstance()->setUniforms(
         core::vector2df(center_pos_x, center_pos_y), core::vector2df(width, height), core::vector2df(tex_center_pos_x, tex_center_pos_y), core::vector2df(tex_width, tex_height), colors);
 
@@ -1083,7 +1049,7 @@ void draw2DImageFromRTT(GLuint texture, size_t texture_w, size_t texture_h,
     glUseProgram(UIShader::UniformColoredTextureRectShader::getInstance()->Program);
     glBindVertexArray(SharedObject::UIVAO);
 
-    setTexture(UIShader::UniformColoredTextureRectShader::getInstance()->TU_tex, texture, GL_LINEAR, GL_LINEAR);
+    UIShader::UniformColoredTextureRectShader::getInstance()->SetTextureUnits(createVector<GLuint>(texture));
     UIShader::UniformColoredTextureRectShader::getInstance()->setUniforms(
         core::vector2df(center_pos_x, center_pos_y), core::vector2df(width, height),
         core::vector2df(tex_center_pos_x, tex_center_pos_y), core::vector2df(tex_width, tex_height),
@@ -1204,3 +1170,30 @@ void GL32_draw2DRectangle(video::SColor color, const core::rect<s32>& position,
 
     glGetError();
 }
+
+bool hasGLExtension(const char* extension) 
+{
+    if (glGetStringi != NULL)
+    {
+        GLint numExtensions = 0;
+        glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
+        for (GLint i = 0; i < numExtensions; i++)
+        {
+            const char* foundExtension =
+                (const char*) glGetStringi(GL_EXTENSIONS, i);
+            if (foundExtension && strcmp(foundExtension, extension) == 0)
+            {
+                return true;
+            }
+        }
+    }
+    else
+    {
+        const char* extensions = (const char*) glGetString(GL_EXTENSIONS);
+        if (extensions && strstr(extensions, extension) != NULL)
+        {
+            return true;
+        }
+    }
+    return false;
+}
diff --git a/src/graphics/glwrap.hpp b/src/graphics/glwrap.hpp
index 2ed1994e1..cd4da2a20 100644
--- a/src/graphics/glwrap.hpp
+++ b/src/graphics/glwrap.hpp
@@ -1,118 +1,12 @@
 #ifndef GLWRAP_HEADER_H
 #define GLWRAP_HEADER_H
 
-#if defined(__APPLE__)
-#    include <OpenGL/gl.h>
-#    include <OpenGL/gl3.h>
-#    define OGL32CTX
-#    ifdef GL_ARB_instanced_arrays
-#        define glVertexAttribDivisor glVertexAttribDivisorARB
-#    endif
-#    ifndef GL_TEXTURE_SWIZZLE_RGBA
-#        define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
-#    endif
-#elif defined(ANDROID)
-#    include <GLES/gl.h>
-#elif defined(WIN32)
-#    define _WINSOCKAPI_
-// has to be included before gl.h because of WINGDIAPI and APIENTRY definitions
-#    include <windows.h>
-#    include <GL/gl.h>
-#else
-#define GL_GLEXT_PROTOTYPES
-#define DEBUG_OUTPUT_DECLARED
-#    include <GL/gl.h>
-#endif
+#include "gl_headers.hpp"
 
 #include <vector>
 #include "irr_driver.hpp"
 #include "utils/log.hpp"
 
-// already includes glext.h, which defines useful GL constants.
-// COpenGLDriver has already loaded the extension GL functions we use (e.g glBeginQuery)
-#include "../../lib/irrlicht/source/Irrlicht/COpenGLDriver.h"
-#ifdef WIN32
-extern PFNGLGENTRANSFORMFEEDBACKSPROC glGenTransformFeedbacks;
-extern PFNGLBINDTRANSFORMFEEDBACKPROC glBindTransformFeedback;
-extern PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback;
-extern PFNGLBEGINTRANSFORMFEEDBACKPROC glBeginTransformFeedback;
-extern PFNGLENDTRANSFORMFEEDBACKPROC glEndTransformFeedback;
-extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC glTransformFeedbackVaryings;
-extern PFNGLBINDBUFFERBASEPROC glBindBufferBase;
-extern PFNGLGENBUFFERSPROC glGenBuffers;
-extern PFNGLBINDBUFFERPROC glBindBuffer;
-extern PFNGLBUFFERDATAPROC glBufferData;
-extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
-extern PFNGLCREATESHADERPROC glCreateShader;
-extern PFNGLCOMPILESHADERPROC glCompileShader;
-extern PFNGLSHADERSOURCEPROC glShaderSource;
-extern PFNGLCREATEPROGRAMPROC glCreateProgram;
-extern PFNGLATTACHSHADERPROC glAttachShader;
-extern PFNGLLINKPROGRAMPROC glLinkProgram;
-extern PFNGLUSEPROGRAMPROC glUseProgram;
-extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
-extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
-extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation;
-extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
-extern PFNGLUNIFORM1FPROC glUniform1f;
-extern PFNGLUNIFORM3FPROC glUniform3f;
-extern PFNGLUNIFORM1FVPROC glUniform1fv;
-extern PFNGLUNIFORM4FVPROC glUniform4fv;
-extern PFNGLDELETESHADERPROC glDeleteShader;
-extern PFNGLGETSHADERIVPROC glGetShaderiv;
-extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
-extern PFNGLACTIVETEXTUREPROC glActiveTexture;
-extern PFNGLUNIFORM2FPROC glUniform2f;
-extern PFNGLUNIFORM1IPROC glUniform1i;
-extern PFNGLUNIFORM3IPROC glUniform3i;
-extern PFNGLUNIFORM4IPROC glUniform4i;
-extern PFNGLGETPROGRAMIVPROC glGetProgramiv;
-extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
-extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
-extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
-extern PFNGLBLENDEQUATIONPROC glBlendEquation;
-extern PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor;
-extern PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced;
-extern PFNGLDRAWELEMENTSBASEVERTEXPROC glDrawElementsBaseVertex;
-extern PFNGLDRAWELEMENTSINSTANCEDPROC glDrawElementsInstanced;
-extern PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC glDrawElementsInstancedBaseVertex;
-extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
-extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
-extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
-extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
-extern PFNGLTEXBUFFERPROC glTexBuffer;
-extern PFNGLBUFFERSUBDATAPROC glBufferSubData;
-extern PFNGLMAPBUFFERPROC glMapBuffer;
-extern PFNGLMAPBUFFERRANGEPROC glMapBufferRange;
-extern PFNGLUNMAPBUFFERPROC glUnmapBuffer;
-extern PFNGLFENCESYNCPROC glFenceSync;
-extern PFNGLCLIENTWAITSYNCPROC glClientWaitSync;
-extern PFNGLVERTEXATTRIBIPOINTERPROC glVertexAttribIPointer;
-extern PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
-extern PFNGLDELETEFRAMEBUFFERSPROC glDeleteFramebuffers;
-extern PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
-extern PFNGLFRAMEBUFFERTEXTURE2DPROC glFramebufferTexture2D;
-extern PFNGLFRAMEBUFFERTEXTUREPROC glFramebufferTexture;
-extern PFNGLTEXIMAGE3DPROC glTexImage3D;
-extern PFNGLGENERATEMIPMAPPROC glGenerateMipmap;
-extern PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
-extern PFNGLTEXIMAGE2DMULTISAMPLEPROC glTexImage2DMultisample;
-extern PFNGLBLITFRAMEBUFFERPROC glBlitFramebuffer;
-extern PFNGLGETUNIFORMBLOCKINDEXPROC glGetUniformBlockIndex;
-extern PFNGLUNIFORMBLOCKBINDINGPROC glUniformBlockBinding;
-extern PFNGLBLENDCOLORPROC glBlendColor;
-extern PFNGLCOMPRESSEDTEXIMAGE2DPROC glCompressedTexImage2D;
-extern PFNGLGETCOMPRESSEDTEXIMAGEPROC glGetCompressedTexImage;
-extern PFNGLTEXSTORAGE1DPROC glTexStorage1D;
-extern PFNGLTEXSTORAGE2DPROC glTexStorage2D;
-extern PFNGLTEXSTORAGE3DPROC glTexStorage3D;
-extern PFNGLBINDIMAGETEXTUREPROC glBindImageTexture;
-extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
-#ifdef DEBUG
-extern PFNGLDEBUGMESSAGECALLBACKARBPROC glDebugMessageCallbackARB;
-#endif
-#endif
-
 
 void initGL();
 GLuint LoadTFBProgram(const char * vertex_file_path, const char **varyings, unsigned varyingscount);
@@ -194,6 +88,8 @@ class GPUTimer;
 
 class ScopedGPUTimer
 {
+protected:
+    GPUTimer &timer;
 public:
     ScopedGPUTimer(GPUTimer &);
     ~ScopedGPUTimer();
@@ -204,6 +100,8 @@ class GPUTimer
     friend class ScopedGPUTimer;
     GLuint query;
     bool initialised;
+    unsigned lastResult;
+    bool canSubmitQuery;
 public:
     GPUTimer();
     unsigned elapsedTimeus();
@@ -241,10 +139,109 @@ void compressTexture(irr::video::ITexture *tex, bool srgb, bool premul_alpha = f
 bool loadCompressedTexture(const std::string& compressed_tex);
 void saveCompressedTexture(const std::string& compressed_tex);
 
-std::pair<unsigned, unsigned> getVAOOffsetAndBase(scene::IMeshBuffer *mb);
-unsigned getVAO(video::E_VERTEX_TYPE type);
-unsigned getVBO(video::E_VERTEX_TYPE type);
-void resetVAO();
+enum InstanceType
+{
+    InstanceTypeDefault,
+    InstanceTypeShadow,
+    InstanceTypeRSM,
+    InstanceTypeGlow,
+    InstanceTypeCount,
+};
+
+#ifdef WIN32
+#pragma pack(push, 1)
+#endif
+struct InstanceData
+{
+    struct
+    {
+        float X;
+        float Y;
+        float Z;
+    } Origin;
+    struct
+    {
+        float X;
+        float Y;
+        float Z;
+    } Orientation;
+    struct
+    {
+        float X;
+        float Y;
+        float Z;
+    } Scale;
+    uint64_t Texture;
+    uint64_t SecondTexture;
+#ifdef WIN32
+};
+#else
+} __attribute__((packed));
+#endif
+
+struct GlowInstanceData
+{
+    struct
+    {
+        float X;
+        float Y;
+        float Z;
+    } Origin;
+    struct
+    {
+        float X;
+        float Y;
+        float Z;
+    } Orientation;
+    struct
+    {
+        float X;
+        float Y;
+        float Z;
+    } Scale;
+    unsigned Color;
+#ifdef WIN32
+};
+#else
+} __attribute__((packed));
+#endif
+#ifdef WIN32
+#pragma pack(pop)
+#endif
+
+class VAOManager : public Singleton<VAOManager>
+{
+    enum VTXTYPE { VTXTYPE_STANDARD, VTXTYPE_TCOORD, VTXTYPE_TANGENT, VTXTYPE_COUNT };
+    GLuint vbo[VTXTYPE_COUNT], ibo[VTXTYPE_COUNT], vao[VTXTYPE_COUNT];
+    GLuint instance_vbo[InstanceTypeCount];
+    size_t instance_count[InstanceTypeCount];
+    void *Ptr[InstanceTypeCount];
+    void *VBOPtr[VTXTYPE_COUNT];
+    std::vector<scene::IMeshBuffer *> storedCPUBuffer[VTXTYPE_COUNT];
+    void *vtx_mirror[VTXTYPE_COUNT], *idx_mirror[VTXTYPE_COUNT];
+    size_t vtx_cnt[VTXTYPE_COUNT], idx_cnt[VTXTYPE_COUNT];
+    std::map<scene::IMeshBuffer*, unsigned> mappedBaseVertex[VTXTYPE_COUNT], mappedBaseIndex[VTXTYPE_COUNT];
+    std::map<std::pair<video::E_VERTEX_TYPE, InstanceType>, GLuint> InstanceVAO;
+
+    void cleanInstanceVAOs();
+    void regenerateBuffer(enum VTXTYPE);
+    void regenerateVAO(enum VTXTYPE);
+    void regenerateInstancedVAO();
+    size_t getVertexPitch(enum VTXTYPE) const;
+    VTXTYPE getVTXTYPE(video::E_VERTEX_TYPE type);
+    void append(scene::IMeshBuffer *, VTXTYPE tp);
+public:
+    VAOManager();
+    std::pair<unsigned, unsigned> getBase(scene::IMeshBuffer *);
+    size_t appendInstance(enum InstanceType, const std::vector<InstanceData> &instance_data);
+    GLuint getInstanceBuffer(InstanceType it) { return instance_vbo[it]; }
+    void *getInstanceBufferPtr(InstanceType it) { return Ptr[it]; }
+    unsigned getVBO(video::E_VERTEX_TYPE type) { return vbo[getVTXTYPE(type)]; }
+    void *getVBOPtr(video::E_VERTEX_TYPE type) { return VBOPtr[getVTXTYPE(type)]; }
+    unsigned getVAO(video::E_VERTEX_TYPE type) { return vao[getVTXTYPE(type)]; }
+    unsigned getInstanceVAO(video::E_VERTEX_TYPE vt, enum InstanceType it) { return InstanceVAO[std::pair<video::E_VERTEX_TYPE, InstanceType>(vt, it)]; }
+    ~VAOManager();
+};
 
 void draw3DLine(const core::vector3df& start,
     const core::vector3df& end, irr::video::SColor color);
@@ -264,4 +261,7 @@ void draw2DImage(const irr::video::ITexture* texture, const irr::core::rect<s32>
 
 void GL32_draw2DRectangle(irr::video::SColor color, const irr::core::rect<s32>& position,
     const irr::core::rect<s32>* clip = 0);
+
+bool hasGLExtension(const char* extension);
+
 #endif
diff --git a/src/graphics/gpuparticles.cpp b/src/graphics/gpuparticles.cpp
index 01831b68a..2c7656863 100644
--- a/src/graphics/gpuparticles.cpp
+++ b/src/graphics/gpuparticles.cpp
@@ -6,6 +6,7 @@
 #include <IParticleSystemSceneNode.h>
 #include "guiengine/engine.hpp"
 #include "graphics/particle_emitter.hpp"
+#include "../../lib/irrlicht/source/Irrlicht/os.h"
 #define COMPONENTCOUNT 8
 
 scene::IParticleSystemSceneNode *ParticleSystemProxy::addParticleNode(
@@ -88,7 +89,7 @@ void ParticleSystemProxy::setHeightmap(const std::vector<std::vector<float> > &h
     has_height_map = true;
     glGenBuffers(1, &heighmapbuffer);
     glBindBuffer(GL_TEXTURE_BUFFER, heighmapbuffer);
-    glBufferData(GL_TEXTURE_BUFFER, width * height * sizeof(float), hm_array, GL_STATIC_DRAW);
+    glBufferData(GL_TEXTURE_BUFFER, width * height * sizeof(float), hm_array, GL_STREAM_COPY);
     glGenTextures(1, &heightmaptexture);
     glBindTexture(GL_TEXTURE_BUFFER, heightmaptexture);
     glTexBuffer(GL_TEXTURE_BUFFER, GL_R32F, heighmapbuffer);
@@ -348,9 +349,7 @@ void ParticleSystemProxy::drawFlip()
     glBlendFunc(GL_ONE, GL_ONE);
     glUseProgram(ParticleShader::FlipParticleRender::getInstance()->Program);
 
-    setTexture(ParticleShader::FlipParticleRender::getInstance()->TU_tex, texture, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR);
-    setTexture(ParticleShader::FlipParticleRender::getInstance()->TU_dtex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
-
+    ParticleShader::FlipParticleRender::getInstance()->SetTextureUnits(std::vector<GLuint>{ texture, irr_driver->getDepthStencilTexture() });
     ParticleShader::FlipParticleRender::getInstance()->setUniforms();
 
     glBindVertexArray(current_rendering_vao);
@@ -365,8 +364,7 @@ void ParticleSystemProxy::drawNotFlip()
         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
     glUseProgram(ParticleShader::SimpleParticleRender::getInstance()->Program);
 
-    setTexture(ParticleShader::SimpleParticleRender::getInstance()->TU_tex, texture, GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR);
-    setTexture(ParticleShader::SimpleParticleRender::getInstance()->TU_dtex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
+    ParticleShader::SimpleParticleRender::getInstance()->SetTextureUnits(std::vector<GLuint>{ texture, irr_driver->getDepthStencilTexture() });
     video::SColorf ColorFrom = video::SColorf(getColorFrom()[0], getColorFrom()[1], getColorFrom()[2]);
     video::SColorf ColorTo = video::SColorf(getColorTo()[0], getColorTo()[1], getColorTo()[2]);
 
@@ -389,12 +387,12 @@ void ParticleSystemProxy::generateVAOs()
     glBindVertexArray(0);
     glGenBuffers(1, &initial_values_buffer);
     glBindBuffer(GL_ARRAY_BUFFER, initial_values_buffer);
-    glBufferData(GL_ARRAY_BUFFER, count * sizeof(ParticleData), ParticleParams, GL_STATIC_DRAW);
+    glBufferData(GL_ARRAY_BUFFER, count * sizeof(ParticleData), ParticleParams, GL_STREAM_COPY);
     glGenBuffers(2, tfb_buffers);
     glBindBuffer(GL_ARRAY_BUFFER, tfb_buffers[0]);
-    glBufferData(GL_ARRAY_BUFFER, count * sizeof(ParticleData), InitialValues, GL_STATIC_DRAW);
+    glBufferData(GL_ARRAY_BUFFER, count * sizeof(ParticleData), InitialValues, GL_STREAM_COPY);
     glBindBuffer(GL_ARRAY_BUFFER, tfb_buffers[1]);
-    glBufferData(GL_ARRAY_BUFFER, count * sizeof(ParticleData), 0, GL_STATIC_DRAW);
+    glBufferData(GL_ARRAY_BUFFER, count * sizeof(ParticleData), 0, GL_STREAM_COPY);
     glBindBuffer(GL_ARRAY_BUFFER, 0);
 
     glGenVertexArrays(1, &current_rendering_vao);
@@ -423,7 +421,7 @@ void ParticleSystemProxy::generateVAOs()
         }
         glGenBuffers(1, &quaternionsbuffer);
         glBindBuffer(GL_ARRAY_BUFFER, quaternionsbuffer);
-        glBufferData(GL_ARRAY_BUFFER, 4 * count * sizeof(float), quaternions, GL_STATIC_DRAW);
+        glBufferData(GL_ARRAY_BUFFER, 4 * count * sizeof(float), quaternions, GL_STREAM_COPY);
         delete[] quaternions;
     }
 
@@ -452,6 +450,12 @@ void ParticleSystemProxy::render() {
     draw();
 }
 
+bool ParticleSystemProxy::update()
+{
+    doParticleSystem(os::Timer::getTime());
+    return (IsVisible && (Particles.size() != 0));
+}
+
 void ParticleSystemProxy::OnRegisterSceneNode()
 {
     doParticleSystem(os::Timer::getTime());
diff --git a/src/graphics/gpuparticles.hpp b/src/graphics/gpuparticles.hpp
index 84fd72fab..94ca75f81 100644
--- a/src/graphics/gpuparticles.hpp
+++ b/src/graphics/gpuparticles.hpp
@@ -81,6 +81,7 @@ public:
     const float* getColorTo() const { return m_color_to; }
     void setHeightmap(const std::vector<std::vector<float> >&, float, float, float, float);
     void setFlip();
+    bool update();
 };
 
 #endif // GPUPARTICLES_H
diff --git a/src/graphics/irr_driver.cpp b/src/graphics/irr_driver.cpp
index 756280e98..15d911208 100644
--- a/src/graphics/irr_driver.cpp
+++ b/src/graphics/irr_driver.cpp
@@ -112,6 +112,10 @@ IrrDriver::IrrDriver()
     m_mipviz = m_wireframe = m_normals = m_ssaoviz = \
         m_lightviz = m_shadowviz = m_distortviz = m_rsm = m_rh = m_gi = false;
     SkyboxCubeMap = m_last_light_bucket_distance = 0;
+    m_shadow_camnodes[0] = NULL;
+    m_shadow_camnodes[1] = NULL;
+    m_shadow_camnodes[2] = NULL;
+    m_shadow_camnodes[3] = NULL;
     memset(object_count, 0, sizeof(object_count));
 }   // IrrDriver
 
@@ -166,6 +170,11 @@ void IrrDriver::IncreaseObjectCount()
     object_count[m_phase]++;
 }
 
+void IrrDriver::IncreasePolyCount(unsigned Polys)
+{
+    poly_count[m_phase] += Polys;
+}
+
 core::array<video::IRenderTarget> &IrrDriver::getMainSetup()
 {
   return m_mrt;
@@ -328,8 +337,8 @@ void IrrDriver::initDevice()
 
         } // end if firstTime
 
-        const core::dimension2d<u32> ssize = m_device->getVideoModeList()
-                                                     ->getDesktopResolution();
+        video::IVideoModeList* modes = m_device->getVideoModeList();
+        const core::dimension2d<u32> ssize = modes->getDesktopResolution();
         if (UserConfigParams::m_width > (int)ssize.Width ||
             UserConfigParams::m_height > (int)ssize.Height)
         {
@@ -339,11 +348,14 @@ void IrrDriver::initDevice()
             UserConfigParams::m_height = (int)ssize.Height;
         }
 
-        core::dimension2d<u32> res = core::dimension2du(UserConfigParams::m_width, UserConfigParams::m_height);
-        res = m_device->getVideoModeList()->getVideoModeResolution(res, res);
-
-        if (res.Width > 0 && res.Height > 0)
+        core::dimension2d<u32> res = core::dimension2du(UserConfigParams::m_width, 
+                                                    UserConfigParams::m_height);
+        
+        
+        if (modes->getVideoModeCount() > 0)
         {
+            res = modes->getVideoModeResolution(res, res);
+
             UserConfigParams::m_width = res.Width;
             UserConfigParams::m_height = res.Height;
         }
@@ -467,25 +479,57 @@ void IrrDriver::initDevice()
 
         m_need_ubo_workaround = false;
         m_need_rh_workaround = false;
+        m_need_srgb_workaround = false;
 #ifdef WIN32
         // Fix for Intel Sandy Bridge on Windows which supports GL up to 3.1 only
-        if (strstr((const char *)glGetString(GL_VENDOR), "Intel") != nullptr && (GLMajorVersion == 3 && GLMinorVersion == 1))
+        if (strstr((const char *)glGetString(GL_VENDOR), "Intel") != NULL && (GLMajorVersion == 3 && GLMinorVersion == 1))
             m_need_ubo_workaround = true;
 #endif
         // Fix for Nvidia and instanced RH
-        if (strstr((const char *)glGetString(GL_VENDOR), "NVIDIA") != nullptr)
+        if (strstr((const char *)glGetString(GL_VENDOR), "NVIDIA") != NULL)
             m_need_rh_workaround = true;
+
+        // Fix for AMD and bindless sRGB textures
+        if (strstr((const char *)glGetString(GL_VENDOR), "ATI") != NULL)
+            m_need_srgb_workaround = true;
     }
     m_glsl = (GLMajorVersion > 3 || (GLMajorVersion == 3 && GLMinorVersion >= 1));
+    initGL();
 
     // Parse extensions
     hasVSLayer = false;
+    hasBaseInstance = false;
+    hasBuffserStorage = false;
+    hasDrawIndirect = false;
+    hasComputeShaders = false;
+    hasTextureStorage = false;
     // Default false value for hasVSLayer if --no-graphics argument is used
     if (!ProfileWorld::isNoGraphics())
     {
-        const GLubyte *extensions = glGetString(GL_EXTENSIONS);
-        if (extensions && strstr((const char*)extensions, "GL_AMD_vertex_shader_layer") != NULL)
-        hasVSLayer = true;
+        if (GLEW_AMD_vertex_shader_layer) {
+            hasVSLayer = true;
+            Log::info("GLDriver", "AMD Vertex Shader Layer enabled");
+        }
+        if (GLEW_ARB_buffer_storage) {
+            hasBuffserStorage = true;
+            Log::info("GLDriver", "ARB Buffer Storage enabled");
+        }
+        if (GLEW_ARB_base_instance) {
+            hasBaseInstance = true;
+            Log::info("GLDriver", "ARB Base Instance enabled");
+        }
+        if (GLEW_ARB_draw_indirect) {
+            hasDrawIndirect = true;
+            Log::info("GLDriver", "ARB Draw Indirect enabled");
+        }
+        if (GLEW_ARB_compute_shader) {
+            hasComputeShaders = true;
+            Log::info("GLDriver", "ARB Compute Shader enabled");
+        }
+        if (GLEW_ARB_texture_storage) {
+            hasTextureStorage = true;
+            Log::info("GLDriver", "ARB Texture Storage enabled");
+        }
     }
 
 
@@ -513,8 +557,7 @@ void IrrDriver::initDevice()
         m_mrt.clear();
         m_mrt.reallocate(2);
 
-        irr::video::COpenGLDriver*    gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver();
-        gl_driver->extGlGenQueries(1, &m_lensflare_query);
+        glGenQueries(1, &m_lensflare_query);
         m_query_issued = false;
 
         scene::IMesh * const sphere = m_scene_manager->getGeometryCreator()->createSphereMesh(1, 16, 16);
@@ -733,7 +776,7 @@ void IrrDriver::applyResolutionSettings()
     // FIXME: this load sequence is (mostly) duplicated from main.cpp!!
     // That's just error prone
     // (we're sure to update main.cpp at some point and forget this one...)
-
+    m_shaders->killShaders();
     // initDevice will drop the current device.
     initDevice();
 
@@ -1601,6 +1644,7 @@ video::ITexture* IrrDriver::applyMask(video::ITexture* texture,
 // ----------------------------------------------------------------------------
 void IrrDriver::setRTT(RTT* rtt)
 {
+    memset(m_shadow_camnodes, 0, 4 * sizeof(void*));
     m_rtts = rtt;
 }
 // ----------------------------------------------------------------------------
@@ -1703,13 +1747,14 @@ void IrrDriver::displayFPS()
     if (UserConfigParams::m_artist_debug_mode)
     {
         sprintf(
-            buffer, "FPS: %i/%i/%i - Objects (P1:%d P2:%d T:%d) - LightDst : ~%d",
+            buffer, "FPS: %i/%i/%i - PolyCount (Solid:%d Shadows:%d) - LightDst : ~%d",
             min, fps, max,
-            object_count[SOLID_NORMAL_AND_DEPTH_PASS],
-            object_count[SOLID_NORMAL_AND_DEPTH_PASS],
-            object_count[TRANSPARENT_PASS],
+            poly_count[SOLID_NORMAL_AND_DEPTH_PASS],
+            poly_count[SHADOW_PASS],
             m_last_light_bucket_distance
         );
+        poly_count[SOLID_NORMAL_AND_DEPTH_PASS] = 0;
+        poly_count[SHADOW_PASS] = 0;
         object_count[SOLID_NORMAL_AND_DEPTH_PASS] = 0;
         object_count[SOLID_NORMAL_AND_DEPTH_PASS] = 0;
         object_count[TRANSPARENT_PASS] = 0;
@@ -2414,8 +2459,8 @@ scene::ISceneNode *IrrDriver::addLight(const core::vector3df &pos, float energy,
 
         if (sun)
         {
-            m_sun_interposer->setPosition(pos);
-            m_sun_interposer->updateAbsolutePosition();
+            //m_sun_interposer->setPosition(pos);
+            //m_sun_interposer->updateAbsolutePosition();
 
             m_lensflare->setPosition(pos);
             m_lensflare->updateAbsolutePosition();
diff --git a/src/graphics/irr_driver.hpp b/src/graphics/irr_driver.hpp
index eb2cc3f61..f324b1dcd 100644
--- a/src/graphics/irr_driver.hpp
+++ b/src/graphics/irr_driver.hpp
@@ -78,9 +78,10 @@ enum TypeFBO
 {
     FBO_SSAO,
     FBO_NORMAL_AND_DEPTHS,
-    FBO_COMBINED_TMP1_TMP2,
+    FBO_COMBINED_DIFFUSE_SPECULAR,
     FBO_COLORS,
-    FBO_LOG_LUMINANCE,
+    FBO_DIFFUSE,
+    FBO_SPECULAR,
     FBO_MLAA_COLORS,
     FBO_MLAA_BLEND,
     FBO_MLAA_TMP,
@@ -111,6 +112,7 @@ enum QueryPerf
 {
     Q_SOLID_PASS1,
     Q_SHADOWS,
+    Q_RSM,
     Q_RH,
     Q_GI,
     Q_ENVMAP,
@@ -140,7 +142,9 @@ enum TypeRTT
     RTT_LINEAR_DEPTH,
     RTT_NORMAL_AND_DEPTH,
     RTT_COLOR,
-    RTT_LOG_LUMINANCE,
+    RTT_DIFFUSE,
+    RTT_SPECULAR,
+
 
     RTT_HALF1,
     RTT_HALF2,
@@ -196,8 +200,15 @@ class IrrDriver : public IEventReceiver, public NoCopy
 private:
     int GLMajorVersion, GLMinorVersion;
     bool hasVSLayer;
+    bool hasBaseInstance;
+    bool hasDrawIndirect;
+    bool hasBuffserStorage;
+    bool hasComputeShaders;
+    bool hasTextureStorage;
     bool m_need_ubo_workaround;
     bool m_need_rh_workaround;
+    bool m_need_srgb_workaround;
+    GLsync m_sync;
     /** The irrlicht device. */
     IrrlichtDevice             *m_device;
     /** Irrlicht scene manager. */
@@ -290,11 +301,41 @@ public:
         return m_need_rh_workaround;
     }
 
+    bool needsRGBBindlessWorkaround() const
+    {
+        return m_need_srgb_workaround;
+    }
+
+    bool hasARB_base_instance() const
+    {
+        return hasBaseInstance;
+    }
+
+    bool hasARB_draw_indirect() const
+    {
+        return hasDrawIndirect;
+    }
+
     bool hasVSLayerExtension() const
     {
         return hasVSLayer;
     }
 
+    bool hasBufferStorageExtension() const
+    {
+        return hasBuffserStorage;
+    }
+
+    bool hasARBComputeShaders() const
+    {
+        return hasComputeShaders;
+    }
+
+    bool hasARBTextureStorage() const
+    {
+        return hasTextureStorage;
+    }
+
     video::SColorf getAmbientLight() const;
 
     struct GlowData {
@@ -332,12 +373,14 @@ private:
     /** Performance stats */
     unsigned             m_last_light_bucket_distance;
     unsigned             object_count[PASS_COUNT];
+    unsigned             poly_count[PASS_COUNT];
     u32                  m_renderpass;
     u32                  m_lensflare_query;
     bool                 m_query_issued;
     class STKMeshSceneNode *m_sun_interposer;
     scene::CLensFlareSceneNode *m_lensflare;
     scene::ICameraSceneNode *m_suncam;
+    scene::ICameraSceneNode *m_shadow_camnodes[4];
     float m_shadows_cam[4][24];
 
     std::vector<GlowData> m_glowing;
@@ -379,6 +422,7 @@ private:
     void renderLights(unsigned pointlightCount);
     void renderShadowsDebug();
     void doScreenShot();
+    void PrepareDrawCalls(scene::ICameraSceneNode *camnode);
 public:
          IrrDriver();
         ~IrrDriver();
@@ -394,6 +438,7 @@ public:
         return sun_ortho_matrix;
     }
     void IncreaseObjectCount();
+    void IncreasePolyCount(unsigned);
     core::array<video::IRenderTarget> &getMainSetup();
     void updateConfigIfRelevant();
     void setAllMaterialFlags(scene::IMesh *mesh) const;
@@ -570,7 +615,7 @@ public:
     // -----------------------------------------------------------------------
     inline video::E_MATERIAL_TYPE getShader(const ShaderType num)  {return m_shaders->getShader(num);}
     // -----------------------------------------------------------------------
-    inline void updateShaders()  {m_shaders->loadShaders();}
+    inline void updateShaders()  {m_shaders->killShaders();}
     // ------------------------------------------------------------------------
     inline video::IShaderConstantSetCallBack* getCallback(const ShaderType num)
     {
diff --git a/src/graphics/light.hpp b/src/graphics/light.hpp
index 8af5d7009..1ecb3c119 100644
--- a/src/graphics/light.hpp
+++ b/src/graphics/light.hpp
@@ -59,6 +59,7 @@ public:
     float getEnergy() const { return m_energy; }
     float getEffectiveEnergy() const { return m_energy_multiplier * m_energy; }
     core::vector3df getColor() const { return core::vector3df(m_color[0], m_color[1], m_color[2]); }
+    void setColor(float r, float g, float b) { m_color[0] = r; m_color[1] = g; m_color[2] = b; }
 
     float getEnergyMultiplier() const { return m_energy_multiplier; }
     void setEnergyMultiplier(float newval) { m_energy_multiplier = newval; }
diff --git a/src/graphics/lod_node.cpp b/src/graphics/lod_node.cpp
index dcf165d70..6a4e0ddf7 100644
--- a/src/graphics/lod_node.cpp
+++ b/src/graphics/lod_node.cpp
@@ -137,19 +137,24 @@ void LODNode::OnAnimate(u32 timeMs)
     }
 }
 
-void LODNode::OnRegisterSceneNode()
+void LODNode::updateVisibility(bool* shown)
 {
     if (!isVisible()) return;
     if (m_nodes.size() == 0) return;
 
-    bool shown = false;
     int level = getLevel();
-    if (level>=0)
+    for (size_t i = 0; i < m_nodes.size(); i++)
     {
-        m_nodes[level]->updateAbsolutePosition();
-        m_nodes[level]->OnRegisterSceneNode();
-        shown = true;
+        m_nodes[i]->setVisible(i == level);
+        if (i == level && shown != NULL)
+            *shown = (i > 0);
     }
+}
+
+void LODNode::OnRegisterSceneNode()
+{
+    bool shown;
+    updateVisibility(&shown);
 
     const u32 now = irr_driver->getDevice()->getTimer()->getTime();
 
@@ -158,6 +163,7 @@ void LODNode::OnRegisterSceneNode()
                                 m_nodes[0]->getType() == scene::ESNT_ANIMATED_MESH) &&
         now > m_last_tick)
     {
+        int level = getLevel();
         if (m_previous_visibility == WAS_HIDDEN && shown)
         {
             scene::IMesh* mesh;
@@ -254,20 +260,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<ISceneNode*>::Iterator it;
-
-    for (it = Children.begin(); it != Children.end(); it++)
-    {
-        if (m_nodes_set.find(*it) == m_nodes_set.end())
-        {
-            assert(*it != NULL);
-            if ((*it)->isVisible())
-            {
-                (*it)->OnRegisterSceneNode();
-            }
-        }
-    }
+    scene::ISceneNode::OnRegisterSceneNode();
 }
 
 void LODNode::add(int level, scene::ISceneNode* node, bool reparent)
diff --git a/src/graphics/lod_node.hpp b/src/graphics/lod_node.hpp
index 3cca1bc28..8c349eb67 100644
--- a/src/graphics/lod_node.hpp
+++ b/src/graphics/lod_node.hpp
@@ -83,6 +83,8 @@ public:
 
     int getLevel();
 
+    void updateVisibility(bool* shown = NULL);
+
     /*
     //! Returns a reference to the current relative transformation matrix.
     //! This is the matrix, this scene node uses instead of scale, translation
diff --git a/src/graphics/particle_emitter.cpp b/src/graphics/particle_emitter.cpp
index 266bdda45..201921afe 100644
--- a/src/graphics/particle_emitter.cpp
+++ b/src/graphics/particle_emitter.cpp
@@ -450,7 +450,7 @@ void ParticleEmitter::setParticleType(const ParticleKind* type)
         else
         {
             if (m_is_glsl)
-                m_node = ParticleSystemProxy::addParticleNode(m_is_glsl, true);
+                m_node = ParticleSystemProxy::addParticleNode(m_is_glsl, false);
             else
                 m_node = irr_driver->addParticleNode();
             
diff --git a/src/graphics/post_processing.cpp b/src/graphics/post_processing.cpp
index 354e01583..0075fa354 100644
--- a/src/graphics/post_processing.cpp
+++ b/src/graphics/post_processing.cpp
@@ -33,6 +33,7 @@
 #include "tracks/track.hpp"
 #include "utils/log.hpp"
 #include "utils/profiler.hpp"
+#include "utils/cpp2011.hpp"
 
 #include <SViewFrustum.h>
 
@@ -216,7 +217,7 @@ static void DrawFullScreenEffect(Args...args)
 static
 void renderBloom(GLuint in)
 {
-    setTexture(FullScreenShader::BloomShader::getInstance()->TU_tex, in, GL_NEAREST, GL_NEAREST);
+    FullScreenShader::BloomShader::getInstance()->SetTextureUnits(createVector<GLuint>(in));
     DrawFullScreenEffect<FullScreenShader::BloomShader>();
 }
 
@@ -227,12 +228,12 @@ void PostProcessing::renderDiffuseEnvMap(const float *bSHCoeff, const float *gSH
     glBlendEquation(GL_FUNC_ADD);
     glBlendFunc(GL_ONE, GL_ONE);
 
-    glUseProgram(FullScreenShader::DiffuseEnvMapShader::Program);
+    glUseProgram(FullScreenShader::DiffuseEnvMapShader::getInstance()->Program);
     glBindVertexArray(SharedObject::FullScreenQuadVAO);
 
-    setTexture(0, irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), GL_NEAREST, GL_NEAREST);
+    FullScreenShader::DiffuseEnvMapShader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH)));
     core::matrix4 TVM = irr_driver->getViewMatrix().getTransposed();
-    FullScreenShader::DiffuseEnvMapShader::setUniforms(TVM, bSHCoeff, gSHCoeff, rSHCoeff, 0);
+    FullScreenShader::DiffuseEnvMapShader::getInstance()->setUniforms(TVM, std::vector<float>(bSHCoeff, bSHCoeff + 9), std::vector<float>(gSHCoeff, gSHCoeff + 9), std::vector<float>(rSHCoeff, rSHCoeff + 9));
 
     glDrawArrays(GL_TRIANGLES, 0, 3);
     glBindVertexArray(0);
@@ -263,26 +264,8 @@ void PostProcessing::renderGI(const core::matrix4 &RHMatrix, const core::vector3
     core::matrix4 InvRHMatrix;
     RHMatrix.getInverse(InvRHMatrix);
     glDisable(GL_DEPTH_TEST);
-    glActiveTexture(GL_TEXTURE0 + FullScreenShader::GlobalIlluminationReconstructionShader::getInstance()->TU_SHR);
-    glBindTexture(GL_TEXTURE_3D, shr);
-    {
-        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    }
-    glActiveTexture(GL_TEXTURE0 + FullScreenShader::GlobalIlluminationReconstructionShader::getInstance()->TU_SHG);
-    glBindTexture(GL_TEXTURE_3D, shg);
-    {
-        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    }
-    glActiveTexture(GL_TEXTURE0 + FullScreenShader::GlobalIlluminationReconstructionShader::getInstance()->TU_SHB);
-    glBindTexture(GL_TEXTURE_3D, shb);
-    {
-        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    }
-    setTexture(FullScreenShader::GlobalIlluminationReconstructionShader::getInstance()->TU_ntex, irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), GL_NEAREST, GL_NEAREST);
-    setTexture(FullScreenShader::GlobalIlluminationReconstructionShader::getInstance()->TU_dtex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
+    FullScreenShader::GlobalIlluminationReconstructionShader::getInstance()->SetTextureUnits(createVector<GLuint>(
+        irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), shr, shg, shb));
     DrawFullScreenEffect<FullScreenShader::GlobalIlluminationReconstructionShader>(RHMatrix, InvRHMatrix, rh_extend);
 }
 
@@ -295,8 +278,7 @@ void PostProcessing::renderSunlight()
   glBlendFunc(GL_ONE, GL_ONE);
   glBlendEquation(GL_FUNC_ADD);
 
-  setTexture(FullScreenShader::SunLightShader::getInstance()->TU_ntex, irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), GL_NEAREST, GL_NEAREST);
-  setTexture(FullScreenShader::SunLightShader::getInstance()->TU_dtex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
+  FullScreenShader::SunLightShader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture()));
   DrawFullScreenEffect<FullScreenShader::SunLightShader>(cb->getPosition(), video::SColorf(cb->getRed(), cb->getGreen(), cb->getBlue()));
 }
 
@@ -309,17 +291,7 @@ void PostProcessing::renderShadowedSunlight(const std::vector<core::matrix4> &su
     glBlendFunc(GL_ONE, GL_ONE);
     glBlendEquation(GL_FUNC_ADD);
 
-    setTexture(FullScreenShader::ShadowedSunLightShader::getInstance()->TU_ntex, irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), GL_NEAREST, GL_NEAREST);
-    setTexture(FullScreenShader::ShadowedSunLightShader::getInstance()->TU_dtex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
-    glActiveTexture(GL_TEXTURE0 + FullScreenShader::ShadowedSunLightShader::getInstance()->TU_shadowtex);
-    glBindTexture(GL_TEXTURE_2D_ARRAY, depthtex);
-    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
-    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
-    glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
-    glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
-
+    FullScreenShader::ShadowedSunLightShader::getInstance()->SetTextureUnits(createVector<GLuint>( irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture(), depthtex ));
     DrawFullScreenEffect<FullScreenShader::ShadowedSunLightShader>(cb->getPosition(), video::SColorf(cb->getRed(), cb->getGreen(), cb->getBlue()));
 }
 
@@ -331,17 +303,13 @@ void PostProcessing::renderGaussian3Blur(FrameBuffer &in_fbo, FrameBuffer &auxil
     {
         auxiliary.Bind();
 
-        setTexture(FullScreenShader::Gaussian3VBlurShader::getInstance()->TU_tex, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        FullScreenShader::Gaussian3VBlurShader::getInstance()->SetTextureUnits(createVector<GLuint>(in_fbo.getRTT()[0]));
         DrawFullScreenEffect<FullScreenShader::Gaussian3VBlurShader>(core::vector2df(inv_width, inv_height));
     }
     {
         in_fbo.Bind();
 
-        setTexture(FullScreenShader::Gaussian3HBlurShader::getInstance()->TU_tex, auxiliary.getRTT()[0], GL_LINEAR, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        FullScreenShader::Gaussian3HBlurShader::getInstance()->SetTextureUnits(createVector<GLuint>(auxiliary.getRTT()[0]));
         DrawFullScreenEffect<FullScreenShader::Gaussian3HBlurShader>(core::vector2df(inv_width, inv_height));
     }
 }
@@ -353,17 +321,13 @@ void PostProcessing::renderGaussian6Blur(FrameBuffer &in_fbo, FrameBuffer &auxil
     {
         auxiliary.Bind();
 
-        setTexture(FullScreenShader::Gaussian6VBlurShader::getInstance()->TU_tex, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        FullScreenShader::Gaussian6VBlurShader::getInstance()->SetTextureUnits(createVector<GLuint>(in_fbo.getRTT()[0]));
         DrawFullScreenEffect<FullScreenShader::Gaussian6VBlurShader>(core::vector2df(inv_width, inv_height));
     }
     {
         in_fbo.Bind();
 
-        setTexture(FullScreenShader::Gaussian6HBlurShader::getInstance()->TU_tex, auxiliary.getRTT()[0], GL_LINEAR, GL_LINEAR);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        FullScreenShader::Gaussian6HBlurShader::getInstance()->SetTextureUnits(createVector<GLuint>(auxiliary.getRTT()[0]));
         DrawFullScreenEffect<FullScreenShader::Gaussian6HBlurShader>(core::vector2df(inv_width, inv_height));
     }
 }
@@ -373,21 +337,12 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
     assert(in_fbo.getWidth() == auxiliary.getWidth() && in_fbo.getHeight() == auxiliary.getHeight());
     float inv_width = 1.0f / in_fbo.getWidth(), inv_height = 1.0f / in_fbo.getHeight();
     {
-#if WIN32
-        if (irr_driver->getGLSLVersion() < 430)
-#endif
+        if (irr_driver->hasARBComputeShaders())
         {
             auxiliary.Bind();
-
-            setTexture(FullScreenShader::Gaussian17TapHShader::getInstance()->TU_tex, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-            setTexture(FullScreenShader::Gaussian17TapHShader::getInstance()->TU_depth, irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0], GL_LINEAR, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            FullScreenShader::Gaussian17TapHShader::getInstance()->SetTextureUnits(createVector<GLuint>(in_fbo.getRTT()[0], irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0]));
             DrawFullScreenEffect<FullScreenShader::Gaussian17TapHShader>(core::vector2df(inv_width, inv_height));
         }
-#if WIN32
         else
         {
 
@@ -398,24 +353,15 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
             FullScreenShader::ComputeGaussian17TapHShader::getInstance()->setUniforms();
             glDispatchCompute(in_fbo.getWidth() / 8, in_fbo.getHeight() / 8, 1);
         }
-#endif
     }
     {
-#if WIN32
-        if (irr_driver->getGLSLVersion() < 430)
-#endif
+        if (irr_driver->hasARBComputeShaders())
         {
             in_fbo.Bind();
 
-            setTexture(FullScreenShader::Gaussian17TapVShader::getInstance()->TU_tex, auxiliary.getRTT()[0], GL_LINEAR, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-            setTexture(FullScreenShader::Gaussian17TapVShader::getInstance()->TU_depth, irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0], GL_LINEAR, GL_LINEAR);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+            FullScreenShader::Gaussian17TapVShader::getInstance()->SetTextureUnits(createVector<GLuint>(auxiliary.getRTT()[0], irr_driver->getFBO(FBO_LINEAR_DEPTH).getRTT()[0]));
             DrawFullScreenEffect<FullScreenShader::Gaussian17TapVShader>(core::vector2df(inv_width, inv_height));
         }
-#if WIN32
         else
         {
             glUseProgram(FullScreenShader::ComputeGaussian17TapVShader::getInstance()->Program);
@@ -425,7 +371,6 @@ void PostProcessing::renderGaussian17TapBlur(FrameBuffer &in_fbo, FrameBuffer &a
             FullScreenShader::ComputeGaussian17TapVShader::getInstance()->setUniforms();
             glDispatchCompute(in_fbo.getWidth() / 8, in_fbo.getHeight() / 8, 1);
         }
-#endif
     }
 }
 
@@ -434,7 +379,7 @@ void PostProcessing::renderPassThrough(GLuint tex)
     glUseProgram(FullScreenShader::PassThroughShader::getInstance()->Program);
     glBindVertexArray(FullScreenShader::PassThroughShader::getInstance()->vao);
 
-    setTexture(FullScreenShader::PassThroughShader::getInstance()->TU_tex, tex, GL_LINEAR, GL_LINEAR);
+    FullScreenShader::PassThroughShader::getInstance()->SetTextureUnits(createVector<GLuint>(tex));
     FullScreenShader::PassThroughShader::getInstance()->setUniforms();
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
@@ -459,7 +404,7 @@ void PostProcessing::renderGlow(unsigned tex)
     glUseProgram(FullScreenShader::GlowShader::getInstance()->Program);
     glBindVertexArray(FullScreenShader::GlowShader::getInstance()->vao);
 
-    setTexture(FullScreenShader::GlowShader::getInstance()->TU_tex, tex, GL_LINEAR, GL_LINEAR);
+    FullScreenShader::GlowShader::getInstance()->SetTextureUnits(createVector<GLuint>(tex));
     FullScreenShader::GlowShader::getInstance()->setUniforms();
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
@@ -473,11 +418,11 @@ void PostProcessing::renderSSAO()
 
     // Generate linear depth buffer
     irr_driver->getFBO(FBO_LINEAR_DEPTH).Bind();
-    setTexture(FullScreenShader::LinearizeDepthShader::getInstance()->TU_tex, irr_driver->getDepthStencilTexture(), GL_LINEAR, GL_LINEAR);
+    FullScreenShader::LinearizeDepthShader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getDepthStencilTexture()));
     DrawFullScreenEffect<FullScreenShader::LinearizeDepthShader>(irr_driver->getSceneManager()->getActiveCamera()->getNearValue(), irr_driver->getSceneManager()->getActiveCamera()->getFarValue());
     irr_driver->getFBO(FBO_SSAO).Bind();
 
-    setTexture(FullScreenShader::SSAOShader::getInstance()->TU_dtex, irr_driver->getRenderTargetTexture(RTT_LINEAR_DEPTH), GL_LINEAR, GL_LINEAR_MIPMAP_NEAREST);
+    FullScreenShader::SSAOShader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_LINEAR_DEPTH)));
     glGenerateMipmap(GL_TEXTURE_2D);
 
     DrawFullScreenEffect<FullScreenShader::SSAOShader>(irr_driver->getSSAORadius(), irr_driver->getSSAOK(), irr_driver->getSSAOSigma());
@@ -505,8 +450,8 @@ void PostProcessing::renderFog()
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
 
-    setTexture(FullScreenShader::FogShader::getInstance()->TU_tex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
-   DrawFullScreenEffect<FullScreenShader::FogShader>(fogmax, startH, endH, start, end, col);
+    FullScreenShader::FogShader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getDepthStencilTexture()));
+    DrawFullScreenEffect<FullScreenShader::FogShader>(fogmax, startH, endH, start, end, col);
 
     glEnable(GL_DEPTH_TEST);
     glDisable(GL_BLEND);
@@ -533,10 +478,7 @@ void PostProcessing::renderMotionBlur(unsigned cam, FrameBuffer &in_fbo, FrameBu
     out_fbo.Bind();
     glClear(GL_COLOR_BUFFER_BIT);
 
-    setTexture(FullScreenShader::MotionBlurShader::getInstance()->TU_cb, in_fbo.getRTT()[0], GL_LINEAR, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    setTexture(FullScreenShader::MotionBlurShader::getInstance()->TU_dtex, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
+    FullScreenShader::MotionBlurShader::getInstance()->SetTextureUnits(createVector<GLuint>(in_fbo.getRTT()[0], irr_driver->getDepthStencilTexture()));
     DrawFullScreenEffect<FullScreenShader::MotionBlurShader>(
                                   // Todo : use a previousPVMatrix per cam, not global
                                   irr_driver->getPreviousPVMatrix(),
@@ -549,7 +491,7 @@ static void renderGodFade(GLuint tex, const SColor &col)
 {
     glUseProgram(FullScreenShader::GodFadeShader::getInstance()->Program);
     glBindVertexArray(FullScreenShader::GodFadeShader::getInstance()->vao);
-    setTexture(FullScreenShader::GodFadeShader::getInstance()->TU_tex, tex, GL_LINEAR, GL_LINEAR);
+    FullScreenShader::GodFadeShader::getInstance()->SetTextureUnits(createVector<GLuint>(tex));
     FullScreenShader::GodFadeShader::getInstance()->setUniforms(col);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
@@ -559,7 +501,7 @@ static void renderGodRay(GLuint tex, const core::vector2df &sunpos)
 {
     glUseProgram(FullScreenShader::GodRayShader::getInstance()->Program);
     glBindVertexArray(FullScreenShader::GodRayShader::getInstance()->vao);
-    setTexture(FullScreenShader::GodRayShader::getInstance()->TU_tex, tex, GL_LINEAR, GL_LINEAR);
+    FullScreenShader::GodRayShader::getInstance()->SetTextureUnits(createVector<GLuint>(tex));
     FullScreenShader::GodRayShader::getInstance()->setUniforms(sunpos);
 
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
@@ -568,15 +510,14 @@ static void renderGodRay(GLuint tex, const core::vector2df &sunpos)
 static void toneMap(FrameBuffer &fbo, GLuint rtt)
 {
     fbo.Bind();
-    setTexture(FullScreenShader::ToneMapShader::getInstance()->TU_tex, rtt, GL_NEAREST, GL_NEAREST);
+    FullScreenShader::ToneMapShader::getInstance()->SetTextureUnits(createVector<GLuint>(rtt));
     DrawFullScreenEffect<FullScreenShader::ToneMapShader>();
 }
 
 static void renderDoF(FrameBuffer &fbo, GLuint rtt)
 {
     fbo.Bind();
-    setTexture(FullScreenShader::DepthOfFieldShader::getInstance()->TU_tex, rtt, GL_LINEAR, GL_LINEAR);
-    setTexture(FullScreenShader::DepthOfFieldShader::getInstance()->TU_depth, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
+    FullScreenShader::DepthOfFieldShader::getInstance()->SetTextureUnits(createVector<GLuint>(rtt, irr_driver->getDepthStencilTexture()));
     DrawFullScreenEffect<FullScreenShader::DepthOfFieldShader>();
 }
 
@@ -592,8 +533,8 @@ void PostProcessing::applyMLAA()
     glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
 
     // Pass 1: color edge detection
-    setTexture(FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->TU_colorMapG, irr_driver->getRenderTargetTexture(RTT_MLAA_COLORS), GL_NEAREST, GL_NEAREST);
     glUseProgram(FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->Program);
+    FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_MLAA_COLORS)));
     FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->setUniforms(PIXEL_SIZE);
 
     glBindVertexArray(FullScreenShader::MLAAColorEdgeDetectionSHader::getInstance()->vao);
@@ -607,8 +548,7 @@ void PostProcessing::applyMLAA()
     glClear(GL_COLOR_BUFFER_BIT);
 
     glUseProgram(FullScreenShader::MLAABlendWeightSHader::getInstance()->Program);
-    setTexture(FullScreenShader::MLAABlendWeightSHader::getInstance()->TU_edgesMap, irr_driver->getRenderTargetTexture(RTT_MLAA_TMP), GL_LINEAR, GL_LINEAR);
-    setTexture(FullScreenShader::MLAABlendWeightSHader::getInstance()->TU_areaMap, getTextureGLuint(m_areamap), GL_NEAREST, GL_NEAREST);
+    FullScreenShader::MLAABlendWeightSHader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_MLAA_TMP), getTextureGLuint(m_areamap)));
     FullScreenShader::MLAABlendWeightSHader::getInstance()->setUniforms(PIXEL_SIZE);
 
     glBindVertexArray(FullScreenShader::MLAABlendWeightSHader::getInstance()->vao);
@@ -621,8 +561,7 @@ void PostProcessing::applyMLAA()
     irr_driver->getFBO(FBO_MLAA_COLORS).Bind();
 
     glUseProgram(FullScreenShader::MLAAGatherSHader::getInstance()->Program);
-    setTexture(FullScreenShader::MLAAGatherSHader::getInstance()->TU_colorMap, irr_driver->getRenderTargetTexture(RTT_MLAA_TMP), GL_NEAREST, GL_NEAREST);
-    setTexture(FullScreenShader::MLAAGatherSHader::getInstance()->TU_blendMap, irr_driver->getRenderTargetTexture(RTT_MLAA_BLEND), GL_NEAREST, GL_NEAREST);
+    FullScreenShader::MLAAGatherSHader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_MLAA_BLEND), irr_driver->getRenderTargetTexture(RTT_MLAA_TMP)));
     FullScreenShader::MLAAGatherSHader::getInstance()->setUniforms(PIXEL_SIZE);
 
     glBindVertexArray(FullScreenShader::MLAAGatherSHader::getInstance()->vao);
@@ -661,8 +600,10 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo
         if (World::getWorld() != NULL)
             hasgodrays = World::getWorld()->getTrack()->hasGodRays();
 
-        if (isRace && UserConfigParams::m_light_shaft && m_sunpixels > 30 && hasgodrays)
+        if (isRace && UserConfigParams::m_light_shaft && hasgodrays)
         {
+            Track* track = World::getWorld()->getTrack();
+
             glEnable(GL_DEPTH_TEST);
             // Grab the sky
             out_fbo->Bind();
@@ -670,12 +611,14 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo
             irr_driver->renderSkybox(camnode);
 
             // Set the sun's color
-            const SColor col = World::getWorld()->getTrack()->getSunColor();
+            const SColor col = track->getGodRaysColor();
             ColorizeProvider * const colcb = (ColorizeProvider *)irr_driver->getCallback(ES_COLORIZE);
             colcb->setColor(col.getRed() / 255.0f, col.getGreen() / 255.0f, col.getBlue() / 255.0f);
 
             // The sun interposer
             STKMeshSceneNode *sun = irr_driver->getSunInterposer();
+            sun->setPosition(track->getGodRaysPosition());
+            sun->updateAbsolutePosition();
             irr_driver->getSceneManager()->drawAll(ESNRP_CAMERA);
             irr_driver->setPhase(GLOW_PASS);
             sun->render();
@@ -690,7 +633,7 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo
             renderGaussian3Blur(irr_driver->getFBO(FBO_QUARTER1), irr_driver->getFBO(FBO_QUARTER2));
 
             // Calculate the sun's position in texcoords
-            const core::vector3df pos = sun->getPosition();
+            const core::vector3df pos = track->getGodRaysPosition();
             float ndc[4];
             core::matrix4 trans = camnode->getProjectionMatrix();
             trans *= camnode->getViewMatrix();
@@ -753,9 +696,8 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo
             glEnable(GL_BLEND);
             glBlendFunc(GL_ONE, GL_ONE);
             glBlendEquation(GL_FUNC_ADD);
-            setTexture(FullScreenShader::BloomBlendShader::getInstance()->TU_tex_128, irr_driver->getRenderTargetTexture(RTT_BLOOM_128), GL_LINEAR, GL_LINEAR);
-            setTexture(FullScreenShader::BloomBlendShader::getInstance()->TU_tex_256, irr_driver->getRenderTargetTexture(RTT_BLOOM_256), GL_LINEAR, GL_LINEAR);
-            setTexture(FullScreenShader::BloomBlendShader::getInstance()->TU_tex_512, irr_driver->getRenderTargetTexture(RTT_BLOOM_512), GL_LINEAR, GL_LINEAR);
+            FullScreenShader::BloomBlendShader::getInstance()->SetTextureUnits(createVector<GLuint>(
+                irr_driver->getRenderTargetTexture(RTT_BLOOM_128), irr_driver->getRenderTargetTexture(RTT_BLOOM_256), irr_driver->getRenderTargetTexture(RTT_BLOOM_512) ));
             DrawFullScreenEffect<FullScreenShader::BloomBlendShader>();
 
             glDisable(GL_BLEND);
@@ -784,6 +726,10 @@ FrameBuffer *PostProcessing::render(scene::ICameraSceneNode * const camnode, boo
         PROFILER_POP_CPU_MARKER();
     }
 
+    // Workaround a bug with srgb fbo on sandy bridge windows
+    if (irr_driver->needUBOWorkaround())
+        return in_fbo;
+
     glEnable(GL_FRAMEBUFFER_SRGB);
     irr_driver->getFBO(FBO_MLAA_COLORS).Bind();
     renderPassThrough(in_fbo->getRTT()[0]);
diff --git a/src/graphics/referee.cpp b/src/graphics/referee.cpp
index ced299263..8dc78b7e1 100644
--- a/src/graphics/referee.cpp
+++ b/src/graphics/referee.cpp
@@ -19,6 +19,7 @@
 #include "graphics/referee.hpp"
 
 #include "graphics/irr_driver.hpp"
+#include "graphics/light.hpp"
 #include "graphics/mesh_tools.hpp"
 #include "karts/abstract_kart.hpp"
 #include "io/file_manager.hpp"
@@ -36,7 +37,6 @@ Vec3                  Referee::m_st_start_offset       = Vec3(-2, 2, 2);
 Vec3                  Referee::m_st_start_rotation     = Vec3(0, 180, 0);
 Vec3                  Referee::m_st_scale              = Vec3(1, 1, 1);
 scene::IAnimatedMesh *Referee::m_st_referee_mesh       = NULL;
-video::ITexture      *Referee::m_st_traffic_lights[3]  = {NULL, NULL, NULL};
 
 // ----------------------------------------------------------------------------
 /** Loads the static mesh.
@@ -93,23 +93,6 @@ void Referee::init()
                         * RAD_TO_DEGREE;
     m_st_start_rotation.setY(m_st_start_rotation.getY()+angle_to_kart);
 
-    std::vector<std::string> colors;
-    node->get("colors", &colors);
-
-    if(colors.size()>3)
-        Log::warn("referee", "Too many colors for referee defined, "
-                             "only first three will be used.");
-    if(colors.size()<3)
-    {
-        Log::fatal("referee",
-                   "Not enough colors for referee defined, aborting.");
-    }
-    for(unsigned int i=0; i<3; i++)
-    {
-        m_st_traffic_lights[i] = irr_driver->getTexture(FileManager::MODEL, colors[i]);
-    }
-
-
     for(unsigned int i=0; i<m_st_referee_mesh->getMeshBufferCount(); i++)
     {
         scene::IMeshBuffer *mb = m_st_referee_mesh->getMeshBuffer(i);
@@ -119,7 +102,7 @@ void Referee::init()
 
         std::string name=StringUtils::getBasename(t->getName()
                                                   .getInternalName().c_str());
-        if(name==colors[0] || name==colors[1] ||name==colors[2] )
+        if (name == "traffic_light.png")
         {
             m_st_traffic_buffer = i;
             break;
@@ -166,6 +149,16 @@ Referee::Referee()
                                m_st_last_start_frame);
 
     irr_driver->applyObjectPassShader(m_scene_node);
+
+    if (irr_driver->isGLSL() && UserConfigParams::m_dynamic_lights)
+    {
+        m_light = irr_driver->addLight(core::vector3df(0.0f, 0.0f, 0.6f), 0.7f, 2.0f,
+            0.7f /* r */, 0.0 /* g */, 0.0f /* b */, false /* sun */, m_scene_node);
+    }
+    else
+    {
+        m_light = NULL;
+    }
 }   // Referee
 
 // ----------------------------------------------------------------------------
@@ -212,6 +205,9 @@ void Referee::attachToSceneNode()
     if(!m_scene_node->getParent())
         m_scene_node->setParent(irr_driver->getSceneManager()
                                           ->getRootSceneNode());
+
+    if (m_light != NULL)
+        m_light->setVisible(true);
 }   // attachToSceneNode
 
 // ----------------------------------------------------------------------------
@@ -223,6 +219,8 @@ void Referee::removeFromSceneGraph()
 {
     if(isAttached())
         irr_driver->removeNode(m_scene_node);
+    if (m_light != NULL)
+        m_light->setVisible(false);
 }   // removeFromSceneGraph
 
 // ----------------------------------------------------------------------------
@@ -232,9 +230,31 @@ void Referee::removeFromSceneGraph()
  */
 void Referee::selectReadySetGo(int rsg)
 {
-    if(m_st_traffic_buffer<0) return;
-    video::SMaterial &m = m_scene_node->getMesh()->getMeshBuffer(m_st_traffic_buffer)->getMaterial();
-    m.setTexture(0, m_st_traffic_lights[rsg]);
+    if (m_st_traffic_buffer < 0)
+        return;
+    video::SMaterial &m = m_scene_node->getMaterial(m_st_traffic_buffer); // m_scene_node->getMesh()->getMeshBuffer(m_st_traffic_buffer)->getMaterial();
+
+    //if (irr_driver->isGLSL() && UserConfigParams::m_dynamic_lights)
+    //    m.MaterialType = irr_driver->getShader(ES_OBJECT_UNLIT);
+
+    core::matrix4* matrix = &m.getTextureMatrix(0);
+    matrix->setTextureTranslate(0.0f, rsg*0.333f);
+
+    if (m_light != NULL)
+    {
+        if (rsg == 0)
+        {
+            ((LightNode*)m_light)->setColor(0.6f, 0.0f, 0.0f);
+        }
+        else if (rsg == 1)
+        {
+            ((LightNode*)m_light)->setColor(0.7f, 0.23f, 0.0f);
+        }
+        else if (rsg == 2)
+        {
+            ((LightNode*)m_light)->setColor(0.0f, 0.6f, 0.0f);
+        }
+    }
 
     // disable lighting, we need to see the traffic light even if facing away
     // from the sun
diff --git a/src/graphics/referee.hpp b/src/graphics/referee.hpp
index 93941f051..754d5ef86 100644
--- a/src/graphics/referee.hpp
+++ b/src/graphics/referee.hpp
@@ -79,6 +79,8 @@ private:
     /** The scene node for an instance of the referee. */
     scene::IAnimatedMeshSceneNode *m_scene_node;
 
+    scene::ISceneNode* m_light;
+
 public:
                 Referee();
                 Referee(const AbstractKart &kart);
diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp
index 28544dc33..ee510d776 100644
--- a/src/graphics/render.cpp
+++ b/src/graphics/render.cpp
@@ -34,7 +34,6 @@
 #include "graphics/screenquad.hpp"
 #include "graphics/shaders.hpp"
 #include "graphics/stkmeshscenenode.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "graphics/wind.hpp"
 #include "io/file_manager.hpp"
 #include "items/item.hpp"
@@ -47,6 +46,7 @@
 #include "utils/helpers.hpp"
 #include "utils/log.hpp"
 #include "utils/profiler.hpp"
+#include "stkscenemanager.hpp"
 
 #include <algorithm>
 #include <limits>
@@ -148,8 +148,12 @@ void IrrDriver::renderGLSL(float dt)
         // TODO: put this outside of the rendering loop
         generateDiffuseCoefficients();
 
+        PROFILER_PUSH_CPU_MARKER("Update Light Info", 0xFF, 0x0, 0x0);
         unsigned plc = UpdateLightsInfo(camnode, dt);
+        PROFILER_POP_CPU_MARKER();
+        PROFILER_PUSH_CPU_MARKER("Compute camera matrix", 0x0, 0xFF, 0x0);
         computeCameraMatrix(camnode, viewport.LowerRightCorner.X - viewport.UpperLeftCorner.X, viewport.LowerRightCorner.Y - viewport.UpperLeftCorner.Y);
+        PROFILER_POP_CPU_MARKER();
         renderScene(camnode, plc, glows, dt, track->hasShadows(), false);
 
         // Debug physic
@@ -272,22 +276,30 @@ void IrrDriver::renderGLSL(float dt)
 void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned pointlightcount, std::vector<GlowData>& glows, float dt, bool hasShadow, bool forceRTT)
 {
     glBindBufferBase(GL_UNIFORM_BUFFER, 0, SharedObject::ViewProjectionMatrixesUBO);
+    m_scene_manager->setActiveCamera(camnode);
 
+    PROFILER_PUSH_CPU_MARKER("- Draw Call Generation", 0xFF, 0xFF, 0xFF);
+    PrepareDrawCalls(camnode);
+    PROFILER_POP_CPU_MARKER();
     // Shadows
     {
-        PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90);
-        ScopedGPUTimer Timer(getGPUTimer(Q_SHADOWS));
         // To avoid wrong culling, use the largest view possible
         m_scene_manager->setActiveCamera(m_suncam);
         if (!m_mipviz && !m_wireframe && UserConfigParams::m_dynamic_lights &&
             UserConfigParams::m_shadows && !irr_driver->needUBOWorkaround() && hasShadow)
         {
+            PROFILER_PUSH_CPU_MARKER("- Shadow", 0x30, 0x6F, 0x90);
             renderShadows();
+            PROFILER_POP_CPU_MARKER();
             if (UserConfigParams::m_gi)
+            {
+                PROFILER_PUSH_CPU_MARKER("- RSM", 0xFF, 0x0, 0xFF);
                 renderRSM();
+                PROFILER_POP_CPU_MARKER();
+            }
         }
         m_scene_manager->setActiveCamera(camnode);
-        PROFILER_POP_CPU_MARKER();
+
     }
 
     PROFILER_PUSH_CPU_MARKER("- Solid Pass 1", 0xFF, 0x00, 0x00);
@@ -323,6 +335,8 @@ void IrrDriver::renderScene(scene::ICameraSceneNode * const camnode, unsigned po
     renderSolidSecondPass();
     PROFILER_POP_CPU_MARKER();
 
+    m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+
     if (getNormals())
     {
         m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind();
@@ -480,29 +494,25 @@ void IrrDriver::computeSunVisibility()
         hasgodrays = World::getWorld()->getTrack()->hasGodRays();
     }
 
-    irr::video::COpenGLDriver*    gl_driver = (irr::video::COpenGLDriver*)m_device->getVideoDriver();
-    if (UserConfigParams::m_light_shaft && hasgodrays)//hasflare || hasgodrays)
+    if (UserConfigParams::m_light_shaft && hasgodrays)
     {
         GLuint res = 0;
         if (m_query_issued)
-            gl_driver->extGlGetQueryObjectuiv(m_lensflare_query, GL_QUERY_RESULT, &res);
+            glGetQueryObjectuiv(m_lensflare_query, GL_QUERY_RESULT, &res);
         m_post_processing->setSunPixels(res);
 
         // Prepare the query for the next frame.
         glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
-        gl_driver->extGlBeginQuery(GL_SAMPLES_PASSED_ARB, m_lensflare_query);
+        glBeginQuery(GL_SAMPLES_PASSED_ARB, m_lensflare_query);
         m_scene_manager->setCurrentRendertime(scene::ESNRP_SOLID);
         m_scene_manager->drawAll(scene::ESNRP_CAMERA);
         irr_driver->setPhase(GLOW_PASS);
         m_sun_interposer->render();
-        gl_driver->extGlEndQuery(GL_SAMPLES_PASSED_ARB);
+        glEndQuery(GL_SAMPLES_PASSED_ARB);
         m_query_issued = true;
 
         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);
     }
@@ -514,7 +524,9 @@ void IrrDriver::renderParticles()
     glDisable(GL_CULL_FACE);
     glEnable(GL_BLEND);
     glBlendEquation(GL_FUNC_ADD);
-    m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT_EFFECT);
+    for (unsigned i = 0; i < ParticlesList::getInstance()->size(); ++i)
+        ParticlesList::getInstance()->at(i)->render();
+//    m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT_EFFECT);
 }
 
 /** Given a matrix transform and a set of points returns an orthogonal projection matrix that maps coordinates of
@@ -584,14 +596,20 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
         50.,
     };
 
-    float *tmp = new float[18 * 8];
 
+    float tmp[16 * 9 + 2];
     memcpy(tmp, irr_driver->getViewMatrix().pointer(), 16 * sizeof(float));
     memcpy(&tmp[16], irr_driver->getProjMatrix().pointer(), 16 * sizeof(float));
     memcpy(&tmp[32], irr_driver->getInvViewMatrix().pointer(), 16 * sizeof(float));
     memcpy(&tmp[48], irr_driver->getInvProjMatrix().pointer(), 16 * sizeof(float));
+    memcpy(&tmp[64], irr_driver->getProjViewMatrix().pointer(), 16 * sizeof(float));
 
     const core::matrix4 &SunCamViewMatrix = m_suncam->getViewMatrix();
+    for (unsigned i = 0; i < 4; i++)
+    {
+        if (!m_shadow_camnodes[i])
+            m_shadow_camnodes[i] = (scene::ICameraSceneNode *) m_suncam->clone();
+    }
     sun_ortho_matrix.clear();
 
     if (World::getWorld() && World::getWorld()->getTrack())
@@ -669,8 +687,8 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
             const float units_per_w = w / 1024;
             const float units_per_h = h / 1024;*/
 
-            m_suncam->setProjectionMatrix(getTighestFitOrthoProj(SunCamViewMatrix, vectors) , true);
-            m_suncam->render();
+            m_shadow_camnodes[i]->setProjectionMatrix(getTighestFitOrthoProj(SunCamViewMatrix, vectors) , true);
+            m_shadow_camnodes[i]->render();
 
             sun_ortho_matrix.push_back(getVideoDriver()->getTransform(video::ETS_PROJECTION) * getVideoDriver()->getTransform(video::ETS_VIEW));
         }
@@ -702,18 +720,17 @@ void IrrDriver::computeCameraMatrix(scene::ICameraSceneNode * const camnode, siz
         assert(sun_ortho_matrix.size() == 4);
         camnode->setNearValue(oldnear);
         camnode->setFarValue(oldfar);
+        camnode->render();
 
         size_t size = irr_driver->getShadowViewProj().size();
         for (unsigned i = 0; i < size; i++)
-            memcpy(&tmp[16 * i + 64], irr_driver->getShadowViewProj()[i].pointer(), 16 * sizeof(float));
+            memcpy(&tmp[16 * i + 80], irr_driver->getShadowViewProj()[i].pointer(), 16 * sizeof(float));
     }
 
-    tmp[128] = float(width);
-    tmp[129] = float(height);
-
+    tmp[144] = float(width);
+    tmp[145] = float(height);
     glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO);
-    glBufferSubData(GL_UNIFORM_BUFFER, 0, (16 * 8 + 2) * sizeof(float), tmp);
-    delete []tmp;
+    glBufferSubData(GL_UNIFORM_BUFFER, 0, (16 * 9 + 2) * sizeof(float), tmp);
 }
 
 
@@ -770,20 +787,40 @@ void IrrDriver::renderGlow(std::vector<GlowData>& glows)
     glDepthMask(GL_FALSE);
     glDisable(GL_BLEND);
 
-    glBindVertexArray(getVAO(EVT_STANDARD));
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(EVT_STANDARD));
     for (u32 i = 0; i < glowcount; i++)
     {
         const GlowData &dat = glows[i];
-        scene::ISceneNode * const cur = dat.node;
+        scene::ISceneNode * cur = dat.node;
 
-        //TODO : implement culling on gpu
-        // Quick box-based culling
-//        const core::aabbox3df nodebox = cur->getTransformedBoundingBox();
-//        if (!nodebox.intersectsWithBox(cambox))
-//            continue;
+        STKMeshSceneNode *node = static_cast<STKMeshSceneNode *>(cur);
+        node->setGlowColors(SColor(0, (unsigned) (dat.b * 255.f), (unsigned)(dat.g * 255.f), (unsigned)(dat.r * 255.f)));
+        if (!irr_driver->hasARB_draw_indirect())
+            node->render();
+    }
 
-        cb->setColor(dat.r, dat.g, dat.b);
-        cur->render();
+    if (irr_driver->hasARB_draw_indirect())
+    {
+        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, GlowPassCmd::getInstance()->drawindirectcmd);
+        glUseProgram(MeshShader::InstancedColorizeShader::getInstance()->Program);
+
+        glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(video::EVT_STANDARD, InstanceTypeGlow));
+        if (UserConfigParams::m_azdo)
+        {
+            if (GlowPassCmd::getInstance()->Size)
+            {
+                glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT,
+                    (const void*)(GlowPassCmd::getInstance()->Offset * sizeof(DrawElementsIndirectCommand)),
+                    GlowPassCmd::getInstance()->Size,
+                    sizeof(DrawElementsIndirectCommand));
+            }
+        }
+        else
+        {
+            for (unsigned i = 0; i < ListInstancedGlow::getInstance()->size(); i++)
+                glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)((GlowPassCmd::getInstance()->Offset + i) * sizeof(DrawElementsIndirectCommand)));
+        }
     }
 
     glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
diff --git a/src/graphics/render_geometry.cpp b/src/graphics/render_geometry.cpp
index e589d9453..74c89078b 100644
--- a/src/graphics/render_geometry.cpp
+++ b/src/graphics/render_geometry.cpp
@@ -16,7 +16,6 @@
 #include "graphics/screenquad.hpp"
 #include "graphics/shaders.hpp"
 #include "graphics/stkmeshscenenode.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "graphics/wind.hpp"
 #include "io/file_manager.hpp"
 #include "items/item.hpp"
@@ -29,6 +28,7 @@
 #include "utils/log.hpp"
 #include "utils/profiler.hpp"
 #include "utils/tuple.hpp"
+#include "stkscenemanager.hpp"
 
 #include <algorithm>
 
@@ -116,19 +116,24 @@ struct custom_unroll_args<N, List...>
 
 
 template<typename Shader, enum E_VERTEX_TYPE VertexType, int ...List, typename... TupleType>
-void renderMeshes1stPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > *meshes)
+void renderMeshes1stPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > &meshes)
 {
     glUseProgram(Shader::getInstance()->Program);
-    glBindVertexArray(getVAO(VertexType));
-    for (unsigned i = 0; i < meshes->size(); i++)
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(VertexType));
+    for (unsigned i = 0; i < meshes.size(); i++)
     {
-        GLMesh &mesh = *(STK::tuple_get<0>(meshes->at(i)));
+        std::vector<GLuint> Textures;
+        std::vector<uint64_t> Handles;
+        GLMesh &mesh = *(STK::tuple_get<0>(meshes.at(i)));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh.vao);
         for (unsigned j = 0; j < TexUnits.size(); j++)
         {
-            if (!mesh.textures[j])
-                mesh.textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
-            compressTexture(mesh.textures[j], TexUnits[j].m_premul_alpha);
-            setTexture(TexUnits[j].m_id, getTextureGLuint(mesh.textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
+            if (UserConfigParams::m_azdo)
+                Handles.push_back(mesh.TextureHandles[TexUnits[j].m_id]);
+            else
+                Textures.push_back(getTextureGLuint(mesh.textures[TexUnits[j].m_id]));
         }
         if (mesh.VAOType != VertexType)
         {
@@ -137,68 +142,61 @@ void renderMeshes1stPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::
 #endif
             continue;
         }
-        custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes->at(i));
+
+        if (UserConfigParams::m_azdo)
+            Shader::getInstance()->SetTextureHandles(Handles);
+        else
+            Shader::getInstance()->SetTextureUnits(Textures);
+        custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes.at(i));
     }
 }
 
-template<int...List>
-struct instanced_custom_unroll_args;
-
-template<>
-struct instanced_custom_unroll_args<>
-{
-    template<typename T, typename ...TupleTypes, typename ...Args>
-    static void exec(const T *Shader, const STK::Tuple<TupleTypes...> &t, Args... args)
-    {
-        const GLMesh *mesh = STK::tuple_get<0>(t);
-        size_t instance_count = STK::tuple_get<1>(t);
-        irr_driver->IncreaseObjectCount();
-        GLenum ptype = mesh->PrimitiveType;
-        GLenum itype = mesh->IndexType;
-        size_t count = mesh->IndexCount;
-
-        Shader->setUniforms(args...);
-        glDrawElementsInstanced(ptype, count, itype, 0, instance_count);
-    }
-};
-
-template<int N, int...List>
-struct instanced_custom_unroll_args<N, List...>
-{
-    template<typename T, typename ...TupleTypes, typename ...Args>
-    static void exec(const T *Shader, const STK::Tuple<TupleTypes...> &t, Args... args)
-    {
-        instanced_custom_unroll_args<List...>::template exec<T>(Shader, t, STK::tuple_get<N>(t), args...);
-    }
-};
-
-template<typename Shader, enum E_VERTEX_TYPE VertexType, int ...List, typename... TupleType>
-void renderInstancedMeshes1stPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > *meshes)
+#ifdef Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+void renderInstancedMeshes1stPass(const std::vector<TexUnit> &TexUnits, std::vector<GLMesh *> &meshes, Args...args)
 {
     glUseProgram(Shader::getInstance()->Program);
-    for (unsigned i = 0; i < meshes->size(); i++)
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeDefault));
+    for (unsigned i = 0; i < meshes.size(); i++)
     {
-        GLMesh &mesh = *(STK::tuple_get<0>(meshes->at(i)));
+        std::vector<GLuint> Textures;
+        GLMesh *mesh = meshes[i];
 #ifdef DEBUG
-        if (mesh.VAOType != VertexType)
+        if (mesh->VAOType != VT)
             Log::error("RenderGeometry", "Wrong instanced vertex format");
 #endif
-        glBindVertexArray(mesh.vao);
         for (unsigned j = 0; j < TexUnits.size(); j++)
-        {
-            if (!mesh.textures[j])
-                mesh.textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
-            compressTexture(mesh.textures[j], TexUnits[j].m_premul_alpha);
-            setTexture(TexUnits[j].m_id, getTextureGLuint(mesh.textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
-        }
-        instanced_custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes->at(i));
+            Textures.push_back(getTextureGLuint(mesh->textures[TexUnits[j].m_id]));
+        Shader::getInstance()->SetTextureUnits(Textures);
+
+        Shader::getInstance()->setUniforms(args...);
+        glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)((SolidPassCmd::getInstance()->Offset[Mat] + i) * sizeof(DrawElementsIndirectCommand)));
     }
 }
+#endif
 
-static GLsync m_sync;
+#ifdef Multi_Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+void multidraw1stPass(Args...args)
+{
+    glUseProgram(Shader::getInstance()->Program);
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeDefault));
+    if (SolidPassCmd::getInstance()->Size[Mat])
+    {
+        Shader::getInstance()->setUniforms(args...);
+        glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT,
+            (const void*)(SolidPassCmd::getInstance()->Offset[Mat] * sizeof(DrawElementsIndirectCommand)),
+            SolidPassCmd::getInstance()->Size[Mat],
+            sizeof(DrawElementsIndirectCommand));
+    }
+}
+#endif
+
+static core::vector3df windDir;
 
 void IrrDriver::renderSolidFirstPass()
 {
+    windDir = getWindDir();
     m_rtts->getFBO(FBO_NORMAL_AND_DEPTHS).Bind();
     glClearColor(0., 0., 0., 0.);
     glDepthMask(GL_TRUE);
@@ -209,96 +207,96 @@ void IrrDriver::renderSolidFirstPass()
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_BLEND);
     glEnable(GL_CULL_FACE);
-    irr_driver->setPhase(SOLID_NORMAL_AND_DEPTH_PASS);
-    ListMatDefault::getInstance()->clear();
-    ListMatAlphaRef::getInstance()->clear();
-    ListMatSphereMap::getInstance()->clear();
-    ListMatDetails::getInstance()->clear();
-    ListMatUnlit::getInstance()->clear();
-    ListMatNormalMap::getInstance()->clear();
-    ListMatGrass::getInstance()->clear();
-    ListMatSplatting::getInstance()->clear();
-    ListInstancedMatDefault::getInstance()->clear();
-    ListInstancedMatAlphaRef::getInstance()->clear();
-    ListInstancedMatGrass::getInstance()->clear();
-    ListInstancedMatNormalMap::getInstance()->clear();
-    // Add a 30 ms timeout
-    GLenum reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 30000000);
-/*    switch (reason)
-    {
-    case GL_ALREADY_SIGNALED:
-        printf("Already Signaled\n");
-        break;
-    case GL_TIMEOUT_EXPIRED:
-        printf("Timeout Expired\n");
-        break;
-    case GL_CONDITION_SATISFIED:
-        printf("Condition Satisfied\n");
-        break;
-    case GL_WAIT_FAILED:
-        printf("Wait Failed\n");
-        break;
-    }*/
-    m_scene_manager->drawAll(scene::ESNRP_SOLID);
 
     if (!UserConfigParams::m_dynamic_lights)
         return;
 
     {
         ScopedGPUTimer Timer(getGPUTimer(Q_SOLID_PASS1));
+        irr_driver->setPhase(SOLID_NORMAL_AND_DEPTH_PASS);
 
-        std::vector<TexUnit> object_pass1_texunits = TexUnits(TexUnit(MeshShader::ObjectPass1Shader::getInstance()->TU_tex, true) );
-        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_STANDARD, 2, 1>(object_pass1_texunits, ListMatDefault::getInstance());
-        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_STANDARD, 2, 1>(object_pass1_texunits, ListMatSphereMap::getInstance());
-        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_2TCOORDS, 2, 1>(object_pass1_texunits, ListMatDetails::getInstance());
-        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_2TCOORDS, 2, 1>(object_pass1_texunits, ListMatSplatting::getInstance());
-        renderMeshes1stPass<MeshShader::ObjectRefPass1Shader, video::EVT_STANDARD, 3, 2, 1>(object_pass1_texunits, ListMatUnlit::getInstance());
-        renderMeshes1stPass<MeshShader::ObjectRefPass1Shader, video::EVT_STANDARD, 3, 2, 1>(TexUnits(TexUnit(MeshShader::ObjectRefPass1Shader::getInstance()->TU_tex, true)), ListMatAlphaRef::getInstance());
-        renderMeshes1stPass<MeshShader::GrassPass1Shader, video::EVT_STANDARD, 3, 2, 1>(TexUnits(TexUnit(MeshShader::GrassPass1Shader::getInstance()->TU_tex, true)), ListMatGrass::getInstance());
+        for (unsigned i = 0; i < ImmediateDrawList::getInstance()->size(); i++)
+            ImmediateDrawList::getInstance()->at(i)->render();
+
+        std::vector<TexUnit> object_pass1_texunits = TexUnits(TexUnit(0, true) );
+        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_STANDARD, 2, 1>(object_pass1_texunits, ListMatDefault::getInstance()->SolidPass);
+        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_2TCOORDS, 2, 1>(object_pass1_texunits, ListMatSplatting::getInstance()->SolidPass);
+        renderMeshes1stPass<MeshShader::ObjectRefPass1Shader, video::EVT_STANDARD, 3, 2, 1>(object_pass1_texunits, ListMatUnlit::getInstance()->SolidPass);
+        renderMeshes1stPass<MeshShader::ObjectRefPass1Shader, video::EVT_STANDARD, 3, 2, 1>(TexUnits(TexUnit(0, true)), ListMatAlphaRef::getInstance()->SolidPass);
+        renderMeshes1stPass<MeshShader::GrassPass1Shader, video::EVT_STANDARD, 3, 2, 1>(TexUnits(TexUnit(0, true)), ListMatGrass::getInstance()->SolidPass);
         renderMeshes1stPass<MeshShader::NormalMapShader, video::EVT_TANGENTS, 2, 1>(TexUnits(
-            TexUnit(MeshShader::NormalMapShader::getInstance()->TU_glossy, true),
-            TexUnit(MeshShader::NormalMapShader::getInstance()->TU_normalmap, false)
-        ), ListMatNormalMap::getInstance());
+            TexUnit(1, false),
+            TexUnit(0, true)
+            ), ListMatNormalMap::getInstance()->SolidPass);
+        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_STANDARD, 2, 1>(object_pass1_texunits, ListMatSphereMap::getInstance()->SolidPass);
+        renderMeshes1stPass<MeshShader::ObjectPass1Shader, video::EVT_2TCOORDS, 2, 1>(object_pass1_texunits, ListMatDetails::getInstance()->SolidPass);
 
-        renderInstancedMeshes1stPass<MeshShader::InstancedObjectPass1Shader, video::EVT_STANDARD>(
-                    TexUnits(TexUnit(MeshShader::InstancedObjectPass1Shader::getInstance()->TU_tex, true)),
-                    ListInstancedMatDefault::getInstance());
-        renderInstancedMeshes1stPass<MeshShader::InstancedObjectRefPass1Shader, video::EVT_STANDARD>(
-                    TexUnits(TexUnit(MeshShader::InstancedObjectRefPass1Shader::getInstance()->TU_tex, true)),
-                    ListInstancedMatAlphaRef::getInstance());
-        renderInstancedMeshes1stPass<MeshShader::InstancedGrassPass1Shader, video::EVT_STANDARD, 2>(
-                    TexUnits(TexUnit(MeshShader::InstancedGrassPass1Shader::getInstance()->TU_tex, true)),
-                    ListInstancedMatGrass::getInstance());
-        renderInstancedMeshes1stPass<MeshShader::InstancedNormalMapShader, video::EVT_TANGENTS>(
-            TexUnits(TexUnit(MeshShader::InstancedNormalMapShader::getInstance()->TU_glossy, true), TexUnit(MeshShader::InstancedNormalMapShader::getInstance()->TU_normalmap, false)),
-            ListInstancedMatNormalMap::getInstance());
+        if (irr_driver->hasARB_draw_indirect())
+            glBindBuffer(GL_DRAW_INDIRECT_BUFFER, SolidPassCmd::getInstance()->drawindirectcmd);
+
+        if (UserConfigParams::m_azdo)
+        {
+#ifdef Multi_Draw_Indirect
+            multidraw1stPass<MeshShader::InstancedObjectPass1Shader, MAT_DEFAULT, video::EVT_STANDARD>();
+            multidraw1stPass<MeshShader::InstancedObjectRefPass1Shader, MAT_ALPHA_REF, video::EVT_STANDARD>();
+            multidraw1stPass<MeshShader::InstancedNormalMapShader, MAT_NORMAL_MAP, video::EVT_TANGENTS>();
+            multidraw1stPass<MeshShader::InstancedObjectPass1Shader, MAT_SPHEREMAP, video::EVT_STANDARD>();
+            multidraw1stPass<MeshShader::InstancedObjectPass1Shader, MAT_DETAIL, video::EVT_2TCOORDS>();
+            multidraw1stPass<MeshShader::InstancedObjectRefPass1Shader, MAT_UNLIT, video::EVT_STANDARD>();
+            multidraw1stPass<MeshShader::InstancedGrassPass1Shader, MAT_GRASS, video::EVT_STANDARD>(windDir);
+#endif
+        }
+#ifdef Draw_Indirect
+        else if (irr_driver->hasARB_draw_indirect())
+        {
+            // Default
+            renderInstancedMeshes1stPass<MeshShader::InstancedObjectPass1Shader, MAT_DEFAULT, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), ListInstancedMatDefault::getInstance()->SolidPass);
+            // Alpha ref
+            renderInstancedMeshes1stPass<MeshShader::InstancedObjectRefPass1Shader, MAT_ALPHA_REF, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), ListInstancedMatAlphaRef::getInstance()->SolidPass);
+            // Unlit
+            renderInstancedMeshes1stPass<MeshShader::InstancedObjectPass1Shader, MAT_UNLIT, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), ListInstancedMatUnlit::getInstance()->SolidPass);
+            // Spheremap
+            renderInstancedMeshes1stPass<MeshShader::InstancedObjectPass1Shader, MAT_SPHEREMAP, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), ListInstancedMatSphereMap::getInstance()->SolidPass);
+            // Grass
+            renderInstancedMeshes1stPass<MeshShader::InstancedGrassPass1Shader, MAT_GRASS, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), ListInstancedMatGrass::getInstance()->SolidPass, windDir);
+
+            // Detail
+            renderInstancedMeshes1stPass<MeshShader::InstancedObjectPass1Shader, MAT_DETAIL, video::EVT_2TCOORDS>(
+                TexUnits(TexUnit(0, true)), ListInstancedMatDetails::getInstance()->SolidPass);
+
+            // Normal Map
+            renderInstancedMeshes1stPass<MeshShader::InstancedNormalMapShader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(
+                TexUnits(TexUnit(1, false), TexUnit(0, true)), ListInstancedMatNormalMap::getInstance()->SolidPass);
+        }
+#endif
     }
 }
 
 template<typename Shader, enum E_VERTEX_TYPE VertexType, int...List, typename... TupleType>
-void renderMeshes2ndPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > *meshes)
+void renderMeshes2ndPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > &meshes, const std::vector<uint64_t> &Prefilled_Handle,
+    const std::vector<GLuint> &Prefilled_Tex)
 {
     glUseProgram(Shader::getInstance()->Program);
-    glBindVertexArray(getVAO(VertexType));
-    for (unsigned i = 0; i < meshes->size(); i++)
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(VertexType));
+    for (unsigned i = 0; i < meshes.size(); i++)
     {
-        GLMesh &mesh = *(STK::tuple_get<0>(meshes->at(i)));
+        std::vector<uint64_t> Handles(Prefilled_Handle);
+        std::vector<GLuint> Textures(Prefilled_Tex);
+        GLMesh &mesh = *(STK::tuple_get<0>(meshes.at(i)));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh.vao);
         for (unsigned j = 0; j < TexUnits.size(); j++)
         {
-            if (!mesh.textures[j])
-                mesh.textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
-            compressTexture(mesh.textures[j], TexUnits[j].m_premul_alpha);
-            setTexture(TexUnits[j].m_id, getTextureGLuint(mesh.textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
-            if (irr_driver->getLightViz())
-            {
-                GLint swizzleMask[] = { GL_ONE, GL_ONE, GL_ONE, GL_ALPHA };
-                glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
-            }
+            if (UserConfigParams::m_azdo)
+                Handles.push_back(mesh.TextureHandles[TexUnits[j].m_id]);
             else
-            {
-                GLint swizzleMask[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
-                glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
-            }
+                Textures.push_back(getTextureGLuint(mesh.textures[TexUnits[j].m_id]));
         }
 
         if (mesh.VAOType != VertexType)
@@ -308,39 +306,51 @@ void renderMeshes2ndPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::
 #endif
             continue;
         }
-        custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes->at(i));
+
+        if (UserConfigParams::m_azdo)
+            Shader::getInstance()->SetTextureHandles(Handles);
+        else
+            Shader::getInstance()->SetTextureUnits(Textures);
+        custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes.at(i));
     }
 }
 
-template<typename Shader, int...List, typename... TupleType>
-void renderInstancedMeshes2ndPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > *meshes)
+#ifdef Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+void renderInstancedMeshes2ndPass(const std::vector<TexUnit> &TexUnits, const std::vector<GLuint> &Prefilled_tex, std::vector<GLMesh *> &meshes, Args...args)
 {
     glUseProgram(Shader::getInstance()->Program);
-    for (unsigned i = 0; i < meshes->size(); i++)
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeDefault));
+    for (unsigned i = 0; i < meshes.size(); i++)
     {
-        GLMesh &mesh = *(STK::tuple_get<0>(meshes->at(i)));
-        glBindVertexArray(mesh.vao);
+        GLMesh *mesh = meshes[i];
+        std::vector<GLuint> Textures(Prefilled_tex);
         for (unsigned j = 0; j < TexUnits.size(); j++)
-        {
-            if (!mesh.textures[j])
-                mesh.textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
-            compressTexture(mesh.textures[j], TexUnits[j].m_premul_alpha);
-            setTexture(TexUnits[j].m_id, getTextureGLuint(mesh.textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
-            if (irr_driver->getLightViz())
-            {
-                GLint swizzleMask[] = { GL_ONE, GL_ONE, GL_ONE, GL_ALPHA };
-                glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
-            }
-            else
-            {
-                GLint swizzleMask[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
-                glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
-            }
-        }
-
-        instanced_custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes->at(i));
+            Textures.push_back(getTextureGLuint(mesh->textures[TexUnits[j].m_id]));
+        Shader::getInstance()->SetTextureUnits(Textures);
+        Shader::getInstance()->setUniforms(args...);
+        glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)((SolidPassCmd::getInstance()->Offset[Mat] + i) * sizeof(DrawElementsIndirectCommand)));
     }
 }
+#endif
+
+#ifdef Multi_Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+void multidraw2ndPass(const std::vector<uint64_t> &Handles, Args... args)
+{
+    glUseProgram(Shader::getInstance()->Program);
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeDefault));
+    if (SolidPassCmd::getInstance()->Size[Mat])
+    {
+        Shader::getInstance()->SetTextureHandles(Handles);
+        Shader::getInstance()->setUniforms(args...);
+        glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT,
+            (const void*)(SolidPassCmd::getInstance()->Offset[Mat] * sizeof(DrawElementsIndirectCommand)),
+            SolidPassCmd::getInstance()->Size[Mat],
+            sizeof(DrawElementsIndirectCommand));
+    }
+}
+#endif
 
 void IrrDriver::renderSolidSecondPass()
 {
@@ -364,77 +374,122 @@ void IrrDriver::renderSolidSecondPass()
     glEnable(GL_DEPTH_TEST);
     glDisable(GL_ALPHA_TEST);
     glDisable(GL_BLEND);
-    setTexture(0, m_rtts->getRenderTarget(RTT_TMP1), GL_NEAREST, GL_NEAREST);
-    setTexture(1, m_rtts->getRenderTarget(RTT_TMP2), GL_NEAREST, GL_NEAREST);
-    setTexture(2, m_rtts->getRenderTarget(RTT_HALF1_R), GL_LINEAR, GL_LINEAR);
+
+    uint64_t DiffuseHandle = 0, SpecularHandle = 0, SSAOHandle = 0, DepthHandle = 0;
+
+    if (UserConfigParams::m_azdo)
+    {
+#ifdef Bindless_Texture_Support
+        DiffuseHandle = glGetTextureSamplerHandleARB(m_rtts->getRenderTarget(RTT_DIFFUSE), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[0]);
+        if (!glIsTextureHandleResidentARB(DiffuseHandle))
+            glMakeTextureHandleResidentARB(DiffuseHandle);
+
+        SpecularHandle = glGetTextureSamplerHandleARB(m_rtts->getRenderTarget(RTT_SPECULAR), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[1]);
+        if (!glIsTextureHandleResidentARB(SpecularHandle))
+            glMakeTextureHandleResidentARB(SpecularHandle);
+
+        SSAOHandle = glGetTextureSamplerHandleARB(m_rtts->getRenderTarget(RTT_HALF1_R), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[2]);
+        if (!glIsTextureHandleResidentARB(SSAOHandle))
+            glMakeTextureHandleResidentARB(SSAOHandle);
+
+        DepthHandle = glGetTextureSamplerHandleARB(getDepthStencilTexture(), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[3]);
+        if (!glIsTextureHandleResidentARB(DepthHandle))
+            glMakeTextureHandleResidentARB(DepthHandle);
+#endif
+    }
 
     {
         ScopedGPUTimer Timer(getGPUTimer(Q_SOLID_PASS2));
 
-        m_scene_manager->drawAll(scene::ESNRP_SOLID);
+        irr_driver->setPhase(SOLID_LIT_PASS);
+
+        for (unsigned i = 0; i < ImmediateDrawList::getInstance()->size(); i++)
+            ImmediateDrawList::getInstance()->at(i)->render();
+
+        std::vector<GLuint> DiffSpecSSAOTex = createVector<GLuint>(m_rtts->getRenderTarget(RTT_DIFFUSE), m_rtts->getRenderTarget(RTT_SPECULAR), m_rtts->getRenderTarget(RTT_HALF1_R));
 
         renderMeshes2ndPass<MeshShader::ObjectPass2Shader, video::EVT_STANDARD, 3, 1>(TexUnits(
-            TexUnit(MeshShader::ObjectPass2Shader::getInstance()->TU_Albedo, true)
-        ), ListMatDefault::getInstance());
-
+            TexUnit(0, true)
+        ), ListMatDefault::getInstance()->SolidPass,
+        createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
         renderMeshes2ndPass<MeshShader::ObjectRefPass2Shader, video::EVT_STANDARD, 3, 1 >(TexUnits(
-            TexUnit(MeshShader::ObjectRefPass2Shader::getInstance()->TU_Albedo, true)
-        ), ListMatAlphaRef::getInstance());
-
-        renderMeshes2ndPass<MeshShader::SphereMapShader, video::EVT_STANDARD, 2, 1>(TexUnits(
-            TexUnit(MeshShader::SphereMapShader::getInstance()->TU_tex, true)
-        ), ListMatSphereMap::getInstance());
-
-        renderMeshes2ndPass<MeshShader::DetailledObjectPass2Shader, video::EVT_2TCOORDS, 1>(TexUnits(
-            TexUnit(MeshShader::DetailledObjectPass2Shader::getInstance()->TU_Albedo, true),
-            TexUnit(MeshShader::DetailledObjectPass2Shader::getInstance()->TU_detail, true)
-        ), ListMatDetails::getInstance());
-
-        renderMeshes2ndPass<MeshShader::GrassPass2Shader, video::EVT_STANDARD, 3, 1>(TexUnits(
-            TexUnit(MeshShader::GrassPass2Shader::getInstance()->TU_Albedo, true)
-        ), ListMatGrass::getInstance());
-
-        renderMeshes2ndPass<MeshShader::ObjectUnlitShader, video::EVT_STANDARD, 1>(TexUnits(
-            TexUnit(MeshShader::ObjectUnlitShader::getInstance()->TU_tex, true)
-        ), ListMatUnlit::getInstance());
-
+            TexUnit(0, true)
+            ), ListMatAlphaRef::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
+        renderMeshes2ndPass<MeshShader::ObjectUnlitShader, video::EVT_STANDARD, 3, 1>(TexUnits(
+            TexUnit(0, true)
+            ), ListMatUnlit::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
         renderMeshes2ndPass<MeshShader::SplattingShader, video::EVT_2TCOORDS, 1>(TexUnits(
-            TexUnit(8, true),
-            TexUnit(MeshShader::SplattingShader::getInstance()->TU_tex_layout, false),
-            TexUnit(MeshShader::SplattingShader::getInstance()->TU_tex_detail0, true),
-            TexUnit(MeshShader::SplattingShader::getInstance()->TU_tex_detail1, true),
-            TexUnit(MeshShader::SplattingShader::getInstance()->TU_tex_detail2, true),
-            TexUnit(MeshShader::SplattingShader::getInstance()->TU_tex_detail3, true)
-        ), ListMatSplatting::getInstance());
-
+            TexUnit(1, false),
+            TexUnit(2, true),
+            TexUnit(3, true),
+            TexUnit(4, true),
+            TexUnit(0, true)
+            ), ListMatSplatting::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
+        renderMeshes2ndPass<MeshShader::SphereMapShader, video::EVT_STANDARD, 2, 1>(TexUnits(
+            TexUnit(0, true)
+            ), ListMatSphereMap::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
+        renderMeshes2ndPass<MeshShader::DetailledObjectPass2Shader, video::EVT_2TCOORDS, 1>(TexUnits(
+            TexUnit(0, true),
+            TexUnit(1, true)
+            ), ListMatDetails::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
+        renderMeshes2ndPass<MeshShader::GrassPass2Shader, video::EVT_STANDARD, 3, 1>(TexUnits(
+            TexUnit(0, true)
+            ), ListMatGrass::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
         renderMeshes2ndPass<MeshShader::ObjectPass2Shader, video::EVT_TANGENTS, 3, 1>(TexUnits(
-            TexUnit(MeshShader::ObjectPass2Shader::getInstance()->TU_Albedo, true)
-        ), ListMatNormalMap::getInstance());
+            TexUnit(0, true)
+            ), ListMatNormalMap::getInstance()->SolidPass, createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle), DiffSpecSSAOTex);
 
+        if (UserConfigParams::m_azdo)
+        {
+#ifdef Multi_Draw_Indirect
+            multidraw2ndPass<MeshShader::InstancedObjectPass2Shader, MAT_DEFAULT, video::EVT_STANDARD>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, 0));
+            multidraw2ndPass<MeshShader::InstancedObjectPass2Shader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, 0));
+            multidraw2ndPass<MeshShader::InstancedObjectRefPass2Shader, MAT_ALPHA_REF, video::EVT_STANDARD>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, 0));
+            multidraw2ndPass<MeshShader::InstancedSphereMapShader, MAT_SPHEREMAP, video::EVT_STANDARD>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, 0));
+            multidraw2ndPass<MeshShader::InstancedDetailledObjectPass2Shader, MAT_DETAIL, video::EVT_2TCOORDS>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, 0, 0));
+            multidraw2ndPass<MeshShader::InstancedObjectUnlitShader, MAT_UNLIT, video::EVT_STANDARD>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, 0));
+            SunLightProvider * const cb = (SunLightProvider *)irr_driver->getCallback(ES_SUNLIGHT);
+            multidraw2ndPass<MeshShader::InstancedGrassPass2Shader, MAT_GRASS, video::EVT_STANDARD>(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, DepthHandle, 0), windDir, cb->getPosition());
+#endif
+        }
+#ifdef Draw_Indirect
+        else if (irr_driver->hasARB_draw_indirect())
+        {
+            // Default
+            renderInstancedMeshes2ndPass<MeshShader::InstancedObjectPass2Shader, MAT_DEFAULT, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), DiffSpecSSAOTex, ListInstancedMatDefault::getInstance()->SolidPass);
+            // Alpha ref
+            renderInstancedMeshes2ndPass<MeshShader::InstancedObjectRefPass2Shader, MAT_ALPHA_REF, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), DiffSpecSSAOTex, ListInstancedMatAlphaRef::getInstance()->SolidPass);
+            // Unlit
+            renderInstancedMeshes2ndPass<MeshShader::InstancedObjectUnlitShader, MAT_UNLIT, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), DiffSpecSSAOTex, ListInstancedMatUnlit::getInstance()->SolidPass);
+            // Spheremap
+            renderInstancedMeshes2ndPass<MeshShader::InstancedSphereMapShader, MAT_SPHEREMAP, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), DiffSpecSSAOTex, ListInstancedMatSphereMap::getInstance()->SolidPass);
+            // Details
+            renderInstancedMeshes2ndPass<MeshShader::InstancedDetailledObjectPass2Shader, MAT_DETAIL, video::EVT_2TCOORDS>(
+                TexUnits(TexUnit(0, true), TexUnit(1, false)), DiffSpecSSAOTex, ListInstancedMatDetails::getInstance()->SolidPass);
 
-        renderInstancedMeshes2ndPass<MeshShader::InstancedObjectPass2Shader>(
-            TexUnits(TexUnit(MeshShader::InstancedObjectPass2Shader::getInstance()->TU_Albedo, true)),
-            ListInstancedMatDefault::getInstance());
-        renderInstancedMeshes2ndPass<MeshShader::InstancedObjectPass2Shader>(
-            TexUnits(TexUnit(MeshShader::InstancedObjectPass2Shader::getInstance()->TU_Albedo, true)),
-            ListInstancedMatNormalMap::getInstance());
-        renderInstancedMeshes2ndPass<MeshShader::InstancedObjectRefPass2Shader>(
-            TexUnits(TexUnit(MeshShader::InstancedObjectRefPass2Shader::getInstance()->TU_Albedo, true)),
-            ListInstancedMatAlphaRef::getInstance());
-        setTexture(MeshShader::InstancedGrassPass2Shader::getInstance()->TU_dtex,
-            irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
-        renderInstancedMeshes2ndPass<MeshShader::InstancedGrassPass2Shader, 3, 2>(
-            TexUnits(TexUnit(MeshShader::InstancedGrassPass2Shader::getInstance()->TU_Albedo, true)),
-            ListInstancedMatGrass::getInstance());
+            // Normal map
+            renderInstancedMeshes2ndPass<MeshShader::InstancedObjectPass2Shader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(
+                TexUnits(TexUnit(0, true)), DiffSpecSSAOTex, ListInstancedMatNormalMap::getInstance()->SolidPass);
+
+            // Grass
+            SunLightProvider * const cb = (SunLightProvider *)irr_driver->getCallback(ES_SUNLIGHT);
+            DiffSpecSSAOTex.push_back(irr_driver->getDepthStencilTexture());
+            renderInstancedMeshes2ndPass<MeshShader::InstancedGrassPass2Shader, MAT_GRASS, video::EVT_STANDARD>(
+                TexUnits(TexUnit(0, true)), DiffSpecSSAOTex, ListInstancedMatGrass::getInstance()->SolidPass, windDir, cb->getPosition());
+        }
+#endif
     }
-    m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
 }
 
 template<enum E_VERTEX_TYPE VertexType, typename... TupleType>
 static void renderMeshNormals(std::vector<STK::Tuple<TupleType...> > *meshes)
 {
     glUseProgram(MeshShader::NormalVisualizer::getInstance()->Program);
-    glBindVertexArray(getVAO(VertexType));
+    glBindVertexArray(VAOManager::getInstance()->getVAO(VertexType));
     for (unsigned i = 0; i < meshes->size(); i++)
     {
         GLMesh &mesh = *(STK::tuple_get<0>(meshes->at(i)));
@@ -452,60 +507,100 @@ static void renderMeshNormals(std::vector<STK::Tuple<TupleType...> > *meshes)
 
 void IrrDriver::renderNormalsVisualisation()
 {
-    renderMeshNormals<video::EVT_STANDARD>(ListMatDefault::getInstance());
-    renderMeshNormals<video::EVT_STANDARD>(ListMatAlphaRef::getInstance());
-    renderMeshNormals<video::EVT_STANDARD>(ListMatSphereMap::getInstance());
+//    renderMeshNormals<video::EVT_STANDARD>(ListMatDefault::getInstance());
+//    renderMeshNormals<video::EVT_STANDARD>(ListMatAlphaRef::getInstance());
+//    renderMeshNormals<video::EVT_STANDARD>(ListMatSphereMap::getInstance());
 //    renderMeshNormals<video::EVT_STANDARD>(ListMatGrass::getInstance());
-    renderMeshNormals<video::EVT_2TCOORDS>(ListMatDetails::getInstance());
-    renderMeshNormals<video::EVT_STANDARD>(ListMatUnlit::getInstance());
-    renderMeshNormals<video::EVT_2TCOORDS>(ListMatSplatting::getInstance());
-    renderMeshNormals<video::EVT_TANGENTS>(ListMatNormalMap::getInstance());
-
+//    renderMeshNormals<video::EVT_2TCOORDS>(ListMatDetails::getInstance());
+//    renderMeshNormals<video::EVT_STANDARD>(ListMatUnlit::getInstance());
+//    renderMeshNormals<video::EVT_2TCOORDS>(ListMatSplatting::getInstance());
+//    renderMeshNormals<video::EVT_TANGENTS>(ListMatNormalMap::getInstance());
 }
 
+template<typename Shader, enum E_VERTEX_TYPE VertexType, int...List, typename... TupleType>
+void renderTransparenPass(const std::vector<TexUnit> &TexUnits, std::vector<STK::Tuple<TupleType...> > *meshes)
+{
+    glUseProgram(Shader::getInstance()->Program);
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(VertexType));
+    for (unsigned i = 0; i < meshes->size(); i++)
+    {
+        std::vector<uint64_t> Handles;
+        std::vector<GLuint> Textures;
+        GLMesh &mesh = *(STK::tuple_get<0>(meshes->at(i)));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh.vao);
+        for (unsigned j = 0; j < TexUnits.size(); j++)
+        {
+            if (!mesh.textures[TexUnits[j].m_id])
+                mesh.textures[TexUnits[j].m_id] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
+            compressTexture(mesh.textures[TexUnits[j].m_id], TexUnits[j].m_premul_alpha);
+            if (UserConfigParams::m_azdo)
+            {
+#ifdef Bindless_Texture_Support
+                if (!mesh.TextureHandles[TexUnits[j].m_id])
+                    mesh.TextureHandles[TexUnits[j].m_id] = glGetTextureSamplerHandleARB(getTextureGLuint(mesh.textures[TexUnits[j].m_id]), Shader::getInstance()->SamplersId[Handles.size()]);
+                if (!glIsTextureHandleResidentARB(mesh.TextureHandles[TexUnits[j].m_id]))
+                    glMakeTextureHandleResidentARB(mesh.TextureHandles[TexUnits[j].m_id]);
+#endif
+                Handles.push_back(mesh.TextureHandles[TexUnits[j].m_id]);
+            }
+            else
+                Textures.push_back(getTextureGLuint(mesh.textures[TexUnits[j].m_id]));
+        }
 
+        if (mesh.VAOType != VertexType)
+        {
+#ifdef DEBUG
+            Log::error("Materials", "Wrong vertex Type associed to pass 2 (hint texture : %s)", mesh.textures[0]->getName().getPath().c_str());
+#endif
+            continue;
+        }
+
+        if (UserConfigParams::m_azdo)
+            Shader::getInstance()->SetTextureHandles(Handles);
+        else
+            Shader::getInstance()->SetTextureUnits(Textures);
+        custom_unroll_args<List...>::template exec(Shader::getInstance(), meshes->at(i));
+    }
+}
 
 static video::ITexture *displaceTex = 0;
 
 void IrrDriver::renderTransparent()
 {
-    irr_driver->setPhase(TRANSPARENT_PASS);
     glEnable(GL_DEPTH_TEST);
     glDisable(GL_ALPHA_TEST);
     glDepthMask(GL_FALSE);
     glEnable(GL_BLEND);
     glBlendEquation(GL_FUNC_ADD);
     glDisable(GL_CULL_FACE);
-    ListBlendTransparent::getInstance()->clear();
-    ListAdditiveTransparent::getInstance()->clear();
-    ListBlendTransparentFog::getInstance()->clear();
-    ListAdditiveTransparentFog::getInstance()->clear();
-    ListDisplacement::getInstance()->clear();
-    m_scene_manager->drawAll(scene::ESNRP_TRANSPARENT);
 
-    glBindVertexArray(getVAO(EVT_STANDARD));
+    irr_driver->setPhase(TRANSPARENT_PASS);
+
+    for (unsigned i = 0; i < ImmediateDrawList::getInstance()->size(); i++)
+        ImmediateDrawList::getInstance()->at(i)->render();
+
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(EVT_STANDARD));
 
     if (World::getWorld() && World::getWorld()->isFogEnabled())
     {
         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        renderMeshes2ndPass<MeshShader::TransparentFogShader, video::EVT_STANDARD, 8, 7, 6, 5, 4, 3, 2, 1>(TexUnits(
-            TexUnit(MeshShader::TransparentFogShader::getInstance()->TU_tex, true)
-        ), ListBlendTransparentFog::getInstance());
+        renderTransparenPass<MeshShader::TransparentFogShader, video::EVT_STANDARD, 8, 7, 6, 5, 4, 3, 2, 1>(TexUnits(
+            TexUnit(0, true)), ListBlendTransparentFog::getInstance());
         glBlendFunc(GL_ONE, GL_ONE);
-        renderMeshes2ndPass<MeshShader::TransparentFogShader, video::EVT_STANDARD, 8, 7, 6, 5, 4, 3, 2, 1>(TexUnits(
-            TexUnit(MeshShader::TransparentFogShader::getInstance()->TU_tex, true)
-        ), ListAdditiveTransparentFog::getInstance());
+        renderTransparenPass<MeshShader::TransparentFogShader, video::EVT_STANDARD, 8, 7, 6, 5, 4, 3, 2, 1>(TexUnits(
+            TexUnit(0, true)), ListAdditiveTransparentFog::getInstance());
     }
     else
     {
         glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
-        renderMeshes2ndPass<MeshShader::TransparentShader, video::EVT_STANDARD, 2, 1>(TexUnits(
-            TexUnit(MeshShader::TransparentShader::getInstance()->TU_tex, true)
-        ), ListBlendTransparent::getInstance());
+        renderTransparenPass<MeshShader::TransparentShader, video::EVT_STANDARD, 2, 1>(TexUnits(
+            TexUnit(0, true)), ListBlendTransparent::getInstance());
         glBlendFunc(GL_ONE, GL_ONE);
-        renderMeshes2ndPass<MeshShader::TransparentShader, video::EVT_STANDARD, 2, 1>(TexUnits(
-            TexUnit(MeshShader::TransparentShader::getInstance()->TU_tex, true)
-        ), ListAdditiveTransparent::getInstance());
+        renderTransparenPass<MeshShader::TransparentShader, video::EVT_STANDARD, 2, 1>(TexUnits(
+            TexUnit(0, true)), ListAdditiveTransparent::getInstance());
     }
 
     if (!UserConfigParams::m_dynamic_lights)
@@ -529,13 +624,16 @@ void IrrDriver::renderTransparent()
     glStencilFunc(GL_ALWAYS, 1, 0xFF);
     glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
 
-    glBindVertexArray(getVAO(EVT_2TCOORDS));
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(EVT_2TCOORDS));
     // Generate displace mask
     // Use RTT_TMP4 as displace mask
     irr_driver->getFBO(FBO_TMP1_WITH_DS).Bind();
     for (unsigned i = 0; i < ListDisplacement::getInstance()->size(); i++)
     {
         const GLMesh &mesh = *(STK::tuple_get<0>(ListDisplacement::getInstance()->at(i)));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh.vao);
         const core::matrix4 &AbsoluteTransformation = STK::tuple_get<1>(ListDisplacement::getInstance()->at(i));
         if (mesh.VAOType != video::EVT_2TCOORDS)
         {
@@ -560,6 +658,8 @@ void IrrDriver::renderTransparent()
     for (unsigned i = 0; i < ListDisplacement::getInstance()->size(); i++)
     {
         const GLMesh &mesh = *(STK::tuple_get<0>(ListDisplacement::getInstance()->at(i)));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh.vao);
         const core::matrix4 &AbsoluteTransformation = STK::tuple_get<1>(ListDisplacement::getInstance()->at(i));
         if (mesh.VAOType != video::EVT_2TCOORDS)
             continue;
@@ -568,10 +668,11 @@ void IrrDriver::renderTransparent()
         GLenum itype = mesh.IndexType;
         size_t count = mesh.IndexCount;
         // Render the effect
-        setTexture(MeshShader::DisplaceShader::getInstance()->TU_displacement_tex, getTextureGLuint(displaceTex), GL_LINEAR, GL_LINEAR, true);
-        setTexture(MeshShader::DisplaceShader::getInstance()->TU_mask_tex, irr_driver->getRenderTargetTexture(RTT_TMP1), GL_LINEAR, GL_LINEAR, true);
-        setTexture(MeshShader::DisplaceShader::getInstance()->TU_color_tex, irr_driver->getRenderTargetTexture(RTT_COLOR), GL_LINEAR, GL_LINEAR, true);
-        setTexture(MeshShader::DisplaceShader::getInstance()->TU_tex, getTextureGLuint(mesh.textures[0]), GL_LINEAR, GL_LINEAR, true);
+        MeshShader::DisplaceShader::getInstance()->SetTextureUnits(
+            createVector<GLuint>(getTextureGLuint(displaceTex),
+                irr_driver->getRenderTargetTexture(RTT_COLOR),
+                irr_driver->getRenderTargetTexture(RTT_TMP1),
+                getTextureGLuint(mesh.textures[0])));
         glUseProgram(MeshShader::DisplaceShader::getInstance()->Program);
         MeshShader::DisplaceShader::getInstance()->setUniforms(AbsoluteTransformation,
             core::vector2df(cb->getDirX(), cb->getDirY()),
@@ -588,15 +689,15 @@ void IrrDriver::renderTransparent()
 }
 
 template<typename T, typename...uniforms>
-void drawShadow(const T *Shader, const GLMesh *mesh, uniforms... Args)
+void drawShadow(const T *Shader, unsigned cascade, const GLMesh *mesh, uniforms... Args)
 {
     irr_driver->IncreaseObjectCount();
     GLenum ptype = mesh->PrimitiveType;
     GLenum itype = mesh->IndexType;
     size_t count = mesh->IndexCount;
 
-    Shader->setUniforms(Args...);
-    glDrawElementsInstancedBaseVertex(ptype, count, itype, (GLvoid *)mesh->vaoOffset, 4, mesh->vaoBaseVertex);
+    Shader->setUniforms(cascade, Args...);
+    glDrawElementsBaseVertex(ptype, count, itype, (GLvoid *)mesh->vaoOffset, mesh->vaoBaseVertex);
 }
 
 template<int...List>
@@ -606,9 +707,9 @@ template<>
 struct shadow_custom_unroll_args<>
 {
     template<typename T, typename ...TupleTypes, typename ...Args>
-    static void exec(const T *Shader, const STK::Tuple<TupleTypes...> &t, Args... args)
+    static void exec(const T *Shader, unsigned cascade, const STK::Tuple<TupleTypes...> &t, Args... args)
     {
-        drawShadow<T>(Shader, STK::tuple_get<0>(t), args...);
+        drawShadow<T>(Shader, cascade, STK::tuple_get<0>(t), args...);
     }
 };
 
@@ -616,81 +717,82 @@ template<int N, int...List>
 struct shadow_custom_unroll_args<N, List...>
 {
     template<typename T, typename ...TupleTypes, typename ...Args>
-    static void exec(const T *Shader, const STK::Tuple<TupleTypes...> &t, Args... args)
+    static void exec(const T *Shader, unsigned cascade, const STK::Tuple<TupleTypes...> &t, Args... args)
     {
-        shadow_custom_unroll_args<List...>::template exec<T>(Shader, t, STK::tuple_get<N>(t), args...);
+        shadow_custom_unroll_args<List...>::template exec<T>(Shader, cascade, t, STK::tuple_get<N>(t), args...);
     }
 };
 
 template<typename T, enum E_VERTEX_TYPE VertexType, int...List, typename... Args>
-void renderShadow(const std::vector<GLuint> TextureUnits, const std::vector<STK::Tuple<Args...> > *t)
+void renderShadow(const std::vector<GLuint> TextureUnits, unsigned cascade, const std::vector<STK::Tuple<Args...> > &t)
 {
     glUseProgram(T::getInstance()->Program);
-    glBindVertexArray(getVAO(VertexType));
-    for (unsigned i = 0; i < t->size(); i++)
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(VertexType));
+    for (unsigned i = 0; i < t.size(); i++)
     {
-        const GLMesh *mesh = STK::tuple_get<0>(t->at(i));
+        std::vector<uint64_t> Handles;
+        std::vector<GLuint> Textures;
+        GLMesh *mesh = STK::tuple_get<0>(t.at(i));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh->vao);
         for (unsigned j = 0; j < TextureUnits.size(); j++)
         {
-            compressTexture(mesh->textures[j], true);
-            setTexture(TextureUnits[j], getTextureGLuint(mesh->textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
+            compressTexture(mesh->textures[TextureUnits[j]], true);
+            if (UserConfigParams::m_azdo)
+                Handles.push_back(mesh->TextureHandles[TextureUnits[j]]);
+            else
+                Textures.push_back(getTextureGLuint(mesh->textures[TextureUnits[j]]));
         }
-
-        shadow_custom_unroll_args<List...>::template exec<T>(T::getInstance(), t->at(i));
+        if (UserConfigParams::m_azdo)
+            T::getInstance()->SetTextureHandles(Handles);
+        else
+            T::getInstance()->SetTextureUnits(Textures);
+        shadow_custom_unroll_args<List...>::template exec<T>(T::getInstance(), cascade, t.at(i));
     }
 }
 
-template<int...List>
-struct instanced_shadow_custom_unroll_args;
-
-template<>
-struct instanced_shadow_custom_unroll_args<>
+#ifdef Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+void renderInstancedShadow(const std::vector<GLuint> TextureUnits, unsigned cascade, const std::vector<GLMesh *> &t, Args ...args)
 {
-    template<typename T, typename ...TupleTypes, typename ...Args>
-    static void exec(const T *Shader, const STK::Tuple<TupleTypes...> &t, Args... args)
+    glUseProgram(Shader::getInstance()->Program);
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeShadow));
+    for (unsigned i = 0; i < t.size(); i++)
     {
-        const GLMesh *mesh = STK::tuple_get<0>(t);
-        size_t instance_count = STK::tuple_get<1>(t);
-        irr_driver->IncreaseObjectCount();
-        GLenum ptype = mesh->PrimitiveType;
-        GLenum itype = mesh->IndexType;
-        size_t count = mesh->IndexCount;
+        std::vector<uint64_t> Handles;
+        std::vector<GLuint> Textures;
+        GLMesh *mesh = t[i];
 
-        Shader->setUniforms(args...);
-        glDrawElementsInstanced(ptype, count, itype, 0, 4 * instance_count);
-    }
-};
-
-template<int N, int...List>
-struct instanced_shadow_custom_unroll_args<N, List...>
-{
-    template<typename T, typename ...TupleTypes, typename ...Args>
-    static void exec(const T *Shader, const STK::Tuple<TupleTypes...> &t, Args... args)
-    {
-        instanced_shadow_custom_unroll_args<List...>::template exec<T>(Shader, t, STK::tuple_get<N>(t), args...);
-    }
-};
-
-template<typename T, int...List, typename... Args>
-void renderInstancedShadow(const std::vector<GLuint> TextureUnits, const std::vector<STK::Tuple<Args...> > *t)
-{
-    glUseProgram(T::getInstance()->Program);
-    for (unsigned i = 0; i < t->size(); i++)
-    {
-        const GLMesh *mesh = STK::tuple_get<0>(t->at(i));
-        glBindVertexArray(mesh->vao_shadow_pass);
         for (unsigned j = 0; j < TextureUnits.size(); j++)
-        {
-            compressTexture(mesh->textures[j], true);
-            setTexture(TextureUnits[j], getTextureGLuint(mesh->textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
-        }
+            Textures.push_back(getTextureGLuint(mesh->textures[TextureUnits[j]]));
 
-        instanced_shadow_custom_unroll_args<List...>::template exec<T>(T::getInstance(), t->at(i));
+        Shader::getInstance()->SetTextureUnits(Textures);
+        Shader::getInstance()->setUniforms(cascade, args...);
+        size_t tmp = ShadowPassCmd::getInstance()->Offset[cascade][Mat] + i;
+        glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)((tmp) * sizeof(DrawElementsIndirectCommand)));
     }
 }
+#endif
+
+#ifdef Multi_Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+static void multidrawShadow(unsigned i, Args ...args)
+{
+    glUseProgram(Shader::getInstance()->Program);
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeShadow));
+    if (ShadowPassCmd::getInstance()->Size[i][Mat])
+    {
+        Shader::getInstance()->setUniforms(i, args...);
+        glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)(ShadowPassCmd::getInstance()->Offset[i][Mat] * sizeof(DrawElementsIndirectCommand)), ShadowPassCmd::getInstance()->Size[i][Mat], sizeof(DrawElementsIndirectCommand));
+    }
+}
+#endif
 
 void IrrDriver::renderShadows()
 {
+    ScopedGPUTimer Timer(getGPUTimer(Q_SHADOWS));
+
     glDepthFunc(GL_LEQUAL);
     glDepthMask(GL_TRUE);
     glEnable(GL_DEPTH_TEST);
@@ -702,35 +804,47 @@ void IrrDriver::renderShadows()
     glClear(GL_DEPTH_BUFFER_BIT);
     glDrawBuffer(GL_NONE);
 
-    irr_driver->setPhase(SHADOW_PASS);
-    ListMatDefault::getInstance()->clear();
-    ListMatAlphaRef::getInstance()->clear();
-    ListMatSphereMap::getInstance()->clear();
-    ListMatDetails::getInstance()->clear();
-    ListMatUnlit::getInstance()->clear();
-    ListMatNormalMap::getInstance()->clear();
-    ListMatGrass::getInstance()->clear();
-    ListMatSplatting::getInstance()->clear();
-    ListInstancedMatDefault::getInstance()->clear();
-    ListInstancedMatAlphaRef::getInstance()->clear();
-    ListInstancedMatGrass::getInstance()->clear();
-    ListInstancedMatNormalMap::getInstance()->clear();
-    m_scene_manager->drawAll(scene::ESNRP_SOLID);
 
-    std::vector<GLuint> noTexUnits;
-    renderShadow<MeshShader::ShadowShader, EVT_STANDARD, 1>(noTexUnits, ListMatDefault::getInstance());
-    renderShadow<MeshShader::ShadowShader, EVT_STANDARD, 1>(noTexUnits, ListMatSphereMap::getInstance());
-    renderShadow<MeshShader::ShadowShader, EVT_2TCOORDS, 1>(noTexUnits, ListMatDetails::getInstance());
-    renderShadow<MeshShader::ShadowShader, EVT_2TCOORDS, 1>(noTexUnits, ListMatSplatting::getInstance());
-    renderShadow<MeshShader::ShadowShader, EVT_TANGENTS, 1>(noTexUnits, ListMatNormalMap::getInstance());
-    renderShadow<MeshShader::RefShadowShader, EVT_STANDARD, 1>(std::vector<GLuint>{ MeshShader::RefShadowShader::getInstance()->TU_tex }, ListMatAlphaRef::getInstance());
-    renderShadow<MeshShader::RefShadowShader, EVT_STANDARD, 1>(std::vector<GLuint>{ MeshShader::RefShadowShader::getInstance()->TU_tex }, ListMatUnlit::getInstance());
-    renderShadow<MeshShader::GrassShadowShader, EVT_STANDARD, 3, 1>(std::vector<GLuint>{ MeshShader::GrassShadowShader::getInstance()->TU_tex }, ListMatGrass::getInstance());
+    for (unsigned cascade = 0; cascade < 4; cascade++)
+    {
 
-    renderInstancedShadow<MeshShader::InstancedShadowShader>(noTexUnits, ListInstancedMatDefault::getInstance());
-    renderInstancedShadow<MeshShader::InstancedRefShadowShader>(std::vector<GLuint>{ MeshShader::InstancedRefShadowShader::getInstance()->TU_tex }, ListInstancedMatAlphaRef::getInstance());
-    renderInstancedShadow<MeshShader::InstancedGrassShadowShader, 2>(std::vector<GLuint>{ MeshShader::InstancedGrassShadowShader::getInstance()->TU_tex }, ListInstancedMatGrass::getInstance());
-    renderInstancedShadow<MeshShader::InstancedShadowShader>(noTexUnits, ListInstancedMatNormalMap::getInstance());
+        std::vector<GLuint> noTexUnits;
+
+        renderShadow<MeshShader::ShadowShader, EVT_STANDARD, 1>(noTexUnits, cascade, ListMatDefault::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::ShadowShader, EVT_STANDARD, 1>(noTexUnits, cascade, ListMatSphereMap::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::ShadowShader, EVT_2TCOORDS, 1>(noTexUnits, cascade, ListMatDetails::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::ShadowShader, EVT_2TCOORDS, 1>(noTexUnits, cascade, ListMatSplatting::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::ShadowShader, EVT_TANGENTS, 1>(noTexUnits, cascade, ListMatNormalMap::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::RefShadowShader, EVT_STANDARD, 1>(std::vector<GLuint>{ 0 }, cascade, ListMatAlphaRef::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::RefShadowShader, EVT_STANDARD, 1>(std::vector<GLuint>{ 0 }, cascade, ListMatUnlit::getInstance()->Shadows[cascade]);
+        renderShadow<MeshShader::GrassShadowShader, EVT_STANDARD, 3, 1>(std::vector<GLuint>{ 0 }, cascade, ListMatGrass::getInstance()->Shadows[cascade]);
+
+        if (irr_driver->hasARB_draw_indirect())
+            glBindBuffer(GL_DRAW_INDIRECT_BUFFER, ShadowPassCmd::getInstance()->drawindirectcmd);
+
+        if (UserConfigParams::m_azdo)
+        {
+#ifdef Multi_Draw_Indirect
+            multidrawShadow<MeshShader::InstancedShadowShader, MAT_DEFAULT, video::EVT_STANDARD>(cascade);
+            multidrawShadow<MeshShader::InstancedShadowShader, MAT_DETAIL, video::EVT_2TCOORDS>(cascade);
+            multidrawShadow<MeshShader::InstancedShadowShader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(cascade);
+            multidrawShadow<MeshShader::InstancedRefShadowShader, MAT_ALPHA_REF, video::EVT_STANDARD>(cascade);
+            multidrawShadow<MeshShader::InstancedRefShadowShader, MAT_UNLIT, video::EVT_STANDARD>(cascade);
+            multidrawShadow<MeshShader::InstancedGrassShadowShader, MAT_GRASS, video::EVT_STANDARD>(cascade, windDir);
+#endif
+        }
+#ifdef Draw_Indirect
+        else if (irr_driver->hasARB_draw_indirect())
+        {
+            renderInstancedShadow<MeshShader::InstancedShadowShader, MAT_DEFAULT, video::EVT_STANDARD>(noTexUnits, cascade, ListInstancedMatDefault::getInstance()->Shadows[cascade]);
+            renderInstancedShadow<MeshShader::InstancedShadowShader, MAT_DETAIL, video::EVT_2TCOORDS>(noTexUnits, cascade, ListInstancedMatDetails::getInstance()->Shadows[cascade]);
+            renderInstancedShadow<MeshShader::InstancedRefShadowShader, MAT_ALPHA_REF, video::EVT_STANDARD>(std::vector < GLuint > { 0 }, cascade, ListInstancedMatAlphaRef::getInstance()->Shadows[cascade]);
+            renderInstancedShadow<MeshShader::InstancedRefShadowShader, MAT_UNLIT, video::EVT_STANDARD>(std::vector < GLuint > { 0 }, cascade, ListInstancedMatUnlit::getInstance()->Shadows[cascade]);
+            renderInstancedShadow<MeshShader::InstancedGrassShadowShader, MAT_GRASS, video::EVT_STANDARD>(std::vector < GLuint > { 0 }, cascade, ListInstancedMatGrass::getInstance()->Shadows[cascade], windDir);
+            renderInstancedShadow<MeshShader::InstancedShadowShader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(noTexUnits, cascade, ListInstancedMatNormalMap::getInstance()->Shadows[cascade]);
+        }
+#endif
+    }
 
     glDisable(GL_POLYGON_OFFSET_FILL);
 }
@@ -761,41 +875,94 @@ struct rsm_custom_unroll_args<N, List...>
 };
 
 template<typename T, enum E_VERTEX_TYPE VertexType, int... Selector, typename... Args>
-void drawRSM(const core::matrix4 & rsm_matrix, const std::vector<GLuint> &TextureUnits, std::vector<STK::Tuple<Args...> > *t)
+void drawRSM(const core::matrix4 & rsm_matrix, const std::vector<GLuint> &TextureUnits, const std::vector<STK::Tuple<Args...> > &t)
 {
     glUseProgram(T::getInstance()->Program);
-    glBindVertexArray(getVAO(VertexType));
-    for (unsigned i = 0; i < t->size(); i++)
+    if (irr_driver->hasARB_base_instance())
+        glBindVertexArray(VAOManager::getInstance()->getVAO(VertexType));
+    for (unsigned i = 0; i < t.size(); i++)
     {
-        GLMesh *mesh = STK::tuple_get<0>(t->at(i));
+        std::vector<GLuint> Textures;
+        GLMesh *mesh = STK::tuple_get<0>(t.at(i));
+        if (!irr_driver->hasARB_base_instance())
+            glBindVertexArray(mesh->vao);
         for (unsigned j = 0; j < TextureUnits.size(); j++)
-        {
-            if (!mesh->textures[j])
-                mesh->textures[j] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
-            compressTexture(mesh->textures[j], true);
-            setTexture(TextureUnits[j], getTextureGLuint(mesh->textures[j]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
-        }
-        rsm_custom_unroll_args<Selector...>::template exec<T>(rsm_matrix, t->at(i));
+            Textures.push_back(getTextureGLuint(mesh->textures[TextureUnits[j]]));
+        T::getInstance()->SetTextureUnits(Textures);
+        rsm_custom_unroll_args<Selector...>::template exec<T>(rsm_matrix, t.at(i));
     }
 }
 
+#ifdef Draw_Indirect
+template<typename Shader, MeshMaterial Mat, video::E_VERTEX_TYPE VT, typename...Args>
+void renderRSMShadow(const std::vector<GLuint> TextureUnits, const std::vector<GLMesh *> &t, Args ...args)
+{
+    glUseProgram(Shader::getInstance()->Program);
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VT, InstanceTypeRSM));
+    for (unsigned i = 0; i < t.size(); i++)
+    {
+        std::vector<uint64_t> Handles;
+        std::vector<GLuint> Textures;
+        GLMesh *mesh = t[i];
+
+        for (unsigned j = 0; j < TextureUnits.size(); j++)
+            Textures.push_back(getTextureGLuint(mesh->textures[TextureUnits[j]]));
+
+        Shader::getInstance()->SetTextureUnits(Textures);
+        Shader::getInstance()->setUniforms(args...);
+        glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)((RSMPassCmd::getInstance()->Offset[Mat] + i)* sizeof(DrawElementsIndirectCommand)));
+    }
+}
+#endif
+
+#ifdef Multi_Draw_Indirect
+template<typename Shader, MeshMaterial Mat, enum E_VERTEX_TYPE VertexType, typename... Args>
+void multidrawRSM(Args...args)
+{
+    glUseProgram(Shader::getInstance()->Program);
+    glBindVertexArray(VAOManager::getInstance()->getInstanceVAO(VertexType, InstanceTypeRSM));
+    if (RSMPassCmd::getInstance()->Size[Mat])
+    {
+        Shader::getInstance()->setUniforms(args...);
+        glMultiDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_SHORT, (const void*)(RSMPassCmd::getInstance()->Offset[Mat] * sizeof(DrawElementsIndirectCommand)), RSMPassCmd::getInstance()->Size[Mat], sizeof(DrawElementsIndirectCommand));
+    }
+}
+#endif
+
 void IrrDriver::renderRSM()
 {
+    ScopedGPUTimer Timer(getGPUTimer(Q_RSM));
     m_rtts->getRSM().Bind();
     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 
-    drawRSM<MeshShader::RSMShader, EVT_STANDARD, 3, 1>(rsm_matrix, std::vector<GLuint>{ MeshShader::RSMShader::getInstance()->TU_tex }, ListMatDefault::getInstance());
-    drawRSM<MeshShader::RSMShader, EVT_STANDARD, 3, 1>(rsm_matrix, std::vector<GLuint>{ MeshShader::RSMShader::getInstance()->TU_tex }, ListMatAlphaRef::getInstance());
-    drawRSM<MeshShader::RSMShader, EVT_TANGENTS, 3, 1>(rsm_matrix, std::vector<GLuint>{ MeshShader::RSMShader::getInstance()->TU_tex }, ListMatNormalMap::getInstance());
-    drawRSM<MeshShader::RSMShader, EVT_STANDARD, 3, 1>(rsm_matrix, std::vector<GLuint>{ MeshShader::RSMShader::getInstance()->TU_tex }, ListMatUnlit::getInstance());
-    drawRSM<MeshShader::RSMShader, EVT_2TCOORDS, 3, 1>(rsm_matrix, std::vector<GLuint>{ MeshShader::RSMShader::getInstance()->TU_tex }, ListMatDetails::getInstance());
-    drawRSM<MeshShader::SplattingRSMShader, EVT_2TCOORDS, 1>(rsm_matrix,
-        std::vector<GLuint>{
-            8,
-            MeshShader::SplattingRSMShader::getInstance()->TU_layout,
-            MeshShader::SplattingRSMShader::getInstance()->TU_detail0,
-            MeshShader::SplattingRSMShader::getInstance()->TU_detail1,
-            MeshShader::SplattingRSMShader::getInstance()->TU_detail2,
-            MeshShader::SplattingRSMShader::getInstance()->TU_detail3},
-            ListMatSplatting::getInstance());
+    drawRSM<MeshShader::RSMShader, EVT_STANDARD, 3, 1>(rsm_matrix, std::vector<GLuint>{ 0 }, ListMatDefault::getInstance()->RSM);
+    drawRSM<MeshShader::RSMShader, EVT_STANDARD, 3, 1>(rsm_matrix, std::vector<GLuint>{ 0 }, ListMatAlphaRef::getInstance()->RSM);
+    drawRSM<MeshShader::RSMShader, EVT_TANGENTS, 3, 1>(rsm_matrix, std::vector<GLuint>{ 0 }, ListMatNormalMap::getInstance()->RSM);
+    drawRSM<MeshShader::RSMShader, EVT_STANDARD, 3, 1>(rsm_matrix, std::vector<GLuint>{ 0 }, ListMatUnlit::getInstance()->RSM);
+    drawRSM<MeshShader::RSMShader, EVT_2TCOORDS, 3, 1>(rsm_matrix, std::vector<GLuint>{ 0 }, ListMatDetails::getInstance()->RSM);
+    drawRSM<MeshShader::SplattingRSMShader, EVT_2TCOORDS, 1>(rsm_matrix, createVector<GLuint>(1, 2, 3, 4, 5), ListMatSplatting::getInstance()->RSM);
+
+    if (irr_driver->hasARB_draw_indirect())
+        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, RSMPassCmd::getInstance()->drawindirectcmd);
+
+    if (UserConfigParams::m_azdo)
+    {
+#ifdef Multi_Draw_Indirect
+        multidrawRSM<MeshShader::InstancedRSMShader, MAT_DEFAULT, video::EVT_STANDARD>(rsm_matrix);
+        multidrawRSM<MeshShader::InstancedRSMShader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(rsm_matrix);
+        multidrawRSM<MeshShader::InstancedRSMShader, MAT_ALPHA_REF, video::EVT_STANDARD>(rsm_matrix);
+        multidrawRSM<MeshShader::InstancedRSMShader, MAT_UNLIT, video::EVT_STANDARD>(rsm_matrix);
+        multidrawRSM<MeshShader::InstancedRSMShader, MAT_DETAIL, video::EVT_2TCOORDS>(rsm_matrix);
+#endif
+    }
+#ifdef Draw_Indirect
+    else if (irr_driver->hasARB_draw_indirect())
+    {
+        renderRSMShadow<MeshShader::InstancedRSMShader, MAT_DEFAULT, video::EVT_STANDARD>(std::vector < GLuint > { 0 }, ListInstancedMatDefault::getInstance()->RSM, rsm_matrix);
+        renderRSMShadow<MeshShader::InstancedRSMShader, MAT_ALPHA_REF, video::EVT_STANDARD>(std::vector < GLuint > { 0 }, ListInstancedMatAlphaRef::getInstance()->RSM, rsm_matrix);
+        renderRSMShadow<MeshShader::InstancedRSMShader, MAT_UNLIT, video::EVT_STANDARD>(std::vector < GLuint > { 0 }, ListInstancedMatUnlit::getInstance()->RSM, rsm_matrix);
+        renderRSMShadow<MeshShader::InstancedRSMShader, MAT_NORMAL_MAP, video::EVT_TANGENTS>(std::vector < GLuint > { 0 }, ListInstancedMatNormalMap::getInstance()->RSM, rsm_matrix);
+        renderRSMShadow<MeshShader::InstancedRSMShader, MAT_DETAIL, video::EVT_2TCOORDS>(std::vector < GLuint > { 0 }, ListInstancedMatDetails::getInstance()->RSM, rsm_matrix);
+    }
+#endif
 }
diff --git a/src/graphics/render_lighting.cpp b/src/graphics/render_lighting.cpp
index d53416f87..d69c7a843 100644
--- a/src/graphics/render_lighting.cpp
+++ b/src/graphics/render_lighting.cpp
@@ -16,7 +16,6 @@
 #include "graphics/screenquad.hpp"
 #include "graphics/shaders.hpp"
 #include "graphics/stkmeshscenenode.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "graphics/wind.hpp"
 #include "io/file_manager.hpp"
 #include "items/item.hpp"
@@ -45,17 +44,13 @@ static void renderPointLights(unsigned count)
     glEnable(GL_DEPTH_TEST);
     glDepthMask(GL_FALSE);
 
-    glUseProgram(LightShader::PointLightShader::Program);
-    glBindVertexArray(LightShader::PointLightShader::vao);
-    glBindBuffer(GL_ARRAY_BUFFER, LightShader::PointLightShader::vbo);
+    glUseProgram(LightShader::PointLightShader::getInstance()->Program);
+    glBindVertexArray(LightShader::PointLightShader::getInstance()->vao);
+    glBindBuffer(GL_ARRAY_BUFFER, LightShader::PointLightShader::getInstance()->vbo);
     glBufferSubData(GL_ARRAY_BUFFER, 0, count * sizeof(LightShader::PointLightInfo), PointLightsInfo);
 
-    setTexture(0, irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), GL_NEAREST, GL_NEAREST);
-    setTexture(1, irr_driver->getDepthStencilTexture(), GL_NEAREST, GL_NEAREST);
-    LightShader::PointLightShader
-        ::setUniforms(core::vector2df(float(UserConfigParams::m_width),
-        float(UserConfigParams::m_height)),
-        200, 0, 1);
+    LightShader::PointLightShader::getInstance()->SetTextureUnits(createVector<GLuint>(irr_driver->getRenderTargetTexture(RTT_NORMAL_AND_DEPTH), irr_driver->getDepthStencilTexture()));
+    LightShader::PointLightShader::getInstance()->setUniforms();
 
     glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, count);
 }
@@ -68,6 +63,9 @@ unsigned IrrDriver::UpdateLightsInfo(scene::ICameraSceneNode * const camnode, fl
     std::vector<LightNode *> BucketedLN[15];
     for (unsigned int i = 0; i < lightcount; i++)
     {
+        if (!m_lights[i]->isVisible())
+            continue;
+
         if (!m_lights[i]->isPointLight())
         {
             m_lights[i]->render();
@@ -140,9 +138,8 @@ void IrrDriver::renderLights(unsigned pointlightcount)
         if (irr_driver->needRHWorkaround())
         {
             glUseProgram(FullScreenShader::NVWorkaroundRadianceHintsConstructionShader::getInstance()->Program);
-            setTexture(FullScreenShader::NVWorkaroundRadianceHintsConstructionShader::getInstance()->TU_ctex, m_rtts->getRSM().getRTT()[0], GL_LINEAR, GL_LINEAR);
-            setTexture(FullScreenShader::NVWorkaroundRadianceHintsConstructionShader::getInstance()->TU_ntex, m_rtts->getRSM().getRTT()[1], GL_LINEAR, GL_LINEAR);
-            setTexture(FullScreenShader::NVWorkaroundRadianceHintsConstructionShader::getInstance()->TU_dtex, m_rtts->getRSM().getDepthTexture(), GL_LINEAR, GL_LINEAR);
+            FullScreenShader::NVWorkaroundRadianceHintsConstructionShader::getInstance()->SetTextureUnits(
+                createVector<GLuint>(m_rtts->getRSM().getRTT()[0], m_rtts->getRSM().getRTT()[1], m_rtts->getRSM().getDepthTexture()));
             for (unsigned i = 0; i < 32; i++)
             {
                 FullScreenShader::NVWorkaroundRadianceHintsConstructionShader::getInstance()->setUniforms(rsm_matrix, rh_matrix, rh_extend, i);
@@ -152,9 +149,13 @@ void IrrDriver::renderLights(unsigned pointlightcount)
         else
         {
             glUseProgram(FullScreenShader::RadianceHintsConstructionShader::getInstance()->Program);
-            setTexture(FullScreenShader::RadianceHintsConstructionShader::getInstance()->TU_ctex, m_rtts->getRSM().getRTT()[0], GL_LINEAR, GL_LINEAR);
-            setTexture(FullScreenShader::RadianceHintsConstructionShader::getInstance()->TU_ntex, m_rtts->getRSM().getRTT()[1], GL_LINEAR, GL_LINEAR);
-            setTexture(FullScreenShader::RadianceHintsConstructionShader::getInstance()->TU_dtex, m_rtts->getRSM().getDepthTexture(), GL_LINEAR, GL_LINEAR);
+            FullScreenShader::RadianceHintsConstructionShader::getInstance()->SetTextureUnits(
+                createVector<GLuint>(
+                    m_rtts->getRSM().getRTT()[0],
+                    m_rtts->getRSM().getRTT()[1],
+                    m_rtts->getRSM().getDepthTexture()
+                )
+            );
             FullScreenShader::RadianceHintsConstructionShader::getInstance()->setUniforms(rsm_matrix, rh_matrix, rh_extend);
             glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, 32);
         }
@@ -162,14 +163,14 @@ void IrrDriver::renderLights(unsigned pointlightcount)
 
     for (unsigned i = 0; i < sun_ortho_matrix.size(); i++)
         sun_ortho_matrix[i] *= getInvViewMatrix();
-    m_rtts->getFBO(FBO_COMBINED_TMP1_TMP2).Bind();
+    m_rtts->getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).Bind();
     if (!UserConfigParams::m_dynamic_lights)
         glClearColor(.5, .5, .5, .5);
     glClear(GL_COLOR_BUFFER_BIT);
     if (!UserConfigParams::m_dynamic_lights)
         return;
 
-    m_rtts->getFBO(FBO_TMP1_WITH_DS).Bind();
+    m_rtts->getFBO(FBO_DIFFUSE).Bind();
     if (UserConfigParams::m_gi)
     {
         ScopedGPUTimer timer(irr_driver->getGPUTimer(Q_GI));
@@ -181,7 +182,7 @@ void IrrDriver::renderLights(unsigned pointlightcount)
         m_post_processing->renderDiffuseEnvMap(blueSHCoeff, greenSHCoeff, redSHCoeff);
     }
 
-    m_rtts->getFBO(FBO_COMBINED_TMP1_TMP2).Bind();
+    m_rtts->getFBO(FBO_COMBINED_DIFFUSE_SPECULAR).Bind();
 
     // Render sunlight if and only if track supports shadow
     if (!World::getWorld() || World::getWorld()->getTrack()->hasShadows())
diff --git a/src/graphics/render_skybox.cpp b/src/graphics/render_skybox.cpp
index adafbf66e..8fcd5c9a6 100644
--- a/src/graphics/render_skybox.cpp
+++ b/src/graphics/render_skybox.cpp
@@ -16,7 +16,6 @@
 #include "graphics/screenquad.hpp"
 #include "graphics/shaders.hpp"
 #include "graphics/stkmeshscenenode.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "graphics/wind.hpp"
 #include "io/file_manager.hpp"
 #include "items/item.hpp"
@@ -440,6 +439,7 @@ GLuint generateCubeMapFromTextures(const std::vector<video::ITexture *> &texture
                     swapPixels(tmp, rgba[i], size, x, y, (size - y - 1), x);
                 }
             }
+            free(tmp);
         }
 
         glBindTexture(GL_TEXTURE_CUBE_MAP, result);
@@ -448,6 +448,7 @@ GLuint generateCubeMapFromTextures(const std::vector<video::ITexture *> &texture
         else
             glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_SRGB_ALPHA, size, size, 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid*)rgba[i]);
     }
+    glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
     for (unsigned i = 0; i < 6; i++)
         delete[] rgba[i];
     return result;
@@ -552,7 +553,7 @@ void IrrDriver::renderSkybox(const scene::ICameraSceneNode *camera)
         return;
     if (!SkyboxCubeMap)
         generateSkyboxCubemap();
-    glBindVertexArray(MeshShader::SkyboxShader::cubevao);
+    glBindVertexArray(MeshShader::SkyboxShader::getInstance()->cubevao);
     glDisable(GL_CULL_FACE);
     assert(SkyboxTextures.size() == 6);
 
@@ -567,15 +568,10 @@ void IrrDriver::renderSkybox(const scene::ICameraSceneNode *camera)
     core::matrix4 invtransform;
     transform.getInverse(invtransform);
 
-    glActiveTexture(GL_TEXTURE0);
-    glBindTexture(GL_TEXTURE_CUBE_MAP, SkyboxCubeMap);
-    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-    glUseProgram(MeshShader::SkyboxShader::Program);
-    MeshShader::SkyboxShader::setUniforms(transform,
-        core::vector2df(float(UserConfigParams::m_width),
-        float(UserConfigParams::m_height)),
-        0);
+    glUseProgram(MeshShader::SkyboxShader::getInstance()->Program);
+    MeshShader::SkyboxShader::getInstance()->setUniforms(transform);
+    MeshShader::SkyboxShader::getInstance()->SetTextureUnits(createVector<GLuint>(SkyboxCubeMap));
+
     glDrawElements(GL_TRIANGLES, 6 * 6, GL_UNSIGNED_INT, 0);
     glBindVertexArray(0);
 }
\ No newline at end of file
diff --git a/src/graphics/rtts.cpp b/src/graphics/rtts.cpp
index aa6ecb6e2..c37c097fb 100644
--- a/src/graphics/rtts.cpp
+++ b/src/graphics/rtts.cpp
@@ -29,11 +29,9 @@ static GLuint generateRTT3D(GLenum target, size_t w, size_t h, size_t d, GLint i
     GLuint result;
     glGenTextures(1, &result);
     glBindTexture(target, result);
-#if WIN32
-    if (irr_driver->getGLSLVersion() >= 420)
+    if (irr_driver->hasARBTextureStorage())
         glTexStorage3D(target, 1, internalFormat, w, h, d);
     else
-#endif
         glTexImage3D(target, 0, internalFormat, w, h, d, 0, format, type, 0);
     return result;
 }
@@ -43,11 +41,9 @@ static GLuint generateRTT(const core::dimension2du &res, GLint internalFormat, G
     GLuint result;
     glGenTextures(1, &result);
     glBindTexture(GL_TEXTURE_2D, result);
-#if WIN32
-    if (irr_driver->getGLSLVersion() >= 420)
+    if (irr_driver->hasARBTextureStorage())
         glTexStorage2D(GL_TEXTURE_2D, mipmaplevel, internalFormat, res.Width, res.Height);
     else
-#endif
         glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, res.Width, res.Height, 0, format, type, 0);
     return result;
 }
@@ -115,6 +111,8 @@ RTT::RTT(size_t width, size_t height)
     RenderTargetTextures[RTT_MLAA_BLEND] = generateRTT(res, GL_SRGB8_ALPHA8, GL_BGR, GL_UNSIGNED_BYTE);
     RenderTargetTextures[RTT_SSAO] = generateRTT(res, GL_R16F, GL_RED, GL_FLOAT);
     RenderTargetTextures[RTT_DISPLACE] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT);
+    RenderTargetTextures[RTT_DIFFUSE] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT);
+    RenderTargetTextures[RTT_SPECULAR] = generateRTT(res, GL_RGBA16F, GL_BGRA, GL_FLOAT);
 
     RenderTargetTextures[RTT_HALF1] = generateRTT(half, GL_RGBA16F, GL_BGRA, GL_FLOAT);
     RenderTargetTextures[RTT_QUARTER1] = generateRTT(quarter, GL_RGBA16F, GL_BGRA, GL_FLOAT);
@@ -133,7 +131,6 @@ RTT::RTT(size_t width, size_t height)
     RenderTargetTextures[RTT_TMP_256] = generateRTT(shadowsize2, GL_RGBA16F, GL_BGR, GL_FLOAT);
     RenderTargetTextures[RTT_BLOOM_128] = generateRTT(shadowsize3, GL_RGBA16F, GL_BGR, GL_FLOAT);
     RenderTargetTextures[RTT_TMP_128] = generateRTT(shadowsize3, GL_RGBA16F, GL_BGR, GL_FLOAT);
-    RenderTargetTextures[RTT_LOG_LUMINANCE] = generateRTT(shadowsize0, GL_R16F, GL_RED, GL_FLOAT);
 
     std::vector<GLuint> somevector;
     somevector.push_back(RenderTargetTextures[RTT_SSAO]);
@@ -143,14 +140,17 @@ RTT::RTT(size_t width, size_t height)
     somevector.push_back(RenderTargetTextures[RTT_NORMAL_AND_DEPTH]);
     FrameBuffers.push_back(new FrameBuffer(somevector, DepthStencilTexture, res.Width, res.Height));
     somevector.clear();
-    somevector.push_back(RenderTargetTextures[RTT_TMP1]);
-    somevector.push_back(RenderTargetTextures[RTT_TMP2]);
+    somevector.push_back(RenderTargetTextures[RTT_DIFFUSE]);
+    somevector.push_back(RenderTargetTextures[RTT_SPECULAR]);
     FrameBuffers.push_back(new FrameBuffer(somevector, DepthStencilTexture, res.Width, res.Height));
     somevector.clear();
     somevector.push_back(RenderTargetTextures[RTT_COLOR]);
     FrameBuffers.push_back(new FrameBuffer(somevector, DepthStencilTexture, res.Width, res.Height));
     somevector.clear();
-    somevector.push_back(RenderTargetTextures[RTT_LOG_LUMINANCE]);
+    somevector.push_back(RenderTargetTextures[RTT_DIFFUSE]);
+    FrameBuffers.push_back(new FrameBuffer(somevector, res.Width, res.Height));
+    somevector.clear();
+    somevector.push_back(RenderTargetTextures[RTT_SPECULAR]);
     FrameBuffers.push_back(new FrameBuffer(somevector, res.Width, res.Height));
     somevector.clear();
     somevector.push_back(RenderTargetTextures[RTT_MLAA_COLORS]);
diff --git a/src/graphics/shaders.cpp b/src/graphics/shaders.cpp
index 289a68f76..0b1391a8e 100644
--- a/src/graphics/shaders.cpp
+++ b/src/graphics/shaders.cpp
@@ -103,10 +103,7 @@
 
 using namespace video;
 
-GLuint getUniformLocation(GLuint program, const char* name)
-{
-    return glGetUniformLocation(program, name);
-}
+std::vector<void(*)()> CleanTable;
 
 Shaders::Shaders()
 {
@@ -294,7 +291,7 @@ static void initShadowVPMUBO()
 {
     glGenBuffers(1, &SharedObject::ViewProjectionMatrixesUBO);
     glBindBuffer(GL_UNIFORM_BUFFER, SharedObject::ViewProjectionMatrixesUBO);
-    glBufferData(GL_UNIFORM_BUFFER, (16 * 8 + 2) * sizeof(float), 0, GL_STATIC_DRAW);
+    glBufferData(GL_UNIFORM_BUFFER, (16 * 9 + 2) * sizeof(float), 0, GL_STREAM_DRAW);
     glBindBuffer(GL_UNIFORM_BUFFER, 0);
 }
 
@@ -402,6 +399,7 @@ void Shaders::loadShaders()
     }
 
     initGL();
+    CleanTable.clear();
     initQuadVBO();
     initQuadBuffer();
     initBillboardVBO();
@@ -409,14 +407,17 @@ void Shaders::loadShaders()
     initFrustrumVBO();
     initShadowVPMUBO();
     initParticleQuadVBO();
-    FullScreenShader::DiffuseEnvMapShader::init();
     MeshShader::BubbleShader::init();
-    LightShader::PointLightShader::init();
-    MeshShader::SkyboxShader::init();
     MeshShader::ViewFrustrumShader::init();
     UtilShader::ColoredLine::init();
 }
 
+void Shaders::killShaders()
+{
+    for (unsigned i = 0; i < CleanTable.size(); i++)
+        CleanTable[i]();
+}
+
 Shaders::~Shaders()
 {
     u32 i;
@@ -565,41 +566,209 @@ namespace UtilShader
 }
 using namespace UtilShader;
 
-void glUniformMatrix4fvWraper(GLuint a, size_t b, unsigned c, const float *d)
-{
-    glUniformMatrix4fv(a, b, c, d);
-}
-
-void glUniform3fWraper(GLuint a, float b, float c, float d)
-{
-    glUniform3f(a, b, c, d);
-}
-
-void glUniform4iWraper(GLuint a, int b, int c, int d, int e)
-{
-    glUniform4i(a, b, c, d, e);
-}
-
-void glUniform2fWraper(GLuint a, float b, float c)
-{
-    glUniform2f(a, b, c);
-}
-
-void glUniform1fWrapper(GLuint a, float b)
-{
-    glUniform1f(a, b);
-}
-
-void glUniform1iWrapper(GLuint a, int b)
-{
-    glUniform1i(a, b);
-}
-
 bool needsUBO()
 {
     return irr_driver->needUBOWorkaround();
 }
 
+void setTextureSampler(GLenum tp, GLuint texunit, GLuint tid, GLuint sid)
+{
+#ifdef GL_VERSION_3_3
+    glActiveTexture(GL_TEXTURE0 + texunit);
+    glBindTexture(tp, tid);
+    glBindSampler(texunit, sid);
+#endif
+}
+
+
+GLuint createNearestSampler()
+{
+#ifdef GL_VERSION_3_3
+    unsigned id;
+    glGenSamplers(1, &id);
+    glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+    return id;
+#endif
+}
+
+void BindTextureNearest(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+}
+
+GLuint createBilinearSampler()
+{
+#ifdef GL_VERSION_3_3
+    unsigned id;
+    glGenSamplers(1, &id);
+    glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+    return id;
+#endif
+}
+
+void BindTextureBilinear(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+}
+
+GLuint createBilinearClampedSampler()
+{
+#ifdef GL_VERSION_3_3
+    unsigned id;
+    glGenSamplers(1, &id);
+    glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+    return id;
+#endif
+}
+
+void BindTextureBilinearClamped(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+}
+
+GLuint createSemiTrilinearSampler()
+{
+#ifdef GL_VERSION_3_3
+    unsigned id;
+    glGenSamplers(1, &id);
+    glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+    return id;
+#endif
+}
+
+void BindTextureSemiTrilinear(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+}
+
+GLuint createTrilinearSampler()
+{
+#ifdef GL_VERSION_3_3
+    unsigned id;
+    glGenSamplers(1, &id);
+    glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    int aniso = UserConfigParams::m_anisotropic;
+    if (aniso == 0) aniso = 1;
+    glSamplerParameterf(id, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso);
+    return id;
+#endif
+}
+
+void BindTextureTrilinearAnisotropic(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_2D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    int aniso = UserConfigParams::m_anisotropic;
+    if (aniso == 0) aniso = 1;
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso);
+}
+
+void BindCubemapTrilinear(unsigned TU, unsigned tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_REPEAT);
+
+    int aniso = UserConfigParams::m_anisotropic;
+    if (aniso == 0) aniso = 1;
+    glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)aniso);
+}
+
+GLuint createShadowSampler()
+{
+#ifdef GL_VERSION_3_3
+    unsigned id;
+    glGenSamplers(1, &id);
+    glSamplerParameteri(id, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glSamplerParameteri(id, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glSamplerParameterf(id, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+    glSamplerParameterf(id, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+    return id;
+#endif
+}
+
+void BindTextureShadow(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
+    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
+    glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
+}
+
+void BindTextureVolume(GLuint TU, GLuint tex)
+{
+    glActiveTexture(GL_TEXTURE0 + TU);
+    glBindTexture(GL_TEXTURE_3D, tex);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
+    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.);
+}
+
+unsigned getGLSLVersion()
+{
+    return irr_driver->getGLSLVersion();
+}
+
 namespace MeshShader
 {
     // Solid Normal and depth pass shaders
@@ -614,8 +783,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     ObjectRefPass1Shader::ObjectRefPass1Shader()
@@ -629,8 +797,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     GrassPass1Shader::GrassPass1Shader()
@@ -640,8 +807,8 @@ namespace MeshShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass1.frag").c_str());
         AssignUniforms("ModelMatrix", "InverseModelMatrix", "windDir");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     NormalMapShader::NormalMapShader()
@@ -655,9 +822,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_normalmap = 1;
-        TU_glossy = 0;
-        AssignTextureUnit(Program, TexUnit(TU_normalmap, "normalMap"), TexUnit(TU_glossy, "DiffuseForAlpha"));
+        AssignSamplerNames(Program, 1, "normalMap", 0, "DiffuseForAlpha");
     }
 
     InstancedObjectPass1Shader::InstancedObjectPass1Shader()
@@ -666,10 +831,10 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_pass1.frag").c_str());
-        TU_tex = 0;
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_object_pass1.frag").c_str());
+
         AssignUniforms();
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -681,10 +846,10 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass1.frag").c_str());
-        TU_tex = 0;
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_objectref_pass1.frag").c_str());
+
         AssignUniforms();
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -696,10 +861,10 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_grass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass1.frag").c_str());
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_objectref_pass1.frag").c_str());
         AssignUniforms("windDir");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -711,15 +876,13 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/encode_normal.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/normalmap.frag").c_str());
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_normalmap.frag").c_str());
         AssignUniforms();
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_normalmap = 1;
-        TU_glossy = 0;
-        AssignTextureUnit(Program, TexUnit(TU_normalmap, "normalMap"), TexUnit(TU_glossy, "DiffuseForAlpha"));
+        AssignSamplerNames(Program, 0, "normalMap", 1, "DiffuseForAlpha");
     }
 
     // Solid Lit pass shaders
@@ -734,14 +897,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_Albedo = 3;
-
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo");
     }
 
     InstancedObjectPass2Shader::InstancedObjectPass2Shader()
@@ -750,16 +906,10 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_pass2.frag").c_str());
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_object_pass2.frag").c_str());
         AssignUniforms();
-        TU_Albedo = 3;
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -772,16 +922,10 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/objectref_pass2.frag").c_str());
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_objectref_pass2.frag").c_str());
         AssignUniforms();
-        TU_Albedo = 3;
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -796,16 +940,23 @@ namespace MeshShader
         AssignUniforms("ModelMatrix");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
-        TU_Albedo = 3;
-        TU_detail = 4;
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo"),
-            TexUnit(TU_detail, "Detail")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo", 4, "Detail");
+    }
+
+    InstancedDetailledObjectPass2Shader::InstancedDetailledObjectPass2Shader()
+    {
+        Program = LoadProgram(
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_detailledobject_pass2.frag").c_str());
+        AssignUniforms();
+
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo", 4, "Detail");
+
+        GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
+        glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
 
     ObjectUnlitShader::ObjectUnlitShader()
@@ -813,14 +964,26 @@ namespace MeshShader
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
-        AssignUniforms("ModelMatrix");
+        AssignUniforms("ModelMatrix", "TextureMatrix");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_tex = 3;
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "tex");
+    }
 
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+    InstancedObjectUnlitShader::InstancedObjectUnlitShader()
+    {
+        Program = LoadProgram(
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_object_unlit.frag").c_str());
+        AssignUniforms();
+
+        GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
+        glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
+
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "tex");
     }
 
     ObjectRefPass2Shader::ObjectRefPass2Shader()
@@ -834,14 +997,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_Albedo = 3;
-
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo");
     }
 
     GrassPass2Shader::GrassPass2Shader()
@@ -851,14 +1007,8 @@ namespace MeshShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/grass_pass2.frag").c_str());
         AssignUniforms("ModelMatrix", "windDir");
-        TU_Albedo = 3;
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "Albedo");
     }
 
     InstancedGrassPass2Shader::InstancedGrassPass2Shader()
@@ -867,18 +1017,10 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_grass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
-            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/grass_pass2.frag").c_str());
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_grass_pass2.frag").c_str());
         AssignUniforms("windDir", "SunDir");
-        TU_Albedo = 3;
-        TU_dtex = 4;
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_Albedo, "Albedo"),
-            TexUnit(TU_dtex, "dtex")
-        );
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "dtex", 4, "Albedo");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -896,14 +1038,23 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_tex = 3;
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "tex");
+    }
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_tex, "tex")
-        );
+    InstancedSphereMapShader::InstancedSphereMapShader()
+    {
+        Program = LoadProgram(
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_object_pass.vert").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_objectpass_spheremap.frag").c_str());
+        AssignUniforms();
+
+        GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
+        glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
+
+        AssignSamplerNames(Program, 0, "DiffuseMap", 1, "SpecularMap", 2, "SSAO", 3, "tex");
     }
 
     SplattingShader::SplattingShader()
@@ -913,22 +1064,16 @@ namespace MeshShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getLightFactor.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/splatting.frag").c_str());
         AssignUniforms("ModelMatrix");
-        TU_tex_layout = 3;
-        TU_tex_detail0 = 4;
-        TU_tex_detail1 = 5;
-        TU_tex_detail2 = 6;
-        TU_tex_detail3 = 7;
 
-        AssignTextureUnit(Program,
-            TexUnit(0, "DiffuseMap"),
-            TexUnit(1, "SpecularMap"),
-            TexUnit(2, "SSAO"),
-            TexUnit(TU_tex_layout, "tex_layout"),
-            TexUnit(TU_tex_detail0, "tex_detail0"),
-            TexUnit(TU_tex_detail1, "tex_detail1"),
-            TexUnit(TU_tex_detail2, "tex_detail2"),
-            TexUnit(TU_tex_detail3, "tex_detail3")
-        );
+        AssignSamplerNames(Program,
+            0, "DiffuseMap",
+            1, "SpecularMap",
+            2, "SSAO",
+            3, "tex_layout",
+            4, "tex_detail0",
+            5, "tex_detail1",
+            6, "tex_detail2",
+            7, "tex_detail3");
     }
 
     GLuint BubbleShader::Program;
@@ -965,9 +1110,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_tex = 0;
-
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     TransparentFogShader::TransparentFogShader()
@@ -980,9 +1123,7 @@ namespace MeshShader
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
 
-        TU_tex = 0;
-
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     BillboardShader::BillboardShader()
@@ -995,9 +1136,7 @@ namespace MeshShader
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
         AssignUniforms("ModelViewMatrix", "ProjectionMatrix", "Position", "Size");
 
-        TU_tex = 0;
-
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     ColorizeShader::ColorizeShader()
@@ -1011,6 +1150,18 @@ namespace MeshShader
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
 
+    InstancedColorizeShader::InstancedColorizeShader()
+    {
+        Program = LoadProgram(
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/glow_object.vert").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/glow_object.frag").c_str());
+        AssignUniforms();
+
+        GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
+        glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
+    }
+
     ShadowShader::ShadowShader()
     {
         // Geometry shader needed
@@ -1029,7 +1180,7 @@ namespace MeshShader
                 GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(),
                 GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/white.frag").c_str());
         }
-        AssignUniforms("ModelMatrix");
+        AssignUniforms("layer", "ModelMatrix");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
@@ -1039,9 +1190,23 @@ namespace MeshShader
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/rsm.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/rsm.frag").c_str());
-        TU_tex = 0;
+
         AssignUniforms("RSMMatrix", "ModelMatrix", "TextureMatrix");
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
+
+        GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
+        glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
+    }
+
+    InstancedRSMShader::InstancedRSMShader()
+    {
+        Program = LoadProgram(
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
+            GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanced_rsm.vert").c_str(),
+            GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_rsm.frag").c_str());
+
+        AssignUniforms("RSMMatrix");
+        AssignSamplerNames(Program, 0, "tex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -1052,13 +1217,9 @@ namespace MeshShader
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/rsm.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/splatting_rsm.frag").c_str());
-        TU_layout = 0;
-        TU_detail0 = 1;
-        TU_detail1 = 2;
-        TU_detail2 = 3;
-        TU_detail3 = 4;
+
         AssignUniforms("RSMMatrix", "ModelMatrix");
-        AssignTextureUnit(Program, TexUnit(TU_layout, "tex_layout"), TexUnit(TU_detail0, "tex_detail0"), TexUnit(TU_detail1, "tex_detail1"), TexUnit(TU_detail2, "tex_detail2"), TexUnit(TU_detail3, "tex_detail3"));
+        AssignSamplerNames(Program, 0, "tex_layout", 1, "tex_detail0", 2, "tex_detail1", 3, "tex_detail2", 4, "tex_detail3");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -1081,9 +1242,10 @@ namespace MeshShader
             Program = LoadProgram(
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanciedshadow.vert").c_str(),
-                GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(),
+                GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/instanced_shadow.geom").c_str(),
                 GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/white.frag").c_str());
         }
+        AssignUniforms("layer");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
@@ -1106,12 +1268,11 @@ namespace MeshShader
                 GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(),
                 GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
         }
-        AssignUniforms("ModelMatrix");
+        AssignUniforms("layer", "ModelMatrix");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
-        TU_tex = 0;
 
-        AssignTextureUnit(Program, { TexUnit(TU_tex, "tex") });
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     InstancedRefShadowShader::InstancedRefShadowShader()
@@ -1124,18 +1285,18 @@ namespace MeshShader
             Program = LoadProgram(
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanciedshadow.vert").c_str(),
-                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
+                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_shadowref.frag").c_str());
         }
         else
         {
             Program = LoadProgram(
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanciedshadow.vert").c_str(),
-                GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(),
-                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
+                GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/instanced_shadow.geom").c_str(),
+                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_shadowref.frag").c_str());
         }
-        TU_tex = 0;
-        AssignTextureUnit(Program, { TexUnit(TU_tex, "tex") });
+        AssignUniforms("layer");
+        AssignSamplerNames(Program, 0, "tex");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
@@ -1158,12 +1319,11 @@ namespace MeshShader
                 GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(),
                 GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
         }
-        AssignUniforms("ModelMatrix", "windDir");
+        AssignUniforms("layer", "ModelMatrix", "windDir");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
-        TU_tex = 0;
 
-        AssignTextureUnit(Program, { TexUnit(TU_tex, "tex") });
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     InstancedGrassShadowShader::InstancedGrassShadowShader()
@@ -1176,20 +1336,20 @@ namespace MeshShader
             Program = LoadProgram(
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanciedgrassshadow.vert").c_str(),
-                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
+                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_shadowref.frag").c_str());
         }
         else
         {
             Program = LoadProgram(
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/utils/getworldmatrix.vert").c_str(),
                 GL_VERTEX_SHADER, file_manager->getAsset("shaders/instanciedgrassshadow.vert").c_str(),
-                GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/shadow.geom").c_str(),
-                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/object_unlit.frag").c_str());
+                GL_GEOMETRY_SHADER, file_manager->getAsset("shaders/instanced_shadow.geom").c_str(),
+                GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/instanced_shadowref.frag").c_str());
         }
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
 
-        AssignUniforms("windDir");
+        AssignSamplerNames(Program, 0, "tex");
+
+        AssignUniforms("layer", "windDir");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
@@ -1212,34 +1372,23 @@ namespace MeshShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/displace.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/displace.frag").c_str());
         AssignUniforms("ModelMatrix", "dir", "dir2");
-        TU_displacement_tex = 0;
-        TU_color_tex = 1;
-        TU_mask_tex = 2;
-        TU_tex = 3;
-        AssignTextureUnit(Program,
-            TexUnit(TU_displacement_tex, "displacement_tex"),
-            TexUnit(TU_color_tex, "color_tex"),
-            TexUnit(TU_mask_tex, "mask_tex"),
-            TexUnit(TU_tex, "tex")
-        );
+
+        AssignSamplerNames(Program,
+            0, "displacement_tex",
+            1, "color_tex",
+            2, "mask_tex",
+            3, "tex");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
 
-    GLuint SkyboxShader::Program;
-    GLuint SkyboxShader::attrib_position;
-    GLuint SkyboxShader::uniform_MM;
-    GLuint SkyboxShader::uniform_tex;
-    GLuint SkyboxShader::cubevao;
-
-    void SkyboxShader::init()
+    SkyboxShader::SkyboxShader()
     {
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/object_pass.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sky.frag").c_str());
-        attrib_position = glGetAttribLocation(Program, "Position");
-        uniform_MM = glGetUniformLocation(Program, "ModelMatrix");
-        uniform_tex = glGetUniformLocation(Program, "tex");
+        AssignUniforms("ModelMatrix");
+        AssignSamplerNames(Program, 0, "tex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -1247,20 +1396,12 @@ namespace MeshShader
         glGenVertexArrays(1, &cubevao);
         glBindVertexArray(cubevao);
         glBindBuffer(GL_ARRAY_BUFFER, SharedObject::cubevbo);
-        glEnableVertexAttribArray(attrib_position);
-        glVertexAttribPointer(attrib_position, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
+        glEnableVertexAttribArray(0);
+        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), 0);
         glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, SharedObject::cubeindexes);
         glBindVertexArray(0);
     }
 
-    void SkyboxShader::setUniforms(const core::matrix4 &ModelMatrix, const core::vector2df &screen, unsigned TU_tex)
-    {
-        if (irr_driver->needUBOWorkaround())
-            bypassUBO(Program);
-        glUniformMatrix4fv(uniform_MM, 1, GL_FALSE, ModelMatrix.pointer());
-        glUniform1i(uniform_tex, TU_tex);
-    }
-
     NormalVisualizer::NormalVisualizer()
     {
         Program = LoadProgram(
@@ -1310,19 +1451,7 @@ namespace MeshShader
 
 namespace LightShader
 {
-
-    GLuint PointLightShader::Program;
-    GLuint PointLightShader::attrib_Position;
-    GLuint PointLightShader::attrib_Color;
-    GLuint PointLightShader::attrib_Energy;
-    GLuint PointLightShader::attrib_Radius;
-    GLuint PointLightShader::uniform_ntex;
-    GLuint PointLightShader::uniform_dtex;
-    GLuint PointLightShader::uniform_spec;
-    GLuint PointLightShader::vbo;
-    GLuint PointLightShader::vao;
-
-    void PointLightShader::init()
+    PointLightShader::PointLightShader()
     {
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/pointlight.vert").c_str(),
@@ -1330,13 +1459,9 @@ namespace LightShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getSpecular.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/pointlight.frag").c_str());
-        attrib_Position = glGetAttribLocation(Program, "Position");
-        attrib_Color = glGetAttribLocation(Program, "Color");
-        attrib_Energy = glGetAttribLocation(Program, "Energy");
-        attrib_Radius = glGetAttribLocation(Program, "Radius");
-        uniform_ntex = glGetUniformLocation(Program, "ntex");
-        uniform_dtex = glGetUniformLocation(Program, "dtex");
-        uniform_spec = glGetUniformLocation(Program, "spec");
+
+        AssignUniforms();
+        AssignSamplerNames(Program, 0, "ntex", 1, "dtex");
 
         glGenVertexArrays(1, &vao);
         glBindVertexArray(vao);
@@ -1345,6 +1470,11 @@ namespace LightShader
         glBindBuffer(GL_ARRAY_BUFFER, vbo);
         glBufferData(GL_ARRAY_BUFFER, MAXLIGHT * sizeof(PointLightInfo), 0, GL_DYNAMIC_DRAW);
 
+        GLuint attrib_Position = glGetAttribLocation(Program, "Position");
+        GLuint attrib_Color = glGetAttribLocation(Program, "Color");
+        GLuint attrib_Energy = glGetAttribLocation(Program, "Energy");
+        GLuint attrib_Radius = glGetAttribLocation(Program, "Radius");
+
         glEnableVertexAttribArray(attrib_Position);
         glVertexAttribPointer(attrib_Position, 3, GL_FLOAT, GL_FALSE, sizeof(PointLightInfo), 0);
         glEnableVertexAttribArray(attrib_Energy);
@@ -1359,17 +1489,6 @@ namespace LightShader
         glVertexAttribDivisor(attrib_Color, 1);
         glVertexAttribDivisor(attrib_Radius, 1);
     }
-
-    void PointLightShader::setUniforms(const core::vector2df &screen, unsigned spec, unsigned TU_ntex, unsigned TU_dtex)
-    {
-        if (irr_driver->needUBOWorkaround())
-            bypassUBO(Program);
-        glUniform1f(uniform_spec, 200);
-
-        glUniform1i(uniform_ntex, TU_ntex);
-        glUniform1i(uniform_dtex, TU_dtex);
-    }
-
 }
 
 
@@ -1409,9 +1528,7 @@ namespace ParticleShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/particle.frag").c_str());
         AssignUniforms("color_from", "color_to");
 
-        TU_tex = 0;
-        TU_dtex = 1;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"), TexUnit(TU_dtex, "dtex"));
+        AssignSamplerNames(Program, 0, "tex", 1, "dtex");
     }
 
     FlipParticleRender::FlipParticleRender()
@@ -1422,9 +1539,7 @@ namespace ParticleShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/particle.frag").c_str());
         AssignUniforms();
 
-        TU_tex = 0;
-        TU_dtex = 1;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"), TexUnit(TU_dtex, "dtex"));
+        AssignSamplerNames(Program, 0, "tex", 1, "dtex");
     }
 }
 
@@ -1453,8 +1568,8 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getCIEXYZ.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bloom.frag").c_str());
         AssignUniforms();
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     BloomBlendShader::BloomBlendShader()
@@ -1463,10 +1578,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bloomblend.frag").c_str());
         AssignUniforms();
-        TU_tex_128 = 0;
-        TU_tex_256 = 1;
-        TU_tex_512 = 2;
-        AssignTextureUnit(Program, TexUnit(TU_tex_128, "tex_128"), TexUnit(TU_tex_256, "tex_256"), TexUnit(TU_tex_512, "tex_512"));
+
+        AssignSamplerNames(Program, 0, "tex_128", 1, "tex_256", 2, "tex_512");
     }
 
     ToneMapShader::ToneMapShader()
@@ -1477,8 +1590,8 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getCIEXYZ.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/tonemap.frag").c_str());
         AssignUniforms();
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "text"));
+
+        AssignSamplerNames(Program, 0, "text");
     }
 
     DepthOfFieldShader::DepthOfFieldShader()
@@ -1486,10 +1599,9 @@ namespace FullScreenShader
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/dof.frag").c_str());
-        TU_tex = 0;
-        TU_depth = 1;
+
         AssignUniforms();
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"), TexUnit(TU_depth, "dtex"));
+        AssignSamplerNames(Program, 0, "tex", 1, "dtex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -1503,42 +1615,22 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getSpecular.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sunlight.frag").c_str());
-        TU_ntex = 0;
-        TU_dtex = 1;
-        AssignTextureUnit(Program, TexUnit(TU_ntex, "ntex"), TexUnit(TU_dtex, "dtex"));
+
+        AssignSamplerNames(Program, 0, "ntex", 1, "dtex");
         AssignUniforms("direction", "col");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
 
-    GLuint DiffuseEnvMapShader::Program;
-    GLuint DiffuseEnvMapShader::uniform_ntex;
-    GLuint DiffuseEnvMapShader::uniform_blueLmn;
-    GLuint DiffuseEnvMapShader::uniform_greenLmn;
-    GLuint DiffuseEnvMapShader::uniform_redLmn;
-    GLuint DiffuseEnvMapShader::uniform_TVM;
-
-    void DiffuseEnvMapShader::init()
+    DiffuseEnvMapShader::DiffuseEnvMapShader()
     {
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/diffuseenvmap.frag").c_str());
-        uniform_ntex = glGetUniformLocation(Program, "ntex");
-        uniform_blueLmn = glGetUniformLocation(Program, "blueLmn[0]");
-        uniform_greenLmn = glGetUniformLocation(Program, "greenLmn[0]");
-        uniform_redLmn = glGetUniformLocation(Program, "redLmn[0]");
-        uniform_TVM = glGetUniformLocation(Program, "TransposeViewMatrix");
-    }
-
-    void DiffuseEnvMapShader::setUniforms(const core::matrix4 &TransposeViewMatrix, const float *blueSHCoeff, const float *greenSHCoeff, const float *redSHCoeff, unsigned TU_ntex)
-    {
-        glUniformMatrix4fv(uniform_TVM, 1, GL_FALSE, TransposeViewMatrix.pointer());
-        glUniform1i(uniform_ntex, TU_ntex);
-        glUniform1fv(uniform_blueLmn, 9, blueSHCoeff);
-        glUniform1fv(uniform_greenLmn, 9, greenSHCoeff);
-        glUniform1fv(uniform_redLmn, 9, redSHCoeff);
+        AssignUniforms("TransposeViewMatrix", "blueLmn[0]", "greenLmn[0]", "redLmn[0]");
+        AssignSamplerNames(Program, 0, "ntex");
     }
 
     ShadowedSunLightShader::ShadowedSunLightShader()
@@ -1549,10 +1641,9 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getSpecular.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/sunlightshadow.frag").c_str());
-        TU_ntex = 0;
-        TU_dtex = 1;
-        TU_shadowtex = 2;
-        AssignTextureUnit(Program, TexUnit(TU_ntex, "ntex"), TexUnit(TU_dtex, "dtex"), TexUnit(TU_shadowtex, "shadowtex"));
+
+        // Use 8 to circumvent a catalyst bug when binding sampler
+        AssignSamplerNames(Program, 0, "ntex", 1, "dtex", 8, "shadowtex");
         AssignUniforms("direction", "col");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
@@ -1578,10 +1669,7 @@ namespace FullScreenShader
         }
 
         AssignUniforms("RSMMatrix", "RHMatrix", "extents");
-        TU_ctex = 0;
-        TU_ntex = 1;
-        TU_dtex = 2;
-        AssignTextureUnit(Program, TexUnit(TU_ctex, "ctex"), TexUnit(TU_ntex, "ntex"), TexUnit(TU_dtex, "dtex"));
+        AssignSamplerNames(Program, 0, "ctex", 1, "ntex", 2, "dtex");
     }
 
     NVWorkaroundRadianceHintsConstructionShader::NVWorkaroundRadianceHintsConstructionShader()
@@ -1594,10 +1682,8 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/rh.frag").c_str());
 
         AssignUniforms("RSMMatrix", "RHMatrix", "extents", "slice");
-        TU_ctex = 0;
-        TU_ntex = 1;
-        TU_dtex = 2;
-        AssignTextureUnit(Program, TexUnit(TU_ctex, "ctex"), TexUnit(TU_ntex, "ntex"), TexUnit(TU_dtex, "dtex"));
+
+        AssignSamplerNames(Program, 0, "ctex", 1, "ntex", 2, "dtex");
     }
 
     RHDebug::RHDebug()
@@ -1623,12 +1709,8 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gi.frag").c_str());
 
         AssignUniforms("RHMatrix", "InvRHMatrix", "extents");
-        TU_ntex = 0;
-        TU_dtex = 1;
-        TU_SHR = 2;
-        TU_SHG = 3;
-        TU_SHB = 4;
-        AssignTextureUnit(Program, TexUnit(TU_ntex, "ntex"), TexUnit(TU_dtex, "dtex"), TexUnit(TU_SHR, "SHR"), TexUnit(TU_SHG, "SHG"), TexUnit(TU_SHB, "SHB"));
+
+        AssignSamplerNames(Program, 0, "ntex", 1, "dtex", 2, "SHR", 3, "SHG", 4, "SHB");
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
     }
@@ -1639,15 +1721,14 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bilateralH.frag").c_str());
         AssignUniforms("pixel");
-        TU_tex = 0;
-        TU_depth = 1;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"), TexUnit(TU_depth, "depth"));
+
+        AssignSamplerNames(Program, 0, "tex", 1, "depth");
     }
 
     ComputeGaussian17TapHShader::ComputeGaussian17TapHShader()
     {
-#if WIN32
-        if (irr_driver->getGLSLVersion() < 420)
+
+        if (irr_driver->hasARBComputeShaders())
             return;
         Program = LoadProgram(
             GL_COMPUTE_SHADER, file_manager->getAsset("shaders/bilateralH.comp").c_str());
@@ -1656,7 +1737,6 @@ namespace FullScreenShader
         TU_dest = 2;
         AssignUniforms();
         AssignTextureUnit(Program, TexUnit(TU_source, "source"), TexUnit(TU_depth, "depth"), TexUnit(TU_dest, "dest"));
-#endif
     }
 
     Gaussian6HBlurShader::Gaussian6HBlurShader()
@@ -1665,8 +1745,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian6h.frag").c_str());
         AssignUniforms("pixel");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     Gaussian3HBlurShader::Gaussian3HBlurShader()
@@ -1675,8 +1755,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian3h.frag").c_str());
         AssignUniforms("pixel");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     Gaussian17TapVShader::Gaussian17TapVShader()
@@ -1685,15 +1765,13 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/bilateralV.frag").c_str());
         AssignUniforms("pixel");
-        TU_tex = 0;
-        TU_depth = 1;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"), TexUnit(TU_depth, "depth"));
+
+        AssignSamplerNames(Program, 0, "tex", 1, "depth");
     }
 
     ComputeGaussian17TapVShader::ComputeGaussian17TapVShader()
     {
-#if WIN32
-        if (irr_driver->getGLSLVersion() < 420)
+        if (irr_driver->hasARBComputeShaders())
             return;
         Program = LoadProgram(
             GL_COMPUTE_SHADER, file_manager->getAsset("shaders/bilateralV.comp").c_str());
@@ -1701,7 +1779,6 @@ namespace FullScreenShader
         TU_depth = 1;
         TU_dest = 2;
         AssignTextureUnit(Program, TexUnit(TU_source, "source"), TexUnit(TU_depth, "depth"), TexUnit(TU_dest, "dest"));
-#endif
     }
 
     Gaussian6VBlurShader::Gaussian6VBlurShader()
@@ -1710,8 +1787,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian6v.frag").c_str());
         AssignUniforms("pixel");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     Gaussian3VBlurShader::Gaussian3VBlurShader()
@@ -1720,8 +1797,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/gaussian3v.frag").c_str());
         AssignUniforms("pixel");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     PassThroughShader::PassThroughShader()
@@ -1729,9 +1806,9 @@ namespace FullScreenShader
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/texturedquad.frag").c_str());
-        TU_tex = 0;
+
         AssignUniforms();
-        AssignTextureUnit(Program, TexUnit(TU_tex, "texture"));
+        AssignSamplerNames(Program, 0, "texture");
         vao = createVAO(Program);
     }
 
@@ -1752,8 +1829,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/linearizedepth.frag").c_str());
         AssignUniforms("zn", "zf");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "texture"));
+
+        AssignSamplerNames(Program, 0, "texture");
     }
 
     GlowShader::GlowShader()
@@ -1762,8 +1839,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/glow.frag").c_str());
         AssignUniforms();
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
         vao = createVAO(Program);
     }
 
@@ -1774,8 +1851,8 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/decodeNormal.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/ssao.frag").c_str());
-        TU_dtex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_dtex, "dtex"));
+
+        AssignSamplerNames(Program, 0, "dtex");
         AssignUniforms("radius", "k", "sigma");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
@@ -1788,9 +1865,9 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/fog.frag").c_str());
-        TU_tex = 0;
+
         AssignUniforms("fogmax", "startH", "endH", "start", "end", "col");
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
 
         GLuint uniform_ViewProjectionMatrixesUBO = glGetUniformBlockIndex(Program, "MatrixesData");
         glUniformBlockBinding(Program, uniform_ViewProjectionMatrixesUBO, 0);
@@ -1803,9 +1880,8 @@ namespace FullScreenShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/utils/getPosFromUVDepth.frag").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/motion_blur.frag").c_str());
         AssignUniforms("previous_viewproj", "center", "boost_amount", "mask_radius");
-        TU_cb = 0;
-        TU_dtex = 1;
-        AssignTextureUnit(Program, TexUnit(TU_dtex, "dtex"), TexUnit(TU_cb, "color_buffer"));
+
+        AssignSamplerNames(Program, 0, "color_buffer", 1, "dtex");
     }
 
     GodFadeShader::GodFadeShader()
@@ -1814,8 +1890,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/godfade.frag").c_str());
         AssignUniforms("col");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
         vao = createVAO(Program);
     }
 
@@ -1824,9 +1900,9 @@ namespace FullScreenShader
         Program = LoadProgram(
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/godray.frag").c_str());
-        TU_tex = 0;
+
         AssignUniforms("sunpos");
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+        AssignSamplerNames(Program, 0, "tex");
         vao = createVAO(Program);
     }
 
@@ -1836,8 +1912,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/mlaa_offset.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/mlaa_color1.frag").c_str());
         AssignUniforms("PIXEL_SIZE");
-        TU_colorMapG = 0;
-        AssignTextureUnit(Program, TexUnit(TU_colorMapG, "colorMapG"));
+
+        AssignSamplerNames(Program, 0, "colorMapG");
         vao = createVAO(Program);
     }
 
@@ -1847,9 +1923,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/screenquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/mlaa_blend2.frag").c_str());
         AssignUniforms("PIXEL_SIZE");
-        TU_edgesMap = 0;
-        TU_areaMap = 1;
-        AssignTextureUnit(Program, TexUnit(TU_edgesMap, "edgesMap"), TexUnit(TU_areaMap, "areaMap"));
+
+        AssignSamplerNames(Program, 0, "edgesMap", 1, "areaMap");
         vao = createVAO(Program);
     }
 
@@ -1859,9 +1934,8 @@ namespace FullScreenShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/mlaa_offset.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/mlaa_neigh3.frag").c_str());
         AssignUniforms("PIXEL_SIZE");
-        TU_blendMap = 0;
-        TU_colorMap = 1;
-        AssignTextureUnit(Program, TexUnit(TU_blendMap, "blendMap"), TexUnit(TU_colorMap, "colorMap"));
+
+        AssignSamplerNames(Program, 0, "blendMap", 1, "colorMap");
         vao = createVAO(Program);
     }
 }
@@ -1874,8 +1948,8 @@ namespace UIShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/texturedquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/texturedquad.frag").c_str());
         AssignUniforms("center", "size", "texcenter", "texsize");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     UniformColoredTextureRectShader::UniformColoredTextureRectShader()
@@ -1885,8 +1959,8 @@ namespace UIShader
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/uniformcolortexturedquad.frag").c_str());
 
         AssignUniforms("center", "size", "texcenter", "texsize", "color");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
     }
 
     ColoredTextureRectShader::ColoredTextureRectShader()
@@ -1895,8 +1969,8 @@ namespace UIShader
             GL_VERTEX_SHADER, file_manager->getAsset("shaders/colortexturedquad.vert").c_str(),
             GL_FRAGMENT_SHADER, file_manager->getAsset("shaders/colortexturedquad.frag").c_str());
         AssignUniforms("center", "size", "texcenter", "texsize");
-        TU_tex = 0;
-        AssignTextureUnit(Program, TexUnit(TU_tex, "tex"));
+
+        AssignSamplerNames(Program, 0, "tex");
 
         glGenVertexArrays(1, &vao);
         glBindVertexArray(vao);
diff --git a/src/graphics/shaders.hpp b/src/graphics/shaders.hpp
index 606070ff4..b5c5adbe5 100644
--- a/src/graphics/shaders.hpp
+++ b/src/graphics/shaders.hpp
@@ -23,7 +23,8 @@
 #include "config/user_config.hpp"
 #include "utils/singleton.hpp"
 
-typedef unsigned int    GLuint;
+#include "gl_headers.hpp"
+
 using namespace irr;
 class ParticleSystemProxy;
 
@@ -51,14 +52,10 @@ public:
 };
 }
 
-void glUniformMatrix4fvWraper(GLuint, size_t, unsigned, const float *mat);
-void glUniform3fWraper(GLuint, float, float, float);
-void glUniform4iWraper(GLuint, int, int, int, int);
-void glUniform2fWraper(GLuint a, float b, float c);
-void glUniform1fWrapper(GLuint, float);
-void glUniform1iWrapper(GLuint, int);
 bool needsUBO();
 
+unsigned getGLSLVersion();
+
 struct UniformHelper
 {
     template<unsigned N = 0>
@@ -69,31 +66,28 @@ struct UniformHelper
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, const core::matrix4 &mat, Args... arg)
     {
-#ifndef GL_FALSE
-#define GL_FALSE 0
-#endif
-        glUniformMatrix4fvWraper(uniforms[N], 1, GL_FALSE, mat.pointer());
+        glUniformMatrix4fv(uniforms[N], 1, GL_FALSE, mat.pointer());
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, const video::SColorf &col, Args... arg)
     {
-        glUniform3fWraper(uniforms[N], col.r, col.g, col.b);
+        glUniform3f(uniforms[N], col.r, col.g, col.b);
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, const video::SColor &col, Args... arg)
     {
-        glUniform4iWraper(uniforms[N], col.getRed(), col.getGreen(), col.getBlue(), col.getAlpha());
+        glUniform4i(uniforms[N], col.getRed(), col.getGreen(), col.getBlue(), col.getAlpha());
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, const core::vector3df &v, Args... arg)
     {
-        glUniform3fWraper(uniforms[N], v.X, v.Y, v.Z);
+        glUniform3f(uniforms[N], v.X, v.Y, v.Z);
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
@@ -101,35 +95,43 @@ struct UniformHelper
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, const core::vector2df &v, Args... arg)
     {
-        glUniform2fWraper(uniforms[N], v.X, v.Y);
+        glUniform2f(uniforms[N], v.X, v.Y);
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, const core::dimension2df &v, Args... arg)
     {
-        glUniform2fWraper(uniforms[N], v.Width, v.Height);
+        glUniform2f(uniforms[N], v.Width, v.Height);
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, float f, Args... arg)
     {
-        glUniform1fWrapper(uniforms[N], f);
+        glUniform1f(uniforms[N], f);
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
     template<unsigned N = 0, typename... Args>
     static void setUniformsHelper(const std::vector<GLuint> &uniforms, int f, Args... arg)
     {
-        glUniform1iWrapper(uniforms[N], f);
+        glUniform1i(uniforms[N], f);
+        setUniformsHelper<N + 1>(uniforms, arg...);
+    }
+
+    template<unsigned N = 0, typename... Args>
+    static void setUniformsHelper(const std::vector<GLuint> &uniforms, const std::vector<float> &v, Args... arg)
+    {
+        glUniform1fv(uniforms[N], v.size(), v.data());
         setUniformsHelper<N + 1>(uniforms, arg...);
     }
 
 };
 
 void bypassUBO(GLuint Program);
-GLuint getUniformLocation(GLuint program, const char* name);
+
+extern std::vector<void(*)()> CleanTable;
 
 template<typename T, typename... Args>
 class ShaderHelperSingleton : public Singleton<T>
@@ -144,7 +146,7 @@ protected:
     template<typename... U>
     void AssignUniforms_impl(const char* name, U... rest)
     {
-        uniforms.push_back(getUniformLocation(Program, name));
+        uniforms.push_back(glGetUniformLocation(Program, name));
         AssignUniforms_impl(rest...);
     }
 
@@ -158,6 +160,16 @@ protected:
 public:
     GLuint Program;
 
+    ShaderHelperSingleton()
+    {
+        CleanTable.push_back(this->kill);
+    }
+
+    ~ShaderHelperSingleton()
+    {
+        glDeleteProgram(Program);
+    }
+
     void setUniforms(const Args & ... args) const
     {
         if (needsUBO())
@@ -166,146 +178,429 @@ public:
     }
 };
 
+enum SamplerType {
+    Trilinear_Anisotropic_Filtered,
+    Semi_trilinear,
+    Bilinear_Filtered,
+    Bilinear_Clamped_Filtered,
+    Nearest_Filtered,
+    Shadow_Sampler,
+    Volume_Linear_Filtered,
+    Trilinear_cubemap,
+};
+
+void setTextureSampler(GLenum, GLuint, GLuint, GLuint);
+
+template<SamplerType...tp>
+struct CreateSamplers;
+
+template<SamplerType...tp>
+struct BindTexture;
+
+template<>
+struct CreateSamplers<>
+{
+    static void exec(std::vector<unsigned> &, std::vector<GLenum> &e)
+    {}
+};
+
+template<>
+struct BindTexture<>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {}
+};
+
+GLuint createNearestSampler();
+
+template<SamplerType...tp>
+struct CreateSamplers<Nearest_Filtered, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createNearestSampler());
+        e.push_back(GL_TEXTURE_2D);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureNearest(unsigned TU, unsigned tid);
+
+template<SamplerType...tp>
+struct BindTexture<Nearest_Filtered, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureNearest(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+GLuint createBilinearSampler();
+
+template<SamplerType...tp>
+struct CreateSamplers<Bilinear_Filtered, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createBilinearSampler());
+        e.push_back(GL_TEXTURE_2D);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureBilinear(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct BindTexture<Bilinear_Filtered, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureBilinear(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+GLuint createBilinearClampedSampler();
+
+template<SamplerType...tp>
+struct CreateSamplers<Bilinear_Clamped_Filtered, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createBilinearClampedSampler());
+        e.push_back(GL_TEXTURE_2D);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureBilinearClamped(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct BindTexture<Bilinear_Clamped_Filtered, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureBilinearClamped(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+GLuint createSemiTrilinearSampler();
+
+template<SamplerType...tp>
+struct CreateSamplers<Semi_trilinear, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createSemiTrilinearSampler());
+        e.push_back(GL_TEXTURE_2D);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureSemiTrilinear(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct BindTexture<Semi_trilinear, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureSemiTrilinear(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+GLuint createTrilinearSampler();
+
+template<SamplerType...tp>
+struct CreateSamplers<Trilinear_Anisotropic_Filtered, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createTrilinearSampler());
+        e.push_back(GL_TEXTURE_2D);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureTrilinearAnisotropic(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct CreateSamplers<Trilinear_cubemap, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createTrilinearSampler());
+        e.push_back(GL_TEXTURE_CUBE_MAP);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindCubemapTrilinear(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct BindTexture<Trilinear_cubemap, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindCubemapTrilinear(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+template<SamplerType...tp>
+struct BindTexture<Trilinear_Anisotropic_Filtered, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureTrilinearAnisotropic(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+template<SamplerType...tp>
+struct CreateSamplers<Volume_Linear_Filtered, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createBilinearSampler());
+        e.push_back(GL_TEXTURE_3D);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureVolume(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct BindTexture<Volume_Linear_Filtered, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureVolume(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+GLuint createShadowSampler();
+
+template<SamplerType...tp>
+struct CreateSamplers<Shadow_Sampler, tp...>
+{
+    static void exec(std::vector<unsigned> &v, std::vector<GLenum> &e)
+    {
+        v.push_back(createShadowSampler());
+        e.push_back(GL_TEXTURE_2D_ARRAY);
+        CreateSamplers<tp...>::exec(v, e);
+    }
+};
+
+void BindTextureShadow(unsigned TU, unsigned tex);
+
+template<SamplerType...tp>
+struct BindTexture<Shadow_Sampler, tp...>
+{
+    static void exec(const std::vector<unsigned> &TU, const std::vector<unsigned> &TexId, unsigned N)
+    {
+        BindTextureShadow(TU[N], TexId[N]);
+        BindTexture<tp...>::exec(TU, TexId, N + 1);
+    }
+};
+
+template<SamplerType...tp>
+class TextureRead
+{
+private:
+    template<unsigned N, typename...Args>
+    void AssignTextureNames_impl(GLuint)
+    {
+        static_assert(N == sizeof...(tp), "Wrong number of texture name");
+    }
+
+    template<unsigned N, typename...Args>
+    void AssignTextureNames_impl(GLuint Program, GLuint TexUnit, const char *name, Args...args)
+    {
+        GLuint location = glGetUniformLocation(Program, name);
+        TextureLocation.push_back(location);
+        glUniform1i(location, TexUnit);
+        TextureUnits.push_back(TexUnit);
+        AssignTextureNames_impl<N + 1>(Program, args...);
+    }
+
+protected:
+    std::vector<GLuint> TextureUnits;
+    std::vector<GLenum> TextureType;
+    std::vector<GLenum> TextureLocation;
+    template<typename...Args>
+    void AssignSamplerNames(GLuint Program, Args...args)
+    {
+        CreateSamplers<tp...>::exec(SamplersId, TextureType);
+
+        glUseProgram(Program);
+        AssignTextureNames_impl<0>(Program, args...);
+        glUseProgram(0);
+    }
+
+public:
+    std::vector<GLuint> SamplersId;
+    void SetTextureUnits(const std::vector<GLuint> &args)
+    {
+        assert(args.size() == sizeof...(tp) && "Too much texture unit provided");
+        if (getGLSLVersion() >= 330)
+        {
+            for (unsigned i = 0; i < args.size(); i++)
+            {
+                setTextureSampler(TextureType[i], TextureUnits[i], args[i], SamplersId[i]);
+            }
+        }
+        else
+            BindTexture<tp...>::exec(TextureUnits, args, 0);
+    }
+
+    ~TextureRead()
+    {
+        for (unsigned i = 0; i < SamplersId.size(); i++)
+            glDeleteSamplers(1, &SamplersId[i]);
+    }
+
+    void SetTextureHandles(const std::vector<uint64_t> &args)
+    {
+        assert(args.size() == TextureLocation.size() && "Wrong Handle count");
+        for (unsigned i = 0; i < args.size(); i++)
+        {
+#ifdef Bindless_Texture_Support
+            if (args[i])
+                glUniformHandleui64ARB(TextureLocation[i], args[i]);
+#endif
+        }
+    }
+};
+
 namespace MeshShader
 {
-class ObjectPass1Shader : public ShaderHelperSingleton<ObjectPass1Shader, core::matrix4, core::matrix4>
+class ObjectPass1Shader : public ShaderHelperSingleton<ObjectPass1Shader, core::matrix4, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
     ObjectPass1Shader();
 };
 
-class ObjectRefPass1Shader : public ShaderHelperSingleton<ObjectRefPass1Shader, core::matrix4, core::matrix4, core::matrix4>
+class ObjectRefPass1Shader : public ShaderHelperSingleton<ObjectRefPass1Shader, core::matrix4, core::matrix4, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
     ObjectRefPass1Shader();
 };
 
-class GrassPass1Shader : public ShaderHelperSingleton<GrassPass1Shader, core::matrix4, core::matrix4, core::vector3df>
+class GrassPass1Shader : public ShaderHelperSingleton<GrassPass1Shader, core::matrix4, core::matrix4, core::vector3df>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     GrassPass1Shader();
 };
 
-class NormalMapShader : public ShaderHelperSingleton<NormalMapShader, core::matrix4, core::matrix4>
+class NormalMapShader : public ShaderHelperSingleton<NormalMapShader, core::matrix4, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_normalmap, TU_glossy;
     NormalMapShader();
 };
 
-class InstancedObjectPass1Shader : public ShaderHelperSingleton<InstancedObjectPass1Shader>
+class InstancedObjectPass1Shader : public ShaderHelperSingleton<InstancedObjectPass1Shader>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     InstancedObjectPass1Shader();
 };
 
-class InstancedObjectRefPass1Shader : public ShaderHelperSingleton<InstancedObjectRefPass1Shader>
+class InstancedObjectRefPass1Shader : public ShaderHelperSingleton<InstancedObjectRefPass1Shader>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     InstancedObjectRefPass1Shader();
 };
 
-class InstancedGrassPass1Shader : public ShaderHelperSingleton<InstancedGrassPass1Shader, core::vector3df>
+class InstancedGrassPass1Shader : public ShaderHelperSingleton<InstancedGrassPass1Shader, core::vector3df>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     InstancedGrassPass1Shader();
 };
 
-class InstancedNormalMapShader : public ShaderHelperSingleton<InstancedNormalMapShader>
+class InstancedNormalMapShader : public ShaderHelperSingleton<InstancedNormalMapShader>, public TextureRead<Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_glossy, TU_normalmap;
-
     InstancedNormalMapShader();
 };
 
-class ObjectPass2Shader : public ShaderHelperSingleton<ObjectPass2Shader, core::matrix4, core::matrix4>
+class ObjectPass2Shader : public ShaderHelperSingleton<ObjectPass2Shader, core::matrix4, core::matrix4>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo;
-
     ObjectPass2Shader();
 };
 
-class InstancedObjectPass2Shader : public ShaderHelperSingleton<InstancedObjectPass2Shader>
+class InstancedObjectPass2Shader : public ShaderHelperSingleton<InstancedObjectPass2Shader>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo;
-
     InstancedObjectPass2Shader();
 };
 
-class InstancedObjectRefPass2Shader : public ShaderHelperSingleton<InstancedObjectRefPass2Shader>
+class InstancedObjectRefPass2Shader : public ShaderHelperSingleton<InstancedObjectRefPass2Shader>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo;
-
     InstancedObjectRefPass2Shader();
 };
 
-class DetailledObjectPass2Shader : public ShaderHelperSingleton<DetailledObjectPass2Shader, core::matrix4>
+class DetailledObjectPass2Shader : public ShaderHelperSingleton<DetailledObjectPass2Shader, core::matrix4>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo, TU_detail;
-
     DetailledObjectPass2Shader();
 };
 
-class ObjectUnlitShader : public ShaderHelperSingleton<ObjectUnlitShader, core::matrix4>
+class InstancedDetailledObjectPass2Shader : public ShaderHelperSingleton<InstancedDetailledObjectPass2Shader>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
+    InstancedDetailledObjectPass2Shader();
+};
 
+class ObjectUnlitShader : public ShaderHelperSingleton<ObjectUnlitShader, core::matrix4, core::matrix4>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
+{
+public:
     ObjectUnlitShader();
 };
 
-class ObjectRefPass2Shader : public ShaderHelperSingleton<ObjectRefPass2Shader, core::matrix4, core::matrix4>
+class InstancedObjectUnlitShader : public ShaderHelperSingleton<InstancedObjectUnlitShader>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo;
+    InstancedObjectUnlitShader();
+};
 
+class ObjectRefPass2Shader : public ShaderHelperSingleton<ObjectRefPass2Shader, core::matrix4, core::matrix4>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
+{
+public:
     ObjectRefPass2Shader();
 };
 
-class GrassPass2Shader : public ShaderHelperSingleton<GrassPass2Shader, core::matrix4, core::vector3df>
+class GrassPass2Shader : public ShaderHelperSingleton<GrassPass2Shader, core::matrix4, core::vector3df>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo;
-
     GrassPass2Shader();
 };
 
-class InstancedGrassPass2Shader : public ShaderHelperSingleton<InstancedGrassPass2Shader, core::vector3df, core::vector3df>
+class InstancedGrassPass2Shader : public ShaderHelperSingleton<InstancedGrassPass2Shader, core::vector3df, core::vector3df>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Nearest_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_Albedo, TU_dtex;
-
     InstancedGrassPass2Shader();
 };
 
-class SphereMapShader : public ShaderHelperSingleton<SphereMapShader, core::matrix4, core::matrix4>
+class SphereMapShader : public ShaderHelperSingleton<SphereMapShader, core::matrix4, core::matrix4>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     SphereMapShader();
 };
 
-class SplattingShader : public ShaderHelperSingleton<SplattingShader, core::matrix4>
+class InstancedSphereMapShader : public ShaderHelperSingleton<InstancedSphereMapShader>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex_layout, TU_tex_detail0, TU_tex_detail1, TU_tex_detail2, TU_tex_detail3;
+    InstancedSphereMapShader();
+};
 
+class SplattingShader : public ShaderHelperSingleton<SplattingShader, core::matrix4>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered>
+{
+public:
     SplattingShader();
 };
 
@@ -319,27 +614,21 @@ public:
     static void setUniforms(const core::matrix4 &ModelViewProjectionMatrix, unsigned TU_tex, float time, float transparency);
 };
 
-class TransparentShader : public ShaderHelperSingleton<TransparentShader, core::matrix4, core::matrix4>
+class TransparentShader : public ShaderHelperSingleton<TransparentShader, core::matrix4, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     TransparentShader();
 };
 
-class TransparentFogShader : public ShaderHelperSingleton<TransparentFogShader, core::matrix4, core::matrix4, float, float, float, float, float, video::SColorf>
+class TransparentFogShader : public ShaderHelperSingleton<TransparentFogShader, core::matrix4, core::matrix4, float, float, float, float, float, video::SColorf>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     TransparentFogShader();
 };
 
-class BillboardShader : public ShaderHelperSingleton<BillboardShader, core::matrix4, core::matrix4, core::vector3df, core::dimension2df>
+class BillboardShader : public ShaderHelperSingleton<BillboardShader, core::matrix4, core::matrix4, core::vector3df, core::dimension2df>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     BillboardShader();
 };
 
@@ -350,59 +639,64 @@ public:
     ColorizeShader();
 };
 
-class ShadowShader : public ShaderHelperSingleton<ShadowShader, core::matrix4>
+class InstancedColorizeShader : public ShaderHelperSingleton<InstancedColorizeShader>
+{
+public:
+    InstancedColorizeShader();
+};
+
+class ShadowShader : public ShaderHelperSingleton<ShadowShader, int, core::matrix4>, public TextureRead<>
 {
 public:
     ShadowShader();
 };
 
-class RSMShader : public ShaderHelperSingleton<RSMShader, core::matrix4, core::matrix4, core::matrix4>
+class RSMShader : public ShaderHelperSingleton<RSMShader, core::matrix4, core::matrix4, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     RSMShader();
 };
 
-class SplattingRSMShader : public ShaderHelperSingleton<SplattingRSMShader, core::matrix4, core::matrix4>
+class InstancedRSMShader : public ShaderHelperSingleton<InstancedRSMShader, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_layout, TU_detail0, TU_detail1, TU_detail2, TU_detail3;
+    InstancedRSMShader();
+};
 
+class SplattingRSMShader : public ShaderHelperSingleton<SplattingRSMShader, core::matrix4, core::matrix4>,
+    public TextureRead<Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered, Trilinear_Anisotropic_Filtered>
+{
+public:
     SplattingRSMShader();
 };
 
-class InstancedShadowShader : public ShaderHelperSingleton<InstancedShadowShader>
+class InstancedShadowShader : public ShaderHelperSingleton<InstancedShadowShader, int>, public TextureRead<>
 {
 public:
     InstancedShadowShader();
 };
 
-class RefShadowShader : public ShaderHelperSingleton<RefShadowShader, core::matrix4>
+class RefShadowShader : public ShaderHelperSingleton<RefShadowShader, int, core::matrix4>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
     RefShadowShader();
 };
 
-class InstancedRefShadowShader : public ShaderHelperSingleton<InstancedRefShadowShader>
+class InstancedRefShadowShader : public ShaderHelperSingleton<InstancedRefShadowShader, int>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
     InstancedRefShadowShader();
 };
 
-class GrassShadowShader : public ShaderHelperSingleton<GrassShadowShader, core::matrix4, core::vector3df>
+class GrassShadowShader : public ShaderHelperSingleton<GrassShadowShader, int, core::matrix4, core::vector3df>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
     GrassShadowShader();
 };
 
-class InstancedGrassShadowShader : public ShaderHelperSingleton<InstancedGrassShadowShader, core::vector3df>
+class InstancedGrassShadowShader : public ShaderHelperSingleton<InstancedGrassShadowShader, int, core::vector3df>, public TextureRead<Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_tex;
     InstancedGrassShadowShader();
 };
 
@@ -412,24 +706,17 @@ public:
     DisplaceMaskShader();
 };
 
-class DisplaceShader : public ShaderHelperSingleton<DisplaceShader, core::matrix4, core::vector2df, core::vector2df>
+class DisplaceShader : public ShaderHelperSingleton<DisplaceShader, core::matrix4, core::vector2df, core::vector2df>, public TextureRead<Bilinear_Filtered, Bilinear_Filtered, Bilinear_Filtered, Trilinear_Anisotropic_Filtered>
 {
 public:
-    GLuint TU_displacement_tex, TU_mask_tex, TU_color_tex, TU_tex;
-
     DisplaceShader();
 };
 
-class SkyboxShader
+class SkyboxShader : public ShaderHelperSingleton<SkyboxShader, core::matrix4>, public TextureRead<Trilinear_cubemap>
 {
 public:
-    static GLuint Program;
-    static GLuint attrib_position;
-    static GLuint uniform_MM, uniform_tex;
-    static GLuint cubevao;
-
-    static void init();
-    static void setUniforms(const core::matrix4 &ModelMatrix, const core::vector2df &screen, unsigned TU_tex);
+    SkyboxShader();
+    GLuint cubevao;
 };
 
 class NormalVisualizer : public ShaderHelperSingleton<NormalVisualizer, core::matrix4, core::matrix4, video::SColor>
@@ -469,17 +756,12 @@ namespace LightShader
     };
 
 
-    class PointLightShader
+    class PointLightShader : public ShaderHelperSingleton<PointLightShader>, public TextureRead<Nearest_Filtered, Nearest_Filtered>
     {
     public:
-        static GLuint Program;
-        static GLuint attrib_Position, attrib_Energy, attrib_Color, attrib_Radius;
-        static GLuint uniform_ntex, uniform_dtex, uniform_spec;
-        static GLuint vbo;
-        static GLuint vao;
-
-        static void init();
-        static void setUniforms(const core::vector2df &screen, unsigned spec, unsigned TU_ntex, unsigned TU_dtex);
+        GLuint vbo;
+        GLuint vao;
+        PointLightShader();
     };
 }
 
@@ -502,19 +784,15 @@ public:
     HeightmapSimulationShader();
 };
 
-class SimpleParticleRender : public ShaderHelperSingleton<SimpleParticleRender, video::SColorf, video::SColorf>
+class SimpleParticleRender : public ShaderHelperSingleton<SimpleParticleRender, video::SColorf, video::SColorf>, public TextureRead<Trilinear_Anisotropic_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_tex, TU_dtex;
-
     SimpleParticleRender();
 };
 
-class FlipParticleRender : public ShaderHelperSingleton<FlipParticleRender>
+class FlipParticleRender : public ShaderHelperSingleton<FlipParticleRender>, public TextureRead<Trilinear_Anisotropic_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_tex, TU_dtex;
-
     FlipParticleRender();
 };
 }
@@ -522,78 +800,58 @@ public:
 namespace FullScreenShader
 {
 
-class BloomShader : public ShaderHelperSingleton<BloomShader>
+class BloomShader : public ShaderHelperSingleton<BloomShader>, public TextureRead<Nearest_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     BloomShader();
 };
 
-class BloomBlendShader : public ShaderHelperSingleton<BloomBlendShader>
+class BloomBlendShader : public ShaderHelperSingleton<BloomBlendShader>, public TextureRead<Bilinear_Filtered, Bilinear_Filtered, Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex_128, TU_tex_256, TU_tex_512;
-
     BloomBlendShader();
 };
 
-class ToneMapShader : public ShaderHelperSingleton<ToneMapShader>
+class ToneMapShader : public ShaderHelperSingleton<ToneMapShader>, public TextureRead<Nearest_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     ToneMapShader();
 };
 
-class DepthOfFieldShader : public ShaderHelperSingleton<DepthOfFieldShader>
+class DepthOfFieldShader : public ShaderHelperSingleton<DepthOfFieldShader>, public TextureRead<Bilinear_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_tex, TU_depth;
-
     DepthOfFieldShader();
 };
 
-class SunLightShader : public ShaderHelperSingleton<SunLightShader, core::vector3df, video::SColorf>
+class SunLightShader : public ShaderHelperSingleton<SunLightShader, core::vector3df, video::SColorf>, public TextureRead<Nearest_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_ntex, TU_dtex;
-
     SunLightShader();
 };
 
-class DiffuseEnvMapShader
+class DiffuseEnvMapShader : public ShaderHelperSingleton<DiffuseEnvMapShader, core::matrix4, std::vector<float>, std::vector<float>, std::vector<float> >, public TextureRead<Nearest_Filtered>
 {
 public:
-    static GLuint Program;
-    static GLuint uniform_ntex, uniform_TVM, uniform_blueLmn, uniform_greenLmn, uniform_redLmn;
-
-    static void init();
-    static void setUniforms(const core::matrix4 &TransposeViewMatrix, const float *blueSHCoeff, const float *greenSHCoeff, const float *redSHCoeff, unsigned TU_ntex);
+    DiffuseEnvMapShader();
 };
 
-class ShadowedSunLightShader : public ShaderHelperSingleton<ShadowedSunLightShader, core::vector3df, video::SColorf>
+class ShadowedSunLightShader : public ShaderHelperSingleton<ShadowedSunLightShader, core::vector3df, video::SColorf>, public TextureRead<Nearest_Filtered, Nearest_Filtered, Shadow_Sampler>
 {
 public:
-    GLuint TU_ntex, TU_dtex, TU_shadowtex;
-
     ShadowedSunLightShader();
 };
 
-class RadianceHintsConstructionShader : public ShaderHelperSingleton<RadianceHintsConstructionShader, core::matrix4, core::matrix4, core::vector3df>
+class RadianceHintsConstructionShader : public ShaderHelperSingleton<RadianceHintsConstructionShader, core::matrix4, core::matrix4, core::vector3df>, public TextureRead<Bilinear_Filtered, Bilinear_Filtered, Bilinear_Filtered>
 {
 public:
-    GLuint TU_ctex, TU_ntex, TU_dtex;
-
     RadianceHintsConstructionShader();
 };
 
 // Workaround for a bug found in kepler nvidia linux and fermi nvidia windows
-class NVWorkaroundRadianceHintsConstructionShader : public ShaderHelperSingleton<NVWorkaroundRadianceHintsConstructionShader, core::matrix4, core::matrix4, core::vector3df, int>
+class NVWorkaroundRadianceHintsConstructionShader : public ShaderHelperSingleton<NVWorkaroundRadianceHintsConstructionShader, core::matrix4, core::matrix4, core::vector3df, int>, public TextureRead<Bilinear_Filtered, Bilinear_Filtered, Bilinear_Filtered>
 {
 public:
-    GLuint TU_ctex, TU_ntex, TU_dtex;
-
     NVWorkaroundRadianceHintsConstructionShader();
 };
 
@@ -605,19 +863,16 @@ public:
     RHDebug();
 };
 
-class GlobalIlluminationReconstructionShader : public ShaderHelperSingleton<GlobalIlluminationReconstructionShader, core::matrix4, core::matrix4, core::vector3df>
+class GlobalIlluminationReconstructionShader : public ShaderHelperSingleton<GlobalIlluminationReconstructionShader, core::matrix4, core::matrix4, core::vector3df>,
+    public TextureRead<Nearest_Filtered, Nearest_Filtered, Volume_Linear_Filtered, Volume_Linear_Filtered, Volume_Linear_Filtered>
 {
 public:
-    GLuint TU_ntex, TU_dtex, TU_SHR, TU_SHG, TU_SHB, uniform_RHMatrix;
-
     GlobalIlluminationReconstructionShader();
 };
 
-class Gaussian17TapHShader : public ShaderHelperSingleton<Gaussian17TapHShader, core::vector2df>
+class Gaussian17TapHShader : public ShaderHelperSingleton<Gaussian17TapHShader, core::vector2df>, public TextureRead<Bilinear_Clamped_Filtered, Bilinear_Clamped_Filtered>
 {
 public:
-    GLuint TU_tex, TU_depth;
-
     Gaussian17TapHShader();
 };
 
@@ -628,27 +883,21 @@ public:
     ComputeGaussian17TapHShader();
 };
 
-class Gaussian6HBlurShader : public ShaderHelperSingleton<Gaussian6HBlurShader, core::vector2df>
+class Gaussian6HBlurShader : public ShaderHelperSingleton<Gaussian6HBlurShader, core::vector2df>, public TextureRead<Bilinear_Clamped_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     Gaussian6HBlurShader();
 };
 
-class Gaussian3HBlurShader : public ShaderHelperSingleton<Gaussian3HBlurShader, core::vector2df>
+class Gaussian3HBlurShader : public ShaderHelperSingleton<Gaussian3HBlurShader, core::vector2df>, public TextureRead<Bilinear_Clamped_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     Gaussian3HBlurShader();
 };
 
-class Gaussian17TapVShader : public ShaderHelperSingleton<Gaussian17TapVShader, core::vector2df>
+class Gaussian17TapVShader : public ShaderHelperSingleton<Gaussian17TapVShader, core::vector2df>, public TextureRead<Bilinear_Clamped_Filtered, Bilinear_Clamped_Filtered>
 {
 public:
-    GLuint TU_tex, TU_depth;
-
     Gaussian17TapVShader();
 };
 
@@ -661,26 +910,21 @@ public:
 };
 
 
-class Gaussian6VBlurShader : public ShaderHelperSingleton<Gaussian6VBlurShader, core::vector2df>
+class Gaussian6VBlurShader : public ShaderHelperSingleton<Gaussian6VBlurShader, core::vector2df>, public TextureRead<Bilinear_Clamped_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     Gaussian6VBlurShader();
 };
 
-class Gaussian3VBlurShader : public ShaderHelperSingleton<Gaussian3VBlurShader, core::vector2df>
+class Gaussian3VBlurShader : public ShaderHelperSingleton<Gaussian3VBlurShader, core::vector2df>, public TextureRead<Bilinear_Clamped_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     Gaussian3VBlurShader();
 };
 
-class PassThroughShader : public ShaderHelperSingleton<PassThroughShader>
+class PassThroughShader : public ShaderHelperSingleton<PassThroughShader>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
     GLuint vao;
 
     PassThroughShader();
@@ -695,87 +939,73 @@ public:
     LayerPassThroughShader();
 };
 
-class LinearizeDepthShader : public ShaderHelperSingleton<LinearizeDepthShader, float, float>
+class LinearizeDepthShader : public ShaderHelperSingleton<LinearizeDepthShader, float, float>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     LinearizeDepthShader();
 };
 
-class GlowShader : public ShaderHelperSingleton<GlowShader>
+class GlowShader : public ShaderHelperSingleton<GlowShader>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
     GLuint vao;
 
     GlowShader();
 };
 
-class SSAOShader : public ShaderHelperSingleton<SSAOShader, float, float, float>
+class SSAOShader : public ShaderHelperSingleton<SSAOShader, float, float, float>, public TextureRead<Semi_trilinear>
 {
 public:
-    GLuint TU_dtex;
-
     SSAOShader();
 };
 
-class FogShader : public ShaderHelperSingleton<FogShader, float, float, float, float, float, core::vector3df>
+class FogShader : public ShaderHelperSingleton<FogShader, float, float, float, float, float, core::vector3df>, public TextureRead<Nearest_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     FogShader();
 };
 
-class MotionBlurShader : public ShaderHelperSingleton<MotionBlurShader, core::matrix4, core::vector2df, float, float>
+class MotionBlurShader : public ShaderHelperSingleton<MotionBlurShader, core::matrix4, core::vector2df, float, float>, public TextureRead<Bilinear_Clamped_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_cb, TU_dtex;
-
     MotionBlurShader();
 };
 
-class GodFadeShader : public ShaderHelperSingleton<GodFadeShader, video::SColorf>
+class GodFadeShader : public ShaderHelperSingleton<GodFadeShader, video::SColorf>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
     GLuint vao;
 
     GodFadeShader();
 };
 
-class GodRayShader : public ShaderHelperSingleton<GodRayShader, core::vector2df>
+class GodRayShader : public ShaderHelperSingleton<GodRayShader, core::vector2df>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
     GLuint vao;
 
     GodRayShader();
 };
 
-class MLAAColorEdgeDetectionSHader : public ShaderHelperSingleton<MLAAColorEdgeDetectionSHader, core::vector2df>
+class MLAAColorEdgeDetectionSHader : public ShaderHelperSingleton<MLAAColorEdgeDetectionSHader, core::vector2df>, public TextureRead<Nearest_Filtered>
 {
 public:
-    GLuint TU_colorMapG;
     GLuint vao;
 
     MLAAColorEdgeDetectionSHader();
 };
 
-class MLAABlendWeightSHader : public ShaderHelperSingleton<MLAABlendWeightSHader, core::vector2df>
+class MLAABlendWeightSHader : public ShaderHelperSingleton<MLAABlendWeightSHader, core::vector2df>, public TextureRead<Bilinear_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_edgesMap, TU_areaMap;
     GLuint vao;
 
     MLAABlendWeightSHader();
 };
 
-class MLAAGatherSHader : public ShaderHelperSingleton<MLAAGatherSHader, core::vector2df>
+class MLAAGatherSHader : public ShaderHelperSingleton<MLAAGatherSHader, core::vector2df>, public TextureRead<Nearest_Filtered, Nearest_Filtered>
 {
 public:
-    GLuint TU_colorMap, TU_blendMap;
     GLuint vao;
 
     MLAAGatherSHader();
@@ -785,26 +1015,21 @@ public:
 
 namespace UIShader
 {
-class TextureRectShader : public ShaderHelperSingleton<TextureRectShader, core::vector2df, core::vector2df, core::vector2df, core::vector2df>
+class TextureRectShader : public ShaderHelperSingleton<TextureRectShader, core::vector2df, core::vector2df, core::vector2df, core::vector2df>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     TextureRectShader();
 };
 
-class UniformColoredTextureRectShader : public ShaderHelperSingleton<UniformColoredTextureRectShader, core::vector2df, core::vector2df, core::vector2df, core::vector2df, video::SColor>
+class UniformColoredTextureRectShader : public ShaderHelperSingleton<UniformColoredTextureRectShader, core::vector2df, core::vector2df, core::vector2df, core::vector2df, video::SColor>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
-
     UniformColoredTextureRectShader();
 };
 
-class ColoredTextureRectShader : public ShaderHelperSingleton<ColoredTextureRectShader, core::vector2df, core::vector2df, core::vector2df, core::vector2df>
+class ColoredTextureRectShader : public ShaderHelperSingleton<ColoredTextureRectShader, core::vector2df, core::vector2df, core::vector2df, core::vector2df>, public TextureRead<Bilinear_Filtered>
 {
 public:
-    GLuint TU_tex;
     GLuint colorvbo;
     GLuint vao;
 
@@ -869,6 +1094,7 @@ public:
     video::IShaderConstantSetCallBack * m_callbacks[ES_COUNT];
 
     void loadShaders();
+    void killShaders();
 private:
     void check(const int num) const;
     
diff --git a/src/graphics/shadow.cpp b/src/graphics/shadow.cpp
index e092f8284..619a552fa 100644
--- a/src/graphics/shadow.cpp
+++ b/src/graphics/shadow.cpp
@@ -23,7 +23,9 @@
 #include <IMeshSceneNode.h>
 #include <ISceneNode.h>
 
-Shadow::Shadow(video::ITexture *texture, scene::ISceneNode *node, float scale = 1.0, float xOffset = 0.0, float yOffset = 0.0)
+Shadow::Shadow(video::ITexture *texture, scene::ISceneNode *node, 
+               float scale = 1.0, float x_offset = 0.0, float y_offset = 0.0,
+               float z_offset = 0.0)
 {
     video::SMaterial m;
     m.setTexture(0, texture);
@@ -33,10 +35,10 @@ Shadow::Shadow(video::ITexture *texture, scene::ISceneNode *node, float scale =
     m_mesh   = irr_driver->createQuadMesh(&m, /*create_one_quad*/true);
     scene::IMeshBuffer *buffer = m_mesh->getMeshBuffer(0);
     irr::video::S3DVertex* v=(video::S3DVertex*)buffer->getVertices();
-    v[0].Pos.X = -scale+xOffset; v[0].Pos.Z =  scale+yOffset; v[0].Pos.Y = 0.01f;
-    v[1].Pos.X =  scale+xOffset; v[1].Pos.Z =  scale+yOffset; v[1].Pos.Y = 0.01f;
-    v[2].Pos.X =  scale+xOffset; v[2].Pos.Z = -scale+yOffset; v[2].Pos.Y = 0.01f;
-    v[3].Pos.X = -scale+xOffset; v[3].Pos.Z = -scale+yOffset; v[3].Pos.Y = 0.01f;
+    v[0].Pos.X = -scale+x_offset; v[0].Pos.Z =  scale+z_offset; v[0].Pos.Y = 0.01f-y_offset;
+    v[1].Pos.X =  scale+x_offset; v[1].Pos.Z =  scale+z_offset; v[1].Pos.Y = 0.01f-y_offset;
+    v[2].Pos.X =  scale+x_offset; v[2].Pos.Z = -scale+z_offset; v[2].Pos.Y = 0.01f-y_offset;
+    v[3].Pos.X = -scale+x_offset; v[3].Pos.Z = -scale+z_offset; v[3].Pos.Y = 0.01f-y_offset;
     v[0].TCoords = core::vector2df(0,0);
     v[1].TCoords = core::vector2df(1,0);
     v[2].TCoords = core::vector2df(1,1);
diff --git a/src/graphics/shadow.hpp b/src/graphics/shadow.hpp
index d55977bde..e234e716b 100644
--- a/src/graphics/shadow.hpp
+++ b/src/graphics/shadow.hpp
@@ -46,9 +46,8 @@ private:
     /** The scene node of the kart to which this shadow belongs. */
     scene::ISceneNode  *m_parent_kart_node;
 public:
-         Shadow(video::ITexture *texture,
-                scene::ISceneNode *node,
-                float scale, float xOffset, float yOffset);
+         Shadow(video::ITexture *texture, scene::ISceneNode *node,
+                float scale, float x_offset, float y_offset,float z_offset);
         ~Shadow();
     void enableShadow();
     void disableShadow();
diff --git a/src/graphics/stk_text_billboard.cpp b/src/graphics/stk_text_billboard.cpp
index 1365d0ee6..c3e4e54ad 100644
--- a/src/graphics/stk_text_billboard.cpp
+++ b/src/graphics/stk_text_billboard.cpp
@@ -75,12 +75,12 @@ scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, gui::ScalableFon
 
     for (unsigned int i = 0; i < m_chars.size(); i++)
     {
-        core::vector3df char_pos(m_chars[i].m_destRect.UpperLeftCorner.X,
-            m_chars[i].m_destRect.UpperLeftCorner.Y, 0);
+        core::vector3df char_pos((float) m_chars[i].m_destRect.UpperLeftCorner.X,
+            (float) m_chars[i].m_destRect.UpperLeftCorner.Y, 0);
         char_pos *= scale;
 
-        core::vector3df char_pos2(m_chars[i].m_destRect.LowerRightCorner.X,
-            m_chars[i].m_destRect.LowerRightCorner.Y, 0);
+        core::vector3df char_pos2((float)m_chars[i].m_destRect.LowerRightCorner.X,
+            (float) m_chars[i].m_destRect.LowerRightCorner.Y, 0);
         char_pos2 *= scale;
 
         core::dimension2di char_size_i = m_chars[i].m_destRect.getSize();
@@ -100,8 +100,8 @@ scene::IMesh* STKTextBillboard::getTextMesh(core::stringw text, gui::ScalableFon
             buffer = map_itr->second;
         }
 
-        float tex_width = m_chars[i].m_texture->getSize().Width;
-        float tex_height = m_chars[i].m_texture->getSize().Height;
+        float tex_width = (float) m_chars[i].m_texture->getSize().Width;
+        float tex_height = (float)m_chars[i].m_texture->getSize().Height;
 
 
         video::S3DVertex vertices[] =
diff --git a/src/graphics/stkanimatedmesh.cpp b/src/graphics/stkanimatedmesh.cpp
index 3c4aacc04..95b0f0561 100644
--- a/src/graphics/stkanimatedmesh.cpp
+++ b/src/graphics/stkanimatedmesh.cpp
@@ -33,6 +33,15 @@ void STKAnimatedMesh::cleanGLMeshes()
             glDeleteVertexArrays(1, &(mesh.vao));
         glDeleteBuffers(1, &(mesh.vertex_buffer));
         glDeleteBuffers(1, &(mesh.index_buffer));
+        if (mesh.instance_buffer)
+            glDeleteBuffers(1, &(mesh.instance_buffer));
+#ifdef Bindless_Texture_Support
+        for (unsigned j = 0; j < 6; j++)
+        {
+            if (mesh.TextureHandles[j] && glIsTextureHandleResidentARB(mesh.TextureHandles[j]))
+                glMakeTextureHandleNonResidentARB(mesh.TextureHandles[j]);
+        }
+#endif
     }
 }
 
@@ -45,21 +54,13 @@ void STKAnimatedMesh::setMesh(scene::IAnimatedMesh* mesh)
     CAnimatedMeshSceneNode::setMesh(mesh);
 }
 
-void STKAnimatedMesh::render()
+void STKAnimatedMesh::update()
 {
     video::IVideoDriver* driver = SceneManager->getVideoDriver();
-
-    bool isTransparentPass =
-        SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
-
-    ++PassCount;
-
     scene::IMesh* m = getMeshForCurrentFrame();
 
     if (m)
-    {
         Box = m->getBoundingBox();
-    }
     else
     {
         Log::error("animated mesh", "Animated Mesh returned no mesh to render.");
@@ -99,11 +100,21 @@ void STKAnimatedMesh::render()
             {
                 MeshMaterial MatType = MaterialTypeToMeshMaterial(type, mb->getVertexType());
                 MeshSolidMaterial[MatType].push_back(&mesh);
+                InitTextures(mesh, MatType);
+            }
+
+            if (irr_driver->hasARB_base_instance())
+            {
+                std::pair<unsigned, unsigned> p = VAOManager::getInstance()->getBase(mb);
+                mesh.vaoBaseVertex = p.first;
+                mesh.vaoOffset = p.second;
+            }
+            else
+            {
+                fillLocalBuffer(mesh, mb);
+                mesh.vao = createVAO(mesh.vertex_buffer, mesh.index_buffer, mb->getVertexType());
+                glBindVertexArray(0);
             }
-            std::pair<unsigned, unsigned> p = getVAOOffsetAndBase(mb);
-            mesh.vaoBaseVertex = p.first;
-            mesh.vaoOffset = p.second;
-            mesh.VAOType = mb->getVertexType();
         }
     }
     firstTime = false;
@@ -114,31 +125,47 @@ void STKAnimatedMesh::render()
         const video::SMaterial& material = ReadOnlyMaterials ? mb->getMaterial() : Materials[i];
         if (isObject(material.MaterialType))
         {
-            if (irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS || irr_driver->getPhase() == TRANSPARENT_PASS)
+
+            size_t size = mb->getVertexCount() * GLmeshes[i].Stride, offset = GLmeshes[i].vaoBaseVertex * GLmeshes[i].Stride;
+            void *buf;
+            if (irr_driver->hasBufferStorageExtension())
+            {
+                buf = VAOManager::getInstance()->getVBOPtr(mb->getVertexType());
+                buf = (char *)buf + offset;
+            }
+            else
             {
                 glBindVertexArray(0);
-                size_t size = mb->getVertexCount() * GLmeshes[i].Stride;
-                glBindBuffer(GL_ARRAY_BUFFER, getVBO(mb->getVertexType()));
+                if (irr_driver->hasARB_base_instance())
+                    glBindBuffer(GL_ARRAY_BUFFER, VAOManager::getInstance()->getVBO(mb->getVertexType()));
+                else
+                    glBindBuffer(GL_ARRAY_BUFFER, GLmeshes[i].vertex_buffer);
                 GLbitfield bitfield = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_RANGE_BIT | GL_MAP_UNSYNCHRONIZED_BIT;
-                void * buf = glMapBufferRange(GL_ARRAY_BUFFER, GLmeshes[i].vaoBaseVertex * GLmeshes[i].Stride, size, bitfield);
-                memcpy(buf, mb->getVertices(), size);
+                buf = glMapBufferRange(GL_ARRAY_BUFFER, offset, size, bitfield);
+            }
+            memcpy(buf, mb->getVertices(), size);
+            if (!irr_driver->hasBufferStorageExtension())
+            {
                 glUnmapBuffer(GL_ARRAY_BUFFER);
                 glBindBuffer(GL_ARRAY_BUFFER, 0);
             }
         }
         if (mb)
             GLmeshes[i].TextureMatrix = getMaterial(i).getTextureMatrix(0);
-
-        video::IMaterialRenderer* rnd = driver->getMaterialRenderer(Materials[i].MaterialType);
-        bool transparent = (rnd && rnd->isTransparent());
-
-       // only render transparent buffer if this is the transparent render pass
-       // and solid only in solid pass
-       if (transparent != isTransparentPass)
-          continue;
     }
 
-    if (irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS || irr_driver->getPhase() == SHADOW_PASS)
+}
+
+void STKAnimatedMesh::render()
+{
+    bool isTransparentPass =
+        SceneManager->getSceneNodeRenderPass() == scene::ESNRP_TRANSPARENT;
+
+    ++PassCount;
+
+    update();
+
+/*    if (irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS || irr_driver->getPhase() == SHADOW_PASS)
     {
         ModelViewProjectionMatrix = computeMVP(AbsoluteTransformation);
         core::matrix4 invmodel;
@@ -158,7 +185,7 @@ void STKAnimatedMesh::render()
             pushVector(ListMatUnlit::getInstance(), mesh, AbsoluteTransformation, core::matrix4::EM4CONST_IDENTITY, mesh->TextureMatrix);
 
         return;
-    }
+    }*/
 
     if (irr_driver->getPhase() == TRANSPARENT_PASS)
     {
diff --git a/src/graphics/stkanimatedmesh.hpp b/src/graphics/stkanimatedmesh.hpp
index c5c113da0..bfcd3f571 100644
--- a/src/graphics/stkanimatedmesh.hpp
+++ b/src/graphics/stkanimatedmesh.hpp
@@ -7,16 +7,15 @@
 #include "graphics/stkmesh.hpp"
 #include "utils/ptr_vector.hpp"
 
-class STKAnimatedMesh : public irr::scene::CAnimatedMeshSceneNode
+class STKAnimatedMesh : public irr::scene::CAnimatedMeshSceneNode, public STKMeshCommon
 {
 protected:
     bool firstTime;
-    PtrVector<GLMesh, REF> MeshSolidMaterial[MAT_COUNT];
-    PtrVector<GLMesh, REF> TransparentMesh[TM_COUNT];
     std::vector<GLMesh> GLmeshes;
     core::matrix4 ModelViewProjectionMatrix;
     void cleanGLMeshes();
 public:
+    virtual void update();
   STKAnimatedMesh(irr::scene::IAnimatedMesh* mesh, irr::scene::ISceneNode* parent,
      irr::scene::ISceneManager* mgr, irr::s32 id,
      const irr::core::vector3df& position = irr::core::vector3df(0,0,0),
@@ -25,6 +24,7 @@ public:
 
   virtual void render();
   virtual void setMesh(irr::scene::IAnimatedMesh* mesh);
+  virtual bool glow() const { return false; }
 };
 
 #endif // STKANIMATEDMESH_HPP
diff --git a/src/graphics/stkbillboard.cpp b/src/graphics/stkbillboard.cpp
index d2713f032..913727ec1 100644
--- a/src/graphics/stkbillboard.cpp
+++ b/src/graphics/stkbillboard.cpp
@@ -47,10 +47,12 @@ void STKBillboard::render()
     core::vector3df pos = getAbsolutePosition();
     glBindVertexArray(billboardvao);
     video::ITexture *tex = Material.getTexture(0);
+    if (tex == NULL)
+        return;
     compressTexture(tex, true, true);
     GLuint texid = getTextureGLuint(tex);
-    setTexture(0, texid, GL_LINEAR, GL_LINEAR);
     glUseProgram(MeshShader::BillboardShader::getInstance()->Program);
+    MeshShader::BillboardShader::getInstance()->SetTextureUnits(createVector<GLuint>(texid));
     MeshShader::BillboardShader::getInstance()->setUniforms(irr_driver->getViewMatrix(), irr_driver->getProjMatrix(), pos, Size);
     glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
     glBindVertexArray(0);
diff --git a/src/graphics/stkinstancedscenenode.cpp b/src/graphics/stkinstancedscenenode.cpp
deleted file mode 100644
index 64c28206f..000000000
--- a/src/graphics/stkinstancedscenenode.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-#include "stkinstancedscenenode.hpp"
-#include "graphics/irr_driver.hpp"
-#include "graphics/callbacks.hpp"
-
-STKInstancedSceneNode::STKInstancedSceneNode(irr::scene::IMesh* mesh, ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
-    const irr::core::vector3df& position,
-    const irr::core::vector3df& rotation,
-    const irr::core::vector3df& scale) :
-    CMeshSceneNode(mesh, parent, mgr, id, position, rotation, scale)
-{
-    m_ref_count = 0;
-    irr_driver->grabAllTextures(mesh);
-
-    if (irr_driver->isGLSL())
-    {
-        createGLMeshes();
-        setAutomaticCulling(0);
-    }
-}
-
-void STKInstancedSceneNode::cleanGL()
-{
-    for (u32 i = 0; i < GLmeshes.size(); ++i)
-    {
-        GLMesh mesh = GLmeshes[i];
-        if (!mesh.vertex_buffer)
-            continue;
-        if (mesh.vao)
-            glDeleteVertexArrays(1, &(mesh.vao));
-        if (mesh.vao_shadow_pass)
-            glDeleteVertexArrays(1, &(mesh.vao_shadow_pass));
-        glDeleteBuffers(1, &(mesh.vertex_buffer));
-        glDeleteBuffers(1, &(mesh.index_buffer));
-    }
-    glDeleteBuffers(1, &instances_vbo);
-}
-
-STKInstancedSceneNode::~STKInstancedSceneNode()
-{
-    irr_driver->dropAllTextures(getMesh());
-    irr_driver->removeMeshFromCache(getMesh());
-
-    if (irr_driver->isGLSL())
-        cleanGL();
-}
-
-void STKInstancedSceneNode::createGLMeshes()
-{
-    for (u32 i = 0; i<Mesh->getMeshBufferCount(); ++i)
-    {
-        scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i);
-        GLmeshes.push_back(allocateMeshBuffer(mb));
-        fillLocalBuffer(GLmeshes.back(), mb);
-    }
-    isMaterialInitialized = false;
-}
-
-void STKInstancedSceneNode::initinstancedvaostate(GLMesh &mesh)
-{
-    mesh.vao = createVAO(mesh.vertex_buffer, mesh.index_buffer, getVTXTYPEFromStride(mesh.Stride));
-    glGenBuffers(1, &instances_vbo);
-    glBindBuffer(GL_ARRAY_BUFFER, instances_vbo);
-    glBufferData(GL_ARRAY_BUFFER, instance_pos.size() * sizeof(float), instance_pos.data(), GL_STATIC_DRAW);
-
-    glEnableVertexAttribArray(7);
-    glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 0);
-    glVertexAttribDivisor(7, 1);
-    glEnableVertexAttribArray(8);
-    glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (GLvoid*)(3 * sizeof(float)));
-    glVertexAttribDivisor(8, 1);
-    glEnableVertexAttribArray(9);
-    glVertexAttribPointer(9, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (GLvoid*)(6 * sizeof(float)));
-    glVertexAttribDivisor(9, 1);
-
-    mesh.vao_shadow_pass = createVAO(mesh.vertex_buffer, mesh.index_buffer, getVTXTYPEFromStride(mesh.Stride));
-    glBindBuffer(GL_ARRAY_BUFFER, instances_vbo);
-    glEnableVertexAttribArray(7);
-    glVertexAttribPointer(7, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), 0);
-    glVertexAttribDivisor(7, 4);
-    glEnableVertexAttribArray(8);
-    glVertexAttribPointer(8, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (GLvoid*)(3 * sizeof(float)));
-    glVertexAttribDivisor(8, 4);
-    glEnableVertexAttribArray(9);
-    glVertexAttribPointer(9, 3, GL_FLOAT, GL_FALSE, 9 * sizeof(float), (GLvoid*)(6 * sizeof(float)));
-    glVertexAttribDivisor(9, 4);
-
-    glBindVertexArray(0);
-}
-
-void STKInstancedSceneNode::setFirstTimeMaterial()
-{
-    if (isMaterialInitialized)
-        return;
-    for (u32 i = 0; i<Mesh->getMeshBufferCount(); ++i)
-    {
-        scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i);
-        if (!mb)
-            continue;
-        video::E_MATERIAL_TYPE type = mb->getMaterial().MaterialType;
-
-        GLMesh &mesh = GLmeshes[i];
-        MeshMaterial MatType = MaterialTypeToMeshMaterial(type, mb->getVertexType());
-        initinstancedvaostate(mesh);
-        MeshSolidMaterial[MatType].push_back(&mesh);
-    }
-    isMaterialInitialized = true;
-}
-
-void STKInstancedSceneNode::addInstance(const core::vector3df &origin, const core::vector3df &orientation, const core::vector3df &scale)
-{
-    instance_pos.push_back(origin.X);
-    instance_pos.push_back(origin.Y);
-    instance_pos.push_back(origin.Z);
-    instance_pos.push_back(orientation.X);
-    instance_pos.push_back(orientation.Y);
-    instance_pos.push_back(orientation.Z);
-    instance_pos.push_back(scale.X);
-    instance_pos.push_back(scale.Y);
-    instance_pos.push_back(scale.Z);
-}
-
-core::matrix4 STKInstancedSceneNode::getInstanceTransform(int id)
-{
-    core::matrix4 mat;
-
-    int offset = id * 9;
-    mat.setTranslation(core::vector3df(
-        instance_pos[offset],
-        instance_pos[offset + 1],
-        instance_pos[offset + 2]));
-    mat.setRotationDegrees(core::vector3df(
-        instance_pos[offset + 3],
-        instance_pos[offset + 4],
-        instance_pos[offset + 5]));
-    mat.setScale(core::vector3df(
-        instance_pos[offset + 6],
-        instance_pos[offset + 7],
-        instance_pos[offset + 8]));
-
-    return mat;
-}
-
-void STKInstancedSceneNode::render()
-{
-    if (!irr_driver->isGLSL())
-    {
-        CMeshSceneNode::render();
-        return;
-    }
-
-    setFirstTimeMaterial();
-
-    
-    for(unsigned i = 0; i < MeshSolidMaterial[MAT_DEFAULT].size(); i++)
-    {
-        GLMesh *mesh = MeshSolidMaterial[MAT_DEFAULT][i];
-        ListInstancedMatDefault::getInstance()->push_back(STK::make_tuple(mesh, instance_pos.size() / 9));
-    }
-
-    for(unsigned i = 0; i < MeshSolidMaterial[MAT_ALPHA_REF].size(); i++)
-    {
-        GLMesh *mesh = MeshSolidMaterial[MAT_ALPHA_REF][i];
-        ListInstancedMatAlphaRef::getInstance()->push_back(STK::make_tuple(mesh, instance_pos.size() / 9));
-    }
-
-    windDir = getWind();
-    SunLightProvider * const cb = (SunLightProvider *)irr_driver->getCallback(ES_SUNLIGHT);
-    for(unsigned i = 0; i < MeshSolidMaterial[MAT_GRASS].size(); i++)
-    {
-        GLMesh *mesh = MeshSolidMaterial[MAT_GRASS][i];
-        ListInstancedMatGrass::getInstance()->push_back(STK::make_tuple(mesh, instance_pos.size() / 9, windDir, cb->getPosition()));
-    }
-
-    for(unsigned i = 0; i < MeshSolidMaterial[MAT_NORMAL_MAP].size(); i++)
-    {
-        GLMesh *mesh = MeshSolidMaterial[MAT_NORMAL_MAP][i];
-        ListInstancedMatNormalMap::getInstance()->push_back(STK::make_tuple(mesh, instance_pos.size() / 9));
-    }
-}
diff --git a/src/graphics/stkinstancedscenenode.hpp b/src/graphics/stkinstancedscenenode.hpp
deleted file mode 100644
index d1063ca6d..000000000
--- a/src/graphics/stkinstancedscenenode.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-#ifndef STKINSTANCEDSCENENODE_HPP
-#define STKINSTANCEDSCENENODE_HPP
-
-#include "stkmesh.hpp"
-#include "utils/leak_check.hpp"
-
-class ListInstancedMatDefault : public MeshList<ListInstancedMatDefault, GLMesh *, size_t>
-{};
-
-class ListInstancedMatAlphaRef : public MeshList<ListInstancedMatAlphaRef, GLMesh *, size_t>
-{};
-
-class ListInstancedMatGrass : public MeshList<ListInstancedMatGrass, GLMesh *, size_t, core::vector3df, core::vector3df>
-{};
-
-class ListInstancedMatNormalMap : public MeshList<ListInstancedMatNormalMap, GLMesh *, size_t>
-{};
-
-class STKInstancedSceneNode : public irr::scene::CMeshSceneNode
-{
-protected:
-    int m_ref_count;
-    std::vector<GLMesh *> MeshSolidMaterial[MAT_COUNT];
-    std::vector<GLMesh> GLmeshes;
-    std::vector<float> instance_pos;
-    core::matrix4 ModelViewProjectionMatrix, TransposeInverseModelView;
-    GLuint instances_vbo;
-    void createGLMeshes();
-    bool isMaterialInitialized;
-    void setFirstTimeMaterial();
-    void initinstancedvaostate(GLMesh &mesh);
-    void cleanGL();
-    core::vector3df windDir;
-public:
-    STKInstancedSceneNode(irr::scene::IMesh* mesh, ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
-        const irr::core::vector3df& position = irr::core::vector3df(0, 0, 0),
-        const irr::core::vector3df& rotation = irr::core::vector3df(0, 0, 0),
-        const irr::core::vector3df& scale = irr::core::vector3df(1.0f, 1.0f, 1.0f));
-    ~STKInstancedSceneNode();
-    virtual void render();
-    void addInstance(const core::vector3df &origin, const core::vector3df &orientation, const core::vector3df &scale);
-
-    int getInstanceCount() const { return instance_pos.size() / 9; }
-
-    core::matrix4 getInstanceTransform(int id);
-
-    void instanceGrab() { m_ref_count++; }
-    void instanceDrop()
-    {
-        m_ref_count--;
-        if (m_ref_count <= 0)
-        {
-            delete this;
-        }
-    }
-
-    LEAK_CHECK();
-};
-
-#endif
diff --git a/src/graphics/stkmesh.cpp b/src/graphics/stkmesh.cpp
index 568479942..2746a5428 100644
--- a/src/graphics/stkmesh.cpp
+++ b/src/graphics/stkmesh.cpp
@@ -126,6 +126,7 @@ GLMesh allocateMeshBuffer(scene::IMeshBuffer* mb)
     GLMesh result = {};
     if (!mb)
         return result;
+    result.mb = mb;
 
     result.IndexCount = mb->getIndexCount();
     switch (mb->getIndexType())
@@ -178,6 +179,7 @@ GLMesh allocateMeshBuffer(scene::IMeshBuffer* mb)
     for (unsigned i = 0; i < 6; i++)
         result.textures[i] = mb->getMaterial().getTexture(i);
     result.TextureMatrix = 0;
+    result.VAOType = mb->getVertexType();
     return result;
 }
 
@@ -229,7 +231,7 @@ core::matrix4 computeMVP(const core::matrix4 &ModelMatrix)
     return ModelViewProjectionMatrix;
 }
 
-core::vector3df getWind()
+core::vector3df getWindDir()
 {
     const float time = irr_driver->getDevice()->getTimer()->getTime() / 1000.0f;
     GrassShaderProvider *gsp = (GrassShaderProvider *)irr_driver->getCallback(ES_GRASS);
@@ -292,4 +294,48 @@ bool isObject(video::E_MATERIAL_TYPE type)
     if (type == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
         return true;
     return false;
+}
+
+static void
+SetTexture(GLMesh &mesh, unsigned i, bool isSrgb)
+{
+    if (!mesh.textures[i])
+        mesh.textures[i] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
+    compressTexture(mesh.textures[i], isSrgb);
+#ifdef Bindless_Texture_Support
+    if (UserConfigParams::m_azdo)
+    {
+        if (!mesh.TextureHandles[i])
+            mesh.TextureHandles[i] = glGetTextureSamplerHandleARB(getTextureGLuint(mesh.textures[i]), MeshShader::ObjectPass1Shader::getInstance()->SamplersId[0]);
+        if (!glIsTextureHandleResidentARB(mesh.TextureHandles[i]))
+            glMakeTextureHandleResidentARB(mesh.TextureHandles[i]);
+    }
+#endif
+}
+
+void InitTextures(GLMesh &mesh, MeshMaterial Mat)
+{
+    switch (Mat)
+    {
+    default:
+    case MAT_DEFAULT:
+    case MAT_ALPHA_REF:
+    case MAT_GRASS:
+    case MAT_SPHEREMAP:
+    case MAT_UNLIT:
+        SetTexture(mesh, 0, true);
+        break;
+    case MAT_DETAIL:
+    case MAT_NORMAL_MAP:
+        SetTexture(mesh, 0, true);
+        SetTexture(mesh, 1, false);
+        break;
+    case MAT_SPLATTING:
+        SetTexture(mesh, 0, true);
+        SetTexture(mesh, 1, true);
+        SetTexture(mesh, 2, true);
+        SetTexture(mesh, 3, true);
+        SetTexture(mesh, 4, true);
+        break;
+    }
 }
\ No newline at end of file
diff --git a/src/graphics/stkmesh.hpp b/src/graphics/stkmesh.hpp
index d95933029..569331b96 100644
--- a/src/graphics/stkmesh.hpp
+++ b/src/graphics/stkmesh.hpp
@@ -35,9 +35,9 @@ enum TransparentMaterial
 
 struct GLMesh {
     GLuint vao;
-    GLuint vao_shadow_pass;
     GLuint vertex_buffer;
     GLuint index_buffer;
+    GLuint instance_buffer;
     video::ITexture *textures[6];
     GLenum PrimitiveType;
     GLenum IndexType;
@@ -47,6 +47,8 @@ struct GLMesh {
     size_t vaoBaseVertex;
     size_t vaoOffset;
     video::E_VERTEX_TYPE VAOType;
+    uint64_t TextureHandles[6];
+    scene::IMeshBuffer *mb;
 };
 
 GLMesh allocateMeshBuffer(scene::IMeshBuffer* mb);
@@ -56,50 +58,132 @@ GLuint createVAO(GLuint vbo, GLuint idx, video::E_VERTEX_TYPE type);
 core::matrix4 computeMVP(const core::matrix4 &ModelViewProjectionMatrix);
 bool isObject(video::E_MATERIAL_TYPE type);
 
-core::vector3df getWind();
+core::vector3df getWindDir();
+
+
+class STKMeshCommon
+{
+protected:
+    bool m_culledForPlayerCam;
+    bool m_culledForShadowCam[4];
+    bool m_culledForRSMCam;
+public:
+    PtrVector<GLMesh, REF> MeshSolidMaterial[MAT_COUNT];
+    PtrVector<GLMesh, REF> TransparentMesh[TM_COUNT];
+    virtual void update() = 0;
+    virtual bool glow() const = 0;
+    virtual bool isImmediateDraw() const { return false; }
+    bool isCulledForPlayerCam() const { return m_culledForPlayerCam; }
+    void setCulledForPlayerCam(bool v) { m_culledForPlayerCam = v; }
+    bool isCulledForShadowCam(unsigned cascade) const { return m_culledForShadowCam[cascade]; }
+    void setCulledForShadowCam(unsigned cascade, bool v) { m_culledForShadowCam[cascade] = v; }
+    bool isCulledForRSMCam() const { return m_culledForRSMCam; }
+    void setCulledForRSMCam(bool v) { m_culledForRSMCam = v; }
+};
 
 template<typename T, typename... Args>
-class MeshList : public Singleton<T>, public std::vector<STK::Tuple<Args...> >
-{};
+class MeshList : public Singleton<T>
+{
+public:
+    std::vector<STK::Tuple<Args...> > SolidPass, Shadows[4], RSM;
+    void clear()
+    {
+        SolidPass.clear();
+        RSM.clear();
+        for (unsigned i = 0; i < 4; i++)
+            Shadows[i].clear();
+    }
+};
 
+template<typename T>
+class InstancedMeshList : public Singleton<T>
+{
+public:
+    std::vector<GLMesh *> SolidPass, Shadows[4], RSM;
+    void clear()
+    {
+        SolidPass.clear();
+        RSM.clear();
+        for (unsigned i = 0; i < 4; i++)
+            Shadows[i].clear();
+    }
+};
+
+// -----------------------------------------Mat Default---------------------------------------------------- //
 class ListMatDefault : public MeshList<ListMatDefault, GLMesh *, core::matrix4, core::matrix4, core::matrix4>
 {};
 
+class ListInstancedMatDefault : public InstancedMeshList<ListInstancedMatDefault>
+{};
+
+
+// -----------------------------------------Mat Alpha Ref---------------------------------------------------- //
 class ListMatAlphaRef : public MeshList<ListMatAlphaRef, GLMesh *, core::matrix4, core::matrix4, core::matrix4>
 {};
 
+class ListInstancedMatAlphaRef : public InstancedMeshList<ListInstancedMatAlphaRef>
+{};
+
+// -----------------------------------------Mat Normap Map---------------------------------------------------- //
 class ListMatNormalMap : public MeshList<ListMatNormalMap, GLMesh *, core::matrix4, core::matrix4, core::matrix4>
 {};
 
+class ListInstancedMatNormalMap : public InstancedMeshList<ListInstancedMatNormalMap>
+{};
+
+// -----------------------------------------Mat Grass---------------------------------------------------- //
 class ListMatGrass : public MeshList<ListMatGrass, GLMesh *, core::matrix4, core::matrix4, core::vector3df>
 {};
 
+class ListInstancedMatGrass : public InstancedMeshList<ListInstancedMatGrass>
+{};
+
+// -----------------------------------------Mat Sphere Map---------------------------------------------------- //
 class ListMatSphereMap : public MeshList<ListMatSphereMap, GLMesh *, core::matrix4, core::matrix4, core::matrix4>
 {};
 
+class ListInstancedMatSphereMap : public InstancedMeshList<ListInstancedMatSphereMap>
+{};
+
+// -----------------------------------------Mat Splatting---------------------------------------------------- //
 class ListMatSplatting : public MeshList<ListMatSplatting, GLMesh *, core::matrix4, core::matrix4>
 {};
 
+// -----------------------------------------Mat Unlit---------------------------------------------------- //
 class ListMatUnlit : public MeshList<ListMatUnlit, GLMesh *, core::matrix4, core::matrix4, core::matrix4>
 {};
 
+class ListInstancedMatUnlit : public InstancedMeshList<ListInstancedMatUnlit>
+{};
+
+// -----------------------------------------Mat Details---------------------------------------------------- //
 class ListMatDetails : public MeshList<ListMatDetails, GLMesh *, core::matrix4, core::matrix4, core::matrix4>
 {};
 
-
-class ListBlendTransparent : public MeshList<ListBlendTransparent, GLMesh *, core::matrix4, core::matrix4>
+class ListInstancedMatDetails : public InstancedMeshList<ListInstancedMatDetails>
 {};
 
-class ListAdditiveTransparent : public MeshList<ListAdditiveTransparent, GLMesh *, core::matrix4, core::matrix4>
+// Transparent
+template <typename T, typename ...Args>
+class MiscList : public Singleton<T>, public std::vector<STK::Tuple<Args...> >
 {};
 
-class ListBlendTransparentFog : public MeshList<ListBlendTransparentFog, GLMesh *, core::matrix4, core::matrix4, float, float, float, float, float, video::SColorf>
+class ListBlendTransparent : public MiscList<ListBlendTransparent, GLMesh *, core::matrix4, core::matrix4>
 {};
 
-class ListAdditiveTransparentFog : public MeshList<ListAdditiveTransparentFog, GLMesh *, core::matrix4, core::matrix4, float, float, float, float, float, video::SColorf>
+class ListAdditiveTransparent : public MiscList<ListAdditiveTransparent, GLMesh *, core::matrix4, core::matrix4>
 {};
 
-class ListDisplacement : public MeshList<ListDisplacement, GLMesh *, core::matrix4>
+class ListBlendTransparentFog : public MiscList<ListBlendTransparentFog, GLMesh *, core::matrix4, core::matrix4, float, float, float, float, float, video::SColorf>
+{};
+
+class ListAdditiveTransparentFog : public MiscList<ListAdditiveTransparentFog, GLMesh *, core::matrix4, core::matrix4, float, float, float, float, float, video::SColorf>
+{};
+
+class ListDisplacement : public MiscList<ListDisplacement, GLMesh *, core::matrix4>
+{};
+
+class ListInstancedGlow : public Singleton<ListInstancedGlow>, public std::vector<GLMesh *>
 {};
 
 // Forward pass (for transparents meshes)
@@ -108,4 +192,6 @@ void drawBubble(const GLMesh &mesh, const core::matrix4 &ModelViewProjectionMatr
 MeshMaterial MaterialTypeToMeshMaterial(video::E_MATERIAL_TYPE, video::E_VERTEX_TYPE);
 TransparentMaterial MaterialTypeToTransparentMaterial(video::E_MATERIAL_TYPE, f32 MaterialTypeParam);
 
+void InitTextures(GLMesh &mesh, MeshMaterial);
+
 #endif // STKMESH_H
diff --git a/src/graphics/stkmeshscenenode.cpp b/src/graphics/stkmeshscenenode.cpp
index 54db127c7..c3a08e50e 100644
--- a/src/graphics/stkmeshscenenode.cpp
+++ b/src/graphics/stkmeshscenenode.cpp
@@ -21,6 +21,7 @@ STKMeshSceneNode::STKMeshSceneNode(irr::scene::IMesh* mesh, ISceneNode* parent,
     isDisplacement = false;
     immediate_draw = false;
     update_each_frame = false;
+    isGlow = false;
 
     if (createGLMeshes)
         this->createGLMeshes();
@@ -70,35 +71,31 @@ void STKMeshSceneNode::setFirstTimeMaterial()
       if (rnd->isTransparent())
       {
           TransparentMaterial TranspMat = MaterialTypeToTransparentMaterial(type, MaterialTypeParam);
-          if (immediate_draw)
-          {
-              fillLocalBuffer(mesh, mb);
-              mesh.vao = createVAO(mesh.vertex_buffer, mesh.index_buffer, mb->getVertexType());
-              glBindVertexArray(0);
-          }
-          else
+          if (!immediate_draw)
               TransparentMesh[TranspMat].push_back(&mesh);
       }
       else
       {
           assert(!isDisplacement);
           MeshMaterial MatType = MaterialTypeToMeshMaterial(type, mb->getVertexType());
-          if (immediate_draw)
+          if (!immediate_draw)
           {
-              fillLocalBuffer(mesh, mb);
-              mesh.vao = createVAO(mesh.vertex_buffer, mesh.index_buffer, mb->getVertexType());
-              glBindVertexArray(0);
+              InitTextures(mesh, MatType);
+              MeshSolidMaterial[MatType].push_back(&mesh);
           }
-          else
-              MeshSolidMaterials[MatType].push_back(&mesh);
       }
 
-      if (!immediate_draw)
+      if (!immediate_draw && irr_driver->hasARB_base_instance())
       {
-          std::pair<unsigned, unsigned> p = getVAOOffsetAndBase(mb);
+          std::pair<unsigned, unsigned> p = VAOManager::getInstance()->getBase(mb);
           mesh.vaoBaseVertex = p.first;
           mesh.vaoOffset = p.second;
-          mesh.VAOType = mb->getVertexType();
+      }
+      else
+      {
+          fillLocalBuffer(mesh, mb);
+          mesh.vao = createVAO(mesh.vertex_buffer, mesh.index_buffer, mb->getVertexType());
+          glBindVertexArray(0);
       }
   }
   isMaterialInitialized = true;
@@ -113,12 +110,23 @@ void STKMeshSceneNode::cleanGLMeshes()
             continue;
         if (mesh.vao)
             glDeleteVertexArrays(1, &(mesh.vao));
-        glDeleteBuffers(1, &(mesh.vertex_buffer));
-        glDeleteBuffers(1, &(mesh.index_buffer));
+        if (mesh.vertex_buffer)
+            glDeleteBuffers(1, &(mesh.vertex_buffer));
+        if (mesh.index_buffer)
+            glDeleteBuffers(1, &(mesh.index_buffer));
+        if (mesh.instance_buffer)
+            glDeleteBuffers(1, &(mesh.instance_buffer));
+#ifdef Bindless_Texture_Support
+        for (unsigned j = 0; j < 6; j++)
+        {
+            if (mesh.TextureHandles[j] && glIsTextureHandleResidentARB(mesh.TextureHandles[j]))
+                glMakeTextureHandleNonResidentARB(mesh.TextureHandles[j]);
+        }
+#endif
     }
     GLmeshes.clear();
     for (unsigned i = 0; i < MAT_COUNT; i++)
-        MeshSolidMaterials[i].clearWithoutDeleting();
+        MeshSolidMaterial[i].clearWithoutDeleting();
 }
 
 void STKMeshSceneNode::setMesh(irr::scene::IMesh* mesh)
@@ -135,14 +143,12 @@ STKMeshSceneNode::~STKMeshSceneNode()
 
 void STKMeshSceneNode::drawGlow(const GLMesh &mesh)
 {
-    ColorizeProvider * const cb = (ColorizeProvider *)irr_driver->getCallback(ES_COLORIZE);
     assert(mesh.VAOType == video::EVT_STANDARD);
 
     GLenum ptype = mesh.PrimitiveType;
     GLenum itype = mesh.IndexType;
     size_t count = mesh.IndexCount;
-
-    MeshShader::ColorizeShader::getInstance()->setUniforms(AbsoluteTransformation, video::SColorf(cb->getRed(), cb->getGreen(), cb->getBlue()));
+    MeshShader::ColorizeShader::getInstance()->setUniforms(AbsoluteTransformation, video::SColorf(glowcolor.getRed() / 255.f, glowcolor.getGreen() / 255.f, glowcolor.getBlue() / 255.f));
     glDrawElementsBaseVertex(ptype, count, itype, (GLvoid *)mesh.vaoOffset, mesh.vaoBaseVertex);
 }
 
@@ -163,26 +169,8 @@ void STKMeshSceneNode::updatevbo()
     }
 }
 
-static video::ITexture *spareWhiteTex = 0;
-
-
-void STKMeshSceneNode::OnRegisterSceneNode()
+void STKMeshSceneNode::update()
 {
-    if (isDisplacement)
-        SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
-    else
-        CMeshSceneNode::OnRegisterSceneNode();
-}
-
-void STKMeshSceneNode::render()
-{
-    irr::video::IVideoDriver* driver = irr_driver->getVideoDriver();
-
-    if (!Mesh || !driver)
-        return;
-
-    ++PassCount;
-
     Box = Mesh->getBoundingBox();
 
     setFirstTimeMaterial();
@@ -194,13 +182,50 @@ void STKMeshSceneNode::render()
             continue;
         GLmeshes[i].TextureMatrix = getMaterial(i).getTextureMatrix(0);
     }
+}
 
-    if (irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS && immediate_draw)
+
+void STKMeshSceneNode::OnRegisterSceneNode()
+{
+    if (isDisplacement)
+        SceneManager->registerNodeForRendering(this, scene::ESNRP_TRANSPARENT);
+    else
+        CMeshSceneNode::OnRegisterSceneNode();
+}
+
+static video::ITexture *spareWhiteTex = 0;
+
+void STKMeshSceneNode::render()
+{
+    irr::video::IVideoDriver* driver = irr_driver->getVideoDriver();
+
+    if (!Mesh || !driver)
+        return;
+
+    ++PassCount;
+
+    update();
+
+    bool isTransparent;
+
+    for (u32 i = 0; i < Mesh->getMeshBufferCount(); ++i)
+    {
+        scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i);
+        if (!mb)
+            continue;
+
+        video::E_MATERIAL_TYPE type = mb->getMaterial().MaterialType;
+        video::IMaterialRenderer* rnd = driver->getMaterialRenderer(type);
+
+        isTransparent = rnd->isTransparent();
+        break;
+    }
+
+    if ((irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS) && immediate_draw && !isTransparent)
     {
         core::matrix4 invmodel;
         AbsoluteTransformation.getInverse(invmodel);
 
-
         glDisable(GL_CULL_FACE);
         if (update_each_frame)
             updatevbo();
@@ -214,6 +239,21 @@ void STKMeshSceneNode::render()
             GLenum itype = mesh.IndexType;
             size_t count = mesh.IndexCount;
 
+            if (!mesh.textures[0])
+                mesh.textures[0] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
+            compressTexture(mesh.textures[0], true);
+            if (UserConfigParams::m_azdo)
+            {
+#ifdef Bindless_Texture_Support
+                if (!mesh.TextureHandles[0])
+                    mesh.TextureHandles[0] = glGetTextureSamplerHandleARB(getTextureGLuint(mesh.textures[0]), MeshShader::TransparentFogShader::getInstance()->SamplersId[0]);
+                if (!glIsTextureHandleResidentARB(mesh.TextureHandles[0]))
+                    glMakeTextureHandleResidentARB(mesh.TextureHandles[0]);
+                MeshShader::ObjectPass1Shader::getInstance()->SetTextureHandles(createVector<uint64_t>(mesh.TextureHandles[0]));
+#endif
+            }
+            else
+                MeshShader::ObjectPass1Shader::getInstance()->SetTextureUnits(std::vector < GLuint > { getTextureGLuint(mesh.textures[0]) });
             MeshShader::ObjectPass1Shader::getInstance()->setUniforms(AbsoluteTransformation, invmodel);
             assert(mesh.vao);
             glBindVertexArray(mesh.vao);
@@ -224,53 +264,25 @@ void STKMeshSceneNode::render()
         return;
     }
 
-    if (irr_driver->getPhase() == SOLID_NORMAL_AND_DEPTH_PASS || irr_driver->getPhase() == SHADOW_PASS)
+    if (irr_driver->getPhase() == SOLID_LIT_PASS  && immediate_draw && !isTransparent)
     {
         core::matrix4 invmodel;
         AbsoluteTransformation.getInverse(invmodel);
 
-        GLMesh* mesh;
-        for_in(mesh, MeshSolidMaterials[MAT_DEFAULT])
-            pushVector(ListMatDefault::getInstance(), mesh, AbsoluteTransformation, invmodel, mesh->TextureMatrix);
-
-        for_in(mesh, MeshSolidMaterials[MAT_ALPHA_REF])
-            pushVector(ListMatAlphaRef::getInstance(), mesh, AbsoluteTransformation, invmodel, mesh->TextureMatrix);
-
-        for_in(mesh, MeshSolidMaterials[MAT_SPHEREMAP])
-            pushVector(ListMatSphereMap::getInstance(), mesh, AbsoluteTransformation, invmodel, mesh->TextureMatrix);
-
-        for_in(mesh, MeshSolidMaterials[MAT_DETAIL])
-            pushVector(ListMatDetails::getInstance(), mesh, AbsoluteTransformation, invmodel, mesh->TextureMatrix);
-
-        windDir = getWind();
-        for_in(mesh, MeshSolidMaterials[MAT_GRASS])
-            pushVector(ListMatGrass::getInstance(), mesh, AbsoluteTransformation, invmodel, windDir);
-
-        for_in(mesh, MeshSolidMaterials[MAT_UNLIT])
-            pushVector(ListMatUnlit::getInstance(), mesh, AbsoluteTransformation, core::matrix4::EM4CONST_IDENTITY, mesh->TextureMatrix);
-
-        for_in(mesh, MeshSolidMaterials[MAT_SPLATTING])
-            pushVector(ListMatSplatting::getInstance(), mesh, AbsoluteTransformation, invmodel);
-
-        for_in(mesh, MeshSolidMaterials[MAT_NORMAL_MAP])
-            pushVector(ListMatNormalMap::getInstance(), mesh, AbsoluteTransformation, invmodel, core::matrix4::EM4CONST_IDENTITY);
-
-        return;
-    }
-
-    if (irr_driver->getPhase() == SOLID_LIT_PASS)
-    {
-        core::matrix4 invmodel;
-        AbsoluteTransformation.getInverse(invmodel);
-
-        if (immediate_draw)
+        glDisable(GL_CULL_FACE);
+        if (!spareWhiteTex)
+            spareWhiteTex = getUnicolorTexture(video::SColor(255, 255, 255, 255));
+        glUseProgram(MeshShader::ObjectPass2Shader::getInstance()->Program);
+        // Only untextured
+        for (unsigned i = 0; i < GLmeshes.size(); i++)
         {
-            glDisable(GL_CULL_FACE);
-            if (!spareWhiteTex)
-                spareWhiteTex = getUnicolorTexture(video::SColor(255, 255, 255, 255));
-            glUseProgram(MeshShader::ObjectPass2Shader::getInstance()->Program);
-            // Only untextured
-            for (unsigned i = 0; i < GLmeshes.size(); i++)
+            irr_driver->IncreaseObjectCount();
+            GLMesh &mesh = GLmeshes[i];
+            GLenum ptype = mesh.PrimitiveType;
+            GLenum itype = mesh.IndexType;
+            size_t count = mesh.IndexCount;
+
+            if (UserConfigParams::m_azdo)
             {
                 irr_driver->IncreaseObjectCount();
                 GLMesh &mesh = GLmeshes[i];
@@ -278,17 +290,54 @@ void STKMeshSceneNode::render()
                 GLenum itype = mesh.IndexType;
                 size_t count = mesh.IndexCount;
 
-                setTexture(MeshShader::ObjectPass2Shader::getInstance()->TU_Albedo, getTextureGLuint(spareWhiteTex), GL_NEAREST, GL_NEAREST, false);
+                if (UserConfigParams::m_azdo)
+                {
+#ifdef Bindless_Texture_Support
+                    GLuint64 DiffuseHandle = glGetTextureSamplerHandleARB(irr_driver->getRenderTargetTexture(RTT_DIFFUSE), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[0]);
+                    if (!glIsTextureHandleResidentARB(DiffuseHandle))
+                        glMakeTextureHandleResidentARB(DiffuseHandle);
+
+                    GLuint64 SpecularHandle = glGetTextureSamplerHandleARB(irr_driver->getRenderTargetTexture(RTT_SPECULAR), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[1]);
+                    if (!glIsTextureHandleResidentARB(SpecularHandle))
+                        glMakeTextureHandleResidentARB(SpecularHandle);
+
+                    GLuint64 SSAOHandle = glGetTextureSamplerHandleARB(irr_driver->getRenderTargetTexture(RTT_HALF1_R), MeshShader::ObjectPass2Shader::getInstance()->SamplersId[2]);
+                    if (!glIsTextureHandleResidentARB(SSAOHandle))
+                        glMakeTextureHandleResidentARB(SSAOHandle);
+
+                    if (!mesh.TextureHandles[0])
+                        mesh.TextureHandles[0] = glGetTextureSamplerHandleARB(getTextureGLuint(spareWhiteTex), MeshShader::TransparentFogShader::getInstance()->SamplersId[0]);
+                    if (!glIsTextureHandleResidentARB(mesh.TextureHandles[0]))
+                        glMakeTextureHandleResidentARB(mesh.TextureHandles[0]);
+                    MeshShader::ObjectPass2Shader::getInstance()->SetTextureHandles(createVector<uint64_t>(DiffuseHandle, SpecularHandle, SSAOHandle, mesh.TextureHandles[0]));
+#endif
+                }
+                else
+                    MeshShader::ObjectPass2Shader::getInstance()->SetTextureUnits(createVector<GLuint>(
+                    irr_driver->getRenderTargetTexture(RTT_DIFFUSE),
+                    irr_driver->getRenderTargetTexture(RTT_SPECULAR),
+                    irr_driver->getRenderTargetTexture(RTT_HALF1_R),
+                    getTextureGLuint(spareWhiteTex)));
+
                 MeshShader::ObjectPass2Shader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix);
                 assert(mesh.vao);
                 glBindVertexArray(mesh.vao);
                 glDrawElements(ptype, count, itype, 0);
                 glBindVertexArray(0);
             }
-            glEnable(GL_CULL_FACE);
-            return;
+            else
+                MeshShader::ObjectPass2Shader::getInstance()->SetTextureUnits(createVector<GLuint>(
+                irr_driver->getRenderTargetTexture(RTT_DIFFUSE),
+                irr_driver->getRenderTargetTexture(RTT_SPECULAR),
+                irr_driver->getRenderTargetTexture(RTT_HALF1_R),
+                getTextureGLuint(mesh.textures[0])));
+            MeshShader::ObjectPass2Shader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix);
+            assert(mesh.vao);
+            glBindVertexArray(mesh.vao);
+            glDrawElements(ptype, count, itype, 0);
+            glBindVertexArray(0);
         }
-
+        glEnable(GL_CULL_FACE);
         return;
     }
 
@@ -300,12 +349,15 @@ void STKMeshSceneNode::render()
             scene::IMeshBuffer* mb = Mesh->getMeshBuffer(i);
             if (!mb)
                 continue;
-            glBindVertexArray(getVAO(video::EVT_STANDARD));
+            if (irr_driver->hasARB_base_instance())
+                glBindVertexArray(VAOManager::getInstance()->getVAO(video::EVT_STANDARD));
+            else
+                glBindVertexArray(GLmeshes[i].vao);
             drawGlow(GLmeshes[i]);
         }
     }
 
-    if (irr_driver->getPhase() == TRANSPARENT_PASS)
+    if (irr_driver->getPhase() == TRANSPARENT_PASS && isTransparent)
     {
         ModelViewProjectionMatrix = computeMVP(AbsoluteTransformation);
 
@@ -340,8 +392,21 @@ void STKMeshSceneNode::render()
                         tmpcol.getGreen() / 255.0f,
                         tmpcol.getBlue() / 255.0f);
 
+                    if (!mesh.textures[0])
+                        mesh.textures[0] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
                     compressTexture(mesh.textures[0], true);
-                    setTexture(MeshShader::TransparentFogShader::getInstance()->TU_tex, getTextureGLuint(mesh.textures[0]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
+                    if (UserConfigParams::m_azdo)
+                    {
+#ifdef Bindless_Texture_Support
+                        if (!mesh.TextureHandles[0])
+                            mesh.TextureHandles[0] = glGetTextureSamplerHandleARB(getTextureGLuint(mesh.textures[0]), MeshShader::TransparentFogShader::getInstance()->SamplersId[0]);
+                        if (!glIsTextureHandleResidentARB(mesh.TextureHandles[0]))
+                            glMakeTextureHandleResidentARB(mesh.TextureHandles[0]);
+                        MeshShader::TransparentFogShader::getInstance()->SetTextureHandles(createVector<uint64_t>(mesh.TextureHandles[0]));
+#endif
+                    }
+                    else
+                        MeshShader::TransparentFogShader::getInstance()->SetTextureUnits(std::vector<GLuint>{ getTextureGLuint(mesh.textures[0]) });
                     MeshShader::TransparentFogShader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix, fogmax, startH, endH, start, end, col);
 
                     assert(mesh.vao);
@@ -360,9 +425,21 @@ void STKMeshSceneNode::render()
                     GLenum ptype = mesh.PrimitiveType;
                     GLenum itype = mesh.IndexType;
                     size_t count = mesh.IndexCount;
-
+                    if (!mesh.textures[0])
+                        mesh.textures[0] = getUnicolorTexture(video::SColor(255, 255, 255, 255));
                     compressTexture(mesh.textures[0], true);
-                    setTexture(MeshShader::TransparentShader::getInstance()->TU_tex, getTextureGLuint(mesh.textures[0]), GL_LINEAR, GL_LINEAR_MIPMAP_LINEAR, true);
+                    if (UserConfigParams::m_azdo)
+                    {
+#ifdef Bindless_Texture_Support
+                        if (!mesh.TextureHandles[0])
+                            mesh.TextureHandles[0] = glGetTextureSamplerHandleARB(getTextureGLuint(mesh.textures[0]), MeshShader::TransparentShader::getInstance()->SamplersId[0]);
+                        if (!glIsTextureHandleResidentARB(mesh.TextureHandles[0]))
+                            glMakeTextureHandleResidentARB(mesh.TextureHandles[0]);
+                        MeshShader::TransparentShader::getInstance()->SetTextureHandles(createVector<uint64_t>(mesh.TextureHandles[0]));
+#endif
+                    }
+                    else
+                        MeshShader::TransparentShader::getInstance()->SetTextureUnits(std::vector<GLuint>{ getTextureGLuint(mesh.textures[0]) });
 
                     MeshShader::TransparentShader::getInstance()->setUniforms(AbsoluteTransformation, mesh.TextureMatrix);
                     assert(mesh.vao);
@@ -375,47 +452,16 @@ void STKMeshSceneNode::render()
         }
 
         GLMesh* mesh;
-
-        if (World::getWorld() && World::getWorld()->isFogEnabled())
-        {
-            const Track * const track = World::getWorld()->getTrack();
-
-            // Todo : put everything in a ubo
-            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 video::SColor tmpcol = track->getFogColor();
-
-            video::SColorf col(tmpcol.getRed() / 255.0f,
-                tmpcol.getGreen() / 255.0f,
-                tmpcol.getBlue() / 255.0f);
-
-            for_in(mesh, TransparentMesh[TM_DEFAULT])
-                pushVector(ListBlendTransparentFog::getInstance(), mesh, AbsoluteTransformation, mesh->TextureMatrix,
-                                                                fogmax, startH, endH, start, end, col);
-            for_in(mesh, TransparentMesh[TM_ADDITIVE])
-                pushVector(ListAdditiveTransparentFog::getInstance(), mesh, AbsoluteTransformation, mesh->TextureMatrix,
-                                                                   fogmax, startH, endH, start, end, col);
-        }
-        else
-        {
-            for_in(mesh, TransparentMesh[TM_DEFAULT])
-                pushVector(ListBlendTransparent::getInstance(), mesh, AbsoluteTransformation, mesh->TextureMatrix);
-
-            for_in(mesh, TransparentMesh[TM_ADDITIVE])
-                pushVector(ListAdditiveTransparent::getInstance(), mesh, AbsoluteTransformation, mesh->TextureMatrix);
-        }
-
-        for_in(mesh, TransparentMesh[TM_DISPLACEMENT])
-            pushVector(ListDisplacement::getInstance(), mesh, AbsoluteTransformation);
-
         if (!TransparentMesh[TM_BUBBLE].empty())
             glUseProgram(MeshShader::BubbleShader::Program);
-        glBindVertexArray(getVAO(video::EVT_STANDARD));
+        if (irr_driver->hasARB_base_instance())
+            glBindVertexArray(VAOManager::getInstance()->getVAO(video::EVT_STANDARD));
         for_in(mesh, TransparentMesh[TM_BUBBLE])
+        {
+            if (irr_driver->hasARB_base_instance())
+                glBindVertexArray(mesh->vao);
             drawBubble(*mesh, ModelViewProjectionMatrix);
+        }
         return;
     }
 }
diff --git a/src/graphics/stkmeshscenenode.hpp b/src/graphics/stkmeshscenenode.hpp
index d9e16283b..a8e8724ed 100644
--- a/src/graphics/stkmeshscenenode.hpp
+++ b/src/graphics/stkmeshscenenode.hpp
@@ -4,11 +4,9 @@
 #include "stkmesh.hpp"
 #include "utils/ptr_vector.hpp"
 
-class STKMeshSceneNode : public irr::scene::CMeshSceneNode
+class STKMeshSceneNode : public irr::scene::CMeshSceneNode, public STKMeshCommon
 {
 protected:
-    PtrVector<GLMesh, REF> MeshSolidMaterials[MAT_COUNT];
-    PtrVector<GLMesh, REF> TransparentMesh[TM_COUNT];
     std::vector<GLMesh> GLmeshes;
     core::matrix4 ModelViewProjectionMatrix;
     core::vector3df windDir;
@@ -24,7 +22,10 @@ protected:
     bool immediate_draw;
     bool update_each_frame;
     bool isDisplacement;
+    bool isGlow;
+    video::SColor glowcolor;
 public:
+    virtual void update();
     void setReloadEachFrame(bool);
     STKMeshSceneNode(irr::scene::IMesh* mesh, ISceneNode* parent, irr::scene::ISceneManager* mgr, irr::s32 id,
         const irr::core::vector3df& position = irr::core::vector3df(0, 0, 0),
@@ -35,6 +36,7 @@ public:
     virtual void setMesh(irr::scene::IMesh* mesh);
     virtual void OnRegisterSceneNode();
     virtual ~STKMeshSceneNode();
+    virtual bool isImmediateDraw() const { return immediate_draw; }
     void setIsDisplacement(bool v) {
         isDisplacement = v;
         for (u32 i = 0; i < Mesh->getMeshBufferCount(); ++i)
@@ -46,6 +48,9 @@ public:
                 mb->getMaterial().MaterialType = irr_driver->getShader(ES_DISPLACE);
         }
     }
+    virtual bool glow() const { return isGlow; }
+    void setGlowColors(const video::SColor &c) { isGlow = true; glowcolor = c; }
+    video::SColor getGlowColor() const { return glowcolor; }
 };
 
 #endif
diff --git a/src/graphics/stkscenemanager.cpp b/src/graphics/stkscenemanager.cpp
new file mode 100644
index 000000000..57721bf20
--- /dev/null
+++ b/src/graphics/stkscenemanager.cpp
@@ -0,0 +1,710 @@
+#include "stkscenemanager.hpp"
+#include "stkmesh.hpp"
+#include "irr_driver.hpp"
+#include <ISceneManager.h>
+#include <ISceneNode.h>
+#include "stkanimatedmesh.hpp"
+#include "stkmeshscenenode.hpp"
+#include "utils/ptr_vector.hpp"
+#include <ICameraSceneNode.h>
+#include <SViewFrustum.h>
+#include "callbacks.hpp"
+#include "utils/cpp2011.hpp"
+#include <omp.h>
+#include "modes/world.hpp"
+#include "tracks/track.hpp"
+#include "lod_node.hpp"
+#include "utils/profiler.hpp"
+#include <unordered_map>
+#include <SViewFrustum.h>
+#include <functional>
+
+static void
+FillInstances_impl(std::vector<std::pair<GLMesh *, scene::ISceneNode *> > InstanceList, InstanceData * InstanceBuffer, DrawElementsIndirectCommand *CommandBuffer,
+    size_t &InstanceBufferOffset, size_t &CommandBufferOffset, size_t &PolyCount, std::function<bool (const scene::ISceneNode *)> cull_func)
+{
+    // Should never be empty
+    GLMesh *mesh = InstanceList.front().first;
+    size_t InitialOffset = InstanceBufferOffset;
+
+    for (unsigned i = 0; i < InstanceList.size(); i++)
+    {
+        auto &Tp = InstanceList[i];
+        scene::ISceneNode *node = Tp.second;
+        if (cull_func(node))
+            continue;
+        InstanceData &Instance = InstanceBuffer[InstanceBufferOffset++];
+        const core::matrix4 &mat = node->getAbsoluteTransformation();
+        const core::vector3df &Origin = mat.getTranslation();
+        const core::vector3df &Orientation = mat.getRotationDegrees();
+        const core::vector3df &Scale = mat.getScale();
+        Instance.Origin.X = Origin.X;
+        Instance.Origin.Y = Origin.Y;
+        Instance.Origin.Z = Origin.Z;
+        Instance.Orientation.X = Orientation.X;
+        Instance.Orientation.Y = Orientation.Y;
+        Instance.Orientation.Z = Orientation.Z;
+        Instance.Scale.X = Scale.X;
+        Instance.Scale.Y = Scale.Y;
+        Instance.Scale.Z = Scale.Z;
+        Instance.Texture = mesh->TextureHandles[0];
+        Instance.SecondTexture = mesh->TextureHandles[1];
+    }
+
+    DrawElementsIndirectCommand &CurrentCommand = CommandBuffer[CommandBufferOffset++];
+    CurrentCommand.baseVertex = mesh->vaoBaseVertex;
+    CurrentCommand.count = mesh->IndexCount;
+    CurrentCommand.firstIndex = mesh->vaoOffset / 2;
+    CurrentCommand.baseInstance = InitialOffset;
+    CurrentCommand.instanceCount = InstanceBufferOffset - InitialOffset;
+
+    PolyCount += (InstanceBufferOffset - InitialOffset) * mesh->IndexCount / 3;
+}
+
+
+static void
+FillInstancesGlow_impl(std::vector<std::pair<GLMesh *, STKMeshCommon *> > InstanceList, GlowInstanceData * InstanceBuffer, DrawElementsIndirectCommand *CommandBuffer,
+    size_t &InstanceBufferOffset, size_t &CommandBufferOffset, std::function<bool (const scene::ISceneNode *)> cull_func)
+{
+    // Should never be empty
+    GLMesh *mesh = InstanceList.front().first;
+    size_t InitialOffset = InstanceBufferOffset;
+
+    for (unsigned i = 0; i < InstanceList.size(); i++)
+    {
+        STKMeshSceneNode *node = dynamic_cast<STKMeshSceneNode*>(InstanceList[i].second);
+        if (cull_func(node))
+            continue;
+        GlowInstanceData &Instance = InstanceBuffer[InstanceBufferOffset++];
+        const core::matrix4 &mat = node->getAbsoluteTransformation();
+        const core::vector3df &Origin = mat.getTranslation();
+        const core::vector3df &Orientation = mat.getRotationDegrees();
+        const core::vector3df &Scale = mat.getScale();
+        Instance.Color = node->getGlowColor().color;
+        Instance.Origin.X = Origin.X;
+        Instance.Origin.Y = Origin.Y;
+        Instance.Origin.Z = Origin.Z;
+        Instance.Orientation.X = Orientation.X;
+        Instance.Orientation.Y = Orientation.Y;
+        Instance.Orientation.Z = Orientation.Z;
+        Instance.Scale.X = Scale.X;
+        Instance.Scale.Y = Scale.Y;
+        Instance.Scale.Z = Scale.Z;
+    }
+
+    DrawElementsIndirectCommand &CurrentCommand = CommandBuffer[CommandBufferOffset++];
+    CurrentCommand.baseVertex = mesh->vaoBaseVertex;
+    CurrentCommand.count = mesh->IndexCount;
+    CurrentCommand.firstIndex = mesh->vaoOffset / 2;
+    CurrentCommand.baseInstance = InitialOffset;
+    CurrentCommand.instanceCount = InstanceBufferOffset - InitialOffset;
+}
+
+
+static
+void FillInstances(const std::unordered_map<scene::IMeshBuffer *, std::vector<std::pair<GLMesh *, scene::ISceneNode*> > > &GatheredGLMesh, std::vector<GLMesh *> &InstancedList,
+    InstanceData *InstanceBuffer, DrawElementsIndirectCommand *CommandBuffer, size_t &InstanceBufferOffset, size_t &CommandBufferOffset, size_t &Polycount,
+    std::function<bool (const scene::ISceneNode *)> cull_func)
+{
+    auto It = GatheredGLMesh.begin(), E = GatheredGLMesh.end();
+    for (; It != E; ++It)
+    {
+        FillInstances_impl(It->second, InstanceBuffer, CommandBuffer, InstanceBufferOffset, CommandBufferOffset, Polycount, cull_func);
+        if (!UserConfigParams::m_azdo)
+            InstancedList.push_back(It->second.front().first);
+    }
+}
+
+static
+void FillInstancesGrass(const std::unordered_map<scene::IMeshBuffer *, std::vector<std::pair<GLMesh *, scene::ISceneNode*> > > &GatheredGLMesh, std::vector<GLMesh *> &InstancedList,
+    InstanceData *InstanceBuffer, DrawElementsIndirectCommand *CommandBuffer, size_t &InstanceBufferOffset, size_t &CommandBufferOffset, const core::vector3df &dir, size_t &PolyCount,
+    std::function<bool (const scene::ISceneNode *)> cull_func)
+{
+    auto It = GatheredGLMesh.begin(), E = GatheredGLMesh.end();
+    SunLightProvider * const cb = (SunLightProvider *)irr_driver->getCallback(ES_SUNLIGHT);
+    for (; It != E; ++It)
+    {
+        FillInstances_impl(It->second, InstanceBuffer, CommandBuffer, InstanceBufferOffset, CommandBufferOffset, PolyCount, cull_func);
+        if (!UserConfigParams::m_azdo)
+            InstancedList.push_back(It->second.front().first);
+    }
+}
+
+static std::unordered_map <scene::IMeshBuffer *, std::vector<std::pair<GLMesh *, scene::ISceneNode*> > > MeshForSolidPass[MAT_COUNT];
+static std::unordered_map <scene::IMeshBuffer *, std::vector<std::pair<GLMesh *, scene::ISceneNode*> > > MeshForShadowPass[4][MAT_COUNT];
+static std::unordered_map <scene::IMeshBuffer *, std::vector<std::pair<GLMesh *, scene::ISceneNode*> > > MeshForRSMPass[MAT_COUNT];
+static std::unordered_map <scene::IMeshBuffer *, std::vector<std::pair<GLMesh *, STKMeshCommon *> > > MeshForGlowPass;
+static std::vector <STKMeshCommon *> DeferredUpdate;
+
+static core::vector3df windDir;
+
+// From irrlicht code
+static
+bool isBoxInFrontOfPlane(const core::plane3df &plane, const core::vector3df edges[8])
+{
+    for (u32 j = 0; j<8; ++j)
+        if (plane.classifyPointRelation(edges[j]) != core::ISREL3D_FRONT)
+            return false;
+    return true;
+}
+
+static
+bool isCulled(const scene::ICameraSceneNode *cam, const scene::ISceneNode *node)
+{
+    if (!node->getAutomaticCulling())
+        return false;
+
+    scene::SViewFrustum frust = *cam->getViewFrustum();
+
+    //transform the frustum to the node's current absolute transformation
+    core::matrix4 invTrans(node->getAbsoluteTransformation(), core::matrix4::EM4CONST_INVERSE);
+    //invTrans.makeInverse();
+    frust.transform(invTrans);
+
+    core::vector3df edges[8];
+    node->getBoundingBox().getEdges(edges);
+
+    for (s32 i = 0; i < scene::SViewFrustum::VF_PLANE_COUNT; ++i)
+        if (isBoxInFrontOfPlane(frust.planes[i], edges))
+            return true;
+    return false;
+}
+
+static void
+handleSTKCommon(scene::ISceneNode *Node, std::vector<scene::ISceneNode *> *ImmediateDraw)
+{
+    STKMeshCommon *node = dynamic_cast<STKMeshCommon*>(Node);
+    if (!node)
+        return;
+    DeferredUpdate.push_back(node);
+
+    if (node->isImmediateDraw())
+    {
+        ImmediateDraw->push_back(Node);
+        return;
+    }
+    for (unsigned Mat = 0; Mat < MAT_COUNT; ++Mat)
+    {
+        GLMesh *mesh;
+        if (irr_driver->hasARB_draw_indirect())
+        {
+            for_in(mesh, node->MeshSolidMaterial[Mat])
+            {
+                if (node->glow())
+                    MeshForGlowPass[mesh->mb].emplace_back(mesh, node);
+
+                if (Mat != MAT_SPLATTING && mesh->TextureMatrix.isIdentity())
+                    MeshForSolidPass[Mat][mesh->mb].emplace_back(mesh, Node);
+                else
+                {
+                    core::matrix4 ModelMatrix = Node->getAbsoluteTransformation(), InvModelMatrix;
+                    ModelMatrix.getInverse(InvModelMatrix);
+                    switch (Mat)
+                    {
+                    case MAT_DEFAULT:
+                        ListMatDefault::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_ALPHA_REF:
+                        ListMatAlphaRef::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_UNLIT:
+                        ListMatUnlit::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_SPLATTING:
+                        ListMatSplatting::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix);
+                        break;
+                    }
+                }
+            }
+        }
+        else
+        {
+            core::matrix4 ModelMatrix = Node->getAbsoluteTransformation(), InvModelMatrix;
+            ModelMatrix.getInverse(InvModelMatrix);
+
+            for_in(mesh, node->MeshSolidMaterial[Mat])
+            {
+                switch (Mat)
+                {
+                case MAT_DEFAULT:
+                    ListMatDefault::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_ALPHA_REF:
+                    ListMatAlphaRef::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_NORMAL_MAP:
+                    ListMatNormalMap::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_DETAIL:
+                    ListMatDetails::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_UNLIT:
+                    ListMatUnlit::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_SPHEREMAP:
+                    ListMatSphereMap::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_SPLATTING:
+                    ListMatSplatting::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix);
+                    break;
+                case MAT_GRASS:
+                    ListMatGrass::getInstance()->SolidPass.emplace_back(mesh, ModelMatrix, InvModelMatrix, windDir);
+                    break;
+                }
+            }
+        }
+        if (!UserConfigParams::m_shadows)
+            return;
+        for (unsigned cascade = 0; cascade < 4; ++cascade)
+        {
+            if (irr_driver->hasARB_draw_indirect())
+            {
+                for_in(mesh, node->MeshSolidMaterial[Mat])
+                    MeshForShadowPass[cascade][Mat][mesh->mb].emplace_back(mesh, Node);
+            }
+            else
+            {
+                core::matrix4 ModelMatrix = Node->getAbsoluteTransformation(), InvModelMatrix;
+                ModelMatrix.getInverse(InvModelMatrix);
+
+                for_in(mesh, node->MeshSolidMaterial[Mat])
+                {
+                    switch (Mat)
+                    {
+                    case MAT_DEFAULT:
+                        ListMatDefault::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_ALPHA_REF:
+                        ListMatAlphaRef::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_NORMAL_MAP:
+                        ListMatNormalMap::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_DETAIL:
+                        ListMatDetails::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_UNLIT:
+                        ListMatUnlit::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_SPHEREMAP:
+                        ListMatSphereMap::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                        break;
+                    case MAT_SPLATTING:
+                        ListMatSplatting::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix);
+                        break;
+                    case MAT_GRASS:
+                        ListMatGrass::getInstance()->Shadows[cascade].emplace_back(mesh, ModelMatrix, InvModelMatrix, windDir);
+                    }
+                }
+            }
+        }
+        if (!UserConfigParams::m_gi)
+            return;
+        if (irr_driver->hasARB_draw_indirect())
+        {
+            for_in(mesh, node->MeshSolidMaterial[Mat])
+                if (Mat != MAT_SPLATTING)
+                    MeshForRSMPass[Mat][mesh->mb].emplace_back(mesh, Node);
+                else
+                {
+                    core::matrix4 ModelMatrix = Node->getAbsoluteTransformation(), InvModelMatrix;
+                    ModelMatrix.getInverse(InvModelMatrix);
+                    ListMatSplatting::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix);
+                }
+        }
+        else
+        {
+            core::matrix4 ModelMatrix = Node->getAbsoluteTransformation(), InvModelMatrix;
+            ModelMatrix.getInverse(InvModelMatrix);
+
+            for_in(mesh, node->MeshSolidMaterial[Mat])
+            {
+                switch (Mat)
+                {
+                case MAT_DEFAULT:
+                    ListMatDefault::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_ALPHA_REF:
+                    ListMatAlphaRef::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_NORMAL_MAP:
+                    ListMatNormalMap::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_DETAIL:
+                    ListMatDetails::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_UNLIT:
+                    ListMatUnlit::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_SPHEREMAP:
+                    ListMatSphereMap::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, mesh->TextureMatrix);
+                    break;
+                case MAT_SPLATTING:
+                    ListMatSplatting::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix);
+                    break;
+                case MAT_GRASS:
+                    ListMatGrass::getInstance()->RSM.emplace_back(mesh, ModelMatrix, InvModelMatrix, windDir);
+                    break;
+                }
+            }
+        }
+    }
+    // Transparent
+    GLMesh *mesh;
+    if (World::getWorld() && World::getWorld()->isFogEnabled())
+    {
+        const Track * const track = World::getWorld()->getTrack();
+
+        // Todo : put everything in a ubo
+        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 video::SColor tmpcol = track->getFogColor();
+
+        video::SColorf col(tmpcol.getRed() / 255.0f,
+            tmpcol.getGreen() / 255.0f,
+            tmpcol.getBlue() / 255.0f);
+
+        for_in(mesh, node->TransparentMesh[TM_DEFAULT])
+            pushVector(ListBlendTransparentFog::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix,
+            fogmax, startH, endH, start, end, col);
+        for_in(mesh, node->TransparentMesh[TM_ADDITIVE])
+            pushVector(ListAdditiveTransparentFog::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix,
+            fogmax, startH, endH, start, end, col);
+    }
+    else
+    {
+        for_in(mesh, node->TransparentMesh[TM_DEFAULT])
+            pushVector(ListBlendTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix);
+        for_in(mesh, node->TransparentMesh[TM_ADDITIVE])
+            pushVector(ListAdditiveTransparent::getInstance(), mesh, Node->getAbsoluteTransformation(), mesh->TextureMatrix);
+    }
+    for_in(mesh, node->TransparentMesh[TM_DISPLACEMENT])
+        pushVector(ListDisplacement::getInstance(), mesh, Node->getAbsoluteTransformation());
+}
+
+static void
+parseSceneManager(core::list<scene::ISceneNode*> List, std::vector<scene::ISceneNode *> *ImmediateDraw,
+    scene::ICameraSceneNode* cam)
+{
+    core::list<scene::ISceneNode*>::Iterator I = List.begin(), E = List.end();
+    for (; I != E; ++I)
+    {
+        if (LODNode *node = dynamic_cast<LODNode *>(*I))
+            node->updateVisibility();
+        if (!(*I)->isVisible())
+            continue;
+        (*I)->updateAbsolutePosition();
+
+        if (ParticleSystemProxy *node = dynamic_cast<ParticleSystemProxy *>(*I))
+        {
+            if (!isCulled(cam, *I) && node->update())
+                ParticlesList::getInstance()->push_back(node);
+            continue;
+        }
+
+        handleSTKCommon(*I, ImmediateDraw);
+
+        parseSceneManager((*I)->getChildren(), ImmediateDraw, cam);
+    }
+}
+
+template<MeshMaterial Mat> static void
+GenDrawCalls(unsigned cascade, std::vector<GLMesh *> &InstancedList,
+    InstanceData *InstanceBuffer, DrawElementsIndirectCommand *CommandBuffer, size_t &InstanceBufferOffset, size_t &CommandBufferOffset, size_t &PolyCount)
+{
+    std::function<bool(const scene::ISceneNode *)> shadowculling = [&](const scene::ISceneNode *nd) {return dynamic_cast<const STKMeshCommon*>(nd)->isCulledForShadowCam(cascade); };
+    if (irr_driver->hasARB_draw_indirect())
+        ShadowPassCmd::getInstance()->Offset[cascade][Mat] = CommandBufferOffset; // Store command buffer offset
+    FillInstances(MeshForShadowPass[cascade][Mat], InstancedList, InstanceBuffer, CommandBuffer, InstanceBufferOffset, CommandBufferOffset, PolyCount, shadowculling);
+    if (UserConfigParams::m_azdo)
+        ShadowPassCmd::getInstance()->Size[cascade][Mat] = CommandBufferOffset - ShadowPassCmd::getInstance()->Offset[cascade][Mat];
+}
+
+template<MeshMaterial Mat> static void
+GenDrawCallsGrass(unsigned cascade, std::vector<GLMesh *> &InstancedList,
+InstanceData *InstanceBuffer, DrawElementsIndirectCommand *CommandBuffer, size_t &InstanceBufferOffset, size_t &CommandBufferOffset, const core::vector3df &dir, size_t &PolyCount)
+{
+    std::function<bool(const scene::ISceneNode *)> shadowculling = [&](const scene::ISceneNode *nd) {return dynamic_cast<const STKMeshCommon*>(nd)->isCulledForShadowCam(cascade); };
+    if (irr_driver->hasARB_draw_indirect())
+        ShadowPassCmd::getInstance()->Offset[cascade][Mat] = CommandBufferOffset; // Store command buffer offset
+    FillInstancesGrass(MeshForShadowPass[cascade][Mat], InstancedList, InstanceBuffer, CommandBuffer, InstanceBufferOffset, CommandBufferOffset, dir, PolyCount, shadowculling);
+    if (UserConfigParams::m_azdo)
+        ShadowPassCmd::getInstance()->Size[cascade][Mat] = CommandBufferOffset - ShadowPassCmd::getInstance()->Offset[cascade][Mat];
+}
+
+void IrrDriver::PrepareDrawCalls(scene::ICameraSceneNode *camnode)
+{
+    windDir = getWindDir();
+    ListBlendTransparent::getInstance()->clear();
+    ListAdditiveTransparent::getInstance()->clear();
+    ListBlendTransparentFog::getInstance()->clear();
+    ListAdditiveTransparentFog::getInstance()->clear();
+    ListDisplacement::getInstance()->clear();
+
+    ListMatDefault::getInstance()->clear();
+    ListMatAlphaRef::getInstance()->clear();
+    ListMatSphereMap::getInstance()->clear();
+    ListMatDetails::getInstance()->clear();
+    ListMatUnlit::getInstance()->clear();
+    ListMatNormalMap::getInstance()->clear();
+    ListMatGrass::getInstance()->clear();
+    ListMatSplatting::getInstance()->clear();
+
+    ImmediateDrawList::getInstance()->clear();
+    ParticlesList::getInstance()->clear();
+    ListInstancedGlow::getInstance()->clear();
+
+    for (unsigned Mat = 0; Mat < MAT_COUNT; ++Mat)
+    {
+        MeshForSolidPass[Mat].clear();
+        MeshForRSMPass[Mat].clear();
+        for (unsigned cascade = 0; cascade < 4; ++cascade)
+            MeshForShadowPass[cascade][Mat].clear();
+    }
+    MeshForGlowPass.clear();
+    DeferredUpdate.clear();
+    core::list<scene::ISceneNode*> List = m_scene_manager->getRootSceneNode()->getChildren();
+
+    parseSceneManager(List, ImmediateDrawList::getInstance(), camnode);
+
+#pragma omp parallel for
+    for (int i = 0; i < (int)DeferredUpdate.size(); i++)
+    {
+        scene::ISceneNode *node = dynamic_cast<scene::ISceneNode *>(DeferredUpdate[i]);
+        DeferredUpdate[i]->setCulledForPlayerCam(isCulled(camnode, node));
+        DeferredUpdate[i]->setCulledForRSMCam(isCulled(m_suncam, node));
+        DeferredUpdate[i]->setCulledForShadowCam(0, isCulled(m_shadow_camnodes[0], node));
+        DeferredUpdate[i]->setCulledForShadowCam(1, isCulled(m_shadow_camnodes[1], node));
+        DeferredUpdate[i]->setCulledForShadowCam(2, isCulled(m_shadow_camnodes[2], node));
+        DeferredUpdate[i]->setCulledForShadowCam(3, isCulled(m_shadow_camnodes[3], node));
+    }
+
+    // Add a 1 s timeout
+    if (!m_sync)
+        m_sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+    PROFILER_PUSH_CPU_MARKER("- Sync Stall", 0xFF, 0x0, 0x0);
+    GLenum reason = glClientWaitSync(m_sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000000);
+    PROFILER_POP_CPU_MARKER();
+    /*    switch (reason)
+    {
+    case GL_ALREADY_SIGNALED:
+    printf("Already Signaled\n");
+    break;
+    case GL_TIMEOUT_EXPIRED:
+    printf("Timeout Expired\n");
+    break;
+    case GL_CONDITION_SATISFIED:
+    printf("Condition Satisfied\n");
+    break;
+    case GL_WAIT_FAILED:
+    printf("Wait Failed\n");
+    break;
+    }*/
+    for (unsigned i = 0; i < DeferredUpdate.size(); i++)
+        DeferredUpdate[i]->update();
+
+    if (!irr_driver->hasARB_draw_indirect())
+        return;
+
+    InstanceData *InstanceBuffer;
+    InstanceData *ShadowInstanceBuffer;
+    InstanceData *RSMInstanceBuffer;
+    GlowInstanceData *GlowInstanceBuffer;
+    DrawElementsIndirectCommand *CmdBuffer;
+    DrawElementsIndirectCommand *ShadowCmdBuffer;
+    DrawElementsIndirectCommand *RSMCmdBuffer;
+    DrawElementsIndirectCommand *GlowCmdBuffer;
+
+    if (irr_driver->hasBufferStorageExtension())
+    {
+        InstanceBuffer = (InstanceData*)VAOManager::getInstance()->getInstanceBufferPtr(InstanceTypeDefault);
+        ShadowInstanceBuffer = (InstanceData*)VAOManager::getInstance()->getInstanceBufferPtr(InstanceTypeShadow);
+        RSMInstanceBuffer = (InstanceData*)VAOManager::getInstance()->getInstanceBufferPtr(InstanceTypeRSM);
+        GlowInstanceBuffer = (GlowInstanceData*)VAOManager::getInstance()->getInstanceBufferPtr(InstanceTypeGlow);
+        CmdBuffer = SolidPassCmd::getInstance()->Ptr;
+        ShadowCmdBuffer = ShadowPassCmd::getInstance()->Ptr;
+        GlowCmdBuffer = GlowPassCmd::getInstance()->Ptr;
+        RSMCmdBuffer = RSMPassCmd::getInstance()->Ptr;
+    }
+
+    ListInstancedMatDefault::getInstance()->clear();
+    ListInstancedMatAlphaRef::getInstance()->clear();
+    ListInstancedMatGrass::getInstance()->clear();
+    ListInstancedMatNormalMap::getInstance()->clear();
+    ListInstancedMatSphereMap::getInstance()->clear();
+    ListInstancedMatDetails::getInstance()->clear();
+    ListInstancedMatUnlit::getInstance()->clear();
+
+    size_t SolidPoly = 0, ShadowPoly = 0, MiscPoly = 0;
+
+    PROFILER_PUSH_CPU_MARKER("- Draw Command upload", 0xFF, 0x0, 0xFF);
+
+    auto playercamculling = [](const scene::ISceneNode *nd) {return dynamic_cast<const STKMeshCommon*>(nd)->isCulledForPlayerCam(); };
+#pragma omp parallel sections
+    {
+#pragma omp section
+        {
+            size_t offset = 0, current_cmd = 0;
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glBindBuffer(GL_ARRAY_BUFFER, VAOManager::getInstance()->getInstanceBuffer(InstanceTypeDefault));
+                InstanceBuffer = (InstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, 10000 * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+                glBindBuffer(GL_DRAW_INDIRECT_BUFFER, SolidPassCmd::getInstance()->drawindirectcmd);
+                CmdBuffer = (DrawElementsIndirectCommand*)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+            }
+
+
+            // Default Material
+            SolidPassCmd::getInstance()->Offset[MAT_DEFAULT] = current_cmd;
+            FillInstances(MeshForSolidPass[MAT_DEFAULT], ListInstancedMatDefault::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_DEFAULT] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_DEFAULT];
+            // Alpha Ref
+            SolidPassCmd::getInstance()->Offset[MAT_ALPHA_REF] = current_cmd;
+            FillInstances(MeshForSolidPass[MAT_ALPHA_REF], ListInstancedMatAlphaRef::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_ALPHA_REF] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_ALPHA_REF];
+            // Unlit
+            SolidPassCmd::getInstance()->Offset[MAT_UNLIT] = current_cmd;
+            FillInstances(MeshForSolidPass[MAT_UNLIT], ListInstancedMatUnlit::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_UNLIT] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_UNLIT];
+            // Spheremap
+            SolidPassCmd::getInstance()->Offset[MAT_SPHEREMAP] = current_cmd;
+            FillInstances(MeshForSolidPass[MAT_SPHEREMAP], ListInstancedMatSphereMap::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_SPHEREMAP] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_SPHEREMAP];
+            // Detail
+            SolidPassCmd::getInstance()->Offset[MAT_DETAIL] = current_cmd;
+            FillInstances(MeshForSolidPass[MAT_DETAIL], ListInstancedMatDetails::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_DETAIL] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_DETAIL];
+            // Normal Map
+            SolidPassCmd::getInstance()->Offset[MAT_NORMAL_MAP] = current_cmd;
+            FillInstances(MeshForSolidPass[MAT_NORMAL_MAP], ListInstancedMatNormalMap::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_NORMAL_MAP] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_NORMAL_MAP];
+
+            // Grass
+            SolidPassCmd::getInstance()->Offset[MAT_GRASS] = current_cmd;
+            FillInstancesGrass(MeshForSolidPass[MAT_GRASS], ListInstancedMatGrass::getInstance()->SolidPass, InstanceBuffer, CmdBuffer, offset, current_cmd, windDir, SolidPoly, playercamculling);
+            SolidPassCmd::getInstance()->Size[MAT_GRASS] = current_cmd - SolidPassCmd::getInstance()->Offset[MAT_GRASS];
+
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glUnmapBuffer(GL_ARRAY_BUFFER);
+                glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
+            }
+        }
+#pragma omp section
+        {
+            size_t offset = 0, current_cmd = 0;
+
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glBindBuffer(GL_ARRAY_BUFFER, VAOManager::getInstance()->getInstanceBuffer(InstanceTypeGlow));
+                GlowInstanceBuffer = (GlowInstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, 10000 * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+                glBindBuffer(GL_DRAW_INDIRECT_BUFFER, GlowPassCmd::getInstance()->drawindirectcmd);
+                GlowCmdBuffer = (DrawElementsIndirectCommand*)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+            }
+
+            // Glow
+            if (irr_driver->hasARB_draw_indirect())
+                GlowPassCmd::getInstance()->Offset = offset; // Store command buffer offset
+
+            auto It = MeshForGlowPass.begin(), E = MeshForGlowPass.end();
+            for (; It != E; ++It)
+            {
+                FillInstancesGlow_impl(It->second, GlowInstanceBuffer, GlowCmdBuffer, offset, current_cmd, playercamculling);
+                if (!UserConfigParams::m_azdo)
+                    ListInstancedGlow::getInstance()->push_back(It->second.front().first);
+            }
+
+            if (UserConfigParams::m_azdo)
+                GlowPassCmd::getInstance()->Size = current_cmd - GlowPassCmd::getInstance()->Offset;
+
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glUnmapBuffer(GL_ARRAY_BUFFER);
+                glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
+            }
+        }
+#pragma omp section
+        {
+            irr_driver->setPhase(SHADOW_PASS);
+
+            size_t offset = 0, current_cmd = 0;
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glBindBuffer(GL_ARRAY_BUFFER, VAOManager::getInstance()->getInstanceBuffer(InstanceTypeShadow));
+                ShadowInstanceBuffer = (InstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, 10000 * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+                glBindBuffer(GL_DRAW_INDIRECT_BUFFER, ShadowPassCmd::getInstance()->drawindirectcmd);
+                ShadowCmdBuffer = (DrawElementsIndirectCommand*)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+            }
+
+            for (unsigned i = 0; i < 4; i++)
+            {
+                // Mat default
+                GenDrawCalls<MAT_DEFAULT>(i, ListInstancedMatDefault::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, ShadowPoly);
+                // Mat AlphaRef
+                GenDrawCalls<MAT_ALPHA_REF>(i, ListInstancedMatAlphaRef::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, ShadowPoly);
+                // Mat Unlit
+                GenDrawCalls<MAT_UNLIT>(i, ListInstancedMatUnlit::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, ShadowPoly);
+                // Mat NormalMap
+                GenDrawCalls<MAT_NORMAL_MAP>(i, ListInstancedMatNormalMap::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, ShadowPoly);
+                // Mat Spheremap
+                GenDrawCalls<MAT_SPHEREMAP>(i, ListInstancedMatSphereMap::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, ShadowPoly);
+                // Mat Detail
+                GenDrawCalls<MAT_DETAIL>(i, ListInstancedMatDetails::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, ShadowPoly);
+                // Mat Grass
+                GenDrawCallsGrass<MAT_GRASS>(i, ListInstancedMatGrass::getInstance()->Shadows[i], ShadowInstanceBuffer, ShadowCmdBuffer, offset, current_cmd, windDir, ShadowPoly);
+            }
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glUnmapBuffer(GL_ARRAY_BUFFER);
+                glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
+            }
+        }
+#pragma omp section
+        {
+            auto rsmcamculling = [](const scene::ISceneNode *nd) {return dynamic_cast<const STKMeshCommon*>(nd)->isCulledForRSMCam(); };
+            size_t offset = 0, current_cmd = 0;
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glBindBuffer(GL_ARRAY_BUFFER, VAOManager::getInstance()->getInstanceBuffer(InstanceTypeRSM));
+                RSMInstanceBuffer = (InstanceData*)glMapBufferRange(GL_ARRAY_BUFFER, 0, 10000 * sizeof(InstanceData), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+                glBindBuffer(GL_DRAW_INDIRECT_BUFFER, RSMPassCmd::getInstance()->drawindirectcmd);
+                RSMCmdBuffer = (DrawElementsIndirectCommand*)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_INVALIDATE_BUFFER_BIT);
+            }
+
+            // Default Material
+            RSMPassCmd::getInstance()->Offset[MAT_DEFAULT] = current_cmd;
+            FillInstances(MeshForRSMPass[MAT_DEFAULT], ListInstancedMatDefault::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly, rsmcamculling);
+            RSMPassCmd::getInstance()->Size[MAT_DEFAULT] = current_cmd - RSMPassCmd::getInstance()->Offset[MAT_DEFAULT];
+            // Alpha Ref
+            RSMPassCmd::getInstance()->Offset[MAT_ALPHA_REF] = current_cmd;
+            FillInstances(MeshForRSMPass[MAT_ALPHA_REF], ListInstancedMatAlphaRef::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly, rsmcamculling);
+            RSMPassCmd::getInstance()->Size[MAT_ALPHA_REF] = current_cmd - RSMPassCmd::getInstance()->Offset[MAT_ALPHA_REF];
+            // Unlit
+            RSMPassCmd::getInstance()->Offset[MAT_UNLIT] = current_cmd;
+            FillInstances(MeshForRSMPass[MAT_UNLIT], ListInstancedMatUnlit::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly, rsmcamculling);
+            RSMPassCmd::getInstance()->Size[MAT_UNLIT] = current_cmd - RSMPassCmd::getInstance()->Offset[MAT_UNLIT];
+            // Detail
+            RSMPassCmd::getInstance()->Offset[MAT_DETAIL] = current_cmd;
+            FillInstances(MeshForRSMPass[MAT_DETAIL], ListInstancedMatDetails::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly, rsmcamculling);
+            RSMPassCmd::getInstance()->Size[MAT_DETAIL] = current_cmd - RSMPassCmd::getInstance()->Offset[MAT_DETAIL];
+            // Normal Map
+            RSMPassCmd::getInstance()->Offset[MAT_NORMAL_MAP] = current_cmd;
+            FillInstances(MeshForRSMPass[MAT_NORMAL_MAP], ListInstancedMatNormalMap::getInstance()->RSM, RSMInstanceBuffer, RSMCmdBuffer, offset, current_cmd, MiscPoly, rsmcamculling);
+            RSMPassCmd::getInstance()->Size[MAT_NORMAL_MAP] = current_cmd - RSMPassCmd::getInstance()->Offset[MAT_NORMAL_MAP];
+
+            if (!irr_driver->hasBufferStorageExtension())
+            {
+                glUnmapBuffer(GL_ARRAY_BUFFER);
+                glUnmapBuffer(GL_DRAW_INDIRECT_BUFFER);
+            }
+        }
+    }
+    PROFILER_POP_CPU_MARKER();
+    poly_count[SOLID_NORMAL_AND_DEPTH_PASS] += SolidPoly;
+    poly_count[SHADOW_PASS] += ShadowPoly;
+
+    glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
+}
\ No newline at end of file
diff --git a/src/graphics/stkscenemanager.hpp b/src/graphics/stkscenemanager.hpp
new file mode 100644
index 000000000..998fe2a72
--- /dev/null
+++ b/src/graphics/stkscenemanager.hpp
@@ -0,0 +1,68 @@
+// Not really a scene manager yet but hold algorithm that
+// rework scene manager output
+
+#ifndef HEADER_STKSCENEMANAGER_HPP
+#define HEADER_STKSCENEMANAGER_HPP
+
+#include "utils/singleton.hpp"
+#include "gl_headers.hpp"
+#include "stkmesh.hpp"
+#include "gpuparticles.hpp"
+
+template<typename T>
+class CommandBuffer : public Singleton<T>
+{
+public:
+    GLuint drawindirectcmd;
+    DrawElementsIndirectCommand *Ptr;
+    CommandBuffer()
+    {
+        glGenBuffers(1, &drawindirectcmd);
+        glBindBuffer(GL_DRAW_INDIRECT_BUFFER, drawindirectcmd);
+#ifdef Buffer_Storage
+        if (irr_driver->hasBufferStorageExtension())
+        {
+            glBufferStorage(GL_DRAW_INDIRECT_BUFFER, 10000 * sizeof(DrawElementsIndirectCommand), 0, GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT);
+            Ptr = (DrawElementsIndirectCommand *)glMapBufferRange(GL_DRAW_INDIRECT_BUFFER, 0, 10000 * sizeof(DrawElementsIndirectCommand), GL_MAP_PERSISTENT_BIT | GL_MAP_WRITE_BIT);
+        }
+        else
+#endif
+        {
+            glBufferData(GL_DRAW_INDIRECT_BUFFER, 10000 * sizeof(DrawElementsIndirectCommand), 0, GL_STREAM_DRAW);
+        }
+    }
+};
+
+class ImmediateDrawList : public Singleton<ImmediateDrawList>, public std::vector<scene::ISceneNode *>
+{};
+
+class ParticlesList : public Singleton<ParticlesList>, public std::vector<ParticleSystemProxy *>
+{};
+
+
+class SolidPassCmd : public CommandBuffer<SolidPassCmd>
+{
+public:
+    size_t Offset[MAT_COUNT], Size[MAT_COUNT];
+};
+
+class ShadowPassCmd : public CommandBuffer<ShadowPassCmd>
+{
+public:
+    size_t Offset[4][MAT_COUNT], Size[4][MAT_COUNT];
+};
+
+class RSMPassCmd : public CommandBuffer<RSMPassCmd>
+{
+public:
+    size_t Offset[MAT_COUNT], Size[MAT_COUNT];
+};
+
+class GlowPassCmd : public CommandBuffer<GlowPassCmd>
+{
+public:
+    size_t Offset, Size;
+};
+
+
+#endif
\ No newline at end of file
diff --git a/src/graphics/rain.cpp b/src/graphics/weather.cpp
similarity index 54%
rename from src/graphics/rain.cpp
rename to src/graphics/weather.cpp
index 3c9a75bd2..2f62e4f06 100644
--- a/src/graphics/rain.cpp
+++ b/src/graphics/weather.cpp
@@ -18,92 +18,80 @@
 
 #include "audio/sfx_base.hpp"
 #include "audio/sfx_manager.hpp"
-#include "graphics/camera.hpp"
-#include "graphics/glwrap.hpp"
-#include "graphics/gpuparticles.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 "graphics/weather.hpp"
 #include "modes/world.hpp"
 #include "states_screens/race_gui.hpp"
-#include "utils/constants.hpp"
 #include "utils/random_generator.hpp"
 
 
-#include <ISceneManager.h>
-#include <SMeshBuffer.h>
+// The weather manager
 
-using namespace video;
-using namespace scene;
-using namespace core;
-
-// The rain manager
-
-Rain::Rain(Camera *camera, irr::scene::ISceneNode* parent) : m_thunder_sound(0)
+Weather::Weather(bool lightning, std::string sound)
 {
-    m_lightning = camera->getIndex()==0;
-
+    m_lightning = lightning;
+    m_thunder_sound = NULL;
+    m_weather_sound = NULL;
+    
     if (m_lightning)
-        m_thunder_sound = sfx_manager->createSoundSource("thunder");
+    {
+        m_thunder_sound = sfx_manager->createSoundSource("thunder");        
+    }
 
-    Material* m = material_manager->getMaterial("rain.png");
-    assert(m != NULL);
+    if (sound != "")
+    {
+        m_weather_sound = sfx_manager->createSoundSource(sound);
+    }
 
     RandomGenerator g;
     m_next_lightning = (float)g.get(35);
-
-//    RainNode *node = new RainNode(irr_driver->getSceneManager(), m->getTexture());
-//    m_node = irr_driver->addPerCameraNode(node, camera->getCameraSceneNode(), parent);
-//    m_node->setAutomaticCulling(0);
-}   // Rain
+}   // Weather
 
 // ----------------------------------------------------------------------------
 
-Rain::~Rain()
+Weather::~Weather()
 {
-//    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);
+    if (m_thunder_sound != NULL) 
+        sfx_manager->deleteSFX(m_thunder_sound);
+        
+    if (m_weather_sound != NULL) 
+        sfx_manager->deleteSFX(m_weather_sound);
 }
 
 // ----------------------------------------------------------------------------
 
-void Rain::update(float dt)
+void Weather::update(float dt)
 {
     if (m_lightning)
     {
         m_next_lightning -= dt;
-
+    
         if (m_next_lightning < 0.0f)
         {
             RaceGUIBase* gui_base = World::getWorld()->getRaceGUI();
+
             if (gui_base != NULL)
             {
                 gui_base->doLightning();
-                if (m_thunder_sound) m_thunder_sound->play();
-            }
 
+                if (m_thunder_sound) 
+                {
+                    m_thunder_sound->play();
+                }
+            }
+    
             RandomGenerator g;
             m_next_lightning = 35 + (float)g.get(35);
         }
     }
-
 }   // update
 
 // ----------------------------------------------------------------------------
 
-void Rain::setPosition(const core::vector3df& position)
+void Weather::playSound()
 {
-//    m_node->getChild()->setPosition(position);
-}   // setPosition
-
-// ----------------------------------------------------------------------------
-
-void Rain::setCamera(scene::ICameraSceneNode* camera)
-{
-//    m_node->setCamera(camera);
+    if (m_weather_sound) 
+    {
+        m_weather_sound->setLoop(true);
+        m_weather_sound->play();
+    }   
 }
diff --git a/src/graphics/rain.hpp b/src/graphics/weather.hpp
similarity index 66%
rename from src/graphics/rain.hpp
rename to src/graphics/weather.hpp
index b8408c2a8..d6fb1b135 100644
--- a/src/graphics/rain.hpp
+++ b/src/graphics/weather.hpp
@@ -16,38 +16,25 @@
 //  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_RAIN_HPP
-#define HEADER_RAIN_HPP
-
-class Camera;
-class PerCameraNode;
-
-#include <vector3d.h>
-namespace irr
-{
-    namespace video { class SMaterial; class ITexture; }
-    namespace scene { class ICameraSceneNode; class ISceneNode; }
-}
-using namespace irr;
+#ifndef HEADER_WEATHER_HPP
+#define HEADER_WEATHER_HPP
 
 class SFXBase;
 
-class Rain
+class Weather
 {
-    PerCameraNode* m_node;
-
-    float m_next_lightning;
     bool m_lightning;
+    float m_next_lightning;
+
     SFXBase* m_thunder_sound;
+    SFXBase* m_weather_sound;
 
 public:
-    Rain(Camera* camera, irr::scene::ISceneNode* parent);
-    virtual ~Rain();
+    Weather(bool lightning, std::string sound);
+    virtual ~Weather();
 
     void update(float dt);
-    void setPosition(const irr::core::vector3df& position);
-
-    void setCamera(scene::ICameraSceneNode* camera);
+    void playSound();
 };
 
 #endif
diff --git a/src/guiengine/engine.hpp b/src/guiengine/engine.hpp
index 5dcfce20d..9669503c7 100644
--- a/src/guiengine/engine.hpp
+++ b/src/guiengine/engine.hpp
@@ -170,7 +170,7 @@ namespace GUIEngine
     inline Skin*                      getSkin()          { return Private::g_skin;           }
 
     Screen*                           getScreenNamed(const char* name);
-    
+
     /** \return the height of the title font in pixels */
     int   getTitleFontHeight();
 
diff --git a/src/guiengine/widgets/button_widget.cpp b/src/guiengine/widgets/button_widget.cpp
index feb18af83..01016b2be 100644
--- a/src/guiengine/widgets/button_widget.cpp
+++ b/src/guiengine/widgets/button_widget.cpp
@@ -43,6 +43,8 @@ void ButtonWidget::add()
     m_id = m_element->getID();
     m_element->setTabOrder(m_id);
     m_element->setTabGroup(false);
+    if (!m_is_visible)
+        m_element->setVisible(false);
 }
 
 // -----------------------------------------------------------------------------
diff --git a/src/guiengine/widgets/dynamic_ribbon_widget.cpp b/src/guiengine/widgets/dynamic_ribbon_widget.cpp
index 0a3040068..c1d2c12d2 100644
--- a/src/guiengine/widgets/dynamic_ribbon_widget.cpp
+++ b/src/guiengine/widgets/dynamic_ribbon_widget.cpp
@@ -491,6 +491,7 @@ void DynamicRibbonWidget::clearItems()
 {
     m_items.clear();
     m_animated_contents = false;
+    m_scroll_offset = 0;
 }
 // -----------------------------------------------------------------------------
 void DynamicRibbonWidget::elementRemoved()
diff --git a/src/guiengine/widgets/ribbon_widget.cpp b/src/guiengine/widgets/ribbon_widget.cpp
index 9432335ed..cc379cd7c 100644
--- a/src/guiengine/widgets/ribbon_widget.cpp
+++ b/src/guiengine/widgets/ribbon_widget.cpp
@@ -722,16 +722,34 @@ EventPropagation RibbonWidget::transmitEvent(Widget* w,
 // ----------------------------------------------------------------------------
 void RibbonWidget::setLabel(const unsigned int id, irr::core::stringw new_name)
 {
-    // This method should only be called AFTER a widget is added
-    assert(m_element != NULL);
+    if (m_element == NULL)
+    {
+        // before adding
+        m_children[id].setText(new_name);
+    }
+    else
+    {
+        // after adding
+        // ignore this call for ribbons without labels
+        if (m_labels.size() == 0) return;
+
+        assert(id < m_labels.size());
+        m_labels[id].setText(new_name.c_str());
+        //m_text = new_name;
+    }
+}   // setLabel
+
+// ----------------------------------------------------------------------------
+
+void RibbonWidget::setItemVisible(const unsigned int id, bool visible)
+{
+    m_children[id].setVisible(visible);
 
     // ignore this call for ribbons without labels
     if (m_labels.size() == 0) return;
 
-    assert(id < m_labels.size());
-    m_labels[id].setText( new_name.c_str() );
-    m_text = new_name;
-}   // setLabel
+    m_labels[id].setVisible(visible);
+} // RibbonWidget
 
 // ----------------------------------------------------------------------------
 int RibbonWidget::findItemNamed(const char* internalName)
diff --git a/src/guiengine/widgets/ribbon_widget.hpp b/src/guiengine/widgets/ribbon_widget.hpp
index 5746b877a..f14558250 100644
--- a/src/guiengine/widgets/ribbon_widget.hpp
+++ b/src/guiengine/widgets/ribbon_widget.hpp
@@ -152,6 +152,8 @@ namespace GUIEngine
           */
         void setLabel(const unsigned int id, irr::core::stringw new_name);
         
+        void setItemVisible(const unsigned int id, bool visible);
+
         /** Returns the ID of the item, or -1 if not found */
         int findItemNamed(const char* internalName);
         
diff --git a/src/guiengine/widgets/spinner_widget.hpp b/src/guiengine/widgets/spinner_widget.hpp
index 8b3e6c326..13496afc3 100644
--- a/src/guiengine/widgets/spinner_widget.hpp
+++ b/src/guiengine/widgets/spinner_widget.hpp
@@ -156,18 +156,34 @@ namespace GUIEngine
         /**
           * \return the maximum value the spinner can take
           */
-        int  getMax()   const { return m_max;   }
-        /**
-          * \brief Sets the maximum value for a spinner.
-          */
-        void setMax(int n) {m_max = n; }
+        // --------------------------------------------------------------------
+        /** Returns the maximum value. */
+        int  getMax()  const { return m_max;   }
+        // --------------------------------------------------------------------
+        /** \brief Sets the maximum value for a spinner.
+         *  If the current value is larger than the new maximum, the current
+         *  value is set to the new maximum. */
+        void setMax(int n)
+        {
+            m_max = n; 
+            if(getValue()>m_max) setValue(m_max);
+        }   // setMax
+        // --------------------------------------------------------------------
         /**
           * \return the minimum value the spinner can take
           */
         int  getMin()   const { return m_min;   }
+        // --------------------------------------------------------------------
+        /** \brief Sets the minimum value for a spinner.
+         *  If the current value is smaller than the new minimum, the current
+         *  value is set to the new minimum. */
+        void setMin(int n)
+        {
+            m_min = n; 
+            if(getValue()<m_min) setValue(m_min);
+        }   // setMin
         
-        void setMin(int n) { m_min = n; }
-        
+        // --------------------------------------------------------------------
         /** Override method from base class Widget */
         virtual void setActivated();
         
diff --git a/src/io/file_manager.cpp b/src/io/file_manager.cpp
index 8520caf05..cacd7b563 100644
--- a/src/io/file_manager.cpp
+++ b/src/io/file_manager.cpp
@@ -151,7 +151,7 @@ FileManager::FileManager()
     if(exe_path.size()==0 || exe_path[exe_path.size()-1]!='/')
         exe_path += "/";
     if ( getenv ( "SUPERTUXKART_DATADIR" ) != NULL )
-        root_dir = std::string(getenv("SUPERTUXKART_DATADIR"))+"/" ;
+        root_dir = std::string(getenv("SUPERTUXKART_DATADIR"))+"/data/" ;
 #ifdef __APPLE__
     else if( macSetBundlePathIfRelevant( root_dir ) ) { root_dir = root_dir + "data/"; }
 #endif
@@ -175,7 +175,7 @@ FileManager::FileManager()
     else
     {
 #ifdef SUPERTUXKART_DATADIR
-        root_dir = SUPERTUXKART_DATADIR;
+        root_dir = SUPERTUXKART_DATADIR"/data/";
         if(root_dir.size()==0 || root_dir[root_dir.size()-1]!='/')
             root_dir+='/';
 
@@ -187,6 +187,8 @@ FileManager::FileManager()
     addRootDirs(root_dir);
     if( fileExists(root_dir+"../../stk-assets"))
         addRootDirs(root_dir+"../../stk-assets");
+    if( fileExists(root_dir+"../../supertuxkart-assets"))
+        addRootDirs(root_dir+"../../supertuxkart-assets");
     if ( getenv ( "SUPERTUXKART_ROOT_PATH" ) != NULL )
         addRootDirs(getenv("SUPERTUXKART_ROOT_PATH"));
 
@@ -351,6 +353,25 @@ FileManager::~FileManager()
     m_file_system = NULL;
 }   // ~FileManager
 
+// ----------------------------------------------------------------------------
+/** Returns true if the specified file exists.
+ */
+bool FileManager::fileExists(const std::string& path) const
+{
+#ifdef DEBUG
+    bool exists = m_file_system->existFile(path.c_str());
+    if(exists) return true;
+    // Now the original file was not found. Test if replacing \ with / helps:
+    std::string s = StringUtils::replace(path, "\\", "/");
+    exists = m_file_system->existFile(s.c_str());
+    if(exists)
+        Log::warn("FileManager", "File '%s' does not exists, but '%s' does!",
+        path.c_str(), s.c_str());
+    return exists;
+#else
+    return m_file_system->existFile(path.c_str());
+#endif
+}   // fileExists
 //-----------------------------------------------------------------------------
 /** Adds paths to the list of stk root directories.
  *  \param roots A ":" separated string of directories to add.
@@ -718,10 +739,10 @@ void FileManager::checkAndCreateConfigDir()
 
         // Try to use the APPDATA directory to store config files and highscore
         // lists. If not defined, used the current directory.
-        if(getenv("APPDATA")!=NULL)
+        if (getenv("APPDATA") != NULL)
         {
             m_user_config_dir  = getenv("APPDATA");
-            if(!checkAndCreateDirectory(m_user_config_dir))
+            if (!checkAndCreateDirectory(m_user_config_dir))
             {
                 Log::error("[FileManager]", "Can't create config dir '%s"
                             ", falling back to '.'.", m_user_config_dir.c_str());
@@ -735,7 +756,7 @@ void FileManager::checkAndCreateConfigDir()
 
 #elif defined(__APPLE__)
 
-        if (getenv("HOME")!=NULL)
+        if (getenv("HOME") != NULL)
         {
             m_user_config_dir = getenv("HOME");
         }
@@ -755,8 +776,10 @@ void FileManager::checkAndCreateConfigDir()
 
         // Remaining unix variants. Use the new standards for config directory
         // i.e. either XDG_CONFIG_HOME or $HOME/.config
-        if (getenv("XDG_CONFIG_HOME")!=NULL){
+        if (getenv("XDG_CONFIG_HOME") !=NULL)
+        {
             m_user_config_dir = getenv("XDG_CONFIG_HOME");
+            checkAndCreateDirectory(m_user_config_dir);
         }
         else if (!getenv("HOME"))
         {
@@ -768,6 +791,8 @@ void FileManager::checkAndCreateConfigDir()
         else
         {
             m_user_config_dir  = getenv("HOME");
+            checkAndCreateDirectory(m_user_config_dir);
+            
             m_user_config_dir += "/.config";
             if(!checkAndCreateDirectory(m_user_config_dir))
             {
diff --git a/src/io/file_manager.hpp b/src/io/file_manager.hpp
index 3983c9e49..40be73d76 100644
--- a/src/io/file_manager.hpp
+++ b/src/io/file_manager.hpp
@@ -133,6 +133,7 @@ public:
     std::string searchMusic(const std::string& file_name) const;
     std::string searchTexture(const std::string& fname) const;
     std::string getUserConfigFile(const std::string& fname) const;
+    bool        fileExists(const std::string& path) const;
     void        listFiles        (std::set<std::string>& result,
                                   const std::string& dir,
                                   bool make_full_path=false) const;
@@ -155,14 +156,6 @@ public:
         m_music_search_path.push_back(path);
     }   // pushMusicSearchPath
 
-    // ------------------------------------------------------------------------
-    /** Returns true if the specified file exists.
-     */
-    bool fileExists(const std::string& path) const
-    {
-        return m_file_system->existFile(path.c_str());
-    }   // fileExists
-
 };   // FileManager
 
 extern FileManager* file_manager;
diff --git a/src/karts/abstract_kart.cpp b/src/karts/abstract_kart.cpp
index 3ad73b442..0171d5043 100644
--- a/src/karts/abstract_kart.cpp
+++ b/src/karts/abstract_kart.cpp
@@ -122,3 +122,13 @@ void AbstractKart::setKartAnimation(AbstractKartAnimation *ka)
     assert( (ka!=NULL) ^ (m_kart_animation!=NULL) );
     m_kart_animation = ka;
 }   // setKartAnimation
+
+// ----------------------------------------------------------------------------
+/** Moves the current physical transform into this kart's position.
+ */
+void AbstractKart::kartIsInRestNow()
+{
+    // Update the kart transforms with the newly computed position
+    // after all karts are reset
+    setTrans(getBody()->getWorldTransform());
+}   // kartIsInRest
\ No newline at end of file
diff --git a/src/karts/abstract_kart.hpp b/src/karts/abstract_kart.hpp
index 7e681e418..5df0e817b 100644
--- a/src/karts/abstract_kart.hpp
+++ b/src/karts/abstract_kart.hpp
@@ -154,6 +154,12 @@ public:
     /** Returns the highest point of the kart (coordinate on up axis) */
     float getHighestPoint() const { return m_kart_highest_point;  }
     // ------------------------------------------------------------------------
+    /** Called after the kart comes to rest. It can be used to e.g. compute
+     *  differences between graphical and physical chassis. Note that 
+     *  overwriting this function is possible, but this implementation must
+     *  be called. */
+    virtual void kartIsInRestNow();
+    // ------------------------------------------------------------------------
     /** Returns true if this kart has no wheels. */
     bool isWheeless() const;
     // ------------------------------------------------------------------------
diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp
index d97a2cf7a..b84ca215c 100644
--- a/src/karts/kart.cpp
+++ b/src/karts/kart.cpp
@@ -607,9 +607,32 @@ void Kart::createPhysics()
                 {
                     int index = (x + 1) / 2 + 1 - z;  // get index of wheel
                     float f = getKartProperties()->getPhysicalWheelPosition();
-                    wheel_pos[index] = p*(orig_factor*(1.0f-f) + bevel_factor*f);
-                    wheel_pos[index].setY(0);
-                }  // if z==-1
+                    // f < 0 indicates to use the old physics position, i.e. 
+                    // to place the wheels outside of the chassis
+                    if(f<0)
+                    {
+                        // All wheel positions are relative to the center of
+                        // the collision shape.
+                        wheel_pos[index].setX(x*0.5f*getKartWidth());
+                        float radius = getKartProperties()->getWheelRadius();
+                        // The y position of the wheels (i.e. the points where
+                        // the suspension is attached to) is just at the
+                        // bottom of the kart. That is half the kart height
+                        // down. The wheel radius is added to the suspension
+                        // length in the physics, so we move the connection
+                        // point 'radius' up. That means that if the suspension
+                        // is fully compressed (0), the wheel will just be at
+                        // the bottom of the kart chassis and touch the ground
+                        wheel_pos[index].setY(- 0.5f*getKartHeight() + radius);
+                        wheel_pos[index].setZ((0.5f*getKartLength() - radius)* z);
+
+                    }
+                    else
+                    {
+                        wheel_pos[index] = p*(orig_factor*(1.0f - f) + bevel_factor*f);
+                        wheel_pos[index].setY(0);
+                    }
+                }  // if y==-1
             }   // for x
         }   // for z
     }   // for y
@@ -669,11 +692,12 @@ void Kart::createPhysics()
     tuning.m_maxSuspensionForce    =
         m_kart_properties->getMaxSuspensionForce();
 
+    const Vec3 &cs = getKartProperties()->getGravityCenterShift();
     for(unsigned int i=0; i<4; i++)
     {
         bool is_front_wheel = i<2;
         btWheelInfo& wheel = m_vehicle->addWheel(
-                            wheel_pos[i],
+                            wheel_pos[i]+cs,
                             wheel_direction, wheel_axle, suspension_rest,
                             wheel_radius, tuning, is_front_wheel);
         wheel.m_suspensionStiffness      = m_kart_properties->getSuspensionStiffness();
@@ -1741,9 +1765,9 @@ void Kart::crashed(const Material *m, const Vec3 &normal)
             impulse.normalize();
         else
             impulse = Vec3(0, 0, -1); // Arbitrary
-        // impulse depends of kart speed
-        impulse *= 0.2f * m_body->getLinearVelocity().length();
-        impulse *= m_kart_properties->getCollisionTerrainImpulse();
+        // impulse depends of kart speed - and speed can be negative
+        impulse *= sqrt(fabsf(getSpeed()))
+                 * m_kart_properties->getCollisionTerrainImpulse();
         m_bounce_back_time = 0.2f;
         m_vehicle->setTimedCentralImpulse(0.1f, impulse);
     }
@@ -2355,7 +2379,8 @@ void Kart::loadData(RaceManager::KartType type, bool is_animated_model)
                           m_node,
                           m_kart_properties->getShadowScale(),
                           m_kart_properties->getShadowXOffset(),
-                          m_kart_properties->getShadowYOffset());
+                          m_kart_properties->getGraphicalYOffset(),
+                          m_kart_properties->getShadowZOffset());
 
     World::getWorld()->kartAdded(this, m_node);
 }   // loadData
@@ -2386,6 +2411,31 @@ void Kart::applyEngineForce(float force)
     }
 }   // applyEngineForce
 
+//-----------------------------------------------------------------------------
+/** Computes the transform of the graphical kart chasses with regards to the
+ *  physical chassis. This function is called once the kart comes to rest
+ *  before the race starts. Based on the current physical kart position, it
+ *  computes an (at this stage Y-only) offset by which the graphical chassis
+ *  is moved so that it appears the way it is designed in blender. This means
+ *  that the distance of the wheels from the chassis (i.e. suspension) appears
+ *  as in blender when karts are in rest.
+ */
+void Kart::kartIsInRestNow()
+{
+    AbstractKart::kartIsInRestNow();
+    float f = 0;
+    for(int i=0; i<m_vehicle->getNumWheels(); i++)
+    {
+        const btWheelInfo &wi = m_vehicle->getWheelInfo(i);
+        f +=  wi.m_chassisConnectionPointCS.getY()
+            - wi.m_raycastInfo.m_suspensionLength - wi.m_wheelsRadius;
+    }
+    m_graphical_y_offset = f/m_vehicle->getNumWheels() 
+                         + getKartProperties()->getGraphicalYOffset();
+
+    m_kart_model->setDefaultSuspension();
+}   // kartIsInRestNow
+
 //-----------------------------------------------------------------------------
 /** Updates the graphics model. Mainly set the graphical position to be the
  *  same as the physics position, but uses offsets to position and rotation
@@ -2486,42 +2536,15 @@ void Kart::updateGraphics(float dt, const Vec3& offset_xyz,
         }
     }
 
-    // Now determine graphical chassis and wheel position depending on
-    // the physics result. The center of gravity of the chassis is at the
-    // bottom of the chassis, but the position of the graphical chassis is at
-    // the bottom of the wheels (i.e. in blender the kart is positioned on
-    // the horizonal plane through (0,0,0)). So first determine how far
-    // above the terrain is the center of the physics body. If the minimum
-    // of those values is larger than the lowest point of the chassis model
-    // the kart chassis would be too high (and look odd), so in this case
-    // move the chassis down so that the wheels (when touching the ground)
-    // look close enough to the chassis.
-    float height_above_terrain[4];
-    float min_hat = 9999.9f;
-    for(unsigned int i=0; i<4; i++)
-    {
-        // Set the suspension length
-        const btWheelInfo &wi = m_vehicle->getWheelInfo(i);
-        height_above_terrain[i] = wi.m_raycastInfo.m_suspensionLength;
-        if(height_above_terrain[i] < min_hat) min_hat = height_above_terrain[i];
-    }
-    float kart_hat = m_kart_model->getLowestPoint();
-
-    if(min_hat >= kart_hat)
-    {
-        for(unsigned int i=0; i<4; i++)
-            height_above_terrain[i] = kart_hat;
-    }
-    m_kart_model->update(dt, m_wheel_rotation_dt, getSteerPercent(),
-                         height_above_terrain, m_speed);
+    m_kart_model->update(dt, m_wheel_rotation_dt, getSteerPercent(), m_speed);
 
     // If the kart is leaning, part of the kart might end up 'in' the track.
     // To avoid this, raise the kart enough to offset the leaning.
     float lean_height = tan(fabsf(m_current_lean)) * getKartWidth()*0.5f;
 
     float heading = m_skidding->getVisualSkidRotation();
-    Vec3 center_shift = Vec3(0, m_skidding->getGraphicalJumpOffset() - kart_hat
-                              + lean_height-m_kart_model->getLowestPoint(), 0);
+    Vec3 center_shift = Vec3(0, m_skidding->getGraphicalJumpOffset() 
+                              + lean_height +m_graphical_y_offset, 0);
     center_shift = getTrans().getBasis() * center_shift;
 
     Moveable::updateGraphics(dt, center_shift,
diff --git a/src/karts/kart.hpp b/src/karts/kart.hpp
index 52c477b9d..087c510ab 100644
--- a/src/karts/kart.hpp
+++ b/src/karts/kart.hpp
@@ -66,13 +66,13 @@ class Kart : public AbstractKart
     friend class Skidding;
 private:
     /** Handles speed increase and capping due to powerup, terrain, ... */
-    MaxSpeed           *m_max_speed;
+    MaxSpeed *m_max_speed;
 
     /** Stores information about the terrain the kart is on. */
-    TerrainInfo        *m_terrain_info;
+    TerrainInfo *m_terrain_info;
 
     /** Handles the powerup of a kart. */
-    Powerup     *m_powerup;
+    Powerup *m_powerup;
 
     /** True if kart is flying (for debug purposes only). */
     bool m_flying;
@@ -106,6 +106,9 @@ private:
      *  new lap is triggered. */
     Vec3 m_xyz_front;
 
+    /** Offset of the graphical kart chassis from the physical chassis. */
+    float m_graphical_y_offset;
+
     /** True if the kart is eliminated. */
     bool m_eliminated;
 
@@ -234,6 +237,7 @@ public:
                         int position, const btTransform& init_transform);
     virtual       ~Kart();
     virtual void   init(RaceManager::KartType type);
+    virtual void   kartIsInRestNow();
     virtual void   updateGraphics(float dt, const Vec3& off_xyz,
                                   const btQuaternion& off_rotation);
     virtual void   createPhysics    ();
diff --git a/src/karts/kart_model.cpp b/src/karts/kart_model.cpp
index 90ef13a5f..fe7e85e05 100644
--- a/src/karts/kart_model.cpp
+++ b/src/karts/kart_model.cpp
@@ -113,8 +113,8 @@ KartModel::KartModel(bool is_master)
 
         // default value for kart suspensions. move to config file later
         // if we find each kart needs custom values
-        m_min_suspension[i] = -0.59f;
-        m_max_suspension[i] = 0.59f;
+        m_min_suspension[i] = -0.07f;
+        m_max_suspension[i] = 0.20f;
         m_dampen_suspension_amplitude[i] = 2.5f;
     }
     m_wheel_filename[0] = "";
@@ -541,6 +541,14 @@ bool KartModel::loadModels(const KartProperties &kart_properties)
         }
     }
 
+    float y_off = kart_properties.getGraphicalYOffset();
+    if(y_off!=0)
+    {
+        for (unsigned int i = 0; i < 4; i++)
+            m_wheel_graphics_position[i].setY(
+                                  m_wheel_graphics_position[i].getY() - y_off);
+    }
+
     // Load the wheel models. This can't be done early, since the default
     // values for the graphical position must be defined, which in turn
     // depend on the size of the model.
@@ -634,9 +642,7 @@ void KartModel::loadWheelInfo(const XMLNode &node,
  */
 void KartModel::reset()
 {
-    // Reset the wheels
-    const float suspension[4]={0,0,0,0};
-    update(0.0f, 0.0f, 0.0f, suspension, 0.0f);
+    update(0.0f, 0.0f, 0.0f, 0.0f);
 
     // Stop any animations currently being played.
     setAnimation(KartModel::AF_DEFAULT);
@@ -741,6 +747,16 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node)
     m_animated_node->setAnimationEndCallback(NULL);
 }   // OnAnimationEnd
 
+// ----------------------------------------------------------------------------
+void KartModel::setDefaultSuspension()
+{
+    for(int i=0; i<m_kart->getVehicle()->getNumWheels(); i++)
+    {
+        const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
+        m_default_physics_suspension[i] = wi.m_raycastInfo.m_suspensionLength;
+    }
+}   // setDefaultSuspension
+
 // ----------------------------------------------------------------------------
 /** Rotates and turns the wheels appropriately, and adjust for suspension
     + updates the speed-weighted objects' animations.
@@ -748,30 +764,35 @@ void KartModel::OnAnimationEnd(scene::IAnimatedMeshSceneNode *node)
  *  \param rotation_dt How far the wheels have rotated since last time.
  *  \param steer The actual steer settings.
  *  \param suspension Suspension height for all four wheels.
- *  \param speed The speed of the kart in meters/sec, used for the speed-weighted objects' animations
+ *  \param speed The speed of the kart in meters/sec, used for the 
+ *         speed-weighted objects' animations
  */
-void KartModel::update(float dt, float rotation_dt, float steer,
-                       const float height_above_terrain[4], float speed)
+void KartModel::update(float dt, float rotation_dt, float steer,  float speed)
 {
    core::vector3df wheel_steer(0, steer*30.0f, 0);
 
     for(unsigned int i=0; i<4; i++)
     {
         if(!m_wheel_node[i]) continue;
+        const btWheelInfo &wi = m_kart->getVehicle()->getWheelInfo(i);
 #ifdef DEBUG
         if(UserConfigParams::m_physics_debug && m_kart)
         {
             // Make wheels that are not touching the ground invisible
-            bool wheel_has_contact =
-                m_kart->getVehicle()->getWheelInfo(i).m_raycastInfo
-                                                     .m_isInContact;
-            m_wheel_node[i]->setVisible(wheel_has_contact);
+            m_wheel_node[i]->setVisible(wi.m_raycastInfo.m_isInContact);
         }
 #endif
+        float rel_suspension = wi.m_raycastInfo.m_suspensionLength
+                             - m_default_physics_suspension[i];
+        // If the suspension is too compressed
+        if(rel_suspension< m_min_suspension[i])
+            rel_suspension = m_min_suspension[i];
+        else if(rel_suspension > m_max_suspension[i])
+            rel_suspension = m_max_suspension[i];
+
         core::vector3df pos =  m_wheel_graphics_position[i].toIrrVector();
-        // 
-        pos.Y = -  height_above_terrain[i] + m_kart_lowest_point
-              + m_wheel_graphics_radius[i];
+        pos.Y -= rel_suspension;
+
         m_wheel_node[i]->setPosition(pos);
 
         // Now calculate the new rotation: (old + change) mod 360
diff --git a/src/karts/kart_model.hpp b/src/karts/kart_model.hpp
index 9d2138642..733fd8aba 100644
--- a/src/karts/kart_model.hpp
+++ b/src/karts/kart_model.hpp
@@ -169,12 +169,16 @@ private:
     /** The speed weighted objects. */
     SpeedWeightedObjectList     m_speed_weighted_objects;
     
-    /** Minimum suspension length. If the displayed suspension is
-     *  shorter than this, the wheel would look wrong. */
+    /** Length of the physics suspension when the kart is at rest. */
+    float m_default_physics_suspension[4];
+
+    /** Minimum suspension length (i.e. most compressed). If the displayed
+     *  suspension is shorter than this, the wheel would look wrong. */
     float         m_min_suspension[4];
 
-    /** Maximum suspension length. If the displayed suspension is
-     *  any longer, the wheel would look too far away from the chassis. */
+    /** Maximum suspension length (i.e. most extended). If the displayed
+     *  suspension is any longer, the wheel would look too far away from the 
+     *  chassis. */
     float         m_max_suspension[4];
 
     /** value used to divide the visual movement of wheels (because the actual movement
@@ -227,8 +231,9 @@ public:
     void          reset();
     void          loadInfo(const XMLNode &node);
     bool          loadModels(const KartProperties &kart_properties);
+    void          setDefaultSuspension();
     void          update(float dt, float rotation_dt, float steer,
-                         const float height_abve_terrain[4], float speed);
+                         float speed);
     void          finishedRace();
     scene::ISceneNode*
                   attachModel(bool animatedModels, bool always_animated);
diff --git a/src/karts/kart_properties.cpp b/src/karts/kart_properties.cpp
index 9882e257b..a1598c4ed 100644
--- a/src/karts/kart_properties.cpp
+++ b/src/karts/kart_properties.cpp
@@ -57,7 +57,7 @@ KartProperties::KartProperties(const std::string &filename)
     m_shadow_file   = "";
     m_shadow_scale    = 1.0f;
     m_shadow_x_offset = 0.0f;
-    m_shadow_y_offset = 0.0f;
+    m_shadow_z_offset = 0.0f;
 
     m_groups.clear();
     m_custom_sfx_id.resize(SFXManager::NUM_CUSTOMS);
@@ -92,7 +92,8 @@ KartProperties::KartProperties(const std::string &filename)
         m_squash_duration = m_downward_impulse_factor =
         m_bubblegum_fade_in_time = m_bubblegum_speed_fraction =
         m_bubblegum_time = m_bubblegum_torque = m_jump_animation_time =
-        m_smooth_flying_impulse = m_physical_wheel_position =
+        m_smooth_flying_impulse = m_physical_wheel_position = 
+        m_graphical_y_offset =
             UNDEFINED;
 
     m_engine_power.resize(RaceManager::DIFFICULTY_COUNT, UNDEFINED);
@@ -272,8 +273,15 @@ void KartProperties::load(const std::string &filename, const std::string &node)
         m_gravity_center_shift.setZ(0);
     }
 
-    //FIXME: magix 0.25 factor to keep it compatible with previous tourning
-    m_wheel_base = fabsf( m_kart_model->getLength()-0.25f);
+    if(m_physical_wheel_position < 0)
+        m_wheel_base = fabsf(m_kart_model->getLength() );
+    else
+    {
+        // The new (atm unused) physical position results in steering to be
+        // not sharp enough - therefore decrease the wheel base somewhat to
+        // approximate the behaviour of the old steering.
+        m_wheel_base = fabsf(m_kart_model->getLength() - 0.25f);
+    }
 
     // Now convert the turn radius into turn angle:
     for(unsigned int i=0; i<m_turn_angle_at_speed.size(); i++)
@@ -315,7 +323,7 @@ void KartProperties::getAllData(const XMLNode * root)
 
     root->get("shadow-scale",      &m_shadow_scale     );
     root->get("shadow-x-offset",   &m_shadow_x_offset  );
-    root->get("shadow-y-offset",   &m_shadow_y_offset  );
+    root->get("shadow-z-offset",   &m_shadow_z_offset  );
 
     root->get("type",     &m_kart_type        );
 
@@ -608,6 +616,11 @@ void KartProperties::getAllData(const XMLNode * root)
         startup_node->get("boost", &m_startup_boost);
     }
 
+    if(const XMLNode *graphics_node = root->getNode("graphics"))
+    {
+        graphics_node->get("y-offset", &m_graphical_y_offset);
+    }
+
     if(m_kart_model)
         m_kart_model->loadInfo(*root);
 }   // getAllData
@@ -728,7 +741,7 @@ void KartProperties::checkAllSet(const std::string &filename)
     CHECK_NEG(m_explosion_invulnerability_time,
                                             "explosion invulnerability-time");
     CHECK_NEG(m_explosion_radius,           "explosion radius"              );
-
+    CHECK_NEG(m_graphical_y_offset,         "graphics y-offset"             );
     for(unsigned int i=RaceManager::DIFFICULTY_FIRST;
         i<=RaceManager::DIFFICULTY_LAST; i++)
     {
diff --git a/src/karts/kart_properties.hpp b/src/karts/kart_properties.hpp
index af99a7736..759675ab9 100644
--- a/src/karts/kart_properties.hpp
+++ b/src/karts/kart_properties.hpp
@@ -112,7 +112,7 @@ private:
                                        *   for this kart.*/
     float m_shadow_x_offset;          /**< X offset of the shadow plane
                                        *   for this kart.*/
-    float m_shadow_y_offset;          /**< Y offset of the shadow plane
+    float m_shadow_z_offset;          /**< Z offset of the shadow plane
                                        *   for this kart.*/
     video::ITexture *m_shadow_texture;/**< The texture with the shadow. */
     video::SColor m_color;            /**< Color the represents the kart in the
@@ -152,7 +152,11 @@ private:
 
     /** The position of the physical wheel is a weighted average of the
      *  two ends of the beveled shape. This determines the weight: 0 = 
-     *  a the widest end, 1 = at the narrowest front end. */
+     *  a the widest end, 1 = at the narrowest front end. If the value is 
+     *  < 0, the old physics settings are used which places the raycast
+     *  wheels outside of the chassis - but result in a more stable
+     *  physics behaviour (which is therefore atm still the default).
+     */
     float m_physical_wheel_position;
 
     /** Time a kart is moved upwards after when it is rescued. */
@@ -200,6 +204,10 @@ private:
     std::string m_wheel_filename[4];
     /**  Radius of the graphical wheels.  */
     float       m_wheel_graphics_radius[4];
+    /** An additional Y offset added to the y position of the graphical
+     *  chassis. Useful for karts that don't have enough space for suspension
+     *  compression. */
+    float       m_graphical_y_offset;
     /** If the kart is supposed to have random wheel rotation at start. */
     bool        m_has_rand_wheels;
     /** Max. length of plunger rubber band. */
@@ -564,6 +572,11 @@ public:
     /** Returns wheel radius. */
     float getWheelRadius            () const {return m_wheel_radius;          }
 
+    // ------------------------------------------------------------------------
+    /** Return the additional Y offset added to the y position of the graphical
+     *  chassis. Useful for karts that don't have enough space for suspension
+     *  compression. */
+    float getGraphicalYOffset() const {return m_graphical_y_offset; }
     // ------------------------------------------------------------------------
     /** Returns parameters for the speed-weighted objects */
     const SpeedWeightedObject::Properties& getSpeedWeightedObjectProperties() const
@@ -827,7 +840,7 @@ public:
     // ------------------------------------------------------------------------
     /** Returns the scale factor by which the shadow plane
      *  had to be set. */
-    float getShadowYOffset          () const {return m_shadow_y_offset;       }
+    float getShadowZOffset          () const {return m_shadow_z_offset;       }
 
     // ------------------------------------------------------------------------
     /** Returns a pointer to the skidding properties. */
@@ -916,7 +929,9 @@ public:
     // ------------------------------------------------------------------------
     /** Returns position of the physical wheel is a weighted average of the
      *  two ends of the beveled shape. This determines the weight: 0 = 
-     *  a the widest end, 1 = at the narrowest, front end. */
+     *  a the widest end, 1 = at the narrowest, front end. If the value is <0,
+     *  the old physics position is picked, which placed the raycast wheels
+     *  outside of the chassis, but gives more stable physics. */
     const float getPhysicalWheelPosition() const 
     {
         return m_physical_wheel_position; 
diff --git a/src/main.cpp b/src/main.cpp
index 8bc70861a..05131c01b 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -854,7 +854,7 @@ int handleCmdLine()
             Log::warn("main", "There is no GP named '%s'.", s.c_str());
             return 0;
         }
-        race_manager->setGrandPrix(gp);
+        race_manager->setGrandPrix(*gp);
     }   // --gp
 
     if(CommandLine::has("--numkarts", &n) ||CommandLine::has("-k", &n))
@@ -1155,8 +1155,12 @@ void askForInternetPermission()
 
 //=============================================================================
 
-#if defined(DEBUG) && defined(WIN32) && !defined(__CYGWIN__)
-#pragma comment(linker, "/SUBSYSTEM:console")
+#if defined(WIN32) && defined(_MSC_VER)
+    #ifdef DEBUG
+        #pragma comment(linker, "/SUBSYSTEM:console")
+    #else
+        #pragma comment(linker, "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
+    #endif
 #endif
 
 // ----------------------------------------------------------------------------
@@ -1436,7 +1440,6 @@ static void cleanSuperTuxKart()
     Referee::cleanup();
     if(ReplayPlay::get())       ReplayPlay::destroy();
     if(race_manager)            delete race_manager;
-    if(addons_manager)          delete addons_manager;
     if(grand_prix_manager)      delete grand_prix_manager;
     if(highscore_manager)       delete highscore_manager;
     if(attachment_manager)      delete attachment_manager;
@@ -1476,6 +1479,10 @@ static void cleanSuperTuxKart()
     }
     Online::RequestManager::deallocate();
 
+    // The addons manager might still be called from a currenty running request
+    // in the request manager, so it can not be deleted earlier.
+    if(addons_manager)  delete addons_manager;
+
     // FIXME: do we need to wait for threads there, can they be
     // moved further up?
     Online::ServersManager::deallocate();
diff --git a/src/main_loop.cpp b/src/main_loop.cpp
index a24789f70..3052c9ed8 100644
--- a/src/main_loop.cpp
+++ b/src/main_loop.cpp
@@ -160,8 +160,6 @@ void MainLoop::run()
             PROFILER_PUSH_CPU_MARKER("Database polling update", 0x00, 0x7F, 0x7F);
             Online::RequestManager::get()->update(dt);
             PROFILER_POP_CPU_MARKER();
-
-            PROFILER_SYNC_FRAME();
         }
         else if (!m_abort && ProfileWorld::isNoGraphics())
         {
@@ -174,8 +172,8 @@ void MainLoop::run()
             PROFILER_POP_CPU_MARKER();
         }
 
-        PROFILER_SYNC_FRAME();
         PROFILER_POP_CPU_MARKER();
+        PROFILER_SYNC_FRAME();
     }  // while !m_abort
 
 }   // run
diff --git a/src/modes/world.cpp b/src/modes/world.cpp
index a25fc6764..62c3807eb 100644
--- a/src/modes/world.cpp
+++ b/src/modes/world.cpp
@@ -122,6 +122,7 @@ World::World() : WorldStatus(), m_clear_color(255,100,101,140)
     m_self_destruct      = false;
     m_schedule_tutorial  = false;
     m_is_network_world   = false;
+    m_weather            = NULL;
 
     m_stop_music_when_dialog_open = true;
 
@@ -197,6 +198,12 @@ void World::init()
         ReplayPlay::get()->Load();
 
     powerup_manager->updateWeightsForRace(num_karts);
+    
+    if (UserConfigParams::m_weather_effects)
+    {
+        m_weather = new Weather(m_track->getWeatherLightning(), 
+                          m_track->getWeatherSound());
+    }
 }   // init
 
 //-----------------------------------------------------------------------------
@@ -264,7 +271,6 @@ void World::reset()
 
     //Reset the Rubber Ball Collect Time to some negative value.
     powerup_manager->setBallCollectTime(-100);
-
 }   // reset
 
 //-----------------------------------------------------------------------------
@@ -378,6 +384,9 @@ World::~World()
         // gui and this must be deleted.
         delete m_race_gui;
     }
+    
+    if (m_weather != NULL)
+        delete m_weather;
 
     for ( unsigned int i = 0 ; i < m_karts.size() ; i++ )
         delete m_karts[i];
@@ -393,6 +402,8 @@ World::~World()
     music_manager->stopMusic();
     m_world = NULL;
 
+    irr_driver->getSceneManager()->clear();
+
 #ifdef DEBUG
     m_magic_number = 0xDEADBEEF;
 #endif
@@ -679,9 +690,7 @@ void World::resetAllKarts()
 
     for ( KartList::iterator i=m_karts.begin(); i!=m_karts.end(); i++)
     {
-        // Update the kart transforms with the newly computed position
-        // after all karts are reset
-        (*i)->setTrans((*i)->getBody()->getWorldTransform());
+        (*i)->kartIsInRestNow();
     }
 
     // Initialise the cameras, now that the correct kart positions are set
@@ -801,6 +810,7 @@ void World::updateWorld(float dt)
     }
     catch (AbortWorldUpdateException& e)
     {
+        (void)e;   // avoid compiler warning
         return;
     }
 
@@ -926,6 +936,11 @@ void World::update(float dt)
     {
         Camera::getCamera(i)->update(dt);
     }
+    
+    if (UserConfigParams::m_graphical_effects && m_weather)
+    {
+        m_weather->update(dt);
+    }
 
     projectile_manager->update(dt);
 
diff --git a/src/modes/world.hpp b/src/modes/world.hpp
index c81cfdf0e..31ff045ec 100644
--- a/src/modes/world.hpp
+++ b/src/modes/world.hpp
@@ -28,6 +28,7 @@
 #include <vector>
 #include <stdexcept>
 
+#include "graphics/weather.hpp"
 #include "modes/world_status.hpp"
 #include "race/highscores.hpp"
 #include "states_screens/race_gui_base.hpp"
@@ -169,6 +170,10 @@ protected:
 
     /** Set when the world is online and counts network players. */
     bool m_is_network_world;
+    
+    /** Used to show weather graphical effects. */
+    Weather* m_weather;
+
 
     virtual void  onGo();
     /** Returns true if the race is over. Must be defined by all modes. */
@@ -356,6 +361,9 @@ public:
     void setNetworkWorld(bool is_networked) { m_is_network_world = is_networked; }
 
     bool isNetworkWorld() const { return m_is_network_world; }
+    
+    /** Returns a pointer to the weather. */
+    Weather* getWeather() {return m_weather;}
 };   // World
 
 #endif
diff --git a/src/modes/world_status.cpp b/src/modes/world_status.cpp
index 29775834e..c7a841f89 100644
--- a/src/modes/world_status.cpp
+++ b/src/modes/world_status.cpp
@@ -44,7 +44,9 @@ WorldStatus::WorldStatus()
     m_play_racestart_sounds = true;
 
     IrrlichtDevice *device = irr_driver->getDevice();
-    if (device->getTimer()->isStopped()) device->getTimer()->start();
+
+    if (device->getTimer()->isStopped())
+        device->getTimer()->start();
 }   // WorldStatus
 
 //-----------------------------------------------------------------------------
@@ -56,13 +58,15 @@ void WorldStatus::reset()
     m_auxiliary_timer = 0.0f;
     // Using SETUP_PHASE will play the track into sfx first, and has no
     // other side effects.
-    m_phase          = UserConfigParams::m_race_now ? RACE_PHASE : SETUP_PHASE;
+    m_phase           = UserConfigParams::m_race_now ? RACE_PHASE : SETUP_PHASE;
     m_previous_phase  = UNDEFINED_PHASE;
     // Just in case that the game is reset during the intro phase
     m_track_intro_sound->stop();
 
     IrrlichtDevice *device = irr_driver->getDevice();
-    if (device->getTimer()->isStopped()) device->getTimer()->start();
+
+    if (device->getTimer()->isStopped()) 
+        device->getTimer()->start();
 }   // reset
 
 //-----------------------------------------------------------------------------
@@ -74,7 +78,9 @@ WorldStatus::~WorldStatus()
     sfx_manager->deleteSFX(m_start_sound);
     sfx_manager->deleteSFX(m_track_intro_sound);
     IrrlichtDevice *device = irr_driver->getDevice();
-    if (device->getTimer()->isStopped())  device->getTimer()->start();
+
+    if (device->getTimer()->isStopped())  
+        device->getTimer()->start();
 }   // ~WorldStatus
 
 //-----------------------------------------------------------------------------
@@ -96,14 +102,12 @@ void WorldStatus::setClockMode(const ClockType mode, const float initial_time)
 void WorldStatus::enterRaceOverState()
 {
     // Don't enter race over if it's already race over
-    if(    m_phase == DELAY_FINISH_PHASE
-        || m_phase == RESULT_DISPLAY_PHASE
-        || m_phase == FINISH_PHASE          )
+    if (m_phase == DELAY_FINISH_PHASE || m_phase == RESULT_DISPLAY_PHASE ||
+        m_phase == FINISH_PHASE)
         return;
 
     m_phase = DELAY_FINISH_PHASE;
     m_auxiliary_timer = 0.0f;
-
 }   // enterRaceOverState
 
 //-----------------------------------------------------------------------------
@@ -120,7 +124,7 @@ void WorldStatus::terminateRace()
  */
 void WorldStatus::update(const float dt)
 {
-    switch(m_phase)
+    switch (m_phase)
     {
         // Note: setup phase must be a separate phase, since the race_manager
         // checks the phase when updating the camera: in the very first time
@@ -130,10 +134,17 @@ void WorldStatus::update(const float dt)
         case SETUP_PHASE:
             m_auxiliary_timer = 0.0f;
             m_phase = TRACK_INTRO_PHASE;
+            
             if (m_play_racestart_sounds)
             {
                 m_track_intro_sound->play();
             }
+
+            if (World::getWorld()->getWeather() != NULL)
+            {
+                 World::getWorld()->getWeather()->playSound();
+            }
+
             return;
         case TRACK_INTRO_PHASE:
             m_auxiliary_timer += dt;
@@ -141,7 +152,9 @@ void WorldStatus::update(const float dt)
             if (UserConfigParams::m_artist_debug_mode &&
                 race_manager->getNumberOfKarts() == 1 &&
                 race_manager->getTrackName() != "tutorial")
+            {
                 m_auxiliary_timer += dt * 6;
+            }
 
             // Work around a bug that occurred on linux once:
             // the sfx_manager kept on reporting that it is playing,
@@ -149,61 +162,86 @@ void WorldStatus::update(const float dt)
             // ... phase. Since the sound effect is about 3 seconds
             // long, we use the aux timer to force the next phase
             // after 3.5 seconds.
-            if(m_track_intro_sound->getStatus()==SFXManager::SFX_PLAYING
-                && m_auxiliary_timer<3.5f)
+            if (m_track_intro_sound->getStatus() == SFXManager::SFX_PLAYING &&
+                m_auxiliary_timer < 3.5f)
                 return;
+
             // Wait before ready phase if sounds are disabled
-            if(!UserConfigParams::m_sfx && m_auxiliary_timer<3.0f)
+            if (!UserConfigParams::m_sfx && m_auxiliary_timer < 3.0f)
                 return;
+
             m_auxiliary_timer = 0.0f;
-            if (m_play_racestart_sounds) m_prestart_sound->play();
+
+            if (m_play_racestart_sounds) 
+                m_prestart_sound->play();
+
             m_phase = READY_PHASE;
-            for(unsigned int i=0; i<World::getWorld()->getNumKarts(); i++)
+            
+            for (unsigned int i = 0; i < World::getWorld()->getNumKarts(); i++)
+            {
                 World::getWorld()->getKart(i)->startEngineSFX();
+            }
 
             break;
         case READY_PHASE:
-            if(m_auxiliary_timer>1.0)
+            if (m_auxiliary_timer > 1.0)
             {
-                if (m_play_racestart_sounds) m_prestart_sound->play();
-                m_phase=SET_PHASE;
+                if (m_play_racestart_sounds)
+                {
+                    m_prestart_sound->play();
+                }
+
+                m_phase = SET_PHASE;
             }
+
             m_auxiliary_timer += dt;
 
             // In artist debug mode, when without opponents, skip the ready/set/go counter faster
             if (UserConfigParams::m_artist_debug_mode &&
                 race_manager->getNumberOfKarts() == 1 &&
                 race_manager->getTrackName() != "tutorial")
+            {
                 m_auxiliary_timer += dt*6;
+            }
+
             return;
-        case SET_PHASE  :
-            if(m_auxiliary_timer>2.0)
+        case SET_PHASE:
+            if (m_auxiliary_timer > 2.0)
             {
                 // set phase is over, go to the next one
-                m_phase=GO_PHASE;
-                if (m_play_racestart_sounds) m_start_sound->play();
+                m_phase = GO_PHASE;
+                if (m_play_racestart_sounds)
+                {
+                    m_start_sound->play();
+                }
 
                 World::getWorld()->getTrack()->startMusic();
 
                 // event
                 onGo();
             }
+
             m_auxiliary_timer += dt;
 
             // In artist debug mode, when without opponents, skip the ready/set/go counter faster
             if (UserConfigParams::m_artist_debug_mode &&
                 race_manager->getNumberOfKarts() == 1  &&
                 race_manager->getTrackName() != "tutorial")
+            {
                 m_auxiliary_timer += dt*6;
+            }
+
             return;
         case GO_PHASE  :
 
             if (m_auxiliary_timer>2.5f && music_manager->getCurrentMusic())
-                music_manager->startMusic(music_manager->getCurrentMusic());
-
-            if(m_auxiliary_timer>3.0f)    // how long to display the 'go' message
             {
-                m_phase=MUSIC_PHASE;
+                music_manager->startMusic(music_manager->getCurrentMusic());
+            }
+
+            if (m_auxiliary_timer > 3.0f)    // how long to display the 'go' message
+            {
+                m_phase = MUSIC_PHASE;
             }
 
             m_auxiliary_timer += dt;
@@ -212,31 +250,38 @@ void WorldStatus::update(const float dt)
             if (UserConfigParams::m_artist_debug_mode &&
                 race_manager->getNumberOfKarts() == 1  &&
                 race_manager->getTrackName() != "tutorial")
+            {
                 m_auxiliary_timer += dt*6;
+            }
+
             break;
         case MUSIC_PHASE:
             // how long to display the 'music' message
-            if(m_auxiliary_timer>stk_config->m_music_credit_time)
-                m_phase=RACE_PHASE;
+            if (m_auxiliary_timer>stk_config->m_music_credit_time)
+            {
+                m_phase = RACE_PHASE;
+            }
+
             m_auxiliary_timer += dt;
             break;
         case RACE_PHASE:
             // Nothing to do for race phase, switch to delay finish phase
             // happens when
             break;
-        case DELAY_FINISH_PHASE :
+        case DELAY_FINISH_PHASE:
         {
             m_auxiliary_timer += dt;
 
             // Change to next phase if delay is over
-            if(m_auxiliary_timer > stk_config->m_delay_finish_time)
+            if (m_auxiliary_timer > stk_config->m_delay_finish_time)
             {
                 m_phase = RESULT_DISPLAY_PHASE;
                 terminateRace();
             }
+
             break;
         }
-        case RESULT_DISPLAY_PHASE :
+        case RESULT_DISPLAY_PHASE:
         {
             break;
         }
@@ -249,7 +294,7 @@ void WorldStatus::update(const float dt)
         default: break;
     }
 
-    switch(m_clock_mode)
+    switch (m_clock_mode)
     {
         case CLOCK_CHRONO:
             m_time += dt;
@@ -290,11 +335,14 @@ void WorldStatus::setTime(const float time)
  */
 void WorldStatus::pause(Phase phase)
 {
-    assert(m_previous_phase==UNDEFINED_PHASE);
+    assert(m_previous_phase == UNDEFINED_PHASE);
+
     m_previous_phase = m_phase;
     m_phase          = phase;
     IrrlichtDevice *device = irr_driver->getDevice();
-    if (!device->getTimer()->isStopped())  device->getTimer()->stop();
+
+    if (!device->getTimer()->isStopped())  
+        device->getTimer()->stop();
 }   // pause
 
 //-----------------------------------------------------------------------------
@@ -307,5 +355,7 @@ void WorldStatus::unpause()
     // in pause to detect incorrect pause/unpause sequences.
     m_previous_phase = UNDEFINED_PHASE;
     IrrlichtDevice *device = irr_driver->getDevice();
-    if (device->getTimer()->isStopped()) device->getTimer()->start();
+
+    if (device->getTimer()->isStopped()) 
+        device->getTimer()->start();
 }   // unpause
diff --git a/src/network/client_network_manager.cpp b/src/network/client_network_manager.cpp
index dbd8e34b5..ee9a5abc0 100644
--- a/src/network/client_network_manager.cpp
+++ b/src/network/client_network_manager.cpp
@@ -117,7 +117,10 @@ ClientNetworkManager::ClientNetworkManager()
 
 ClientNetworkManager::~ClientNetworkManager()
 {
-    pthread_cancel(*m_thread_keyboard);
+    // On windows in release mode there is no console, and the
+    // thread is not created.
+    if(m_thread_keyboard)
+        pthread_cancel(*m_thread_keyboard);
 }
 
 void ClientNetworkManager::run()
@@ -133,10 +136,17 @@ void ClientNetworkManager::run()
 
     Log::info("ClientNetworkManager", "Host initialized.");
 
+    // On windows in release mode the console is suppressed, so nothing can
+    // be read from std::cin. Since getline(std::cin,...) then returns
+    // an empty string, the waitInput thread is running all the time, consuming
+    // CPU. Therefore don't start the console thread in this case.
+#if defined(WIN32) && defined(_MSC_VER) && !defined(DEBUG)
+    m_thread_keyboard = NULL;
+#else
     // listen keyboard console input
     m_thread_keyboard = (pthread_t*)(malloc(sizeof(pthread_t)));
     pthread_create(m_thread_keyboard, NULL, waitInput, NULL);
-
+#endif
     NetworkManager::run();
 
     Log::info("ClientNetworkManager", "Ready !");
diff --git a/src/network/protocols/get_public_address.cpp b/src/network/protocols/get_public_address.cpp
index 33379e71b..8e4095065 100644
--- a/src/network/protocols/get_public_address.cpp
+++ b/src/network/protocols/get_public_address.cpp
@@ -28,6 +28,11 @@
 #include "utils/random_generator.hpp"
 
 #include <assert.h>
+
+#ifdef __MINGW32__
+#  define _WIN32_WINNT 0x501
+#endif
+
 #ifdef WIN32
 #  include <winsock2.h>
 #  include <ws2tcpip.h>
@@ -36,6 +41,7 @@
 #endif
 #include <sys/types.h>
 
+
 int stunRand()
 {
     static bool init = false;
diff --git a/src/network/stk_host.cpp b/src/network/stk_host.cpp
index aef89c96f..e22a8fcb3 100644
--- a/src/network/stk_host.cpp
+++ b/src/network/stk_host.cpp
@@ -24,9 +24,16 @@
 #include "utils/time.hpp"
 
 #include <string.h>
-#ifdef WIN32
+#if defined(WIN32) && !defined(__MINGW32__)
 #  include "Ws2tcpip.h"
 #  define   inet_ntop  InetNtop
+
+// TODO: It's very ugly hack which allows to compile STK on windows using gcc.
+// Solution would be nice seen.
+#elif defined(__MINGW32__)
+#  include "Ws2tcpip.h"
+#  define   inet_ntop
+
 #else
 #  include <arpa/inet.h>
 #  include <errno.h>
diff --git a/src/online/request.cpp b/src/online/request.cpp
index 48919a159..b3d919242 100644
--- a/src/online/request.cpp
+++ b/src/online/request.cpp
@@ -60,9 +60,14 @@ namespace Online
     void Request::execute()
     {
         assert(isBusy());
+        // Abort as early as possible if abort is requested
+        if(RequestManager::get()->getAbort()) return;
         prepareOperation();
+        if(RequestManager::get()->getAbort()) return;
         operation();
+        if(RequestManager::get()->getAbort()) return;
         setExecuted();
+        if(RequestManager::get()->getAbort()) return;
         afterOperation();
     }   // execute
 
diff --git a/src/online/request_manager.cpp b/src/online/request_manager.cpp
index 7dea5ab8e..a8ab28ee5 100644
--- a/src/online/request_manager.cpp
+++ b/src/online/request_manager.cpp
@@ -221,7 +221,9 @@ namespace Online
 
             me->m_request_queue.unlock();
             me->m_current_request->execute();
-            me->addResult(me->m_current_request);
+            // This test is necessary in case that execute() was aborted
+            // (otherwise the assert in addResult will be triggered).
+            if (!me->getAbort()) me->addResult(me->m_current_request);
             me->m_request_queue.lock();
         } // while handle all requests
 
diff --git a/src/patch b/src/patch
deleted file mode 100644
index 937f74c5b..000000000
--- a/src/patch
+++ /dev/null
@@ -1,66 +0,0 @@
-diff --git a/src/karts/kart.cpp b/src/karts/kart.cpp
-index f9d6cd9..f243d2a 100644
---- a/src/karts/kart.cpp
-+++ b/src/karts/kart.cpp
-@@ -1098,7 +1098,7 @@ void Kart::update(float dt)
- 
-     // TODO: hiker said this probably will be moved to btKart or so when updating bullet engine.
-     // Neutralize any yaw change if the kart leaves the ground, so the kart falls more or less
--    // straight after jumping, but still allowing some "boat shake" (roll and pitch).
-+    // straight after jumping, but still allowing some "boat shake" (roIll and pitch).
-     // Otherwise many non perfect jumps end in a total roll over or a serious change of
-     // direction, sometimes 90 or even full U turn (real but less fun for a karting game).
-     // As side effect steering becames a bit less responsive (any wheel on air), but not too bad.
-@@ -2023,30 +2023,6 @@ void Kart::updatePhysics(float dt)
-     m_max_speed->setMinSpeed(min_speed);
-     m_max_speed->update(dt);
- 
--    // If the kart is flying, keep its up-axis aligned to gravity (which in
--    // turn typically means the kart is parallel to the ground). This avoids
--    // that the kart rotates in mid-air and lands on its side.
--    if(m_vehicle->getNumWheelsOnGround()==0)
--    {
--        btVector3 kart_up = getTrans().getBasis().getColumn(1);  // up vector
--        btVector3 terrain_up = m_body->getGravity();
--        float g = World::getWorld()->getTrack()->getGravity();
--        // Normalize the gravity, g is the length of the vector
--        btVector3 new_up = 0.9f * kart_up + 0.1f * terrain_up/-g;
--        // Get the rotation (hpr) based on current heading.
--        Vec3 rotation(getHeading(), new_up);
--        btMatrix3x3 m;
--        m.setEulerZYX(rotation.getX(), rotation.getY(), rotation.getZ());
--        // We can't use getXYZ() for the position here, since the position is
--        // based on interpolation, while the actual center-of-mass-transform
--        // is based on the actual value every 1/60 of a second (using getXYZ()
--        // would result in the kart being pushed ahead a bit, making it jump
--        // much further, depending on fps)
--        btTransform new_trans(m, m_body->getCenterOfMassTransform().getOrigin());
--        //setTrans(new_trans);
--        m_body->setCenterOfMassTransform(new_trans);
--    }
--
-     // To avoid tunneling (which can happen on long falls), clamp the
-     // velocity in Y direction. Tunneling can happen if the Y velocity
-     // is larger than the maximum suspension travel (per frame), since then
-diff --git a/src/physics/btKart.cpp b/src/physics/btKart.cpp
-index bdb8f55..2aa0096 100644
---- a/src/physics/btKart.cpp
-+++ b/src/physics/btKart.cpp
-@@ -387,6 +387,17 @@ void btKart::updateVehicle( btScalar step )
-         if(m_wheelInfo[i].m_raycastInfo.m_isInContact)
-             m_num_wheels_on_ground++;
-     }
-+
-+    // If the kart is flying, try to keep it parallel to the ground.
-+    if(m_num_wheels_on_ground==0)
-+    {
-+        btVector3 kart_up    = getChassisWorldTransform().getBasis().getColumn(1);
-+        btVector3 terrain_up(0,1,0);
-+        btVector3 axis = kart_up.cross(terrain_up);
-+        // Times 10 gives a nicely balanced feeling.
-+        m_chassisBody->applyTorqueImpulse(axis * 10);
-+    }
-+
-     // Work around: make sure that either both wheels on one axis
-     // are on ground, or none of them. This avoids the problem of
-     // the kart suddenly getting additional angular velocity because
diff --git a/src/physics/irr_debug_drawer.hpp b/src/physics/irr_debug_drawer.hpp
index b384f12bd..6dc54d4ed 100644
--- a/src/physics/irr_debug_drawer.hpp
+++ b/src/physics/irr_debug_drawer.hpp
@@ -20,8 +20,8 @@
 #define HEADER_IRR_DEBUG_DRAWER_HPP
 
 #include "btBulletDynamicsCommon.h"
-#include "graphics/glwrap.hpp"
 
+#include <SColor.h>
 #include "utils/vec3.hpp"
 #include <map>
 #include <vector>
diff --git a/src/physics/physical_object.cpp b/src/physics/physical_object.cpp
index e80a6c1cd..539f9c13d 100644
--- a/src/physics/physical_object.cpp
+++ b/src/physics/physical_object.cpp
@@ -25,7 +25,6 @@ using namespace irr;
 
 #include "graphics/material_manager.hpp"
 #include "graphics/mesh_tools.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "io/file_manager.hpp"
 #include "io/xml_node.hpp"
 #include "modes/world.hpp"
@@ -242,16 +241,6 @@ void PhysicalObject::init()
             Log::fatal("PhysicalObject", "Unknown node type");
         }
     }
-    else if (dynamic_cast<TrackObjectPresentationInstancing*>(presentation) != NULL)
-    {
-        TrackObjectPresentationInstancing* instancing = dynamic_cast<TrackObjectPresentationInstancing*>(presentation);
-        STKInstancedSceneNode* instancing_group = instancing->getInstancingGroup();
-        if (instancing_group != NULL)
-        {
-            scene::IMesh* mesh = instancing_group->getMesh();
-            MeshTools::minMax3D(mesh, &min, &max);
-        }
-    }
     else
     {
         Log::fatal("PhysicalObject", "Unknown node type");
diff --git a/src/race/grand_prix_data.cpp b/src/race/grand_prix_data.cpp
index b4d058e15..32faafa9b 100644
--- a/src/race/grand_prix_data.cpp
+++ b/src/race/grand_prix_data.cpp
@@ -24,7 +24,6 @@
 #include "config/player_manager.hpp"
 #include "io/file_manager.hpp"
 #include "io/utf_writer.hpp"
-#include "states_screens/dialogs/random_gp_dialog.hpp"
 #include "tracks/track_manager.hpp"
 #include "tracks/track.hpp"
 #include "utils/string_utils.hpp"
@@ -81,45 +80,56 @@ void GrandPrixData::createRandomGP(const unsigned int number_of_tracks,
 
     changeTrackNumber(number_of_tracks, track_group);
     changeReverse(use_reverse);
-}
+}   // createRandomGP
 
 // ----------------------------------------------------------------------------
+/** Either adds or removes tracks to get the requested numder of tracks in
+ *  a random GP.
+ *  \param number_of_tracks How many tracks should be in the random list.
+ *  \param track_group From which group to select the tracks.
+ */
 void GrandPrixData::changeTrackNumber(const unsigned int number_of_tracks,
                                       const std::string& track_group)
 {
     // The problem with the track groups is that "all" isn't a track group
     // TODO: Add "all" to the track groups and rewrite this more elegant
     std::vector<int> track_indices;
-    size_t available_tracks;
     if (track_group == "all")
     {
-        available_tracks = track_manager->getNumberOfTracks();
+        for(unsigned int i=0; i<track_manager->getNumberOfTracks(); i++)
+        {
+            const Track *track = track_manager->getTrack(i);
+            // Ignore no-racing tracks:
+            if(!track->isRaceTrack())
+                continue;
+
+            // Only add tracks that are not already picked.
+            if(std::find(m_tracks.begin(), m_tracks.end(), track->getIdent())==
+                m_tracks.end())
+                track_indices.push_back(i);
+        }
     }
     else
     {
         track_indices = track_manager->getTracksInGroup(track_group);
-        available_tracks = track_indices.size();
     }
-    assert(number_of_tracks <= available_tracks);
+    assert(number_of_tracks <= track_indices.size() + m_tracks.size());
 
     // add or remove the right number of tracks
     if (m_tracks.size() < number_of_tracks)
     {
         while (m_tracks.size() < number_of_tracks)
         {
-            int index = (track_group == "all") ?
-                         rand() % available_tracks :
-                         track_indices[rand() % available_tracks];
+            int index       = rand() % track_indices.size();
+            int track_index = track_indices[index];
 
-            const Track *track = track_manager->getTrack(index);
+            const Track *track = track_manager->getTrack(track_index);
             std::string id = track->getIdent();
-            // Avoid duplicate tracks
-            if (std::find(m_tracks.begin(), m_tracks.end(), id) != m_tracks.end())
-                continue;
 
             m_tracks.push_back(id);
             m_laps.push_back(track->getDefaultNumberOfLaps());
             m_reversed.push_back(false); // This will be changed later in the code
+            track_indices.erase(track_indices.begin()+index);
         }
     }
     else if (m_tracks.size() > number_of_tracks)
@@ -134,7 +144,7 @@ void GrandPrixData::changeTrackNumber(const unsigned int number_of_tracks,
 
     assert(m_tracks.size() == m_laps.size()    );
     assert(m_laps.size()   == m_reversed.size());
-}
+}   // changeTrackNumber
 
 // ----------------------------------------------------------------------------
 /** Updates the GP data with newly decided reverse requirements.
diff --git a/src/race/grand_prix_data.hpp b/src/race/grand_prix_data.hpp
index ea8422e43..36530b84c 100644
--- a/src/race/grand_prix_data.hpp
+++ b/src/race/grand_prix_data.hpp
@@ -135,6 +135,9 @@ public:
     /** @return the internal indentifier of the Grand Prix (not translated) */
     const std::string& getId()       const { return m_id;                 }
 
+    // ------------------------------------------------------------------------
+    /** Returns true if this GP is a random GP. */
+    bool isRandomGP() const { return m_id=="random"; }
     // ------------------------------------------------------------------------
     /** Returns the filename of the grand prix xml file. */
     const std::string& getFilename() const { return m_filename;           }
diff --git a/src/race/grand_prix_manager.hpp b/src/race/grand_prix_manager.hpp
index d4800204a..5eabbb719 100644
--- a/src/race/grand_prix_manager.hpp
+++ b/src/race/grand_prix_manager.hpp
@@ -45,8 +45,6 @@ private:
     /** Generates a new unique indentifier for a user defined grand prix */
     std::string generateId();
 
-    bool existsName(const irr::core::stringw& name) const;
-
 public:
                    GrandPrixManager();
                   ~GrandPrixManager();
@@ -54,6 +52,7 @@ public:
     GrandPrixData* getGrandPrix(const int i) const { return m_gp_data[i];     }
     GrandPrixData* getGrandPrix(const std::string& s) const;
     unsigned int   getNumberOfGrandPrix()    const { return m_gp_data.size(); }
+    bool existsName(const irr::core::stringw& name) const;
     void           checkConsistency();
 
     // Methods for the gp editor
diff --git a/src/race/race_manager.cpp b/src/race/race_manager.cpp
index 2626530b4..07362e7c8 100644
--- a/src/race/race_manager.cpp
+++ b/src/race/race_manager.cpp
@@ -46,6 +46,7 @@
 #include "network/protocol_manager.hpp"
 #include "network/network_world.hpp"
 #include "network/protocols/start_game_protocol.hpp"
+#include "states_screens/grand_prix_cutscene.hpp"
 #include "states_screens/grand_prix_lose.hpp"
 #include "states_screens/grand_prix_win.hpp"
 #include "states_screens/kart_selection.hpp"
@@ -678,16 +679,17 @@ void RaceManager::exitRace(bool delete_world)
             }
         }
 
+        if (delete_world) World::deleteWorld();
+        delete_world = false;
+
+        StateManager::get()->enterGameState();
+        race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
+        race_manager->setNumKarts(0);
+        race_manager->setNumPlayers(0);
+        race_manager->setNumLocalPlayers(0);
+
         if (someHumanPlayerWon)
         {
-            if (delete_world) World::deleteWorld();
-            delete_world = false;
-
-            StateManager::get()->enterGameState();
-            race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
-            race_manager->setNumKarts(0);
-            race_manager->setNumPlayers(0);
-            race_manager->setNumLocalPlayers(0);
             race_manager->startSingleRace("gpwin", 999, false);
             GrandPrixWin* scene = GrandPrixWin::getInstance();
             StateManager::get()->pushScreen(scene);
@@ -695,14 +697,6 @@ void RaceManager::exitRace(bool delete_world)
         }
         else
         {
-            if (delete_world) World::deleteWorld();
-            delete_world = false;
-
-            StateManager::get()->enterGameState();
-            race_manager->setMinorMode(RaceManager::MINOR_MODE_CUTSCENE);
-            race_manager->setNumKarts(0);
-            race_manager->setNumPlayers(0);
-            race_manager->setNumLocalPlayers(0);
             race_manager->startSingleRace("gplose", 999, false);
             GrandPrixLose* scene = GrandPrixLose::getInstance();
             StateManager::get()->pushScreen(scene);
@@ -713,7 +707,8 @@ void RaceManager::exitRace(bool delete_world)
             }
             else
             {
-                std::cerr << "RaceManager::exitRace() : what's going on?? no winners and no losers??\n";
+                Log::error("RaceManager", "There are no winners and no losers."
+                           "This should have never happend\n");
                 std::vector<std::string> karts;
                 karts.push_back(UserConfigParams::m_default_kart);
                 scene->setKarts(karts);
@@ -767,12 +762,9 @@ void RaceManager::rerunRace()
 
 //-----------------------------------------------------------------------------
 
-void RaceManager::startGP(const GrandPrixData* gp, bool from_overworld,
+void RaceManager::startGP(const GrandPrixData &gp, bool from_overworld,
                           bool continue_saved_gp)
 {
-    assert(gp != NULL);
-    //std::cout << gp->getId();
-
     StateManager::get()->enterGameState();
     setGrandPrix(gp);
     setCoinTarget( 0 ); // Might still be set from a previous challenge
diff --git a/src/race/race_manager.hpp b/src/race/race_manager.hpp
index 379fd3ed0..1dbb9a7ee 100644
--- a/src/race/race_manager.hpp
+++ b/src/race/race_manager.hpp
@@ -185,12 +185,13 @@ public:
             case MINOR_MODE_SOCCER:         return _("Soccer");
             default: assert(false); return NULL;
         }
-    }
+    }   // getNameOf
 
     // ------------------------------------------------------------------------
-    static bool hasAI(const MinorRaceModeType mode)
+    /** Returns if the currently set minor game mode can be used by the AI. */
+    bool hasAI()
     {
-        switch (mode)
+        switch (m_minor_mode)
         {
             case MINOR_MODE_NORMAL_RACE:    return true;
             case MINOR_MODE_TIME_TRIAL:     return true;
@@ -200,7 +201,7 @@ public:
             case MINOR_MODE_SOCCER:         return false;
             default: assert(false);         return false;
         }
-    }
+    }   // hasAI
 
 
     // ------------------------------------------------------------------------
@@ -410,9 +411,9 @@ public:
     void setDifficulty(Difficulty diff);
 
     // ------------------------------------------------------------------------
-    void setGrandPrix(const GrandPrixData *gp)
+    void setGrandPrix(const GrandPrixData &gp)
     {
-        m_grand_prix = *gp;
+        m_grand_prix = gp;
         m_coin_target = 0;
     }
     // ------------------------------------------------------------------------
@@ -682,7 +683,7 @@ public:
       * \brief Higher-level method to start a GP without having to care about
       *  the exact startup sequence
       */
-    void  startGP(const GrandPrixData* gp, bool from_overworld,
+    void  startGP(const GrandPrixData &gp, bool from_overworld,
                   bool continue_saved_gp);
 
     /**
diff --git a/src/states_screens/arenas_screen.cpp b/src/states_screens/arenas_screen.cpp
index f556629ac..fd4ead99e 100644
--- a/src/states_screens/arenas_screen.cpp
+++ b/src/states_screens/arenas_screen.cpp
@@ -25,7 +25,7 @@
 #include "io/file_manager.hpp"
 #include "states_screens/state_manager.hpp"
 #include "states_screens/arenas_screen.hpp"
-#include "states_screens/dialogs/track_info_dialog.hpp"
+#include "states_screens/track_info_screen.hpp"
 #include "tracks/track.hpp"
 #include "tracks/track_manager.hpp"
 #include "utils/random_generator.hpp"
@@ -163,13 +163,11 @@ void ArenasScreen::eventCallback(Widget* widget, const std::string& name, const
             RandomGenerator random;
             const int randomID = random.get(curr_group.size());
 
-            Track* clickedTrack = track_manager->getTrack( curr_group[randomID] );
-            if (clickedTrack != NULL)
+            Track* clicked_track = track_manager->getTrack( curr_group[randomID] );
+            if (clicked_track != NULL)
             {
-                ITexture* screenshot = irr_driver->getTexture( clickedTrack->getScreenshotFile().c_str() );
-
-                new TrackInfoDialog(selection, clickedTrack->getIdent(), clickedTrack->getName(),
-                                    screenshot, 0.8f, 0.7f);
+                TrackInfoScreen::getInstance()->setTrack(clicked_track);
+                StateManager::get()->pushScreen(TrackInfoScreen::getInstance());
             }
 
         }
@@ -182,13 +180,11 @@ void ArenasScreen::eventCallback(Widget* widget, const std::string& name, const
         }
         else
         {
-            Track* clickedTrack = track_manager->getTrack(selection);
-            if (clickedTrack != NULL)
+            Track* clicked_track = track_manager->getTrack(selection);
+            if (clicked_track != NULL)
             {
-                ITexture* screenshot = irr_driver->getTexture( clickedTrack->getScreenshotFile().c_str() );
-
-                new TrackInfoDialog(selection, clickedTrack->getIdent(), clickedTrack->getName(),
-                                    screenshot, 0.8f, 0.7f);
+                TrackInfoScreen::getInstance()->setTrack(clicked_track);
+                StateManager::get()->pushScreen(TrackInfoScreen::getInstance());
             }   // clickedTrack !=  NULL
         }   // if random_track
 
diff --git a/src/states_screens/dialogs/addons_loading.cpp b/src/states_screens/dialogs/addons_loading.cpp
index 1ebf2f3ad..a4959aafe 100644
--- a/src/states_screens/dialogs/addons_loading.cpp
+++ b/src/states_screens/dialogs/addons_loading.cpp
@@ -190,11 +190,12 @@ void AddonsLoading::init()
 }   // init
 
 // ----------------------------------------------------------------------------
-void AddonsLoading::escapePressed()
+bool AddonsLoading::onEscapePressed()
 {
     stopDownload();
     ModalDialog::dismiss();
-}   // escapePressed
+    return true;
+}   // onEscapePressed
 
 // ----------------------------------------------------------------------------
 
@@ -335,7 +336,6 @@ void AddonsLoading::stopDownload()
         // order to avoid a memory leak, we let network_http free
         // the request.
         //m_download_request->setManageMemory(true);
-        assert(false);
         m_download_request->cancel();
     };
 }   // startDownload
diff --git a/src/states_screens/dialogs/addons_loading.hpp b/src/states_screens/dialogs/addons_loading.hpp
index fdb7fd5e3..27f52a431 100644
--- a/src/states_screens/dialogs/addons_loading.hpp
+++ b/src/states_screens/dialogs/addons_loading.hpp
@@ -23,6 +23,7 @@
 #include "addons/addons_manager.hpp"
 #include "guiengine/widgets.hpp"
 #include "guiengine/modaldialog.hpp"
+#include "utils/cpp2011.hpp"
 #include "utils/synchronised.hpp"
 
 namespace Online { class HTTPRequest; }
@@ -32,7 +33,6 @@ namespace Online { class HTTPRequest; }
   */
 class AddonsLoading : public GUIEngine::ModalDialog
 {
-  virtual void escapePressed();
 private:
     GUIEngine::LabelWidget       *m_state;
     GUIEngine::ProgressBarWidget *m_progress;
@@ -70,6 +70,7 @@ public:
      * */
     void onUpdate(float delta);
     void voteClicked();
+    virtual bool onEscapePressed() OVERRIDE;
     
 };   // AddonsLoading
 
diff --git a/src/states_screens/dialogs/confirm_resolution_dialog.cpp b/src/states_screens/dialogs/confirm_resolution_dialog.cpp
index 36f00240f..c4c1896ab 100644
--- a/src/states_screens/dialogs/confirm_resolution_dialog.cpp
+++ b/src/states_screens/dialogs/confirm_resolution_dialog.cpp
@@ -64,6 +64,15 @@ void ConfirmResolutionDialog::onUpdate(float dt)
 
 }
 
+// ----------------------------------------------------------------------------
+
+bool ConfirmResolutionDialog::onEscapePressed()
+{
+    ModalDialog::dismiss();
+    irr_driver->cancelResChange();
+    return true;
+}   // escapePressed
+
 // ------------------------------------------------------------------------------------------------------
 
 void ConfirmResolutionDialog::updateMessage()
diff --git a/src/states_screens/dialogs/confirm_resolution_dialog.hpp b/src/states_screens/dialogs/confirm_resolution_dialog.hpp
index 5a03bfc72..dc3ad1605 100644
--- a/src/states_screens/dialogs/confirm_resolution_dialog.hpp
+++ b/src/states_screens/dialogs/confirm_resolution_dialog.hpp
@@ -20,6 +20,7 @@
 #define HEADER_CONFIRM_RES_DIALOG_HPP
 
 #include "guiengine/modaldialog.hpp"
+#include "utils/cpp2011.hpp"
 
 /**
  * \brief Dialog shown after a resolution switch sot he user may confirm if
@@ -28,6 +29,7 @@
  */
 class ConfirmResolutionDialog : public GUIEngine::ModalDialog
 {
+private:
     /** number of seconds left before resolution is considered unplayable */
     float m_remaining_time;
         
@@ -40,6 +42,7 @@ public:
     GUIEngine::EventPropagation processEvent(const std::string& eventSource);
     
     virtual void onUpdate(float dt);
+    virtual bool onEscapePressed() OVERRIDE;
 };
   
 
diff --git a/src/states_screens/dialogs/debug_slider.cpp b/src/states_screens/dialogs/debug_slider.cpp
index 006a05c8b..38a210b51 100644
--- a/src/states_screens/dialogs/debug_slider.cpp
+++ b/src/states_screens/dialogs/debug_slider.cpp
@@ -39,6 +39,7 @@ DebugSliderDialog::DebugSliderDialog() : ModalDialog(0.85f, 0.25f, MODAL_DIALOG_
     loadFromFile("debug_slider.stkgui");
 }
 
+#if !defined(__APPLE__)
 void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S)
 {
     getWidget<SpinnerWidget>(id.c_str())->setValue(G());
@@ -46,6 +47,7 @@ void DebugSliderDialog::setSliderHook(std::string id, unsigned min, unsigned max
     getWidget<SpinnerWidget>(id.c_str())->setMax(max);
     Setters[id] = S;
 }
+#endif
 
 // ------------------------------------------------------------------------------------------------------
 
@@ -67,12 +69,16 @@ void DebugSliderDialog::onEnterPressedInternal()
 
 GUIEngine::EventPropagation DebugSliderDialog::processEvent(const std::string& eventSource)
 {
+#if !defined(__APPLE__)
     if (Setters.find(eventSource) == Setters.end())
+#endif
         return GUIEngine::EVENT_LET;
+#if !defined(__APPLE__)
     int value = getWidget<SpinnerWidget>(eventSource.c_str())->getValue();
     Log::info("DebugSlider", "Value for <%s> : %i", eventSource.c_str(), value);
     Setters[eventSource](value);
     return GUIEngine::EVENT_BLOCK;
+#endif
 }
 
 // ------------------------------------------------------------------------------------------------------
diff --git a/src/states_screens/dialogs/debug_slider.hpp b/src/states_screens/dialogs/debug_slider.hpp
index 5c4aaf734..8cd01acbc 100644
--- a/src/states_screens/dialogs/debug_slider.hpp
+++ b/src/states_screens/dialogs/debug_slider.hpp
@@ -33,14 +33,17 @@ class DebugSliderDialog : public GUIEngine::ModalDialog
 private:
 
     std::string m_id;
+#if !defined(__APPLE__)
     std::map<std::string, std::function<void(int)> >Setters;
+#endif
 
 public:
     DebugSliderDialog();
 
     ~DebugSliderDialog();
-
+#if !defined(__APPLE__)
     void setSliderHook(std::string id, unsigned min, unsigned max, std::function<int()> G, std::function<void(int)> S);
+#endif
 
     virtual void onEnterPressedInternal() OVERRIDE;
     virtual void onUpdate(float dt) OVERRIDE;
diff --git a/src/states_screens/dialogs/enter_gp_name_dialog.cpp b/src/states_screens/dialogs/enter_gp_name_dialog.cpp
index e75c64abc..7d68bf09b 100644
--- a/src/states_screens/dialogs/enter_gp_name_dialog.cpp
+++ b/src/states_screens/dialogs/enter_gp_name_dialog.cpp
@@ -84,17 +84,13 @@ void EnterGPNameDialog::onEnterPressedInternal()
     if (name.size() > 0 && name != "Random Grand Prix")
     {
         // check for duplicate names
-        for (unsigned int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++)
+        if (grand_prix_manager->existsName(name))
         {
-            const GrandPrixData* gp = grand_prix_manager->getGrandPrix(i);
-            if (gp->getName() == name)
-            {
-                LabelWidget* label = getWidget<LabelWidget>("title");
-                assert(label != NULL);
-                label->setText(_("Another grand prix with this name already exists."), false);
-                sfx_manager->quickSound("anvil");
-                return;
-            }
+            LabelWidget* label = getWidget<LabelWidget>("title");
+            assert(label != NULL);
+            label->setText(_("Another grand prix with this name already exists."), false);
+            sfx_manager->quickSound("anvil");
+            return;
         }
 
         // It's unsafe to delete from inside the event handler so we do it
diff --git a/src/states_screens/dialogs/gp_info_dialog.cpp b/src/states_screens/dialogs/gp_info_dialog.cpp
index 79ad221ca..dc411c69c 100644
--- a/src/states_screens/dialogs/gp_info_dialog.cpp
+++ b/src/states_screens/dialogs/gp_info_dialog.cpp
@@ -254,7 +254,7 @@ void GPInfoDialog::onEnterPressedInternal()
     ModalDialog::dismiss();
     // Disable accidentally unlocking of a challenge
     PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
-    race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false, false);
+    race_manager->startGP(*grand_prix_manager->getGrandPrix(gp_id), false, false);
 }
 
 // ----------------------------------------------------------------------------
@@ -270,8 +270,8 @@ GUIEngine::EventPropagation GPInfoDialog::processEvent(const std::string& event_
         // becomes invalid!
         std::string save_source = event_source;
         ModalDialog::dismiss();
-        race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false,
-                              (save_source == "continue"));
+        //race_manager->startGP(grand_prix_manager->getGrandPrix(gp_id), false,
+        //                      (save_source == "continue"));
         return GUIEngine::EVENT_BLOCK;
     }
 
diff --git a/src/states_screens/dialogs/race_paused_dialog.cpp b/src/states_screens/dialogs/race_paused_dialog.cpp
index 8da45da27..33c8adc15 100644
--- a/src/states_screens/dialogs/race_paused_dialog.cpp
+++ b/src/states_screens/dialogs/race_paused_dialog.cpp
@@ -103,7 +103,7 @@ void RacePausedDialog::onEnterPressedInternal()
 GUIEngine::EventPropagation
            RacePausedDialog::processEvent(const std::string& eventSource)
 {
-    GUIEngine::RibbonWidget* chocie_ribbon =
+    GUIEngine::RibbonWidget* choice_ribbon =
             getWidget<GUIEngine::RibbonWidget>("choiceribbon");
 
     if (eventSource == "backbtn")
@@ -115,7 +115,7 @@ GUIEngine::EventPropagation
     else if (eventSource == "choiceribbon")
     {
         const std::string& selection =
-            chocie_ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER);
+            choice_ribbon->getSelectionIDString(PLAYER_ID_GAME_MASTER);
 
         if (selection == "exit")
         {
diff --git a/src/states_screens/dialogs/random_gp_dialog.cpp b/src/states_screens/dialogs/random_gp_dialog.cpp
deleted file mode 100644
index 848d01834..000000000
--- a/src/states_screens/dialogs/random_gp_dialog.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-//  SuperTuxKart - a fun racing game with go-kart
-//  Copyright (C) 2014 konstin
-//
-//  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 "guiengine/engine.hpp"
-#include "guiengine/widgets/icon_button_widget.hpp"
-#include "guiengine/widgets/spinner_widget.hpp"
-#include "race/grand_prix_manager.hpp"
-#include "race/race_manager.hpp"
-#include "states_screens/dialogs/random_gp_dialog.hpp"
-#include "tracks/track_manager.hpp"
-
-#include <IGUIEnvironment.h>
-#include <IGUIStaticText.h>
-
-using irr::core::stringc;
-using irr::core::stringw;
-using irr::gui::IGUIStaticText;
-
-typedef GUIEngine::SpinnerWidget Spinner;
-
-RandomGPInfoDialog::RandomGPInfoDialog()
-{
-    // Defaults - loading selection from last time frrom a file would be better
-    m_number_of_tracks = 2; // We can assume that there are at least 2 standard tracks
-    m_trackgroup = "standard";
-    m_use_reverse = GrandPrixData::GP_NO_REVERSE;
-
-    doInit();
-    m_curr_time = 0.0f;
-
-    m_under_title = m_area.getHeight()/7;
-    m_over_body = m_area.getHeight()/7 + SPINNER_HEIGHT + 10; // 10px space
-    m_lower_bound = m_area.getHeight()*6/7;
-
-    m_gp.createRandomGP(m_number_of_tracks, m_trackgroup, m_use_reverse);
-
-    addTitle();
-    addSpinners();
-    addTracks();
-    addScreenshot();
-    addButtons();
-    addRestartButton();
-}
-
-// ----------------------------------------------------------------------------
-
-void RandomGPInfoDialog::addSpinners()
-{
-    const int laps_width = 150;
-    // "20*4" because there a 4 separators between the spinners with 20px each
-    int label_spinner_width = m_area.getWidth() - laps_width - 20*4;
-    // This scaling ensures that the spinners are smaller than the available
-    // area, look well and are (hopefully) big enough for their labels
-    if (m_area.getWidth() < 700)
-        label_spinner_width /= 2;
-    else if (m_area.getWidth() < 1500)
-        label_spinner_width /= 3;
-    else
-        label_spinner_width /= 4;
-    const int left = (m_area.getWidth() - label_spinner_width*2 - laps_width)/2;
-
-    // Trackgroup chooser
-    Spinner* spinner = new Spinner(false);
-    spinner->m_properties[GUIEngine::PROP_ID] = "Trackgroup";
-    spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true";
-    spinner->setParent(m_irrlicht_window);
-    m_widgets.push_back(spinner);
-    spinner->add();
-    spinner->move(left, m_under_title, label_spinner_width, SPINNER_HEIGHT);
-    // Fill it with all the track group names
-    spinner->addLabel("all");
-    int index_standard;
-    const std::vector<std::string>& groups = track_manager->getAllTrackGroups();
-    for (unsigned int i = 0; i < groups.size(); i++)
-    {
-        spinner->addLabel(stringw(groups[i].c_str()));
-        if(groups[i] == "standard")
-            index_standard  = i+1;
-    }
-    // The value can only be set here because SpinnerWidget resets the value
-    // every time a label is added
-    spinner->setValue(index_standard);
-
-    // Number of laps chooser
-    spinner = new Spinner(false);
-    spinner->setValue(m_number_of_tracks);
-    spinner->setMin(1);
-    spinner->setMax(track_manager->getTracksInGroup("standard").size());
-    spinner->setParent(m_irrlicht_window);
-    spinner->m_properties[GUIEngine::PROP_ID] = "Number of tracks";
-    spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true";
-    m_widgets.push_back(spinner);
-    spinner->add();
-    spinner->move(left + label_spinner_width + 20/2, m_under_title, laps_width, SPINNER_HEIGHT);
-
-    // reverse choose
-    spinner = new Spinner(false);
-    spinner->setParent(m_irrlicht_window);
-    spinner->m_properties[GUIEngine::PROP_ID] = "reverse";
-    spinner->m_properties[GUIEngine::PROP_WRAP_AROUND] = "true";
-    m_widgets.push_back(spinner);
-    spinner->add();
-    spinner->move(left + label_spinner_width + laps_width + 20/2, m_under_title, label_spinner_width, SPINNER_HEIGHT);
-    spinner->addLabel("no reverse");
-    spinner->addLabel("all reverse");
-    spinner->addLabel("mixed");
-}
-
-// ----------------------------------------------------------------------------
-
-void RandomGPInfoDialog::addRestartButton()
-{
-    GUIEngine::IconButtonWidget* button = new GUIEngine::IconButtonWidget();
-    button->setImage("gui/restart.png");
-    button->setParent(m_irrlicht_window);
-    button->m_properties[GUIEngine::PROP_ID] = "reload";
-    m_widgets.push_back(button);
-    button->add();
-    button->move(m_area.getWidth() - 20 - 32, 20, 32, 32);
-}
-
-// ----------------------------------------------------------------------------
-
-GUIEngine::EventPropagation RandomGPInfoDialog::processEvent(
-                                                 const std::string& eventSource)
-{
-    if (eventSource == "start")
-    {
-        // Save GP data, since dismiss will delete this object.
-        GrandPrixData gp = m_gp;
-        ModalDialog::dismiss();
-        race_manager->startGP(&gp, false, false);
-        return GUIEngine::EVENT_BLOCK;
-    }
-    else if (eventSource == "Number of tracks")
-    {
-        // The old gp can be reused because there's only track deletion/adding
-        m_number_of_tracks = getWidget<Spinner>("Number of tracks")->getValue();
-        m_gp.changeTrackNumber(m_number_of_tracks, m_trackgroup);
-        addTracks();
-    }
-    else if (eventSource == "Trackgroup")
-    {
-        Spinner* t = getWidget<Spinner>("Trackgroup");
-        Spinner* s = getWidget<Spinner>("Number of tracks");
-
-        m_trackgroup = stringc(t->getStringValue()).c_str();
-
-        // Update the maximum for the number of tracks since it's depending on
-        // the current track. The current value in the Number-of-tracks-spinner
-        // has to be updated, since otherwise the displayed (and used) value
-        // can be bigger than the maximum. (Might be a TODO to fix this)
-        unsigned int max = (m_trackgroup == "all") ?
-                           track_manager->getNumberOfTracks() :
-                           track_manager->getTracksInGroup(m_trackgroup).size();
-        m_number_of_tracks = std::min(max, m_number_of_tracks);
-        s->setMax(max);
-        if (s->getValue() > (signed)max)
-            s->setValue(max);
-
-        // Create a new (i.e. with new tracks) random gp, since the old
-        // tracks might not all belong to the newly selected group.
-        m_gp.createRandomGP(m_number_of_tracks, m_trackgroup, m_use_reverse,
-                            /*new_tracks*/true);
-        addTracks();
-    }
-    else if (eventSource == "reverse")
-    {
-        Spinner* r = getWidget<Spinner>("reverse");
-        m_use_reverse = static_cast<GrandPrixData::GPReverseType>(r->getValue());
-        m_gp.changeReverse(m_use_reverse);
-    }
-    else if (eventSource == "reload")
-    {
-        m_gp.createRandomGP(m_number_of_tracks, m_trackgroup, m_use_reverse,
-                           /*new_tracks*/true);
-        addTracks();
-    }
-
-    return GUIEngine::EVENT_LET;
-}
-
diff --git a/src/states_screens/dialogs/random_gp_dialog.hpp b/src/states_screens/dialogs/random_gp_dialog.hpp
deleted file mode 100644
index 35a058825..000000000
--- a/src/states_screens/dialogs/random_gp_dialog.hpp
+++ /dev/null
@@ -1,51 +0,0 @@
-//  SuperTuxKart - a fun racing game with go-kart
-//  Copyright (C) 2014 konstin
-//
-//  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_RANDOM_GP_INFO_DIALOG_HPP
-#define HEADER_RANDOM_GP_INFO_DIALOG_HPP
-
-#include "states_screens/dialogs/gp_info_dialog.hpp"
-
-#include <string>
-
-class RandomGPInfoDialog : public GPInfoDialog
-{
-private:
-    /** How many tracks to pick. */
-    unsigned int m_number_of_tracks;
-
-    /** Name of the track group from which to pick tracks. */
-    std::string m_trackgroup;
-
-    /** How reverse settings should be determined. */
-    GrandPrixData::GPReverseType m_use_reverse;
-
-public:
-    static const int SPINNER_HEIGHT = 40;
-
-    RandomGPInfoDialog();
-
-    /** Adds a SpinnerWidgets to choose the track groups, one to choose the
-     * number of tracks and one to choose if  the tracks should be raced in
-     * reverse. The Spinners are centered. */
-    void addSpinners();
-    void addRestartButton();
-
-    GUIEngine::EventPropagation processEvent(const std::string& eventSource);
-};
-
-#endif
diff --git a/src/states_screens/easter_egg_screen.cpp b/src/states_screens/easter_egg_screen.cpp
index 474615e32..bf0790cde 100644
--- a/src/states_screens/easter_egg_screen.cpp
+++ b/src/states_screens/easter_egg_screen.cpp
@@ -26,7 +26,7 @@
 #include "guiengine/widgets/icon_button_widget.hpp"
 #include "io/file_manager.hpp"
 #include "states_screens/state_manager.hpp"
-#include "states_screens/dialogs/track_info_dialog.hpp"
+#include "states_screens/track_info_screen.hpp"
 #include "tracks/track.hpp"
 #include "tracks/track_manager.hpp"
 #include "utils/translation.hpp"
@@ -80,19 +80,13 @@ void EasterEggScreen::eventCallback(Widget* widget, const std::string& name, con
                 std::string track = m_random_track_list.front();
                 m_random_track_list.pop_front();
                 m_random_track_list.push_back(track);
-                Track* clickedTrack = track_manager->getTrack( track );
+                Track* clicked_track = track_manager->getTrack( track );
 
 
-                if (clickedTrack != NULL)
+                if (clicked_track != NULL)
                 {
-                    ITexture* screenshot =
-                        irr_driver->getTexture( clickedTrack->getScreenshotFile(),
-                                                "While loading screenshot for track '%s':",
-                                                clickedTrack->getFilename()   );
-
-                    new TrackInfoDialog(selection, clickedTrack->getIdent(),
-                                        translations->fribidize(clickedTrack->getName()),
-                                        screenshot, 0.8f, 0.7f);
+                    TrackInfoScreen::getInstance()->setTrack(clicked_track);
+                    StateManager::get()->pushScreen(TrackInfoScreen::getInstance());
                 }
 
             }
@@ -100,22 +94,13 @@ void EasterEggScreen::eventCallback(Widget* widget, const std::string& name, con
             {
                 unlock_manager->playLockSound();
             }
-            else if (selection == RibbonWidget::NO_ITEM_ID)
+            else if (selection != RibbonWidget::NO_ITEM_ID)
             {
-            }
-            else
-            {
-                Track* clickedTrack = track_manager->getTrack(selection);
-                if (clickedTrack != NULL)
+                Track* clicked_track = track_manager->getTrack(selection);
+                if (clicked_track != NULL)
                 {
-                    ITexture* screenshot =
-                        irr_driver->getTexture( clickedTrack->getScreenshotFile(),
-                                                "While loading screenshot for track '%s'",
-                                                clickedTrack->getFilename());
-
-                    new TrackInfoDialog(selection, clickedTrack->getIdent(),
-                                        translations->fribidize(clickedTrack->getName()),
-                                        screenshot, 0.8f, 0.7f);
+                    TrackInfoScreen::getInstance()->setTrack(clicked_track);
+                    StateManager::get()->pushScreen(TrackInfoScreen::getInstance());
                 }
             }
         }
diff --git a/src/states_screens/edit_gp_screen.cpp b/src/states_screens/edit_gp_screen.cpp
index 8ce3a6490..a9a3b66d4 100644
--- a/src/states_screens/edit_gp_screen.cpp
+++ b/src/states_screens/edit_gp_screen.cpp
@@ -103,20 +103,17 @@ void EditGPScreen::eventCallback(GUIEngine::Widget* widget, const std::string& n
                 setModified(true);
             }
         }
-        else if (m_action == "add" || m_action == "edit")
+        else if (m_action == "edit")
         {
-            if (m_action == "edit")
-            {
-                edit();
-            }
-            else
-            {
-                EditTrackScreen* edit = EditTrackScreen::getInstance();
-                assert(edit != NULL);
-                //By default, 3 laps and no reversing
-                edit->setSelection(NULL, 3, false);
-                StateManager::get()->pushScreen(edit);
-            }
+            edit();
+        }
+        else if (m_action == "add")
+        {
+            EditTrackScreen* edit = EditTrackScreen::getInstance();
+            assert(edit != NULL);
+            //By default, 3 laps and no reversing
+            edit->setSelection(NULL, 3, false);
+            StateManager::get()->pushScreen(edit);
         }
         else if (m_action == "remove")
         {
diff --git a/src/states_screens/edit_track_screen.cpp b/src/states_screens/edit_track_screen.cpp
index a01bfc7c4..c3b68fe29 100644
--- a/src/states_screens/edit_track_screen.cpp
+++ b/src/states_screens/edit_track_screen.cpp
@@ -234,6 +234,8 @@ void EditTrackScreen::selectTrack(const std::string& id)
         tracks->setSelection("", PLAYER_ID_GAME_MASTER, true);
         selected_track->setText(_("Select a track"), true);
 
+        // We can't set a better default for number of laps. On the other
+        // hand, if a track is selected, the number of laps will be updated.
         laps->setValue(3);
 
         reverse->setVisible(true);
diff --git a/src/states_screens/feature_unlocked.cpp b/src/states_screens/feature_unlocked.cpp
index 32510f341..3f24204ee 100644
--- a/src/states_screens/feature_unlocked.cpp
+++ b/src/states_screens/feature_unlocked.cpp
@@ -297,8 +297,7 @@ void FeatureUnlockedCutScene::init()
             m_unlocked_stuff[n].m_root_gift_node = kart_model->attachModel(true, false);
             m_unlocked_stuff[n].m_scale = 5.0f;
             kart_model->setAnimation(KartModel::AF_DEFAULT);
-            float susp[4]={0,0,0,0};
-            kart_model->update(0.0f, 0.0f, 0.0f, susp, 0.0f);
+            kart_model->update(0.0f, 0.0f, 0.0f, 0.0f);
 
 #ifdef DEBUG
             m_unlocked_stuff[n].m_root_gift_node->setName("unlocked kart");
diff --git a/src/states_screens/gp_info_screen.cpp b/src/states_screens/gp_info_screen.cpp
new file mode 100644
index 000000000..5efdee60c
--- /dev/null
+++ b/src/states_screens/gp_info_screen.cpp
@@ -0,0 +1,364 @@
+//  SuperTuxKart - a fun racing game with go-kart
+//  Copyright (C) 2009-2014  Marianne Gagnon
+//                2014       Joerg Henrichs, konstin
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 3
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#include "states_screens/gp_info_screen.hpp"
+
+#include "audio/sfx_manager.hpp"
+#include "challenges/unlock_manager.hpp"
+#include "config/player_manager.hpp"
+#include "config/saved_grand_prix.hpp"
+#include "graphics/irr_driver.hpp"
+#include "guiengine/engine.hpp"
+#include "guiengine/screen.hpp"
+#include "guiengine/widgets/icon_button_widget.hpp"
+#include "guiengine/widgets/label_widget.hpp"
+#include "guiengine/widgets/list_widget.hpp"
+#include "guiengine/widgets/ribbon_widget.hpp"
+#include "guiengine/widgets/spinner_widget.hpp"
+#include "io/file_manager.hpp"
+#include "race/grand_prix_manager.hpp"
+#include "race/grand_prix_data.hpp"
+#include "race/race_manager.hpp"
+#include "states_screens/state_manager.hpp"
+#include "states_screens/tracks_screen.hpp"
+#include "tracks/track.hpp"
+#include "tracks/track_manager.hpp"
+#include "utils/translation.hpp"
+
+#include <IGUIEnvironment.h>
+#include <IGUIStaticText.h>
+
+#include <algorithm>
+
+using irr::gui::IGUIStaticText;
+using namespace GUIEngine;
+
+DEFINE_SCREEN_SINGLETON( GPInfoScreen );
+
+/** Constructor, initialised some variables which might be used before
+ *  loadedFromFile is called.
+ */
+GPInfoScreen::GPInfoScreen() : Screen("gp_info.stkgui")
+{
+    m_curr_time = 0.0f;
+    // Necessary to test if loadedFroMFile() was executed (in setGP)
+    m_reverse_spinner   = NULL;
+    m_screenshot_widget = NULL;
+}   // GPInfoScreen
+
+// ----------------------------------------------------------------------------
+/** Called when the stkgui file is read. It stores the pointer to various
+ *  widgets and adds the right names for reverse mode.
+ */
+void GPInfoScreen::loadedFromFile()
+{
+    // The group spinner is filled in init every time the screen is shown
+    // (since the groups can change if addons are added/deleted).
+    m_group_spinner      = getWidget<SpinnerWidget>("group-spinner");
+    m_reverse_spinner    = getWidget<SpinnerWidget>("reverse-spinner");
+    m_reverse_spinner->addLabel(_("No"));
+    m_reverse_spinner->addLabel(_("Yes"));
+    m_reverse_spinner->addLabel(_("Random"));
+    m_reverse_spinner->setValue(0);
+
+    m_num_tracks_spinner = getWidget<SpinnerWidget>("track-spinner");
+    // Only init the number of tracks here, this way the previously selected
+    // number of tracks will be the default.
+    m_num_tracks_spinner->setValue(1);
+    int number_of_tracks = m_num_tracks_spinner->getValue();
+}   // loadedFromFile
+
+// ----------------------------------------------------------------------------
+/** Sets the GP to be displayed. If the identifier is 'random', no gp info
+ *  will be loaded.
+ */
+void GPInfoScreen::setGP(const std::string &gp_ident)
+{
+    if(gp_ident!="random")
+        m_gp = *grand_prix_manager->getGrandPrix(gp_ident);
+    else
+    {
+        // Doesn't matter what kind of GP we create, it just gets the
+        // right id ("random").
+        m_gp.createRandomGP(1, "standard", 
+                            m_reverse_spinner ? getReverse()
+                                              : GrandPrixData::GP_NO_REVERSE);
+    }
+}   // setGP
+
+// ----------------------------------------------------------------------------
+/** Converts the currently selected reverse status into a value of type 
+*  GPReverseType .
+*/
+GrandPrixData::GPReverseType GPInfoScreen::getReverse() const
+{
+    switch (m_reverse_spinner->getValue())
+    {
+    case 0: return GrandPrixData::GP_NO_REVERSE;     break;
+    case 1: return GrandPrixData::GP_ALL_REVERSE;    break;
+    case 2: return GrandPrixData::GP_RANDOM_REVERSE; break;
+    default: assert(false); 
+    }   // switch
+    // Avoid compiler warning
+    return GrandPrixData::GP_NO_REVERSE;
+}   // getReverse
+// ----------------------------------------------------------------------------
+void GPInfoScreen::beforeAddingWidget()
+{
+    bool random = m_gp.isRandomGP();
+    if (!random)
+    {
+        // Check if there is a saved GP:
+        SavedGrandPrix* saved_gp = SavedGrandPrix::getSavedGP(
+            StateManager::get()->getActivePlayerProfile(0)->getUniqueID(),
+            m_gp.getId(),
+            race_manager->getDifficulty(),
+            race_manager->getNumberOfKarts(),
+            race_manager->getNumLocalPlayers());
+
+        RibbonWidget* ribbonButtons = getWidget<RibbonWidget>("buttons");
+        int id_continue_button = ribbonButtons->findItemNamed("continue");
+        ribbonButtons->setItemVisible(id_continue_button, saved_gp != NULL);
+        ribbonButtons->setLabel(id_continue_button, _("Continue saved GP"));
+    }
+    else
+    {
+        RibbonWidget* ribbonButtons = getWidget<RibbonWidget>("buttons");
+        int id_continue_button = ribbonButtons->findItemNamed("continue");
+        ribbonButtons->setItemVisible(id_continue_button, true);
+        ribbonButtons->setLabel(id_continue_button, _("Reload"));
+    }
+}
+
+// ----------------------------------------------------------------------------
+/** Called before the screen is shown. It adds the screenshot icon, and
+ *  initialises all widgets depending on GP mode (random or not), if a saved
+ *  GP is available etc.
+ */
+void GPInfoScreen::init()
+{
+    Screen::init();
+    m_curr_time = 0.0f;
+
+    bool random = m_gp.isRandomGP();
+
+    SpinnerWidget *reverse_spinner = getWidget<SpinnerWidget>("reverse-spinner");
+    getWidget<LabelWidget  >("track-text"   )->setVisible(random);
+    m_num_tracks_spinner->setVisible(random);
+    getWidget<LabelWidget  >("group-text"   )->setVisible(random);
+    m_group_spinner->setVisible(random);
+
+
+    if(random)
+    {
+        RibbonWidget *rb = getWidget<RibbonWidget>("buttons");
+        rb->setLabel(1,_(L"Reload") );
+        getWidget<LabelWidget>("name")->setText(_("Random Grand Prix"), false);
+        std::string restart = file_manager->getAsset(FileManager::GUI, "restart.png");
+
+        // We have to recreate the group spinner, but a new group might have
+        // been added or deleted since the last time this screen was shown.
+        m_group_spinner->clearLabels();
+        m_group_spinner->addLabel("all");
+        int index_standard;
+        const std::vector<std::string>& groups = track_manager->getAllTrackGroups();
+        for (unsigned int i = 0; i < groups.size(); i++)
+        {
+            m_group_spinner->addLabel(stringw(groups[i].c_str()));
+            if (groups[i] == "standard")
+                index_standard = i + 1;
+        }
+        // Try to keep a previously selected group value
+        if(m_group_spinner->getValue() >= (int)groups.size())
+        {
+            m_group_spinner->setValue(index_standard);
+            m_group_name = "standard";
+        }
+        else
+            m_group_name = stringc(m_group_spinner->getStringValue().c_str()).c_str();
+
+        // If there are more tracks selected atm as in the group (which can 
+        // happen if the group has been changed since last time this screen
+        // was shown), adjust it:
+        int max_num_tracks = m_group_name=="all" 
+                           ? track_manager->getNumberOfRaceTracks()
+                           : track_manager->getTracksInGroup(m_group_name).size();
+        m_num_tracks_spinner->setMax(max_num_tracks);
+        if(m_num_tracks_spinner->getValue() > max_num_tracks)
+        {
+            m_num_tracks_spinner->setValue(max_num_tracks);
+        }
+
+        // Now create the random GP:
+        m_gp.createRandomGP(m_num_tracks_spinner->getValue(), 
+                            m_group_name, getReverse(), true);
+    }
+    else
+    {
+        getWidget<LabelWidget>("name")->setText(m_gp.getName(), false);
+        m_gp.checkConsistency();
+    }
+
+    addTracks();
+    addScreenshot();
+}   // init
+
+// ----------------------------------------------------------------------------
+/** Updates the list of tracks shown.
+ */
+void GPInfoScreen::addTracks()
+{
+    const std::vector<std::string> tracks = m_gp.getTrackNames();
+
+    ListWidget *list = getWidget<ListWidget>("tracks");
+    list->clear();
+    for (unsigned int i = 0; i < tracks.size(); i++)
+    {
+        const Track *track = track_manager->getTrack(tracks[i]);
+        std::string s = StringUtils::toString(i);
+        list->addItem(s, translations->fribidize(track->getName()));
+    }
+}   // addTracks
+
+// ----------------------------------------------------------------------------
+/** Creates a screenshot widget in the placeholder of the GUI.
+ */
+void GPInfoScreen::addScreenshot()
+{
+    Widget* screenshot_div = getWidget("screenshot_div");
+
+    if(!m_screenshot_widget)
+    {
+        m_screenshot_widget = new IconButtonWidget(
+            IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
+            false, false, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
+        m_widgets.push_back(m_screenshot_widget);
+    }
+    // images are saved squared, but must be stretched to 4:3
+    m_screenshot_widget->setCustomAspectRatio(4.0f / 3.0f);
+    m_screenshot_widget->m_x = screenshot_div->m_x;
+    m_screenshot_widget->m_y = screenshot_div->m_y;
+    m_screenshot_widget->m_w = screenshot_div->m_w;
+    m_screenshot_widget->m_h = screenshot_div->m_h;
+
+
+    // Temporary icon, will replace it just after 
+    // (but it will be shown if the given icon is not found)
+    m_screenshot_widget->m_properties[PROP_ICON] = "gui/main_help.png";
+    m_screenshot_widget->add();
+
+    const Track *track = track_manager->getTrack(m_gp.getTrackId(0));
+    video::ITexture* screenshot = irr_driver->getTexture(track->getScreenshotFile(),
+                                    "While loading screenshot for track '%s':",
+                                           track->getFilename()            );
+    if (screenshot != NULL)
+        m_screenshot_widget->setImage(screenshot);
+}   // addScreenShot
+
+// ----------------------------------------------------------------------------
+/** Handle user input.
+ */
+void GPInfoScreen::eventCallback(Widget *, const std::string &name,
+                                 const int player_id)
+{
+    if(name=="buttons")
+    {
+        const std::string &button = getWidget<RibbonWidget>("buttons")
+                                  ->getSelectionIDString(PLAYER_ID_GAME_MASTER);
+
+        // The continue button becomes a 'reload' button in random GP:
+        if(button=="continue" && m_gp.isRandomGP())
+        {
+            // Create a new GP:
+            m_gp.createRandomGP(m_num_tracks_spinner->getValue(),
+                                m_group_name, getReverse(),
+                                /*new tracks*/ true );
+            addTracks();
+        }
+        else if (button == "start" || button=="continue")
+        {
+            // Normal GP: start/continue a saved GP
+            int n = getWidget<SpinnerWidget>("ai-spinner")->getValue();
+
+            race_manager->setNumKarts(race_manager->getNumLocalPlayers() + n);
+            race_manager->startGP(m_gp, false, (name == "continue"));
+        }
+    }   // name=="buttons"
+    else if (name=="group-spinner")
+    {
+        m_group_name = stringc(m_group_spinner->getStringValue()).c_str();
+
+        // Update the maximum for the number of tracks since it's depending on
+        // the current track. The current value in the Number-of-tracks-spinner
+        // has to be updated, since otherwise the displayed (and used) value
+        // can be bigger than the maximum. (Might be a TODO to fix this)
+        int max_num_tracks = m_group_name=="all" 
+                           ? track_manager->getNumberOfRaceTracks()
+                           : track_manager->getTracksInGroup(m_group_name).size();
+        m_num_tracks_spinner->setMax(max_num_tracks);
+        int number_of_tracks = std::min(max_num_tracks,
+                                        m_num_tracks_spinner->getValue());
+        if (m_num_tracks_spinner->getValue() > max_num_tracks)
+            m_num_tracks_spinner->setValue(max_num_tracks);
+        // Create a new (i.e. with new tracks) random gp, since the old
+        // tracks might not all belong to the newly selected group.
+        
+        m_gp.createRandomGP(m_num_tracks_spinner->getValue(), m_group_name,
+                            getReverse(),  /*new_tracks*/true);
+        addTracks();
+    }
+    else if (name=="track-spinner")
+    {
+        m_gp.changeTrackNumber(m_num_tracks_spinner->getValue(), m_group_name);
+        addTracks();
+    }
+    else if (name=="reverse-spinner")
+    {
+        m_gp.changeReverse(getReverse());
+    }
+    else if(name=="back")
+    {
+        StateManager::get()->escapePressed();
+    }
+
+}   // eventCallback
+
+// ----------------------------------------------------------------------------
+/** Called every update. Used to cycle the screenshots.
+ *  \param dt Time step size.
+ */
+void GPInfoScreen::onUpdate(float dt)
+{
+    if (dt == 0)
+        return; // if nothing changed, return right now
+
+    m_curr_time += dt;
+    int frame_after = (int)(m_curr_time / 1.5f);
+
+    const std::vector<std::string> tracks = m_gp.getTrackNames();
+    if (frame_after >= (int)tracks.size())
+    {
+        frame_after = 0;
+        m_curr_time = 0;
+    }
+
+    Track* track = track_manager->getTrack(tracks[frame_after]);
+    std::string file = track->getScreenshotFile();
+    m_screenshot_widget->setImage(file, IconButtonWidget::ICON_PATH_TYPE_ABSOLUTE);
+    m_screenshot_widget->m_properties[PROP_ICON] = file;
+}   // onUpdate
\ No newline at end of file
diff --git a/src/states_screens/gp_info_screen.hpp b/src/states_screens/gp_info_screen.hpp
new file mode 100644
index 000000000..8e72b89b2
--- /dev/null
+++ b/src/states_screens/gp_info_screen.hpp
@@ -0,0 +1,88 @@
+//  SuperTuxKart - a fun racing game with go-kart
+//  Copyright (C) 2009-2014 Marianne Gagnon
+//                2014      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.
+
+
+#ifndef HEADER_GP_INFO_SCREEN_HPP
+#define HEADER_GP_INFO_SCREEN_HPP
+
+#include "guiengine/screen.hpp"
+#include "race/grand_prix_data.hpp"
+
+class GrandPrixData;
+
+namespace GUIEngine
+{
+    class IconButtonWidget;
+    class SpinnerWidget;
+}
+
+/**
+ * \brief Dialog that shows information about a specific grand prix
+ * \ingroup states_screens
+ */
+class GPInfoScreen : public GUIEngine::Screen,
+                     public GUIEngine::ScreenSingleton<GPInfoScreen>
+{
+private:
+    /** Spinner for the different track groups. */
+    GUIEngine::SpinnerWidget *m_group_spinner;
+
+    /** Spinner for reverse mode. */
+    GUIEngine::SpinnerWidget *m_reverse_spinner;
+
+    /** Spinner for number of tracks (in case of random GP). */
+    GUIEngine::SpinnerWidget *m_num_tracks_spinner;
+
+    /** The currently selected group name. */
+    std::string m_group_name;
+
+protected: // Necessary for RandomGPInfoScreen
+    GUIEngine::IconButtonWidget* m_screenshot_widget;
+    float m_curr_time;
+
+    /** The grand prix data. */
+    GrandPrixData m_gp;
+
+    /** \brief display all the tracks according to the current gp
+     * For a normal gp info dialog, it just creates a label for every track.
+     * But with a random gp info dialog, it tries to reuse as many
+     * labels as possible by just changing their text. */
+    void addTracks();
+    void addScreenshot();
+    void updateRandomGP();
+    GrandPrixData::GPReverseType getReverse() const;
+
+public:
+    GPInfoScreen();
+    /** Places the focus back on the selected GP, in the case that the dialog
+     * was cancelled and we're returning to the track selection screen */
+    virtual ~GPInfoScreen() {}
+
+    void onEnterPressedInternal();
+    virtual void eventCallback(GUIEngine::Widget *, const std::string &name,
+                               const int player_id);
+    virtual void loadedFromFile() OVERRIDE;
+    virtual void init() OVERRIDE;
+    virtual void beforeAddingWidget() OVERRIDE;
+
+    virtual void onUpdate(float dt);
+
+    void setGP(const std::string &gp_ident);
+};   // GPInfoScreen
+
+#endif
diff --git a/src/states_screens/grand_prix_cutscene.cpp b/src/states_screens/grand_prix_cutscene.cpp
new file mode 100644
index 000000000..850852aec
--- /dev/null
+++ b/src/states_screens/grand_prix_cutscene.cpp
@@ -0,0 +1,91 @@
+//  SuperTuxKart - a fun racing game with go-kart
+//  Copyright (C) 2014 konstin
+//
+//  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 "guiengine/scalable_font.hpp"
+#include "guiengine/widgets/button_widget.hpp"
+#include "modes/cutscene_world.hpp"
+#include "race/grand_prix_data.hpp"
+#include "race/grand_prix_manager.hpp"
+#include "race/race_manager.hpp"
+#include "states_screens/grand_prix_cutscene.hpp"
+#include "tracks/track_manager.hpp"
+
+#include <string>
+#include <vector>
+
+typedef GUIEngine::ButtonWidget Button;
+
+/** A Button to save the GP if it was a random GP */
+void GrandPrixCutscene::saveGPButton()
+{
+    if (race_manager->getGrandPrix().getId() != "random")
+        getWidget<Button>("save")->setVisible(false);
+}   // saveGPButton
+
+// ----------------------------------------------------------------------------
+
+/** \brief Creates a new GP with the same content as the current and saves it
+ *  The GP that the race_manager provides can't be used because we need some
+ *  functions and settings that the GP manager only gives us through
+ *  createNewGP(). */
+void GrandPrixCutscene::onNewGPWithName(const irr::core::stringw& name)
+{
+    // create a new GP with the correct filename and a unique id
+    GrandPrixData* gp = grand_prix_manager->createNewGP(name);
+    const GrandPrixData current_gp = race_manager->getGrandPrix();
+    std::vector<std::string> tracks  = current_gp.getTrackNames();
+    std::vector<int>         laps    = current_gp.getLaps();
+    std::vector<bool>        reverse = current_gp.getReverse();
+    for (unsigned int i = 0; i < laps.size(); i++)
+        gp->addTrack(track_manager->getTrack(tracks[i]), laps[i], reverse[i]);
+    gp->writeToFile();
+
+    // Avoid double-save which can have bad side-effects
+    getWidget<Button>("save")->setVisible(false);
+}   // onNewGPWithName
+
+// ----------------------------------------------------------------------------
+
+void GrandPrixCutscene::eventCallback(GUIEngine::Widget* widget,
+                                      const std::string& name,
+                                      const int playerID)
+{
+    if (name == "continue")
+    {
+        ((CutsceneWorld*)World::getWorld())->abortCutscene();
+    }
+    else if (name == "save")
+    {
+        new EnterGPNameDialog(this, 0.5f, 0.4f);
+    }
+}   // eventCallback
+
+// ----------------------------------------------------------------------------
+
+bool GrandPrixCutscene::onEscapePressed()
+{
+    ((CutsceneWorld*)World::getWorld())->abortCutscene();
+    return false;
+}   // onEscapePressed
+
+// ----------------------------------------------------------------------------
+
+void GrandPrixCutscene::tearDown()
+{
+    Screen::tearDown();
+}   // tearDown
+
diff --git a/src/states_screens/grand_prix_cutscene.hpp b/src/states_screens/grand_prix_cutscene.hpp
new file mode 100644
index 000000000..d85834b7b
--- /dev/null
+++ b/src/states_screens/grand_prix_cutscene.hpp
@@ -0,0 +1,48 @@
+//  SuperTuxKart - a fun racing game with go-kart
+//  Copyright (C) 2014 konstin
+//
+//  This program is free software; you can redistribute it and/or
+//  modify it under the terms of the GNU General Public License
+//  as published by the Free Software Foundation; either version 3
+//  of the License, or (at your option) any later version.
+//
+//  This program is distributed in the hope that it will be useful,
+//  but WITHOUT ANY WARRANTY; without even the implied warranty of
+//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+//  GNU General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+#ifndef HEADER_GRAND_PRIX_CUTSCENE_HPP
+#define HEADER_GRAND_PRIX_CUTSCENE_HPP
+
+#include "guiengine/screen.hpp"
+#include "guiengine/widgets/button_widget.hpp"
+#include "race/race_manager.hpp"
+#include "states_screens/dialogs/enter_gp_name_dialog.hpp"
+
+#include <string>
+
+class GrandPrixCutscene: public GUIEngine::CutsceneScreen,
+                         public EnterGPNameDialog::INewGPListener
+{
+    friend class GUIEngine::ScreenSingleton<GrandPrixCutscene>;
+public:
+    GrandPrixCutscene(const char * filename) : CutsceneScreen(filename) {}
+protected:
+    void saveGPButton();
+
+    /** implement callback from INewGPListener */
+    void onNewGPWithName(const irr::core::stringw& name);
+
+    // implement callbacks from parent class GUIEngine::Screen
+    void eventCallback(GUIEngine::Widget* widget,
+                       const std::string& name,
+                       const int playerID) OVERRIDE;
+    bool onEscapePressed() OVERRIDE;
+    void tearDown() OVERRIDE;
+};
+
+#endif
diff --git a/src/states_screens/grand_prix_editor_screen.cpp b/src/states_screens/grand_prix_editor_screen.cpp
index 784408889..1552835c1 100644
--- a/src/states_screens/grand_prix_editor_screen.cpp
+++ b/src/states_screens/grand_prix_editor_screen.cpp
@@ -29,7 +29,6 @@
 #include "states_screens/edit_gp_screen.hpp"
 #include "states_screens/dialogs/enter_gp_name_dialog.hpp"
 #include "states_screens/dialogs/gp_info_dialog.hpp"
-#include "states_screens/dialogs/track_info_dialog.hpp"
 #include "tracks/track.hpp"
 #include "tracks/track_manager.hpp"
 #include "utils/translation.hpp"
@@ -205,6 +204,9 @@ void GrandPrixEditorScreen::loadGPList()
     // Reset GP list everytime (accounts for locking changes, etc.)
     gplist_widget->clearItems();
 
+    // ensures that no GP and no track is NULL
+    grand_prix_manager->checkConsistency();
+
     // Build GP list
     for (unsigned int i = 0; i < grand_prix_manager->getNumberOfGrandPrix(); i++)
     {
@@ -215,23 +217,7 @@ void GrandPrixEditorScreen::loadGPList()
         for (unsigned int t=0; t<tracks.size(); t++)
         {
             Track* track = track_manager->getTrack(tracks[t]);
-            if (track == NULL)
-            {
-                Log::warn("GrandPrixEditor",
-                    "Grand Prix '%s' refers to track '%s', which does not exist\n",
-                    gp->getId().c_str(), tracks[t].c_str());
-            }
-            else
-            {
-                sshot_files.push_back(track->getScreenshotFile());
-            }
-        }
-        if (sshot_files.size() == 0)
-        {
-            Log::warn("GrandPrixEditor",
-                "Grand Prix '%s' does not contain any valid track\n",
-                gp->getId().c_str());
-            sshot_files.push_back("gui/main_help.png");
+            sshot_files.push_back(track->getScreenshotFile());
         }
 
         gplist_widget->addAnimatedItem(translations->fribidize(gp->getName()), gp->getId(),
diff --git a/src/states_screens/grand_prix_editor_screen.hpp b/src/states_screens/grand_prix_editor_screen.hpp
index 4d763846b..318acf0c0 100644
--- a/src/states_screens/grand_prix_editor_screen.hpp
+++ b/src/states_screens/grand_prix_editor_screen.hpp
@@ -18,8 +18,8 @@
 #ifndef HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP
 #define HEADER_GRAND_PRIX_EDITOR_SCREEN_HPP
 
-#include "dialogs/enter_gp_name_dialog.hpp"
 #include "guiengine/screen.hpp"
+#include "states_screens/dialogs/enter_gp_name_dialog.hpp"
 #include "states_screens/dialogs/message_dialog.hpp"
 
 
diff --git a/src/states_screens/grand_prix_lose.cpp b/src/states_screens/grand_prix_lose.cpp
index 27673542b..be86e77e9 100644
--- a/src/states_screens/grand_prix_lose.cpp
+++ b/src/states_screens/grand_prix_lose.cpp
@@ -26,6 +26,7 @@
 #include "graphics/irr_driver.hpp"
 #include "guiengine/engine.hpp"
 #include "guiengine/scalable_font.hpp"
+#include "guiengine/widgets/button_widget.hpp"
 #include "guiengine/widgets/label_widget.hpp"
 #include "io/file_manager.hpp"
 #include "items/item_manager.hpp"
@@ -34,6 +35,7 @@
 #include "modes/cutscene_world.hpp"
 #include "modes/overworld.hpp"
 #include "modes/world.hpp"
+#include "race/race_manager.hpp"
 #include "states_screens/feature_unlocked.hpp"
 #include "states_screens/main_menu_screen.hpp"
 #include "states_screens/state_manager.hpp"
@@ -47,7 +49,6 @@
 #include <ICameraSceneNode.h>
 #include <ILightSceneNode.h>
 #include <IMeshSceneNode.h>
-//#include <iostream>
 
 using namespace irr::core;
 using namespace irr::gui;
@@ -82,42 +83,20 @@ DEFINE_SCREEN_SINGLETON( GrandPrixLose );
 
 // -------------------------------------------------------------------------------------
 
-GrandPrixLose::GrandPrixLose() : CutsceneScreen("grand_prix_lose.stkgui")
-{
-}   // GrandPrixLose
-
-// -------------------------------------------------------------------------------------
-
 void GrandPrixLose::onCutsceneEnd()
 {
-    TrackObjectManager* tobjman = World::getWorld()->getTrack()->getTrackObjectManager();
-    if (m_kart_node[0] != NULL)
-        m_kart_node[0]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
-    if (m_kart_node[1] != NULL)
-        m_kart_node[1]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
-    if (m_kart_node[2] != NULL)
-        m_kart_node[2]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
-    if (m_kart_node[3] != NULL)
-        m_kart_node[3]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
+    for (int i = 0; i < 4; i++)
+    {
+        if (m_kart_node[i] != NULL)
+            m_kart_node[i]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
+        m_kart_node[i] = NULL;
+    }
 
     for (unsigned int i = 0; i<m_all_kart_models.size(); i++)
         delete m_all_kart_models[i];
 
     m_all_kart_models.clear();
-
-    m_kart_node[0] = NULL;
-    m_kart_node[1] = NULL;
-    m_kart_node[2] = NULL;
-    m_kart_node[3] = NULL;
-}
-
-// -------------------------------------------------------------------------------------
-
-bool GrandPrixLose::onEscapePressed()
-{
-    ((CutsceneWorld*)World::getWorld())->abortCutscene();
-    return false;
-}
+}   // onCutsceneEnd
 
 // -------------------------------------------------------------------------------------
 
@@ -142,19 +121,14 @@ void GrandPrixLose::init()
 
     World::getWorld()->setPhase(WorldStatus::RACE_PHASE);
 
+    saveGPButton();
+
     m_phase = 1;
     m_global_time = 0.0f;
 }   // init
 
 // -------------------------------------------------------------------------------------
 
-void GrandPrixLose::tearDown()
-{
-    Screen::tearDown();
-}   // tearDown
-
-// -------------------------------------------------------------------------------------
-
 void GrandPrixLose::onUpdate(float dt)
 {
     m_global_time += dt;
@@ -177,7 +151,7 @@ void GrandPrixLose::onUpdate(float dt)
             }
         }
     }
-    
+
     // ---- title
     const int w = irr_driver->getFrameSize().Width;
     const int h = irr_driver->getFrameSize().Height;
@@ -194,27 +168,13 @@ void GrandPrixLose::onUpdate(float dt)
 
 // -------------------------------------------------------------------------------------
 
-void GrandPrixLose::eventCallback(GUIEngine::Widget* widget,
-                                            const std::string& name,
-                                            const int playerID)
-{
-    if (name == "continue")
-    {
-        ((CutsceneWorld*)World::getWorld())->abortCutscene();
-    }
-}   // eventCallback
-
-// -------------------------------------------------------------------------------------
-
 void GrandPrixLose::setKarts(std::vector<std::string> ident_arg)
 {
     TrackObjectManager* tobjman = World::getWorld()->getTrack()->getTrackObjectManager();
 
     assert(ident_arg.size() > 0);
     if ((int)ident_arg.size() > MAX_KART_COUNT)
-    {
         ident_arg.resize(MAX_KART_COUNT);
-    }
 
     // (there is at least one kart so kart node 0 is sure to be set)
     m_kart_node[1] = NULL;
@@ -252,7 +212,8 @@ void GrandPrixLose::setKarts(std::vector<std::string> ident_arg)
         }
         else
         {
-            Log::warn("GrandPrixLose", "Could not find a kart named '%s'.", ident_arg[n].c_str());
+            Log::warn("GrandPrixLose", "A kart named '%s' could not be found\n",
+                      ident_arg[n].c_str());
             m_kart_node[n] = NULL;
         } // if kart != NULL
     }
diff --git a/src/states_screens/grand_prix_lose.hpp b/src/states_screens/grand_prix_lose.hpp
index bf30dfc70..a1cd0d6a4 100644
--- a/src/states_screens/grand_prix_lose.hpp
+++ b/src/states_screens/grand_prix_lose.hpp
@@ -21,6 +21,7 @@
 
 #include "guiengine/screen.hpp"
 #include "karts/kart_model.hpp"
+#include "states_screens/grand_prix_cutscene.hpp"
 
 #include <vector>
 #include <string>
@@ -33,11 +34,13 @@ class TrackObject;
   * \brief Screen shown at the end of a Grand Prix
   * \ingroup states_screens
   */
-class GrandPrixLose : public GUIEngine::CutsceneScreen, public GUIEngine::ScreenSingleton<GrandPrixLose>
+class GrandPrixLose :
+    public GrandPrixCutscene,
+    public GUIEngine::ScreenSingleton<GrandPrixLose>
 {
     friend class GUIEngine::ScreenSingleton<GrandPrixLose>;
 
-    GrandPrixLose();
+    GrandPrixLose(): GrandPrixCutscene("grand_prix_lose.stkgui") {};
 
     /** Global evolution of time */
     float m_global_time;
@@ -52,27 +55,11 @@ class GrandPrixLose : public GUIEngine::CutsceneScreen, public GUIEngine::Screen
     float m_kart_x, m_kart_y, m_kart_z;
 
 public:
-
-    virtual void onCutsceneEnd() OVERRIDE;
-
-    virtual bool onEscapePressed() OVERRIDE;
-    
-    /** \brief implement callback from parent class GUIEngine::Screen */
-    virtual void loadedFromFile() OVERRIDE;
-
-    /** \brief implement optional callback from parent class GUIEngine::Screen */
-    void onUpdate(float dt) OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
+    // implement callbacks from parent class GUIEngine::Screen
     void init() OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
-    void tearDown() OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
-    void eventCallback(GUIEngine::Widget* widget, const std::string& name,
-                       const int playerID) OVERRIDE;
-
+    void loadedFromFile() OVERRIDE;
+    void onCutsceneEnd() OVERRIDE;
+    void onUpdate(float dt) OVERRIDE;
     /** \brief set which karts lost this GP */
     void setKarts(std::vector<std::string> ident);
 };
diff --git a/src/states_screens/grand_prix_win.cpp b/src/states_screens/grand_prix_win.cpp
index 97a0b835f..92e15dd6f 100644
--- a/src/states_screens/grand_prix_win.cpp
+++ b/src/states_screens/grand_prix_win.cpp
@@ -28,6 +28,7 @@
 #include "graphics/irr_driver.hpp"
 #include "guiengine/engine.hpp"
 #include "guiengine/scalable_font.hpp"
+#include "guiengine/widgets/button_widget.hpp"
 #include "guiengine/widgets/label_widget.hpp"
 #include "io/file_manager.hpp"
 #include "items/item_manager.hpp"
@@ -35,6 +36,7 @@
 #include "karts/kart_properties_manager.hpp"
 #include "modes/cutscene_world.hpp"
 #include "modes/world.hpp"
+#include "race/race_manager.hpp"
 #include "states_screens/feature_unlocked.hpp"
 #include "states_screens/state_manager.hpp"
 #include "tracks/track.hpp"
@@ -66,26 +68,17 @@ DEFINE_SCREEN_SINGLETON( GrandPrixWin );
 
 // -------------------------------------------------------------------------------------
 
-GrandPrixWin::GrandPrixWin() : CutsceneScreen("grand_prix_win.stkgui")
+GrandPrixWin::GrandPrixWin() : GrandPrixCutscene("grand_prix_win.stkgui")
 {
-    m_kart_node[0] = NULL;
-    m_kart_node[1] = NULL;
-    m_kart_node[2] = NULL;
-
-    m_podium_steps[0] = NULL;
-    m_podium_steps[1] = NULL;
-    m_podium_steps[2] = NULL;
-
+    for (int i = 0; i < 3; i++)
+    {
+        m_kart_node[i] = NULL;
+        m_podium_steps[i] = NULL;
+    }
 }   // GrandPrixWin
 
 // -------------------------------------------------------------------------------------
 
-GrandPrixWin::~GrandPrixWin()
-{
-}
-
-// -------------------------------------------------------------------------------------
-
 void GrandPrixWin::onCutsceneEnd()
 {
     for (unsigned int i = 0; i<m_all_kart_models.size(); i++)
@@ -99,31 +92,17 @@ void GrandPrixWin::onCutsceneEnd()
         m_unlocked_label = NULL;
     }
 
-    TrackObjectManager* tobjman = World::getWorld()->getTrack()->getTrackObjectManager();
-    if (m_kart_node[0] != NULL)
-        m_kart_node[0]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
-    if (m_kart_node[1] != NULL)
-        m_kart_node[1]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
-    if (m_kart_node[2] != NULL)
-        m_kart_node[2]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
-
-    m_kart_node[0] = NULL;
-    m_kart_node[1] = NULL;
-    m_kart_node[2] = NULL;
-
-    m_podium_steps[0] = NULL;
-    m_podium_steps[1] = NULL;
-    m_podium_steps[2] = NULL;
+    for (int i = 0; i < 3; i++)
+    {
+        if (m_kart_node[i] != NULL)
+            m_kart_node[i]->getPresentation<TrackObjectPresentationSceneNode>()->getNode()->remove();
+        m_kart_node[i] = NULL;
+        m_podium_steps[i] = NULL;
+    }
 }
 
 // -------------------------------------------------------------------------------------
 
-void GrandPrixWin::loadedFromFile()
-{
-}   // loadedFromFile
-
-// -------------------------------------------------------------------------------------
-
 void GrandPrixWin::init()
 {
     std::vector<std::string> parts;
@@ -136,6 +115,7 @@ void GrandPrixWin::init()
     World::getWorld()->setPhase(WorldStatus::RACE_PHASE);
 
 
+    saveGPButton();
     if (PlayerManager::getCurrentPlayer()->getRecentlyCompletedChallenges().size() > 0)
     {
         const core::dimension2d<u32>& frame_size = GUIEngine::getDriver()->getCurrentRenderTargetSize();
@@ -195,21 +175,6 @@ void GrandPrixWin::init()
 
 // -------------------------------------------------------------------------------------
 
-bool GrandPrixWin::onEscapePressed()
-{
-    ((CutsceneWorld*)World::getWorld())->abortCutscene();
-    return false;
-}
-
-// -------------------------------------------------------------------------------------
-
-void GrandPrixWin::tearDown()
-{
-    Screen::tearDown();
-}   // tearDown
-
-// -------------------------------------------------------------------------------------
-
 void GrandPrixWin::onUpdate(float dt)
 {
     m_global_time += dt;
@@ -228,17 +193,11 @@ void GrandPrixWin::onUpdate(float dt)
                 if (fabsf(m_kart_z[k] - KARTS_DEST_Z) > dt)
                 {
                     if (m_kart_z[k] < KARTS_DEST_Z - dt)
-                    {
                         m_kart_z[k] += dt;
-                    }
                     else if (m_kart_z[k] > KARTS_DEST_Z + dt)
-                    {
                         m_kart_z[k] -= dt;
-                    }
                     else
-                    {
                         m_kart_z[k] = KARTS_DEST_Z;
-                    }
                     karts_not_yet_done++;
                 }
 
@@ -250,9 +209,7 @@ void GrandPrixWin::onUpdate(float dt)
         } // end for
 
         if (karts_not_yet_done == 0)
-        {
             m_phase = 2;
-        }
     }
 
     // ---- Karts Rotate
@@ -281,7 +238,8 @@ void GrandPrixWin::onUpdate(float dt)
             }
         } // end for
 
-        if (karts_not_yet_done == 0) m_phase = 3;
+        if (karts_not_yet_done == 0)
+            m_phase = 3;
     }
 
     // ---- Podium Rises
@@ -308,7 +266,6 @@ void GrandPrixWin::onUpdate(float dt)
                 }
             }
         } // end for
-
     }
 
 
@@ -325,17 +282,6 @@ void GrandPrixWin::onUpdate(float dt)
                                     true/* center h */, true /* center v */ );
 }   // onUpdate
 
-// -------------------------------------------------------------------------------------
-
-void GrandPrixWin::eventCallback(GUIEngine::Widget* widget,
-                                            const std::string& name,
-                                            const int playerID)
-{
-    if (name == "continue")
-    {
-        ((CutsceneWorld*)World::getWorld())->abortCutscene();
-    }
-}   // eventCallback
 
 // -------------------------------------------------------------------------------------
 
@@ -385,17 +331,11 @@ void GrandPrixWin::setKarts(const std::string idents_arg[3])
         if (meshPresentation != NULL)
         {
             if (meshPresentation->getModelFile() == "gpwin_podium1.b3d")
-            {
                 m_podium_steps[0] = currObj;
-            }
             else if (meshPresentation->getModelFile() == "gpwin_podium2.b3d")
-            {
                 m_podium_steps[1] = currObj;
-            }
             else if (meshPresentation->getModelFile() == "gpwin_podium3.b3d")
-            {
                 m_podium_steps[2] = currObj;
-            }
         }
     }
 
diff --git a/src/states_screens/grand_prix_win.hpp b/src/states_screens/grand_prix_win.hpp
index a702f134d..06e999d12 100644
--- a/src/states_screens/grand_prix_win.hpp
+++ b/src/states_screens/grand_prix_win.hpp
@@ -22,6 +22,7 @@
 #include "audio/sfx_base.hpp"
 #include "guiengine/screen.hpp"
 #include "karts/kart_model.hpp"
+#include "states_screens/grand_prix_cutscene.hpp"
 
 namespace irr { namespace scene { class ISceneNode; class ICameraSceneNode; class ILightSceneNode; class IMeshSceneNode; } }
 namespace GUIEngine { class LabelWidget; }
@@ -32,13 +33,15 @@ class TrackObject;
   * \brief Screen shown at the end of a Grand Prix
   * \ingroup states_screens
   */
-class GrandPrixWin : public GUIEngine::CutsceneScreen, public GUIEngine::ScreenSingleton<GrandPrixWin>
+class GrandPrixWin :
+    public GrandPrixCutscene,
+    public GUIEngine::ScreenSingleton<GrandPrixWin>
 {
     friend class GUIEngine::ScreenSingleton<GrandPrixWin>;
 
     GrandPrixWin();
 
-    virtual ~GrandPrixWin();
+    virtual ~GrandPrixWin() {};
 
     /** Global evolution of time */
     double m_global_time;
@@ -47,41 +50,22 @@ class GrandPrixWin : public GUIEngine::CutsceneScreen, public GUIEngine::ScreenS
 
     TrackObject* m_kart_node[3];
 
-    //irr::scene::IMeshSceneNode* m_podium_step[3];
-    //irr::scene::ISceneNode* m_kart_node[3];
-
     /** A copy of the kart model for each kart used. */
     std::vector<KartModel*> m_all_kart_models;
 
     GUIEngine::LabelWidget* m_unlocked_label;
 
     int m_phase;
-    
+
     float m_kart_x[3], m_kart_y[3], m_kart_z[3];
-    //float m_podium_x[3], m_podium_z[3];
     float m_kart_rotation[3];
 
 public:
-
-    virtual void onCutsceneEnd() OVERRIDE;
-
-    virtual bool onEscapePressed() OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
-    virtual void loadedFromFile() OVERRIDE;
-
-    /** \brief implement optional callback from parent class GUIEngine::Screen */
-    void onUpdate(float dt) OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
+    // implement callbacks from parent class GUIEngine::Screen
     void init() OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
-    void tearDown() OVERRIDE;
-
-    /** \brief implement callback from parent class GUIEngine::Screen */
-    void eventCallback(GUIEngine::Widget* widget, const std::string& name,
-                       const int playerID) OVERRIDE;
+    void loadedFromFile() OVERRIDE {};
+    void onCutsceneEnd() OVERRIDE;
+    void onUpdate(float dt) OVERRIDE;
 
     /** \pre must be called after pushing the screen, but before onUpdate had the chance to be invoked */
     void setKarts(const std::string idents[3]);
diff --git a/src/states_screens/race_setup_screen.cpp b/src/states_screens/race_setup_screen.cpp
index 9e4a415ae..1553c6d42 100644
--- a/src/states_screens/race_setup_screen.cpp
+++ b/src/states_screens/race_setup_screen.cpp
@@ -142,12 +142,6 @@ void RaceSetupScreen::eventCallback(Widget* widget, const std::string& name, con
             unlock_manager->playLockSound();
         }
     }
-    else if (name == "aikartamount")
-    {
-        SpinnerWidget* w = dynamic_cast<SpinnerWidget*>(widget);
-        race_manager->setNumKarts( race_manager->getNumLocalPlayers() + w->getValue() );
-        UserConfigParams::m_num_karts = race_manager->getNumLocalPlayers() + w->getValue();
-    }
     else if (name == "back")
     {
         StateManager::get()->escapePressed();
@@ -209,16 +203,6 @@ void RaceSetupScreen::onGameModeChanged()
     RaceManager::MinorRaceModeType gamemode =
         RaceManager::getModeIDFromInternalName(gamemode_str);
 
-    // deactivate the AI karts count widget for modes for which we have no AI
-    SpinnerWidget* kartamount = getWidget<SpinnerWidget>("aikartamount");
-    if (!RaceManager::hasAI(gamemode))
-    {
-        kartamount->setDeactivated();
-    }
-    else
-    {
-        kartamount->setActivated();
-    }
 }   // onGameModeChanged
 
 // -----------------------------------------------------------------------------
@@ -240,17 +224,6 @@ void RaceSetupScreen::init()
         w->setSelection( UserConfigParams::m_difficulty, PLAYER_ID_GAME_MASTER );
     }
 
-    SpinnerWidget* kartamount = getWidget<SpinnerWidget>("aikartamount");
-    kartamount->setActivated();
-
-    // Avoid negative numbers (which can happen if e.g. the number of karts
-    // in a previous race was lower than the number of players now.
-    int num_ai = UserConfigParams::m_num_karts-race_manager->getNumLocalPlayers();
-    if(num_ai<0) num_ai = 0;
-    kartamount->setValue(num_ai);
-    kartamount->setMax(stk_config->m_max_karts - race_manager->getNumLocalPlayers() );
-    race_manager->setNumKarts(num_ai +  race_manager->getNumLocalPlayers());
-
     DynamicRibbonWidget* w2 = getWidget<DynamicRibbonWidget>("gamemode");
     assert( w2 != NULL );
     w2->clearItems();
diff --git a/src/states_screens/register_screen.cpp b/src/states_screens/register_screen.cpp
index 067cfcf3b..8fad53ed4 100644
--- a/src/states_screens/register_screen.cpp
+++ b/src/states_screens/register_screen.cpp
@@ -224,7 +224,7 @@ void RegisterScreen::doRegister()
     }
     else if (username.size() < 3 || username.size() > 30)
     {
-        m_info_widget->setText(_("Username has to be between 4 and 30 characters long!"), false);
+        m_info_widget->setText(_("Online username has to be between 3 and 30 characters long!"), false);
     }
     else if (password.size() < 8 || password.size() > 30)
     {
diff --git a/src/states_screens/dialogs/track_info_dialog.cpp b/src/states_screens/track_info_screen.cpp
similarity index 52%
rename from src/states_screens/dialogs/track_info_dialog.cpp
rename to src/states_screens/track_info_screen.cpp
index e7d7025f2..0e8914283 100644
--- a/src/states_screens/dialogs/track_info_dialog.cpp
+++ b/src/states_screens/track_info_screen.cpp
@@ -1,5 +1,6 @@
 //  SuperTuxKart - a fun racing game with go-kart
-//  Copyright (C) 2009-2013 Marianne Gagnon
+//  Copyright (C) 2009-2014 Marianne Gagnon
+//                2014      Joerg Henrichs
 //
 //  This program is free software; you can redistribute it and/or
 //  modify it under the terms of the GNU General Public License
@@ -15,7 +16,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 "states_screens/dialogs/track_info_dialog.hpp"
+#include "states_screens/track_info_screen.hpp"
 
 #include "challenges/unlock_manager.hpp"
 #include "config/player_manager.hpp"
@@ -24,8 +25,10 @@
 #include "guiengine/engine.hpp"
 #include "guiengine/screen.hpp"
 #include "guiengine/widgets/button_widget.hpp"
+#include "guiengine/widgets/check_box_widget.hpp"
 #include "guiengine/widgets/icon_button_widget.hpp"
 #include "guiengine/widgets/label_widget.hpp"
+#include "guiengine/widgets/ribbon_widget.hpp"
 #include "guiengine/widgets/spinner_widget.hpp"
 #include "io/file_manager.hpp"
 #include "karts/kart_properties.hpp"
@@ -49,80 +52,121 @@ using namespace irr::video;
 using namespace irr::core;
 using namespace GUIEngine;
 
-// ------------------------------------------------------------------------------------------------------
+DEFINE_SCREEN_SINGLETON( TrackInfoScreen );
 
-TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::string& trackIdent,
-                                 const irr::core::stringw& trackName, ITexture* screenshot,
-                                 const float w, const float h) : ModalDialog(w, h)
+// ----------------------------------------------------------------------------
+/** Constructor, which loads the corresponding track_info.stkgui file. */
+TrackInfoScreen::TrackInfoScreen()
+          : Screen("track_info.stkgui")
 {
-    loadFromFile("track_info_dialog.stkgui");
+    m_screenshot = NULL;
+}   // TrackInfoScreen
 
+// ----------------------------------------------------------------------------
+/* Saves some often used pointers. */
+void TrackInfoScreen::loadedFromFile()
+{
+    m_lap_spinner     = getWidget<SpinnerWidget>("lap-spinner");
+    m_ai_kart_spinner = getWidget<SpinnerWidget>("ai-spinner");
+    m_reverse         = getWidget<CheckBoxWidget>("reverse");
+}   // loadedFromFile
+
+// ----------------------------------------------------------------------------
+void TrackInfoScreen::setTrack(Track *track)
+{
+    m_track = track;
+}   // setTrack
+
+// ----------------------------------------------------------------------------
+/** Initialised the display. The previous screen has to setup m_track before
+ *  pushing this screen using TrackInfoScreen::getInstance()->setTrack().
+ */
+void TrackInfoScreen::init()
+{
     const bool has_laps       = race_manager->modeHasLaps();
     const bool has_highscores = race_manager->modeHasHighscores();
 
-    m_track_ident = trackIdent;
-    m_ribbon_item = ribbonItem;
-
-    getWidget<LabelWidget>("name")->setText(trackName.c_str(), false);
-
-    Track* track = track_manager->getTrack(trackIdent);
-    //I18N: when showing who is the author of track '%s' (place %s where the name of the author should appear)
-    getWidget<LabelWidget>("author")->setText( _("Track by %s", track->getDesigner().c_str()), false );
+    getWidget<LabelWidget>("name")->setText(m_track->getName(), false);
 
+    //I18N: when showing who is the author of track '%s'
+    //I18N: (place %s where the name of the author should appear)
+    getWidget<LabelWidget>("author")->setText( _("Track by %s", m_track->getDesigner()),
+                                               false );
 
     // ---- Track screenshot
     Widget* screenshot_div = getWidget("screenshot_div");
-    IconButtonWidget* screenshotWidget = new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
-                                                              false /* tab stop */, false /* focusable */);
-    // images are saved squared, but must be stretched to 4:
-    screenshotWidget->setCustomAspectRatio(4.0f / 3.0f);
+    if(!m_screenshot)
+    {
+        m_screenshot =
+            new IconButtonWidget(IconButtonWidget::SCALE_MODE_KEEP_CUSTOM_ASPECT_RATIO,
+                                 false /* tab stop */, false /* focusable */);
+        m_screenshot->setCustomAspectRatio(4.0f / 3.0f);
 
-    screenshotWidget->m_x = screenshot_div->m_x;
-    screenshotWidget->m_y = screenshot_div->m_y;
-    screenshotWidget->m_w = screenshot_div->m_w;
-    screenshotWidget->m_h = screenshot_div->m_h;
+        m_screenshot->m_x = screenshot_div->m_x;
+        m_screenshot->m_y = screenshot_div->m_y;
+        m_screenshot->m_w = screenshot_div->m_w;
+        m_screenshot->m_h = screenshot_div->m_h;
+        m_screenshot->add();
+        m_widgets.push_back(m_screenshot);
+    }
+    // images are saved squared, but must be stretched to 4:
 
     // temporary icon, will replace it just after (but it will be shown if the given icon is not found)
-    screenshotWidget->m_properties[PROP_ICON] = "gui/main_help.png";
-    screenshotWidget->setParent(m_irrlicht_window);
-    screenshotWidget->add();
+    m_screenshot->m_properties[PROP_ICON] = "gui/main_help.png";
 
+    ITexture* screenshot = irr_driver->getTexture(m_track->getScreenshotFile(),
+                                    "While loading screenshot for track '%s':",
+                                           m_track->getFilename()            );
     if (screenshot != NULL)
-        screenshotWidget->setImage(screenshot);
-    m_widgets.push_back(screenshotWidget);
+        m_screenshot->setImage(screenshot);
 
-
-    // ---- Lap count m_spinner
+    // Lap count m_lap_spinner
+    // -----------------------
+    m_lap_spinner->setVisible(has_laps);
+    getWidget<LabelWidget>("lap-text")->setVisible(has_laps);
     if (has_laps)
     {
-        m_spinner = getWidget<SpinnerWidget>("lapcountspinner");
         if (UserConfigParams::m_artist_debug_mode)
-            m_spinner->setMin(0);
-
-        m_spinner->setValue(track->getActualNumberOfLap());
-        race_manager->setNumLaps(m_spinner->getValue());
+            m_lap_spinner->setMin(0);
+        m_lap_spinner->setValue(m_track->getActualNumberOfLap());
+        race_manager->setNumLaps(m_lap_spinner->getValue());
     }
-    else
+
+    // Number of AIs
+    // -------------
+    const bool has_AI = race_manager->hasAI();
+    m_ai_kart_spinner->setVisible(has_AI);
+    getWidget<LabelWidget>("ai-text")->setVisible(has_AI);
+    if (has_AI)
     {
-        getWidget<SpinnerWidget>("lapcountspinner")->setVisible(false);
-        m_spinner = NULL;
-    }
+        m_ai_kart_spinner->setActivated();
 
+        // Avoid negative numbers (which can happen if e.g. the number of karts
+        // in a previous race was lower than the number of players now.
+        int num_ai = UserConfigParams::m_num_karts - race_manager->getNumLocalPlayers();
+        if (num_ai < 0) num_ai = 0;
+        m_ai_kart_spinner->setValue(num_ai);
+        race_manager->setNumKarts(num_ai + race_manager->getNumLocalPlayers());
+        m_ai_kart_spinner->setMax(stk_config->m_max_karts - race_manager->getNumLocalPlayers());
+        // A ftl reace needs at least three karts to make any sense
+        if(race_manager->getMinorMode()==RaceManager::MINOR_MODE_FOLLOW_LEADER)
+        {
+            m_ai_kart_spinner->setMin(3-race_manager->getNumLocalPlayers());
+        }
+        else
+            m_ai_kart_spinner->setMin(0);
+
+    }   // has_AI
 
     // Reverse track
-    const bool reverse_available = track->reverseAvailable() &&
+    // -------------
+    const bool reverse_available = m_track->reverseAvailable() &&
                race_manager->getMinorMode() != RaceManager::MINOR_MODE_EASTER_EGG;
+    m_reverse->setVisible(reverse_available);
+    getWidget<LabelWidget>("reverse-text")->setVisible(reverse_available);
     if (reverse_available)
     {
-        m_checkbox = getWidget<CheckBoxWidget>("reverse");
-        m_checkbox->setState(race_manager->getReverseTrack());
-    }
-    else
-    {
-        getWidget<CheckBoxWidget>("reverse")->setVisible(false);
-        getWidget<LabelWidget>("reverse-text")->setVisible(false);
-        m_checkbox = NULL;
-        race_manager->setReverseTrack(false);
+        m_reverse->setState(race_manager->getReverseTrack());
     }
 
     // ---- High Scores
@@ -150,27 +194,17 @@ TrackInfoDialog::TrackInfoDialog(const std::string& ribbonItem, const std::strin
         getWidget<LabelWidget>("highscore3")->setVisible(false);
     }
 
-    getWidget<ButtonWidget>("start")->setFocusForPlayer( PLAYER_ID_GAME_MASTER );
+}   // init
 
-}   // TrackInfoDialog
+// ----------------------------------------------------------------------------
 
-// ------------------------------------------------------------------------------------------------------
-
-TrackInfoDialog::~TrackInfoDialog()
+TrackInfoScreen::~TrackInfoScreen()
 {
-    // Place focus back on selected track, in case the dialog was cancelled and we're back to
-    // the track selection screen after
-    GUIEngine::Screen* curr_screen = GUIEngine::getCurrentScreen();
-    if (curr_screen->getName() == "tracks.stkgui")
-    {
-        ((TracksScreen*)curr_screen)->setFocusOnTrack(m_ribbon_item);
-    }
+}   // ~TrackInfoScreen
 
-}   // ~TrackInfoDialog
+// ----------------------------------------------------------------------------
 
-// ------------------------------------------------------------------------------------------------------
-
-void TrackInfoDialog::updateHighScores()
+void TrackInfoScreen::updateHighScores()
 {
     std::string game_mode_ident = RaceManager::getIdentOf( race_manager->getMinorMode() );
     const Highscores::HighscoreType type = "HST_" + game_mode_ident;
@@ -179,7 +213,7 @@ void TrackInfoDialog::updateHighScores()
         highscore_manager->getHighscores(type,
                                          race_manager->getNumberOfKarts(),
                                          race_manager->getDifficulty(),
-                                         m_track_ident,
+                                         m_track->getIdent(),
                                          race_manager->getNumLaps(),
                                          race_manager->getReverseTrack()  );
     const int amount = highscores->getNumberEntries();
@@ -226,43 +260,46 @@ void TrackInfoDialog::updateHighScores()
     }
 }   // updateHighScores
 
-// ------------------------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
 
-void TrackInfoDialog::onEnterPressedInternal()
+void TrackInfoScreen::onEnterPressedInternal()
 {
 
     // Create a copy of member variables we still need, since they will
     // not be accessible after dismiss:
-    const int num_laps = (m_spinner == NULL ? -1 : m_spinner->getValue());
-    const bool reverse_track = m_checkbox == NULL ? false
-                                                  : m_checkbox->getState();
-    track_manager->getTrack(m_track_ident)->setActualNumberOfLaps(num_laps);
+    const int num_laps = race_manager->modeHasLaps() ? m_lap_spinner->getValue() 
+                                                     : -1;
+    const bool reverse_track = m_reverse == NULL ? false
+                                                  : m_reverse->getState();
+    m_track->setActualNumberOfLaps(num_laps);
     race_manager->setReverseTrack(reverse_track);
-    std::string track_ident = m_track_ident;
+
     // Disable accidentally unlocking of a challenge
     PlayerManager::getCurrentPlayer()->setCurrentChallenge("");
 
-    ModalDialog::dismiss();
-    race_manager->startSingleRace(track_ident, num_laps, false);
+    race_manager->startSingleRace(m_track->getIdent(), num_laps, false);
 }   // onEnterPressedInternal
 
-// ------------------------------------------------------------------------------------------------------
-
-GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eventSource)
+// ----------------------------------------------------------------------------
+void TrackInfoScreen::eventCallback(Widget* widget, const std::string& name,
+                                   const int playerID)
 {
-    if (eventSource == "start" )
+    if (name == "buttons")
     {
-        onEnterPressedInternal();
-        return GUIEngine::EVENT_BLOCK;
+        const std::string &button = getWidget<GUIEngine::RibbonWidget>("buttons")
+                                  ->getSelectionIDString(PLAYER_ID_GAME_MASTER);
+        if(button=="start")
+            onEnterPressedInternal();
+        else if(button=="back")
+            StateManager::get()->escapePressed();
     }
-    else if (eventSource == "closePopup")
+    else if (name == "back")
     {
-        ModalDialog::dismiss();
-        return GUIEngine::EVENT_BLOCK;
+        StateManager::get()->escapePressed();
     }
-    else if (eventSource == "reverse")
+    else if (name == "reverse")
     {
-        race_manager->setReverseTrack(m_checkbox->getState());
+        race_manager->setReverseTrack(m_reverse->getState());
         // Makes sure the highscores get swapped when clicking the 'reverse'
         // checkbox.
         if (race_manager->modeHasHighscores())
@@ -270,16 +307,21 @@ GUIEngine::EventPropagation TrackInfoDialog::processEvent(const std::string& eve
             updateHighScores();
         }
     }
-    else if (eventSource == "lapcountspinner")
+    else if (name == "lap-spinner")
     {
-        assert(m_spinner != NULL);
-        const int num_laps = m_spinner->getValue();
+        assert(race_manager->modeHasLaps());
+        const int num_laps = m_lap_spinner->getValue();
         race_manager->setNumLaps(num_laps);
         UserConfigParams::m_num_laps = num_laps;
         updateHighScores();
     }
+    else if (name=="ai-spinner")
+    {
+        SpinnerWidget* w = dynamic_cast<SpinnerWidget*>(widget);
+        const int num_ai = m_ai_kart_spinner->getValue();
+        race_manager->setNumKarts( race_manager->getNumLocalPlayers() + num_ai );
+        UserConfigParams::m_num_karts = race_manager->getNumLocalPlayers() + num_ai;
+    }
+}   // eventCallback
 
-    return GUIEngine::EVENT_LET;
-}   // processEvent
-
-// ------------------------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
diff --git a/src/states_screens/dialogs/track_info_dialog.hpp b/src/states_screens/track_info_screen.hpp
similarity index 50%
rename from src/states_screens/dialogs/track_info_dialog.hpp
rename to src/states_screens/track_info_screen.hpp
index 3367ac5c2..00387bc60 100644
--- a/src/states_screens/dialogs/track_info_dialog.hpp
+++ b/src/states_screens/track_info_screen.hpp
@@ -1,5 +1,6 @@
 //  SuperTuxKart - a fun racing game with go-kart
-//  Copyright (C) 2009-2013 Marianne Gagnon
+//  Copyright (C) 2009-2014 Marianne Gagnon
+//                2014      Joerg Henrichs
 //
 //  This program is free software; you can redistribute it and/or
 //  modify it under the terms of the GNU General Public License
@@ -16,55 +17,65 @@
 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 
-#ifndef HEADER_TRACKINFO_DIALOG_HPP
-#define HEADER_TRACKINFO_DIALOG_HPP
+#ifndef HEADER_TRACK_INFO_SCREEN_HPP
+#define HEADER_TRACK_INFO_SCREEN_HPP
 
-#include "guiengine/modaldialog.hpp"
-#include "guiengine/widgets/check_box_widget.hpp"
-
-static const int HIGHSCORE_COUNT = 3;
+#include "guiengine/screen.hpp"
 
 namespace GUIEngine
 {
-    class SpinnerWidget;
+    class CheckBoxWidget;
     class IconButtonWidget;
     class LabelWidget;
+    class SpinnerWidget;
+    class Widget;
 }
+class Track;
 
 /**
  * \brief Dialog that shows the information about a given track
  * \ingroup states_screens
  */
-class TrackInfoDialog : public GUIEngine::ModalDialog
+class TrackInfoScreen : public GUIEngine::Screen,
+                        public GUIEngine::ScreenSingleton<TrackInfoScreen>
 {
-    std::string m_track_ident;
-    std::string m_ribbon_item;
-    
+    static const int HIGHSCORE_COUNT = 3;
+
+    /** A pointer to the track of which the info is shown. */
+    Track *m_track;
+
     // When there is no need to tab through / click on images/labels, we can add directly
     // irrlicht labels (more complicated uses require the use of our widget set)
-    GUIEngine::SpinnerWidget* m_spinner;
-    GUIEngine::CheckBoxWidget* m_checkbox;
+    /** Spinner for number of laps. */
+    GUIEngine::SpinnerWidget* m_lap_spinner;
+
+    /** Spinner for number of AI karts. */
+    GUIEngine::SpinnerWidget* m_ai_kart_spinner;
+
+    /** Screenshot widget. */
+    GUIEngine::IconButtonWidget *m_screenshot;
+
+    /** Check box for reverse mode. */
+    GUIEngine::CheckBoxWidget* m_reverse;
+
+    /** The icons for the highscore list. */
     GUIEngine::IconButtonWidget* m_kart_icons[HIGHSCORE_COUNT];
+
+    /** The actual highscore text values shown. */
     GUIEngine::LabelWidget* m_highscore_entries[HIGHSCORE_COUNT];
     
     void updateHighScores();
     
 public:
-    /**
-     * \brief Creates a track info modal dialog with given percentage of screen width and height
-     * \param ribbonItem identifier name of the ribbon item that was clicked in the track selection
-     *        screen to get there (often will be 'trackIdent' but may also be the random item)
-     * \param trackIdent identifier name of the track to show information about
-     * \param trackName  human-readable, possibly translated, name of the track to show information about
-     * \param screenshot screenshot of the track to show information about
-     */
-    TrackInfoDialog(const std::string& ribbonItem, const std::string& trackIdent,
-                    const irr::core::stringw& trackName, irr::video::ITexture* screenshot,
-                    const float percentWidth, const float percentHeight);
-    virtual ~TrackInfoDialog();
+    TrackInfoScreen();
+    virtual ~TrackInfoScreen();
     
+    virtual void init();
+    virtual void loadedFromFile();
+    virtual void eventCallback(GUIEngine::Widget *,const std::string &name ,
+                               const int player_id);
     void onEnterPressedInternal();
-    GUIEngine::EventPropagation processEvent(const std::string& eventSource);
+    void setTrack(Track *track);
 };
 
 #endif
diff --git a/src/states_screens/tracks_screen.cpp b/src/states_screens/tracks_screen.cpp
index 94f715553..62e94f6df 100644
--- a/src/states_screens/tracks_screen.cpp
+++ b/src/states_screens/tracks_screen.cpp
@@ -28,9 +28,8 @@
 #include "race/grand_prix_data.hpp"
 #include "race/grand_prix_manager.hpp"
 #include "states_screens/state_manager.hpp"
-#include "states_screens/dialogs/gp_info_dialog.hpp"
-#include "states_screens/dialogs/random_gp_dialog.hpp"
-#include "states_screens/dialogs/track_info_dialog.hpp"
+#include "states_screens/track_info_screen.hpp"
+#include "states_screens/gp_info_screen.hpp"
 #include "tracks/track.hpp"
 #include "tracks/track_manager.hpp"
 #include "utils/translation.hpp"
@@ -81,14 +80,8 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name,
 
             if (clicked_track)
             {
-                ITexture* screenshot =
-                    irr_driver->getTexture(clicked_track->getScreenshotFile(),
-                                   "While loading screenshot for track '%s':",
-                                   clicked_track->getFilename()               );
-
-                new TrackInfoDialog(selection, clicked_track->getIdent(),
-                              translations->fribidize(clicked_track->getName()),
-                              screenshot, 0.8f, 0.7f);
+                TrackInfoScreen::getInstance()->setTrack(clicked_track);
+                StateManager::get()->pushScreen(TrackInfoScreen::getInstance());
             }   // if clicked_track
 
         }   // selection=="random_track"
@@ -104,14 +97,8 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name,
             Track* clicked_track = track_manager->getTrack(selection);
             if (clicked_track)
             {
-                ITexture* screenshot =
-                    irr_driver->getTexture(clicked_track->getScreenshotFile(),
-                                    "While loading screenshot for track '%s'",
-                                    clicked_track->getFilename());
-
-                new TrackInfoDialog(selection, clicked_track->getIdent(),
-                             translations->fribidize(clicked_track->getName()),
-                             screenshot, 0.8f, 0.7f);
+                TrackInfoScreen::getInstance()->setTrack(clicked_track);
+                StateManager::get()->pushScreen(TrackInfoScreen::getInstance());
             }
         }
     }   // name=="tracks"
@@ -127,10 +114,10 @@ void TracksScreen::eventCallback(Widget* widget, const std::string& name,
         }
         else
         {
-            if (selection == "Random Grand Prix")
-                new RandomGPInfoDialog();
-            else
-                new GPInfoDialog(selection);
+            GPInfoScreen *gpis = GPInfoScreen::getInstance();
+            gpis->setGP( selection == "Random Grand Prix" ? "random" 
+                                                          : selection);
+            StateManager::get()->pushScreen(gpis);
         }
     }
     else if (name == "trackgroups")
diff --git a/src/tracks/check_manager.cpp b/src/tracks/check_manager.cpp
index 7ece0621e..1281fd1cb 100644
--- a/src/tracks/check_manager.cpp
+++ b/src/tracks/check_manager.cpp
@@ -150,6 +150,7 @@ unsigned int CheckManager::getLapLineIndex() const
     }
 
     Log::fatal("CheckManager", "Error, no kind of lap line for track found, aborting.");
+    return -1;
 }   // getLapLineIndex
 
 // ----------------------------------------------------------------------------
diff --git a/src/tracks/model_definition_loader.cpp b/src/tracks/model_definition_loader.cpp
index 0e0303ced..98159e961 100644
--- a/src/tracks/model_definition_loader.cpp
+++ b/src/tracks/model_definition_loader.cpp
@@ -22,7 +22,6 @@ using namespace irr;
 #include "graphics/irr_driver.hpp"
 #include "graphics/lod_node.hpp"
 #include "graphics/mesh_tools.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "io/xml_node.hpp"
 #include "tracks/track.hpp"
 
@@ -111,35 +110,6 @@ LODNode* ModelDefinitionLoader::instanciateAsLOD(const XMLNode* node, scene::ISc
 
 // ----------------------------------------------------------------------------
 
-STKInstancedSceneNode* ModelDefinitionLoader::instanciate(const irr::core::vector3df& position,
-                                const irr::core::vector3df& rotation,
-                                const irr::core::vector3df scale,
-                                const std::string& name)
-{
-    if (m_instancing_nodes.find(name) == m_instancing_nodes.end())
-    {
-        if (m_lod_groups.find(name) == m_lod_groups.end())
-        {
-            Log::warn("Instancing", "Cannot find instancing model <%s>", name.c_str());
-            return NULL;
-        }
-
-        scene::IMesh* mesh = irr_driver->getMesh(m_lod_groups[name][0].m_model_file);
-        mesh = MeshTools::createMeshWithTangents(mesh, &MeshTools::isNormalMap);
-        irr_driver->setAllMaterialFlags(mesh);
-
-        m_instancing_nodes[name] = new STKInstancedSceneNode(mesh,
-            irr_driver->getSceneManager()->getRootSceneNode(), irr_driver->getSceneManager(), -1);
-        m_track->addNode(m_instancing_nodes[name]);
-    }
-
-    m_instancing_nodes[name]->addInstance(position, rotation, scale);
-    m_instancing_nodes[name]->instanceGrab();
-    return m_instancing_nodes[name];
-}
-
-// ----------------------------------------------------------------------------
-
 void ModelDefinitionLoader::clear()
 {
     m_lod_groups.clear();
diff --git a/src/tracks/model_definition_loader.hpp b/src/tracks/model_definition_loader.hpp
index 3c750348d..b3bf75694 100644
--- a/src/tracks/model_definition_loader.hpp
+++ b/src/tracks/model_definition_loader.hpp
@@ -83,10 +83,6 @@ public:
 
     void addModelDefinition(const XMLNode* xml);
     LODNode* instanciateAsLOD(const XMLNode* xml_node, scene::ISceneNode* parent);
-    STKInstancedSceneNode* instanciate(const core::vector3df& position,
-                                       const irr::core::vector3df& rotation,
-                                       const irr::core::vector3df scale,
-                                       const std::string& name);
 
     void clear();
 
diff --git a/src/tracks/track.cpp b/src/tracks/track.cpp
index 6c46d2eff..5b3b255a2 100644
--- a/src/tracks/track.cpp
+++ b/src/tracks/track.cpp
@@ -37,7 +37,6 @@
 #include "graphics/particle_emitter.hpp"
 #include "graphics/particle_kind.hpp"
 #include "graphics/particle_kind_manager.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "graphics/stk_text_billboard.hpp"
 #include "guiengine/scalable_font.hpp"
 #include "io/file_manager.hpp"
@@ -132,7 +131,10 @@ Track::Track(const std::string &filename)
     m_sky_particles         = NULL;
     m_sky_dx                = 0.05f;
     m_sky_dy                = 0.0f;
-    m_weather_type          = WEATHER_NONE;
+    m_godrays_opacity       = 1.0f;
+    m_godrays_color         = video::SColor(255, 255, 255, 255);
+    m_weather_lightning      = false;
+    m_weather_sound         = "";
     m_cache_track           = UserConfigParams::m_cache_overworld &&
                               m_ident=="overworld";
     m_minimap_x_scale       = 1.0f;
@@ -272,7 +274,7 @@ void Track::cleanup()
 {
     QuadGraph::destroy();
     ItemManager::destroy();
-    resetVAO();
+    VAOManager::kill();
 
     ParticleKindManager::get()->cleanUpTrackSpecificGfx();
     // Clear reminder of transformed textures
@@ -427,7 +429,6 @@ void Track::cleanup()
         }
 #endif
     }   // if verbose
-
 }   // cleanup
 
 //-----------------------------------------------------------------------------
@@ -442,6 +443,9 @@ void Track::loadTrackInfo()
     m_fog_height_end        = 100.0f;
     m_gravity               = 9.80665f;
     m_smooth_normals        = false;
+    m_godrays               = false;
+    m_godrays_opacity       = 1.0f;
+    m_godrays_color         = video::SColor(255, 255, 255, 255);
                               /* ARGB */
     m_fog_color             = video::SColor(255, 77, 179, 230);
     m_default_ambient_color = video::SColor(255, 120, 120, 120);
@@ -484,7 +488,6 @@ void Track::loadTrackInfo()
     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);
     root->get("color-level-in",        &m_color_inlevel);
@@ -740,24 +743,10 @@ void Track::convertTrackToBullet(scene::ISceneNode *node)
     node->updateAbsolutePosition();
 
     std::vector<core::matrix4> matrices;
-
-    STKInstancedSceneNode* instancing_node = dynamic_cast<STKInstancedSceneNode*>(node);
-    if (instancing_node != NULL)
-    {
-        int count = instancing_node->getInstanceCount();
-        for (int i = 0; i < count; i++)
-        {
-            matrices.push_back(instancing_node->getInstanceTransform(i));
-        }
-    }
-    else
-    {
-        matrices.push_back(node->getAbsoluteTransformation());
-    }
+    matrices.push_back(node->getAbsoluteTransformation());
 
     const core::vector3df &pos   = node->getAbsolutePosition();
 
-
     scene::IMesh *mesh;
     // In case of readonly materials we have to get the material from
     // the mesh, otherwise from the node. This is esp. important for
@@ -1061,20 +1050,6 @@ bool Track::loadMainTrack(const XMLNode &root)
         }
     }
 
-    // Load instancing models (for the moment they are loaded the same way as LOD to simplify implementation)
-    const XMLNode *instancing_xml_node = root.getNode("instancing");
-    if (instancing_xml_node != NULL)
-    {
-        for (unsigned int i = 0; i < instancing_xml_node->getNumNodes(); i++)
-        {
-            const XMLNode* lod_group_xml = instancing_xml_node->getNode(i);
-            for (unsigned int j = 0; j < lod_group_xml->getNumNodes(); j++)
-            {
-                lodLoader.addModelDefinition(lod_group_xml->getNode(j));
-            }
-        }
-    }
-
     for (unsigned int i=0; i<track_node->getNumNodes(); i++)
     {
         const XMLNode *n=track_node->getNode(i);
@@ -1734,6 +1709,14 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
         node->get("fog-end-height",   &m_fog_height_end);
     }
 
+    if (const XMLNode *node = root->getNode("lightshaft"))
+    {
+        m_godrays = true;
+        node->get("opacity", &m_godrays_opacity);
+        node->get("color", &m_godrays_color);
+        node->get("xyz", &m_godrays_position);
+    }
+
     loadMainTrack(*root);
     unsigned int main_track_count = m_all_nodes.size();
 
@@ -1753,20 +1736,6 @@ void Track::loadTrackModel(bool reverse_track, unsigned int mode_id)
         }
     }
 
-    // Load instancing models (for the moment they are loaded the same way as LOD to simplify implementation)
-    const XMLNode *instancing_xml_node = root->getNode("instancing");
-    if (instancing_xml_node != NULL)
-    {
-        for (unsigned int i = 0; i < instancing_xml_node->getNumNodes(); i++)
-        {
-            const XMLNode* lod_group_xml = instancing_xml_node->getNode(i);
-            for (unsigned int j = 0; j < lod_group_xml->getNumNodes(); j++)
-            {
-                model_def_loader.addModelDefinition(lod_group_xml->getNode(j));
-            }
-        }
-    }
-
     std::map<std::string, XMLNode*> library_nodes;
     loadObjects(root, path, model_def_loader, true, NULL, library_nodes);
 
@@ -2007,20 +1976,6 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin
                         }
                     }
                 }
-
-                // Load instancing definitions
-                const XMLNode *instancing_xml_node = libroot->getNode("instancing");
-                if (instancing_xml_node != NULL)
-                {
-                    for (unsigned int i = 0; i < instancing_xml_node->getNumNodes(); i++)
-                    {
-                        const XMLNode* instancing_group_xml = instancing_xml_node->getNode(i);
-                        for (unsigned int j = 0; j < instancing_group_xml->getNumNodes(); j++)
-                        {
-                            model_def_loader.addModelDefinition(instancing_group_xml->getNode(j));
-                        }
-                    }
-                }
             }
             else
             {
@@ -2029,11 +1984,15 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin
             }
 
             scene::ISceneNode* parent = irr_driver->getSceneManager()->addEmptySceneNode();
+#ifdef DEBUG
+            parent->setName(("libnode_" + name).c_str());
+#endif
             parent->setPosition(xyz);
             parent->setRotation(hpr);
             parent->setScale(scale);
             parent->updateAbsolutePosition();
             loadObjects(libroot, lib_path, model_def_loader, create_lod_definitions, parent, library_nodes);
+            //m_all_nodes.push_back(parent);
         }
         else if (name == "water")
         {
@@ -2095,27 +2054,16 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin
         else if (name == "weather")
         {
             std::string weather_particles;
-            std::string weather_type;
+
             node->get("particles", &weather_particles);
-            node->get("type", &weather_type);
+            node->get("lightning", &m_weather_lightning);
+            node->get("sound", &m_weather_sound);
 
             if (weather_particles.size() > 0)
             {
                 m_sky_particles =
                     ParticleKindManager::get()->getParticles(weather_particles);
             }
-            else if (weather_type.size() > 0)
-            {
-                if (weather_type == "rain")
-                {
-                    m_weather_type = WEATHER_RAIN;
-                }
-                else
-                {
-                    Log::error("track", "Unknown weather type : '%s'",
-                               weather_type.c_str());
-                }
-            }
             else
             {
                 Log::error("track", "Bad weather node found - ignored.\n");
@@ -2132,7 +2080,7 @@ void Track::loadObjects(const XMLNode* root, const std::string& path, ModelDefin
         }
         else if (name == "instancing")
         {
-            // handled above
+            // TODO: eventually remove, this is now automatic
         }
         else if (name == "subtitles")
         {
diff --git a/src/tracks/track.hpp b/src/tracks/track.hpp
index cd442b62c..1bdf040fb 100644
--- a/src/tracks/track.hpp
+++ b/src/tracks/track.hpp
@@ -67,12 +67,6 @@ namespace Scripting
 
 const int HEIGHT_MAP_RESOLUTION = 256;
 
-enum WeatherType
-{
-    WEATHER_NONE,
-    WEATHER_RAIN
-};
-
 struct OverworldForceField
 {
     core::vector3df m_position;
@@ -317,7 +311,8 @@ private:
     ParticleKind*            m_sky_particles;
 
     /** Use a special built-in wheather */
-    WeatherType              m_weather_type;
+    bool                     m_weather_lightning;
+    std::string              m_weather_sound;
 
     /** A simple class to keep information about a track mode. */
     class TrackMode
@@ -390,7 +385,12 @@ private:
     float m_bloom_threshold;
 
     bool m_lensflare;
+
     bool m_godrays;
+    core::vector3df m_godrays_position;
+    float m_godrays_opacity;
+    video::SColor m_godrays_color;
+
     bool m_shadows;
 
     float m_displacement_speed;
@@ -408,6 +408,7 @@ private:
     /** The number of laps the track will be raced in a random GP.
      * m_actual_number_of_laps is initialised with this value.*/
     int m_default_number_of_laps;
+
     /** The number of laps that is predefined in a track info dialog. */
     int m_actual_number_of_laps;
 
@@ -426,7 +427,7 @@ private:
 
 public:
 
-    bool reverseAvailable() { return m_reverse_available; }
+    bool reverseAvailable() const { return m_reverse_available; }
     void handleAnimatedTextures(scene::ISceneNode *node, const XMLNode &xml);
 
     static const float NOHIT;
@@ -470,8 +471,16 @@ public:
     /** Returns true if this track has an arena mode. */
     bool isArena() const { return m_is_arena; }
     // ------------------------------------------------------------------------
+    /** Returns true if this track is a racing track. This means it is not an
+     *  internal track (like cut scenes), arena, or soccer field. */
+    bool isRaceTrack() const 
+    {
+        return !m_internal && !m_is_arena && !m_is_soccer;
+    }   // isRaceTrack
+    // ------------------------------------------------------------------------
     /** Returns true if this track has easter eggs. */
     bool hasEasterEggs() const { return m_has_easter_eggs; }
+    // ------------------------------------------------------------------------
     bool               isSoccer             () const { return m_is_soccer; }
     // ------------------------------------------------------------------------
     void               loadTrackModel  (World* parent,
@@ -568,7 +577,9 @@ public:
     unsigned int getNumberOfStartPositions() const
                                           { return m_start_transforms.size(); }
     // ------------------------------------------------------------------------
-    WeatherType   getWeatherType          () const { return m_weather_type; }
+    bool getWeatherLightning() {return m_weather_lightning;}
+    // ------------------------------------------------------------------------
+    std::string getWeatherSound() {return m_weather_sound;}
     // ------------------------------------------------------------------------
     ParticleKind* getSkyParticles         () { return m_sky_particles; }
     // ------------------------------------------------------------------------
@@ -620,6 +631,9 @@ public:
 
     bool hasLensFlare() const { return m_lensflare; }
     bool hasGodRays() const { return m_godrays; }
+    core::vector3df getGodRaysPosition() const { return m_godrays_position; }
+    float getGodRaysOpacity() const { return m_godrays_opacity; }
+    video::SColor getGodRaysColor() const { return m_godrays_color; }
     bool hasShadows() const { return m_shadows; }
 
     void addNode(scene::ISceneNode* node) { m_all_nodes.push_back(node); }
diff --git a/src/tracks/track_manager.cpp b/src/tracks/track_manager.cpp
index b2092b13e..94c9a5637 100644
--- a/src/tracks/track_manager.cpp
+++ b/src/tracks/track_manager.cpp
@@ -57,6 +57,19 @@ void TrackManager::addTrackSearchDir(const std::string &dir)
     m_track_search_path.push_back(dir);
 }   // addTrackDir
 
+//-----------------------------------------------------------------------------
+/** Returns the number of racing tracks. Those are tracks that are not 
+ *  internal (like cut scenes), arenas, or soccer fields.
+ */
+int TrackManager::getNumberOfRaceTracks() const
+{
+    int n=0;
+    for(unsigned int i=0; i<m_tracks.size(); i++)
+        if(m_tracks[i]->isRaceTrack())
+            n++;
+    return n;
+}   // getNumberOfRaceTracks
+
 //-----------------------------------------------------------------------------
 /** Get TrackData by the track identifier.
  *  \param ident Identifier = basename of the directory the track is in.
diff --git a/src/tracks/track_manager.hpp b/src/tracks/track_manager.hpp
index 3faf53921..7d2b86ed1 100644
--- a/src/tracks/track_manager.hpp
+++ b/src/tracks/track_manager.hpp
@@ -53,9 +53,6 @@ private:
     /** List of all soccer arena groups. */
     Group2Indices                            m_soccer_arena_groups;
 
-    /** List of all groups (for both normal tracks and arenas) */
-    //std::vector<std::string>                 m_all_group_names;
-
     /** List of the names of all groups containing tracks */
     std::vector<std::string>                 m_track_group_names;
 
@@ -85,6 +82,7 @@ public:
     void  removeTrack(const std::string &ident);
     bool  loadTrack(const std::string& dirname);
     void  removeAllCachedData();
+    int   getNumberOfRaceTracks() const;
     Track* getTrack(const std::string& ident) const;
     // ------------------------------------------------------------------------
     /** Sets a list of track as being unavailable (e.g. in network mode the
diff --git a/src/tracks/track_object.cpp b/src/tracks/track_object.cpp
index e5566bdd2..7ddc7fe85 100644
--- a/src/tracks/track_object.cpp
+++ b/src/tracks/track_object.cpp
@@ -105,9 +105,6 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent,
     bool lod_instance = false;
     xml_node.get("lod_instance", &lod_instance);
 
-    bool instancing = false;
-    xml_node.get("instancing", &instancing);
-
     m_soccer_ball = false;
     xml_node.get("soccer_ball", &m_soccer_ball);
     
@@ -163,14 +160,7 @@ void TrackObject::init(const XMLNode &xml_node, scene::ISceneNode* parent,
     {
         scene::ISceneNode *glownode = NULL;
 
-        if (instancing)
-        {
-            m_type = "lod";
-            TrackObjectPresentationInstancing* instancing_node =
-                new TrackObjectPresentationInstancing(xml_node, parent, model_def_loader);
-            m_presentation = instancing_node;
-        }
-        else if (lod_instance)
+        if (lod_instance)
         {
             m_type = "lod";
             TrackObjectPresentationLOD* lod_node =
diff --git a/src/tracks/track_object_presentation.cpp b/src/tracks/track_object_presentation.cpp
index 9d4f5950d..0e568fd64 100644
--- a/src/tracks/track_object_presentation.cpp
+++ b/src/tracks/track_object_presentation.cpp
@@ -28,7 +28,6 @@
 #include "graphics/particle_emitter.hpp"
 #include "graphics/particle_kind_manager.hpp"
 #include "graphics/stkmeshscenenode.hpp"
-#include "graphics/stkinstancedscenenode.hpp"
 #include "io/file_manager.hpp"
 #include "io/xml_node.hpp"
 #include "input/device_manager.hpp"
@@ -169,59 +168,6 @@ TrackObjectPresentationLOD::~TrackObjectPresentationLOD()
 
 // ----------------------------------------------------------------------------
 
-TrackObjectPresentationInstancing::TrackObjectPresentationInstancing(const XMLNode& xml_node,
-    scene::ISceneNode* parent,
-    ModelDefinitionLoader& model_def_loader) : TrackObjectPresentationSceneNode(xml_node)
-{
-    m_instancing_group = NULL;
-    m_fallback_scene_node = NULL;
-
-    std::string instancing_model;
-    xml_node.get("instancing_model", &instancing_model);
-
-    m_node = irr_driver->getSceneManager()->addEmptySceneNode(parent);
-    m_node->setPosition(m_init_xyz);
-    m_node->setRotation(m_init_hpr);
-    m_node->setScale(m_init_scale);
-    m_node->updateAbsolutePosition();
-    if (irr_driver->isGLSL())
-    {
-        m_instancing_group = model_def_loader.instanciate(m_node->getAbsolutePosition(),
-            m_node->getAbsoluteTransformation().getRotationDegrees(), m_node->getAbsoluteTransformation().getScale(),
-            instancing_model);
-    }
-    else
-    {
-        scene::IMesh* mesh = model_def_loader.getFirstMeshFor(instancing_model);
-        scene::IMeshSceneNode* node = irr_driver->addMesh(mesh, m_node);
-        node->grab();
-        irr_driver->grabAllTextures(mesh);
-        mesh->grab();
-        World::getWorld()->getTrack()->addNode(node);
-
-        m_fallback_scene_node = node;
-    }
-}
-
-TrackObjectPresentationInstancing::~TrackObjectPresentationInstancing()
-{
-    if (m_instancing_group != NULL)
-        m_instancing_group->instanceDrop();
-
-    if (m_fallback_scene_node != NULL)
-    {
-        scene::IMesh* mesh = m_fallback_scene_node->getMesh();
-        irr_driver->dropAllTextures(mesh);
-        mesh->drop();
-        if (mesh->getReferenceCount() == 1)
-            irr_driver->removeMeshFromCache(mesh);
-
-        m_fallback_scene_node->drop();
-    }
-}
-
-// ----------------------------------------------------------------------------
-
 TrackObjectPresentationMesh::TrackObjectPresentationMesh(const XMLNode& xml_node,
     bool enabled, scene::ISceneNode* parent) :
     TrackObjectPresentationSceneNode(xml_node)
diff --git a/src/tracks/track_object_presentation.hpp b/src/tracks/track_object_presentation.hpp
index 161c88646..ad427d52c 100644
--- a/src/tracks/track_object_presentation.hpp
+++ b/src/tracks/track_object_presentation.hpp
@@ -171,20 +171,6 @@ public:
     virtual ~TrackObjectPresentationLOD();
 };
 
-class TrackObjectPresentationInstancing : public TrackObjectPresentationSceneNode
-{
-    STKInstancedSceneNode* m_instancing_group;
-    scene::IMeshSceneNode* m_fallback_scene_node;
-public:
-
-    TrackObjectPresentationInstancing(const XMLNode& xml_node,
-        scene::ISceneNode* parent,
-        ModelDefinitionLoader& model_def_loader);
-    virtual ~TrackObjectPresentationInstancing();
-
-    STKInstancedSceneNode* getInstancingGroup() { return m_instancing_group;  }
-};
-
 /**
  * \ingroup tracks
  * A track object representation that consists of a mesh scene node.
diff --git a/src/utils/cpp2011.hpp b/src/utils/cpp2011.hpp
index d1d5a2d79..2cf0a2f88 100644
--- a/src/utils/cpp2011.hpp
+++ b/src/utils/cpp2011.hpp
@@ -27,4 +27,28 @@ void pushVector(std::vector<T> *vec, Args ...args)
     vec->emplace_back(args...);
 #endif
 }
+
+struct Util
+{
+    template <typename T>
+    static void populate(std::vector<T> &v)
+    { }
+
+    template <typename T, typename...Args>
+    static void populate(std::vector<T> &v, T t, Args... args)
+    {
+        v.push_back(t);
+        populate<T>(v, args...);
+    }
+};
+
+
+template<typename T, typename...Args>
+static std::vector<T> createVector(Args...args)
+{
+    std::vector<T> result = std::vector<T>();
+    Util::template populate<T>(result, args...);
+    return result;
+}
+
 #endif
\ No newline at end of file
diff --git a/src/utils/debug.cpp b/src/utils/debug.cpp
index 57a74ec8e..a6bfc6fcc 100644
--- a/src/utils/debug.cpp
+++ b/src/utils/debug.cpp
@@ -34,6 +34,7 @@
 #include "utils/profiler.hpp"
 #include <IGUIEnvironment.h>
 #include <IGUIContextMenu.h>
+
 using namespace irr;
 using namespace gui;
 
@@ -436,34 +437,36 @@ bool onEvent(const SEvent &event)
                 }
                 else if (cmdID == DEBUG_VISUAL_VALUES)
                 {
+#if !defined(__APPLE__)
                     DebugSliderDialog *dsd = new DebugSliderDialog();
-                    dsd->setSliderHook( "red_slider", 0, 255, [](){ return irr_driver->getAmbientLight().r * 255.; },
+                    dsd->setSliderHook( "red_slider", 0, 255, [](){ return irr_driver->getAmbientLight().r * 255.f; },
                         [](int v){
                             video::SColorf ambient = irr_driver->getAmbientLight();
-                            ambient.setColorComponentValue(0, v / 255.);
+                            ambient.setColorComponentValue(0, v / 255.f);
                             irr_driver->setAmbientLight(ambient); }
                     );
-                    dsd->setSliderHook("green_slider", 0, 255, [](){ return irr_driver->getAmbientLight().g * 255.; },
+                    dsd->setSliderHook("green_slider", 0, 255, [](){ return irr_driver->getAmbientLight().g * 255.f; },
                         [](int v){
                         video::SColorf ambient = irr_driver->getAmbientLight();
-                        ambient.setColorComponentValue(1, v / 255.);
+                        ambient.setColorComponentValue(1, v / 255.f);
                         irr_driver->setAmbientLight(ambient); }
                     );
-                    dsd->setSliderHook("blue_slider", 0, 255, [](){ return irr_driver->getAmbientLight().b * 255.; },
+                    dsd->setSliderHook("blue_slider", 0, 255, [](){ return irr_driver->getAmbientLight().b * 255.f; },
                         [](int v){
                         video::SColorf ambient = irr_driver->getAmbientLight();
-                        ambient.setColorComponentValue(2, v / 255.);
+                        ambient.setColorComponentValue(2, v / 255.f);
                         irr_driver->setAmbientLight(ambient); }
                     );
-                    dsd->setSliderHook("ssao_radius", 0, 100, [](){ return irr_driver->getSSAORadius() * 10; },
-                        [](int v){irr_driver->setSSAORadius(v / 10.); }
+                    dsd->setSliderHook("ssao_radius", 0, 100, [](){ return irr_driver->getSSAORadius() * 10.f; },
+                        [](int v){irr_driver->setSSAORadius(v / 10.f); }
                     );
-                    dsd->setSliderHook("ssao_k", 0, 100, [](){ return irr_driver->getSSAOK() * 10; },
-                        [](int v){irr_driver->setSSAOK(v / 10.); }
+                    dsd->setSliderHook("ssao_k", 0, 100, [](){ return irr_driver->getSSAOK() * 10.f; },
+                        [](int v){irr_driver->setSSAOK(v / 10.f); }
                     );
-                    dsd->setSliderHook("ssao_sigma", 0, 100, [](){ return irr_driver->getSSAOSigma() * 10; },
-                        [](int v){irr_driver->setSSAOSigma(v / 10.); }
+                    dsd->setSliderHook("ssao_sigma", 0, 100, [](){ return irr_driver->getSSAOSigma() * 10.f; },
+                        [](int v){irr_driver->setSSAOSigma(v / 10.f); }
                     );
+#endif
                 }
             }
 
diff --git a/src/utils/profiler.cpp b/src/utils/profiler.cpp
index 11eb7ba63..6df0432dd 100644
--- a/src/utils/profiler.cpp
+++ b/src/utils/profiler.cpp
@@ -33,6 +33,7 @@ static const char* GPU_Phase[Q_LAST] =
 {
     "Solid Pass 1",
     "Shadows",
+    "RSM",
     "RH",
     "GI",
     "Env Map",
@@ -255,6 +256,7 @@ void Profiler::synchronizeFrame()
 /// Draw the markers
 void Profiler::draw()
 {
+    PROFILER_PUSH_CPU_MARKER("ProfilerDraw", 0xFF, 0xFF, 0x00);
     video::IVideoDriver*    driver = irr_driver->getVideoDriver();
     std::stack<Marker>      hovered_markers;
 
@@ -263,7 +265,7 @@ void Profiler::draw()
     // Force to show the pointer
     irr_driver->showPointer();
 
-    int read_id = (m_freeze_state == FROZEN ? !m_write_id : m_write_id);
+    int read_id = !m_write_id;
 
     // Compute some values for drawing (unit: pixels, but we keep floats for reducing errors accumulation)
     core::dimension2d<u32>    screen_size    = driver->getScreenSize();
@@ -293,7 +295,7 @@ void Profiler::draw()
             else end = std::max(end, m.end);
         }
     }
-
+    
     const double duration = end - start;
     const double factor = profiler_width / duration;
 
@@ -351,33 +353,36 @@ void Profiler::draw()
             m_first_capture_sweep = false;
         }
     }
-
+    
+    // GPU profiler
     QueryPerf hovered_gpu_marker = Q_LAST;
     long hovered_gpu_marker_elapsed = 0;
+    int gpu_y = int(y_offset + nb_thread_infos*line_height + line_height/2);
+    float total = 0;
+    unsigned int gpu_timers[Q_LAST];
+    for (unsigned i = 0; i < Q_LAST; i++)
+    {
+        gpu_timers[i] = irr_driver->getGPUTimer(i).elapsedTimeus();
+        total += gpu_timers[i];
+    }
+    
+    static video::SColor colors[] = {
+        video::SColor(255, 255, 0, 0),
+        video::SColor(255, 0, 255, 0),
+        video::SColor(255, 0, 0, 255),
+        video::SColor(255, 255, 255, 0),
+        video::SColor(255, 255, 0, 255),
+        video::SColor(255, 0, 255, 255)
+    };
+
     if (hovered_markers.size() == 0)
     {
-        int gpu_y = int(y_offset + nb_thread_infos*line_height + line_height/2);
-        float total = 0;
-        for (unsigned i = 0; i < Q_LAST; i++)
-        {
-            total += irr_driver->getGPUTimer(i).elapsedTimeus();
-        }
-
-        static video::SColor colors[] = {
-            video::SColor(255, 255, 0, 0),
-            video::SColor(255, 0, 255, 0),
-            video::SColor(255, 0, 0, 255),
-            video::SColor(255, 255, 255, 0),
-            video::SColor(255, 255, 0, 255),
-            video::SColor(255, 0, 255, 255)
-        };
-
         float curr_val = 0;
         for (unsigned i = 0; i < Q_LAST; i++)
         {
             //Log::info("GPU Perf", "Phase %d : %d us\n", i, irr_driver->getGPUTimer(i).elapsedTimeus());
 
-            float elapsed = float(irr_driver->getGPUTimer(i).elapsedTimeus());
+            float elapsed = float(gpu_timers[i]);
             core::rect<s32> pos((s32)(x_offset + (curr_val / total)*profiler_width),
                 (s32)(y_offset + gpu_y),
                 (s32)(x_offset + ((curr_val + elapsed) / total)*profiler_width),
@@ -389,7 +394,7 @@ void Profiler::draw()
             if (pos.isPointInside(mouse_pos))
             {
                 hovered_gpu_marker = (QueryPerf)i;
-                hovered_gpu_marker_elapsed = irr_driver->getGPUTimer(i).elapsedTimeus();
+                hovered_gpu_marker_elapsed = gpu_timers[i];
             }
 
             if (m_capture_report)
@@ -421,7 +426,7 @@ void Profiler::draw()
 
     // Draw the hovered markers' names
     gui::ScalableFont* font = GUIEngine::getFont();
-    if(font)
+    if (font)
     {
         core::stringw text;
         while(!hovered_markers.empty())
@@ -449,6 +454,8 @@ void Profiler::draw()
     {
         font->draw("Capturing profiler report...", MARKERS_NAMES_POS, video::SColor(0xFF, 0x00, 0x90, 0x00));
     }
+
+    PROFILER_POP_CPU_MARKER();
 }
 
 //-----------------------------------------------------------------------------
diff --git a/src/utils/vs.hpp b/src/utils/vs.hpp
index ffc47ba16..017f07400 100644
--- a/src/utils/vs.hpp
+++ b/src/utils/vs.hpp
@@ -11,3 +11,7 @@
 #  define round(x)  (floorf(x + 0.5))
 #endif
 
+#ifdef __MINGW32__
+	#include <cmath>
+	using std::isnan;
+#endif
diff --git a/src/windows_installer/icon_rc.template b/src/windows_installer/icon_rc.template
new file mode 100644
index 000000000..d9cc5fd32
--- /dev/null
+++ b/src/windows_installer/icon_rc.template
@@ -0,0 +1 @@
+100 ICON "@PROJECT_SOURCE_DIR@/src/windows_installer/icon.ico"