]> git.lizzy.rs Git - minetest.git/blobdiff - src/shader.cpp
Fix item and wield meshes (#6596)
[minetest.git] / src / shader.cpp
index d39a8307c632e11a1f3098f329c3f0e33daf05c3..3b49a36bad48c25f6753e1f0bb61a9314c6dd010 100644 (file)
@@ -32,10 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <IMaterialRenderer.h>
 #include <IMaterialRendererServices.h>
 #include <IShaderConstantSetCallBack.h>
+#include "client/renderingengine.h"
 #include "EShaderTypes.h"
 #include "log.h"
 #include "gamedef.h"
-#include "strfnd.h" // trim()
 #include "client/tile.h"
 
 /*
@@ -56,7 +56,7 @@ std::string getShaderPath(const std::string &name_of_shader,
                const std::string &filename)
 {
        std::string combined = name_of_shader + DIR_DELIM + filename;
-       std::string fullpath = "";
+       std::string fullpath;
        /*
                Check from cache
        */
@@ -68,8 +68,7 @@ std::string getShaderPath(const std::string &name_of_shader,
                Check from shader_path
        */
        std::string shader_path = g_settings->get("shader_path");
-       if(shader_path != "")
-       {
+       if (!shader_path.empty()) {
                std::string testpath = shader_path + DIR_DELIM + combined;
                if(fs::PathExists(testpath))
                        fullpath = testpath;
@@ -78,8 +77,7 @@ std::string getShaderPath(const std::string &name_of_shader,
        /*
                Check from default data directory
        */
-       if(fullpath == "")
-       {
+       if (fullpath.empty()) {
                std::string rel_path = std::string("client") + DIR_DELIM
                                + "shaders" + DIR_DELIM
                                + name_of_shader + DIR_DELIM
@@ -110,9 +108,9 @@ class SourceShaderCache
                // Try to use local shader instead if asked to
                if(prefer_local){
                        std::string path = getShaderPath(name_of_shader, filename);
-                       if(path != ""){
+                       if(!path.empty()){
                                std::string p = readFile(path);
-                               if(p != ""){
+                               if (!p.empty()) {
                                        m_programs[combined] = p;
                                        return;
                                }
@@ -140,7 +138,7 @@ class SourceShaderCache
                if (n != m_programs.end())
                        return n->second;
                std::string path = getShaderPath(name_of_shader, filename);
-               if (path == "") {
+               if (path.empty()) {
                        infostream << "SourceShaderCache::getOrLoad(): No path found for \""
                                << combined << "\"" << std::endl;
                        return "";
@@ -148,7 +146,7 @@ class SourceShaderCache
                infostream << "SourceShaderCache::getOrLoad(): Loading path \""
                        << path << "\"" << std::endl;
                std::string p = readFile(path);
-               if (p != "") {
+               if (!p.empty()) {
                        m_programs[combined] = p;
                        return p;
                }
@@ -168,29 +166,27 @@ class SourceShaderCache
        }
 };
 
+
 /*
        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 (IShaderConstantSetterFactory *factory : factories)
+                       m_setters.push_back(factory->create());
+       }
+
+       ~ShaderCallback()
+       {
+               for (IShaderConstantSetter *setter : m_setters)
+                       delete setter;
+       }
 
        virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData)
        {
@@ -199,20 +195,27 @@ class ShaderCallback : public video::IShaderConstantSetCallBack
 
                bool is_highlevel = userData;
 
-               m_scsr->onSetConstants(services, is_highlevel, m_name);
+               for (IShaderConstantSetter *setter : m_setters)
+                       setter->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)
+       MainShaderConstantSetter() :
+               m_world_view_proj("mWorldViewProj"),
+               m_world("mWorld")
        {}
-       ~MainShaderConstantSetter() {}
+       ~MainShaderConstantSetter() = default;
 
        virtual void onSetConstants(video::IMaterialRendererServices *services,
                        bool is_highlevel)
@@ -220,50 +223,43 @@ class MainShaderConstantSetter : public IShaderConstantSetter
                video::IVideoDriver *driver = services->getVideoDriver();
                sanity_check(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);
-
-               // 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
-               core::matrix4 transWorld = driver->getTransform(video::ETS_WORLD);
-               transWorld = transWorld.getTransposed();
-               if(is_highlevel)
-                       services->setVertexShaderConstant("mTransWorld", transWorld.pointer(), 16);
-               else
-                       services->setVertexShaderConstant(transWorld.pointer(), 8, 4);
-
-               // set world matrix
+               // Set world matrix
                core::matrix4 world = driver->getTransform(video::ETS_WORLD);
-               if(is_highlevel)
-                       services->setVertexShaderConstant("mWorld", 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);
 
        }
 };
 
+
+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();
        ~ShaderSource();
 
        /*
@@ -303,22 +299,15 @@ class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterR
        // 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;
+       std::thread::id m_main_thread;
 
        // Cache of source shaders
        // This should be only accessed from the main thread
@@ -328,65 +317,57 @@ class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterR
        // The first position contains a dummy shader.
        std::vector<ShaderInfo> m_shaderinfo_cache;
        // The former container is behind this mutex
-       Mutex m_shaderinfo_cache_mutex;
+       std::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)
+IWritableShaderSource *createShaderSource()
 {
-       return new ShaderSource(device);
+       return new ShaderSource();
 }
 
 /*
        Generate shader given the shader name.
 */
-ShaderInfo generate_shader(std::string name,
-               u8 material_type, u8 drawtype,
-               IrrlichtDevice *device,
-               video::IShaderConstantSetCallBack *callback,
+ShaderInfo generate_shader(const std::string &name,
+               u8 material_type, u8 drawtype, std::vector<ShaderCallback *> &callbacks,
+               const std::vector<IShaderConstantSetterFactory *> &setter_factories,
                SourceShaderCache *sourcecache);
 
 /*
        Load shader programs
 */
-void load_shaders(std::string name, SourceShaderCache *sourcecache,
+void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
                video::E_DRIVER_TYPE drivertype, bool enable_shaders,
                std::string &vertex_program, std::string &pixel_program,
                std::string &geometry_program, bool &is_highlevel);
 
-ShaderSource::ShaderSource(IrrlichtDevice *device):
-               m_device(device)
+ShaderSource::ShaderSource()
 {
-       assert(m_device); // Pre-condition
-
-       m_shader_callback = new ShaderCallback(this, "default");
-
-       m_main_thread = thr_get_current_thread_id();
+       m_main_thread = std::this_thread::get_id();
 
        // Add a dummy ShaderInfo as the first index, named ""
-       m_shaderinfo_cache.push_back(ShaderInfo());
+       m_shaderinfo_cache.emplace_back();
 
        // Add main global constant setter
-       addGlobalConstantSetter(new MainShaderConstantSetter(device));
+       addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
 }
 
 ShaderSource::~ShaderSource()
 {
-       for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
-                       iter != m_global_setters.end(); ++iter) {
-               delete *iter;
+       for (ShaderCallback *callback : m_callbacks) {
+               delete callback;
        }
-       m_global_setters.clear();
-
-       if (m_shader_callback) {
-               m_shader_callback->drop();
-               m_shader_callback = NULL;
+       for (IShaderConstantSetterFactory *setter_factorie : m_setter_factories) {
+               delete setter_factorie;
        }
 }
 
@@ -397,36 +378,34 @@ u32 ShaderSource::getShader(const std::string &name,
                Get shader
        */
 
-       if (thr_is_current_thread(m_main_thread)) {
+       if (std::this_thread::get_id() == m_main_thread) {
                return getShaderIdDirect(name, material_type, drawtype);
-       } else {
-               /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
+       }
 
-               // We're gonna ask the result to be put into here
+       /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
 
-               static ResultQueue<std::string, u32, u8, u8> result_queue;
+       // We're gonna ask the result to be put into here
 
-               // Throw a request in
-               m_get_shader_queue.add(name, 0, 0, &result_queue);
+       static ResultQueue<std::string, u32, u8, u8> result_queue;
 
-               /* infostream<<"Waiting for shader from main thread, name=\""
-                               <<name<<"\""<<std::endl;*/
+       // Throw a request in
+       m_get_shader_queue.add(name, 0, 0, &result_queue);
 
-               while(true) {
-                       GetResult<std::string, u32, u8, u8>
-                               result = result_queue.pop_frontNoEx();
+       /* infostream<<"Waiting for shader from main thread, name=\""
+                       <<name<<"\""<<std::endl;*/
 
-                       if (result.key == name) {
-                               return result.item;
-                       }
-                       else {
-                               errorstream << "Got shader with invalid name: " << result.key << std::endl;
-                       }
+       while(true) {
+               GetResult<std::string, u32, u8, u8>
+                       result = result_queue.pop_frontNoEx();
+
+               if (result.key == name) {
+                       return result.item;
                }
 
+               errorstream << "Got shader with invalid name: " << result.key << std::endl;
        }
 
-       infostream<<"getShader(): Failed"<<std::endl;
+       infostream << "getShader(): Failed" << std::endl;
 
        return 0;
 }
@@ -440,7 +419,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
        //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
 
        // Empty name means shader 0
-       if(name == ""){
+       if (name.empty()) {
                infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
                return 0;
        }
@@ -456,14 +435,14 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name,
        /*
                Calling only allowed from main thread
        */
-       if (!thr_is_current_thread(m_main_thread)) {
+       if (std::this_thread::get_id() != m_main_thread) {
                errorstream<<"ShaderSource::getShaderIdDirect() "
                                "called not from main thread"<<std::endl;
                return 0;
        }
 
-       ShaderInfo info = generate_shader(name, material_type, drawtype, m_device,
-                       m_shader_callback, &m_sourcecache);
+       ShaderInfo info = generate_shader(name, material_type, drawtype,
+                       m_callbacks, m_setter_factories, &m_sourcecache);
 
        /*
                Add shader to caches (add dummy shaders too)
@@ -504,7 +483,7 @@ void ShaderSource::insertSourceShader(const std::string &name_of_shader,
                        "name_of_shader=\""<<name_of_shader<<"\", "
                        "filename=\""<<filename<<"\""<<std::endl;*/
 
-       sanity_check(thr_is_current_thread(m_main_thread));
+       sanity_check(std::this_thread::get_id() == m_main_thread);
 
        m_sourcecache.insert(name_of_shader, filename, program, true);
 }
@@ -524,26 +503,20 @@ void ShaderSource::rebuildShaders()
        */
 
        // Recreate shaders
-       for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
-               ShaderInfo *info = &m_shaderinfo_cache[i];
-               if(info->name != ""){
+       for (ShaderInfo &i : m_shaderinfo_cache) {
+               ShaderInfo *info = &i;
+               if (!info->name.empty()) {
                        *info = generate_shader(info->name, info->material_type,
-                                       info->drawtype, m_device, m_shader_callback, &m_sourcecache);
+                                       info->drawtype, 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, u8 material_type, u8 drawtype,
-               IrrlichtDevice *device, video::IShaderConstantSetCallBack *callback,
+ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
+               std::vector<ShaderCallback *> &callbacks,
+               const std::vector<IShaderConstantSetterFactory *> &setter_factories,
                SourceShaderCache *sourcecache)
 {
        ShaderInfo shaderinfo;
@@ -551,33 +524,27 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
        shaderinfo.material_type = material_type;
        shaderinfo.drawtype = drawtype;
        shaderinfo.material = video::EMT_SOLID;
-       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_VERTEX_ALPHA;
-                       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;
+       switch (material_type) {
+       case TILE_MATERIAL_OPAQUE:
+       case TILE_MATERIAL_LIQUID_OPAQUE:
+               shaderinfo.base_material = video::EMT_SOLID;
+               break;
+       case TILE_MATERIAL_ALPHA:
+       case TILE_MATERIAL_LIQUID_TRANSPARENT:
+               shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
+               break;
+       case TILE_MATERIAL_BASIC:
+       case TILE_MATERIAL_WAVING_LEAVES:
+       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();
-       sanity_check(driver);
+       video::IVideoDriver *driver = RenderingEngine::get_video_driver();
 
        video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
        if(!gpu){
@@ -598,7 +565,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                        enable_shaders, vertex_program, pixel_program,
                        geometry_program, is_highlevel);
        // Check hardware/driver support
-       if(vertex_program != "" &&
+       if (!vertex_program.empty() &&
                        !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
                        !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
                infostream<<"generate_shader(): vertex shaders disabled "
@@ -606,7 +573,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                                <<std::endl;
                vertex_program = "";
        }
-       if(pixel_program != "" &&
+       if (!pixel_program.empty() &&
                        !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
                        !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
                infostream<<"generate_shader(): pixel shaders disabled "
@@ -614,7 +581,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                                <<std::endl;
                pixel_program = "";
        }
-       if(geometry_program != "" &&
+       if (!geometry_program.empty() &&
                        !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
                infostream<<"generate_shader(): geometry shaders disabled "
                                "because of missing driver/hardware support."
@@ -623,7 +590,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
        }
 
        // If no shaders are used, don't make a separate material type
-       if(vertex_program == "" && pixel_program == "" && geometry_program == "")
+       if (vertex_program.empty() && pixel_program.empty() && geometry_program.empty())
                return shaderinfo;
 
        // Create shaders header
@@ -645,7 +612,8 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                "NDT_NODEBOX",
                "NDT_GLASSLIKE_FRAMED",
                "NDT_FIRELIKE",
-               "NDT_GLASSLIKE_FRAMED_OPTIONAL"
+               "NDT_GLASSLIKE_FRAMED_OPTIONAL",
+               "NDT_PLANTLIKE_ROOTED",
        };
 
        for (int i = 0; i < 14; i++){
@@ -662,10 +630,11 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                "TILE_MATERIAL_LIQUID_TRANSPARENT",
                "TILE_MATERIAL_LIQUID_OPAQUE",
                "TILE_MATERIAL_WAVING_LEAVES",
-               "TILE_MATERIAL_WAVING_PLANTS"
+               "TILE_MATERIAL_WAVING_PLANTS",
+               "TILE_MATERIAL_OPAQUE"
        };
 
-       for (int i = 0; i < 6; i++){
+       for (int i = 0; i < 7; i++){
                shaders_header += "#define ";
                shaders_header += materialTypes[i];
                shaders_header += " ";
@@ -767,6 +736,10 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
        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;
@@ -783,6 +756,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                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;
@@ -792,14 +766,14 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                        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
+                       cb, // Set-constant callback
                        shaderinfo.base_material,  // Base material
                        1                          // Userdata passed to callback
                        );
@@ -808,6 +782,10 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                                        "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;
                }
        }
@@ -816,7 +794,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                shadermat = gpu->addShaderMaterial(
                        vertex_program_ptr,   // Vertex shader program
                        pixel_program_ptr,    // Pixel shader program
-                       callback,             // Set-constant callback
+                       cb, // Set-constant callback
                        shaderinfo.base_material,  // Base material
                        0                     // Userdata passed to callback
                        );
@@ -826,9 +804,13 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
                                        "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
@@ -839,7 +821,7 @@ ShaderInfo generate_shader(std::string name, u8 material_type, u8 drawtype,
        return shaderinfo;
 }
 
-void load_shaders(std::string name, SourceShaderCache *sourcecache,
+void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
                video::E_DRIVER_TYPE drivertype, bool enable_shaders,
                std::string &vertex_program, std::string &pixel_program,
                std::string &geometry_program, bool &is_highlevel)
@@ -864,10 +846,28 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
                        pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
                        geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
                }
-               if(vertex_program != "" || pixel_program != "" || geometry_program != ""){
+               if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
                        is_highlevel = true;
                        return;
                }
        }
 
 }
+
+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;
+}