Fix a memleak pointed by @Zeno- in MeshUpdateQueue
[oweals/minetest.git] / src / shader.cpp
index 39296f6a3a60fcfac5cfebf1e63d55aa855f336e..66f32c9a1846bc6e6439f63829841bbc72306698 100644 (file)
@@ -18,15 +18,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
+#include <fstream>
+#include <iterator>
 #include "shader.h"
 #include "irrlichttypes_extrabloated.h"
 #include "debug.h"
-#include "main.h" // for g_settings
 #include "filesys.h"
 #include "util/container.h"
 #include "util/thread.h"
 #include "settings.h"
-#include <iterator>
 #include <ICameraSceneNode.h>
 #include <IGPUProgrammingServices.h>
 #include <IMaterialRenderer.h>
@@ -35,7 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "EShaderTypes.h"
 #include "log.h"
 #include "gamedef.h"
-#include "strfnd.h" // trim()
+#include "client/tile.h"
 
 /*
        A cache from shader name to shader path
@@ -102,10 +102,8 @@ std::string getShaderPath(const std::string &name_of_shader,
 class SourceShaderCache
 {
 public:
-       void insert(const std::string &name_of_shader,
-                       const std::string &filename,
-                       const std::string &program,
-                       bool prefer_local)
+       void insert(const std::string &name_of_shader, const std::string &filename,
+               const std::string &program, bool prefer_local)
        {
                std::string combined = name_of_shader + DIR_DELIM + filename;
                // Try to use local shader instead if asked to
@@ -121,42 +119,43 @@ public:
                }
                m_programs[combined] = program;
        }
+
        std::string get(const std::string &name_of_shader,
-                       const std::string &filename)
+               const std::string &filename)
        {
                std::string combined = name_of_shader + DIR_DELIM + filename;
-               std::map<std::string, std::string>::iterator n;
-               n = m_programs.find(combined);
-               if(n != m_programs.end())
+               StringMap::iterator n = m_programs.find(combined);
+               if (n != m_programs.end())
                        return n->second;
                return "";
        }
+
        // Primarily fetches from cache, secondarily tries to read from filesystem
        std::string getOrLoad(const std::string &name_of_shader,
-                       const std::string &filename)
+               const std::string &filename)
        {
                std::string combined = name_of_shader + DIR_DELIM + filename;
-               std::map<std::string, std::string>::iterator n;
-               n = m_programs.find(combined);
-               if(n != m_programs.end())
+               StringMap::iterator n = m_programs.find(combined);
+               if (n != m_programs.end())
                        return n->second;
                std::string path = getShaderPath(name_of_shader, filename);
-               if(path == ""){
-                       infostream<<"SourceShaderCache::getOrLoad(): No path found for \""
-                                       <<combined<<"\""<<std::endl;
+               if (path == "") {
+                       infostream << "SourceShaderCache::getOrLoad(): No path found for \""
+                               << combined << "\"" << std::endl;
                        return "";
                }
-               infostream<<"SourceShaderCache::getOrLoad(): Loading path \""<<path
-                               <<"\""<<std::endl;
+               infostream << "SourceShaderCache::getOrLoad(): Loading path \""
+                       << path << "\"" << std::endl;
                std::string p = readFile(path);
-               if(p != ""){
+               if (p != "") {
                        m_programs[combined] = p;
                        return p;
                }
                return "";
        }
 private:
-       std::map<std::string, std::string> m_programs;
+       StringMap m_programs;
+
        std::string readFile(const std::string &path)
        {
                std::ifstream is(path.c_str(), std::ios::binary);
@@ -168,50 +167,54 @@ private:
        }
 };
 
+
 /*
        ShaderCallback: Sets constants that can be used in shaders
 */
 
-class IShaderConstantSetterRegistry
-{
-public:
-       virtual ~IShaderConstantSetterRegistry(){};
-       virtual void onSetConstants(video::IMaterialRendererServices *services,
-                       bool is_highlevel, const std::string &name) = 0;
-};
-
 class ShaderCallback : public video::IShaderConstantSetCallBack
 {
-       IShaderConstantSetterRegistry *m_scsr;
-       std::string m_name;
+       std::vector<IShaderConstantSetter*> m_setters;
 
 public:
-       ShaderCallback(IShaderConstantSetterRegistry *scsr, const std::string &name):
-               m_scsr(scsr),
-               m_name(name)
-       {}
-       ~ShaderCallback() {}
+       ShaderCallback(const std::vector<IShaderConstantSetterFactory*> &factories)
+       {
+               for (u32 i = 0; i < factories.size(); ++i)
+                       m_setters.push_back(factories[i]->create());
+       }
+
+       ~ShaderCallback()
+       {
+               for (u32 i = 0; i < m_setters.size(); ++i)
+                       delete m_setters[i];
+       }
 
        virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
        {
                video::IVideoDriver *driver = services->getVideoDriver();
-               assert(driver);
+               sanity_check(driver != NULL);
 
                bool is_highlevel = userData;
 
-               m_scsr->onSetConstants(services, is_highlevel, m_name);
+               for (u32 i = 0; i < m_setters.size(); ++i)
+                       m_setters[i]->onSetConstants(services, is_highlevel);
        }
 };
 
+
 /*
        MainShaderConstantSetter: Set basic constants required for almost everything
 */
 
 class MainShaderConstantSetter : public IShaderConstantSetter
 {
+       CachedVertexShaderSetting<float, 16> m_world_view_proj;
+       CachedVertexShaderSetting<float, 16> m_world;
+
 public:
-       MainShaderConstantSetter(IrrlichtDevice *device):
-               m_device(device)
+       MainShaderConstantSetter() :
+               m_world_view_proj("mWorldViewProj"),
+               m_world("mWorld")
        {}
        ~MainShaderConstantSetter() {}
 
@@ -219,56 +222,47 @@ public:
                        bool is_highlevel)
        {
                video::IVideoDriver *driver = services->getVideoDriver();
-               assert(driver);
-
-               // set inverted world matrix
-               core::matrix4 invWorld = driver->getTransform(video::ETS_WORLD);
-               invWorld.makeInverse();
-               if(is_highlevel)
-                       services->setVertexShaderConstant("mInvWorld", invWorld.pointer(), 16);
-               else
-                       services->setVertexShaderConstant(invWorld.pointer(), 0, 4);
+               sanity_check(driver);
 
-               // set clip matrix
+               // Set clip matrix
                core::matrix4 worldViewProj;
                worldViewProj = driver->getTransform(video::ETS_PROJECTION);
                worldViewProj *= driver->getTransform(video::ETS_VIEW);
                worldViewProj *= driver->getTransform(video::ETS_WORLD);
-               if(is_highlevel)
-                       services->setVertexShaderConstant("mWorldViewProj", worldViewProj.pointer(), 16);
+               if (is_highlevel)
+                       m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
                else
-                       services->setVertexShaderConstant(worldViewProj.pointer(), 4, 4);
+                       services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
 
-               // set transposed world matrix
+               // Set world matrix
                core::matrix4 world = driver->getTransform(video::ETS_WORLD);
-               world = world.getTransposed();
-               if(is_highlevel)
-                       services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
+               if (is_highlevel)
+                       m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
                else
-                       services->setVertexShaderConstant(world.pointer(), 8, 4);
+                       services->setVertexShaderConstant(world.pointer(), 4, 4);
+
        }
+};
 
-private:
-       IrrlichtDevice *m_device;
+
+class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
+{
+public:
+       virtual IShaderConstantSetter* create()
+               { return new MainShaderConstantSetter(); }
 };
 
+
 /*
        ShaderSource
 */
 
-class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterRegistry
+class ShaderSource : public IWritableShaderSource
 {
 public:
        ShaderSource(IrrlichtDevice *device);
        ~ShaderSource();
 
-       /*
-               Gets a shader material id from cache or
-               - if main thread, from getShaderIdDirect
-               - if other thread, adds to request queue and waits for main thread
-       */
-       u32 getShaderId(const std::string &name);
-
        /*
                - If shader material specified by name is found from cache,
                  return the cached id.
@@ -276,25 +270,22 @@ public:
 
                The id 0 points to a null shader. Its material is EMT_SOLID.
        */
-       u32 getShaderIdDirect(const std::string &name);
-
-       // Finds out the name of a cached shader.
-       std::string getShaderName(u32 id);
+       u32 getShaderIdDirect(const std::string &name,
+               const u8 material_type, const u8 drawtype);
 
        /*
                If shader specified by the name pointed by the id doesn't
-               exist, create it, then return the cached shader.
+               exist, create it, then return id.
 
                Can be called from any thread. If called from some other thread
                and not found in cache, the call is queued to the main thread
                for processing.
        */
-       ShaderInfo getShader(u32 id);
 
-       ShaderInfo getShader(const std::string &name)
-       {
-               return getShader(getShaderId(name));
-       }
+       u32 getShader(const std::string &name,
+               const u8 material_type, const u8 drawtype);
+
+       ShaderInfo getShaderInfo(u32 id);
 
        // Processes queued shader requests from other threads.
        // Shall be called from the main thread.
@@ -309,22 +300,17 @@ public:
        // Shall be called from the main thread.
        void rebuildShaders();
 
-       void addGlobalConstantSetter(IShaderConstantSetter *setter)
+       void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter)
        {
-               m_global_setters.push_back(setter);
+               m_setter_factories.push_back(setter);
        }
 
-       void onSetConstants(video::IMaterialRendererServices *services,
-                       bool is_highlevel, const std::string &name);
-
 private:
 
        // The id of the thread that is allowed to use irrlicht directly
        threadid_t m_main_thread;
        // The irrlicht device
        IrrlichtDevice *m_device;
-       // The set-constants callback
-       ShaderCallback *m_shader_callback;
 
        // Cache of source shaders
        // This should be only accessed from the main thread
@@ -333,17 +319,17 @@ private:
        // A shader id is index in this array.
        // The first position contains a dummy shader.
        std::vector<ShaderInfo> m_shaderinfo_cache;
-       // Maps a shader name to an index in the former.
-       std::map<std::string, u32> m_name_to_id;
-       // The two former containers are behind this mutex
-       JMutex m_shaderinfo_cache_mutex;
+       // The former container is behind this mutex
+       Mutex m_shaderinfo_cache_mutex;
 
        // Queued shader fetches (to be processed by the main thread)
        RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
 
-       // Global constant setters
-       // TODO: Delete these in the destructor
-       std::vector<IShaderConstantSetter*> m_global_setters;
+       // Global constant setter factories
+       std::vector<IShaderConstantSetterFactory *> m_setter_factories;
+
+       // Shader callbacks
+       std::vector<ShaderCallback *> m_callbacks;
 };
 
 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
@@ -354,8 +340,10 @@ IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
 /*
        Generate shader given the shader name.
 */
-ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
-               video::IShaderConstantSetCallBack *callback,
+ShaderInfo generate_shader(const std::string &name,
+               u8 material_type, u8 drawtype,
+               IrrlichtDevice *device, std::vector<ShaderCallback *> &callbacks,
+               const std::vector<IShaderConstantSetterFactory*> &setter_factories,
                SourceShaderCache *sourcecache);
 
 /*
@@ -369,53 +357,40 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
 ShaderSource::ShaderSource(IrrlichtDevice *device):
                m_device(device)
 {
-       assert(m_device);
+       assert(m_device); // Pre-condition
 
-       m_shader_callback = new ShaderCallback(this, "default");
-
-       m_main_thread = get_current_thread_id();
+       m_main_thread = thr_get_current_thread_id();
 
        // Add a dummy ShaderInfo as the first index, named ""
        m_shaderinfo_cache.push_back(ShaderInfo());
-       m_name_to_id[""] = 0;
 
        // Add main global constant setter
-       addGlobalConstantSetter(new MainShaderConstantSetter(device));
+       addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
 }
 
 ShaderSource::~ShaderSource()
 {
-       //m_shader_callback->drop();
-
-       for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
-                       iter != m_global_setters.end(); iter++) {
+       for (std::vector<ShaderCallback *>::iterator iter = m_callbacks.begin();
+                       iter != m_callbacks.end(); ++iter) {
+               delete *iter;
+       }
+       for (std::vector<IShaderConstantSetterFactory *>::iterator iter = m_setter_factories.begin();
+                       iter != m_setter_factories.end(); ++iter) {
                delete *iter;
        }
-       m_global_setters.clear();
 }
 
-u32 ShaderSource::getShaderId(const std::string &name)
+u32 ShaderSource::getShader(const std::string &name,
+               const u8 material_type, const u8 drawtype)
 {
-       //infostream<<"getShaderId(): \""<<name<<"\""<<std::endl;
-
-       {
-               /*
-                       See if shader already exists
-               */
-               JMutexAutoLock lock(m_shaderinfo_cache_mutex);
-               std::map<std::string, u32>::iterator n;
-               n = m_name_to_id.find(name);
-               if(n != m_name_to_id.end())
-                       return n->second;
-       }
-
        /*
                Get shader
        */
-       if(get_current_thread_id() == m_main_thread){
-               return getShaderIdDirect(name);
+
+       if (thr_is_current_thread(m_main_thread)) {
+               return getShaderIdDirect(name, material_type, drawtype);
        } else {
-               /*errorstream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;*/
+               /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
 
                // We're gonna ask the result to be put into here
 
@@ -427,24 +402,21 @@ u32 ShaderSource::getShaderId(const std::string &name)
                /* infostream<<"Waiting for shader from main thread, name=\""
                                <<name<<"\""<<std::endl;*/
 
-               try{
-                       while(true) {
-                               // Wait result for a second
-                               GetResult<std::string, u32, u8, u8>
-                                       result = result_queue.pop_front(1000);
+               while(true) {
+                       GetResult<std::string, u32, u8, u8>
+                               result = result_queue.pop_frontNoEx();
 
-                               if (result.key == name) {
-                                       return result.item;
-                               }
+                       if (result.key == name) {
+                               return result.item;
+                       }
+                       else {
+                               errorstream << "Got shader with invalid name: " << result.key << std::endl;
                        }
                }
-               catch(ItemNotFoundException &e){
-                       errorstream<<"Waiting for shader " << name << " timed out."<<std::endl;
-                       return 0;
-               }
+
        }
 
-       infostream<<"getShaderId(): Failed"<<std::endl;
+       infostream<<"getShader(): Failed"<<std::endl;
 
        return 0;
 }
@@ -452,7 +424,8 @@ u32 ShaderSource::getShaderId(const std::string &name)
 /*
        This method generates all the shaders
 */
-u32 ShaderSource::getShaderIdDirect(const std::string &name)
+u32 ShaderSource::getShaderIdDirect(const std::string &name,
+               const u8 material_type, const u8 drawtype)
 {
        //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
 
@@ -462,69 +435,45 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name)
                return 0;
        }
 
+       // Check if already have such instance
+       for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
+               ShaderInfo *info = &m_shaderinfo_cache[i];
+               if(info->name == name && info->material_type == material_type &&
+                       info->drawtype == drawtype)
+                       return i;
+       }
+
        /*
                Calling only allowed from main thread
        */
-       if(get_current_thread_id() != m_main_thread){
+       if (!thr_is_current_thread(m_main_thread)) {
                errorstream<<"ShaderSource::getShaderIdDirect() "
                                "called not from main thread"<<std::endl;
                return 0;
        }
 
-       /*
-               See if shader already exists
-       */
-       {
-               JMutexAutoLock lock(m_shaderinfo_cache_mutex);
-
-               std::map<std::string, u32>::iterator n;
-               n = m_name_to_id.find(name);
-               if(n != m_name_to_id.end()){
-                       /*infostream<<"getShaderIdDirect(): \""<<name
-                                       <<"\" found in cache"<<std::endl;*/
-                       return n->second;
-               }
-       }
-
-       /*infostream<<"getShaderIdDirect(): \""<<name
-                       <<"\" NOT found in cache. Creating it."<<std::endl;*/
-
-       ShaderInfo info = generate_shader(name, m_device,
-                       m_shader_callback, &m_sourcecache);
+       ShaderInfo info = generate_shader(name, material_type, drawtype,
+                       m_device, m_callbacks, m_setter_factories, &m_sourcecache);
 
        /*
                Add shader to caches (add dummy shaders too)
        */
 
-       JMutexAutoLock lock(m_shaderinfo_cache_mutex);
+       MutexAutoLock lock(m_shaderinfo_cache_mutex);
 
        u32 id = m_shaderinfo_cache.size();
        m_shaderinfo_cache.push_back(info);
-       m_name_to_id[name] = id;
 
-       /*infostream<<"getShaderIdDirect(): "
-                       <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
+       infostream<<"getShaderIdDirect(): "
+                       <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
 
        return id;
 }
 
-std::string ShaderSource::getShaderName(u32 id)
-{
-       JMutexAutoLock lock(m_shaderinfo_cache_mutex);
 
-       if(id >= m_shaderinfo_cache.size()){
-               errorstream<<"ShaderSource::getShaderName(): id="<<id
-                               <<" >= m_shaderinfo_cache.size()="
-                               <<m_shaderinfo_cache.size()<<std::endl;
-               return "";
-       }
-
-       return m_shaderinfo_cache[id].name;
-}
-
-ShaderInfo ShaderSource::getShader(u32 id)
+ShaderInfo ShaderSource::getShaderInfo(u32 id)
 {
-       JMutexAutoLock lock(m_shaderinfo_cache_mutex);
+       MutexAutoLock lock(m_shaderinfo_cache_mutex);
 
        if(id >= m_shaderinfo_cache.size())
                return ShaderInfo();
@@ -534,20 +483,8 @@ ShaderInfo ShaderSource::getShader(u32 id)
 
 void ShaderSource::processQueue()
 {
-       /*
-               Fetch shaders
-       */
-       if(!m_get_shader_queue.empty()){
-               GetRequest<std::string, u32, u8, u8>
-                               request = m_get_shader_queue.pop();
 
-               /**errorstream<<"ShaderSource::processQueue(): "
-                               <<"got shader request with "
-                               <<"name=\""<<request.key<<"\""
-                               <<std::endl;**/
 
-               m_get_shader_queue.pushResult(request,getShaderIdDirect(request.key));
-       }
 }
 
 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
@@ -557,14 +494,14 @@ void ShaderSource::insertSourceShader(const std::string &name_of_shader,
                        "name_of_shader=\""<<name_of_shader<<"\", "
                        "filename=\""<<filename<<"\""<<std::endl;*/
 
-       assert(get_current_thread_id() == m_main_thread);
+       sanity_check(thr_is_current_thread(m_main_thread));
 
        m_sourcecache.insert(name_of_shader, filename, program, true);
 }
 
 void ShaderSource::rebuildShaders()
 {
-       JMutexAutoLock lock(m_shaderinfo_cache_mutex);
+       MutexAutoLock lock(m_shaderinfo_cache_mutex);
 
        /*// Oh well... just clear everything, they'll load sometime.
        m_shaderinfo_cache.clear();
@@ -580,50 +517,51 @@ void ShaderSource::rebuildShaders()
        for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
                ShaderInfo *info = &m_shaderinfo_cache[i];
                if(info->name != ""){
-                       *info = generate_shader(info->name, m_device,
-                                       m_shader_callback, &m_sourcecache);
+                       *info = generate_shader(info->name, info->material_type,
+                                       info->drawtype, m_device, m_callbacks,
+                                       m_setter_factories, &m_sourcecache);
                }
        }
 }
 
-void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
-               bool is_highlevel, const std::string &name)
-{
-       for(u32 i=0; i<m_global_setters.size(); i++){
-               IShaderConstantSetter *setter = m_global_setters[i];
-               setter->onSetConstants(services, is_highlevel);
-       }
-}
 
-ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
-               video::IShaderConstantSetCallBack *callback,
+ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
+               IrrlichtDevice *device, std::vector<ShaderCallback *> &callbacks,
+               const std::vector<IShaderConstantSetterFactory*> &setter_factories,
                SourceShaderCache *sourcecache)
 {
-       /*infostream<<"generate_shader(): "
-                       "\""<<name<<"\""<<std::endl;*/
-
        ShaderInfo shaderinfo;
        shaderinfo.name = name;
+       shaderinfo.material_type = material_type;
+       shaderinfo.drawtype = drawtype;
        shaderinfo.material = video::EMT_SOLID;
-
-       /*
-               Get the base material
-       */
-       std::string base_material_name =
-               trim(sourcecache->getOrLoad(name, "base.txt"));
-       for(s32 i = 0; video::sBuiltInMaterialTypeNames[i] != 0; i++){
-               if(video::sBuiltInMaterialTypeNames[i] == base_material_name){
-                       shaderinfo.material = (video::E_MATERIAL_TYPE) i;
+       switch(material_type){
+               case TILE_MATERIAL_BASIC:
+                       shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
                        break;
-               }
+               case TILE_MATERIAL_ALPHA:
+                       shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+                       break;
+               case TILE_MATERIAL_LIQUID_TRANSPARENT:
+                       shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+                       break;
+               case TILE_MATERIAL_LIQUID_OPAQUE:
+                       shaderinfo.base_material = video::EMT_SOLID;
+                       break;
+               case TILE_MATERIAL_WAVING_LEAVES:
+                       shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+                       break;
+               case TILE_MATERIAL_WAVING_PLANTS:
+                       shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
+               break;
        }
 
        bool enable_shaders = g_settings->getBool("enable_shaders");
-       if(!enable_shaders)
+       if (!enable_shaders)
                return shaderinfo;
 
        video::IVideoDriver* driver = device->getVideoDriver();
-       assert(driver);
+       sanity_check(driver);
 
        video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
        if(!gpu){
@@ -643,7 +581,6 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
        load_shaders(name, sourcecache, driver->getDriverType(),
                        enable_shaders, vertex_program, pixel_program,
                        geometry_program, is_highlevel);
-
        // Check hardware/driver support
        if(vertex_program != "" &&
                        !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
@@ -675,25 +612,117 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
 
        // Create shaders header
        std::string shaders_header = "#version 120\n";
-       
+
+       static const char* drawTypes[] = {
+               "NDT_NORMAL",
+               "NDT_AIRLIKE",
+               "NDT_LIQUID",
+               "NDT_FLOWINGLIQUID",
+               "NDT_GLASSLIKE",
+               "NDT_ALLFACES",
+               "NDT_ALLFACES_OPTIONAL",
+               "NDT_TORCHLIKE",
+               "NDT_SIGNLIKE",
+               "NDT_PLANTLIKE",
+               "NDT_FENCELIKE",
+               "NDT_RAILLIKE",
+               "NDT_NODEBOX",
+               "NDT_GLASSLIKE_FRAMED",
+               "NDT_FIRELIKE",
+               "NDT_GLASSLIKE_FRAMED_OPTIONAL"
+       };
+
+       for (int i = 0; i < 14; i++){
+               shaders_header += "#define ";
+               shaders_header += drawTypes[i];
+               shaders_header += " ";
+               shaders_header += itos(i);
+               shaders_header += "\n";
+       }
+
+       static const char* materialTypes[] = {
+               "TILE_MATERIAL_BASIC",
+               "TILE_MATERIAL_ALPHA",
+               "TILE_MATERIAL_LIQUID_TRANSPARENT",
+               "TILE_MATERIAL_LIQUID_OPAQUE",
+               "TILE_MATERIAL_WAVING_LEAVES",
+               "TILE_MATERIAL_WAVING_PLANTS"
+       };
+
+       for (int i = 0; i < 6; i++){
+               shaders_header += "#define ";
+               shaders_header += materialTypes[i];
+               shaders_header += " ";
+               shaders_header += itos(i);
+               shaders_header += "\n";
+       }
+
+       shaders_header += "#define MATERIAL_TYPE ";
+       shaders_header += itos(material_type);
+       shaders_header += "\n";
+       shaders_header += "#define DRAW_TYPE ";
+       shaders_header += itos(drawtype);
+       shaders_header += "\n";
+
+       if (g_settings->getBool("generate_normalmaps")) {
+               shaders_header += "#define GENERATE_NORMALMAPS 1\n";
+       } else {
+               shaders_header += "#define GENERATE_NORMALMAPS 0\n";
+       }
+       shaders_header += "#define NORMALMAPS_STRENGTH ";
+       shaders_header += ftos(g_settings->getFloat("normalmaps_strength"));
+       shaders_header += "\n";
+       float sample_step;
+       int smooth = (int)g_settings->getFloat("normalmaps_smooth");
+       switch (smooth){
+       case 0:
+               sample_step = 0.0078125; // 1.0 / 128.0
+               break;
+       case 1:
+               sample_step = 0.00390625; // 1.0 / 256.0
+               break;
+       case 2:
+               sample_step = 0.001953125; // 1.0 / 512.0
+               break;
+       default:
+               sample_step = 0.0078125;
+               break;
+       }
+       shaders_header += "#define SAMPLE_STEP ";
+       shaders_header += ftos(sample_step);
+       shaders_header += "\n";
+
        if (g_settings->getBool("enable_bumpmapping"))
                shaders_header += "#define ENABLE_BUMPMAPPING\n";
 
        if (g_settings->getBool("enable_parallax_occlusion")){
+               int mode = g_settings->getFloat("parallax_occlusion_mode");
+               float scale = g_settings->getFloat("parallax_occlusion_scale");
+               float bias = g_settings->getFloat("parallax_occlusion_bias");
+               int iterations = g_settings->getFloat("parallax_occlusion_iterations");
                shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
+               shaders_header += "#define PARALLAX_OCCLUSION_MODE ";
+               shaders_header += itos(mode);
+               shaders_header += "\n";
                shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
-               shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
+               shaders_header += ftos(scale);
                shaders_header += "\n";
                shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
-               shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
+               shaders_header += ftos(bias);
                shaders_header += "\n";
-               }
+               shaders_header += "#define PARALLAX_OCCLUSION_ITERATIONS ";
+               shaders_header += itos(iterations);
+               shaders_header += "\n";
+       }
 
+       shaders_header += "#define USE_NORMALMAPS ";
        if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
-               shaders_header += "#define USE_NORMALMAPS\n";
+               shaders_header += "1\n";
+       else
+               shaders_header += "0\n";
 
        if (g_settings->getBool("enable_waving_water")){
-               shaders_header += "#define ENABLE_WAVING_WATER\n";
+               shaders_header += "#define ENABLE_WAVING_WATER 1\n";
                shaders_header += "#define WATER_WAVE_HEIGHT ";
                shaders_header += ftos(g_settings->getFloat("water_wave_height"));
                shaders_header += "\n";
@@ -703,31 +732,46 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
                shaders_header += "#define WATER_WAVE_SPEED ";
                shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
                shaders_header += "\n";
+       } else{
+               shaders_header += "#define ENABLE_WAVING_WATER 0\n";
        }
 
+       shaders_header += "#define ENABLE_WAVING_LEAVES ";
        if (g_settings->getBool("enable_waving_leaves"))
-               shaders_header += "#define ENABLE_WAVING_LEAVES\n";
+               shaders_header += "1\n";
+       else
+               shaders_header += "0\n";
 
+       shaders_header += "#define ENABLE_WAVING_PLANTS ";
        if (g_settings->getBool("enable_waving_plants"))
-               shaders_header += "#define ENABLE_WAVING_PLANTS\n";
+               shaders_header += "1\n";
+       else
+               shaders_header += "0\n";
 
-       if(pixel_program != "")
-               pixel_program = shaders_header + pixel_program;
-       if(vertex_program != "")
-               vertex_program = shaders_header + vertex_program;
-       if(geometry_program != "")
-               geometry_program = shaders_header + geometry_program;
+       if (g_settings->getBool("tone_mapping"))
+               shaders_header += "#define ENABLE_TONE_MAPPING\n";
+
+       shaders_header += "#define FOG_START ";
+       shaders_header += ftos(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
+       shaders_header += "\n";
 
        // Call addHighLevelShaderMaterial() or addShaderMaterial()
        const c8* vertex_program_ptr = 0;
        const c8* pixel_program_ptr = 0;
        const c8* geometry_program_ptr = 0;
-       if(vertex_program != "")
+       if (!vertex_program.empty()) {
+               vertex_program = shaders_header + vertex_program;
                vertex_program_ptr = vertex_program.c_str();
-       if(pixel_program != "")
+       }
+       if (!pixel_program.empty()) {
+               pixel_program = shaders_header + pixel_program;
                pixel_program_ptr = pixel_program.c_str();
-       if(geometry_program != "")
+       }
+       if (!geometry_program.empty()) {
+               geometry_program = shaders_header + geometry_program;
                geometry_program_ptr = geometry_program.c_str();
+       }
+       ShaderCallback *cb = new ShaderCallback(setter_factories);
        s32 shadermat = -1;
        if(is_highlevel){
                infostream<<"Compiling high level shaders for "<<name<<std::endl;
@@ -737,23 +781,26 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
                        video::EVST_VS_1_1,   // Vertex shader version
                        pixel_program_ptr,    // Pixel shader program
                        "pixelMain",          // Pixel shader entry point
-                       video::EPST_PS_1_1,   // Pixel shader version
+                       video::EPST_PS_1_2,   // Pixel shader version
                        geometry_program_ptr, // Geometry shader program
                        "geometryMain",       // Geometry shader entry point
                        video::EGST_GS_4_0,   // Geometry shader version
                        scene::EPT_TRIANGLES,      // Geometry shader input
                        scene::EPT_TRIANGLE_STRIP, // Geometry shader output
                        0,                         // Support maximum number of vertices
-                       callback,             // Set-constant callback
-                       shaderinfo.material,  // Base material
-                       1                     // Userdata passed to callback
+                       cb, // Set-constant callback
+                       shaderinfo.base_material,  // Base material
+                       1                          // Userdata passed to callback
                        );
-
                if(shadermat == -1){
                        errorstream<<"generate_shader(): "
                                        "failed to generate \""<<name<<"\", "
                                        "addHighLevelShaderMaterial failed."
                                        <<std::endl;
+                       dumpShaderProgram(warningstream, "Vertex", vertex_program);
+                       dumpShaderProgram(warningstream, "Pixel", pixel_program);
+                       dumpShaderProgram(warningstream, "Geometry", geometry_program);
+                       delete cb;
                        return shaderinfo;
                }
        }
@@ -762,8 +809,8 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
                shadermat = gpu->addShaderMaterial(
                        vertex_program_ptr,   // Vertex shader program
                        pixel_program_ptr,    // Pixel shader program
-                       callback,             // Set-constant callback
-                       shaderinfo.material,  // Base material
+                       cb, // Set-constant callback
+                       shaderinfo.base_material,  // Base material
                        0                     // Userdata passed to callback
                        );
 
@@ -772,9 +819,13 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
                                        "failed to generate \""<<name<<"\", "
                                        "addShaderMaterial failed."
                                        <<std::endl;
+                       dumpShaderProgram(warningstream, "Vertex", vertex_program);
+                       dumpShaderProgram(warningstream,"Pixel", pixel_program);
+                       delete cb;
                        return shaderinfo;
                }
        }
+       callbacks.push_back(cb);
 
        // HACK, TODO: investigate this better
        // Grab the material renderer once more so minetest doesn't crash on exit
@@ -817,3 +868,21 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
        }
 
 }
+
+void dumpShaderProgram(std::ostream &output_stream,
+               const std::string &program_type, const std::string &program)
+{
+       output_stream << program_type << " shader program:" << std::endl <<
+               "----------------------------------" << std::endl;
+       size_t pos = 0;
+       size_t prev = 0;
+       s16 line = 1;
+       while ((pos = program.find("\n", prev)) != std::string::npos) {
+               output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
+                       std::endl;
+               prev = pos + 1;
+       }
+       output_stream << line << ": " << program.substr(prev) << std::endl <<
+               "End of " << program_type << " shader program." << std::endl <<
+               " " << std::endl;
+}