]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/shader.cpp
make formspec textarea wordwrap
[dragonfireclient.git] / src / shader.cpp
index 9e1a51f23cec789a167400e0a9e3d7da6ac7e389..4013add6cf9c2d37e4076f58d0e8a756712a8662 100644 (file)
@@ -1,19 +1,19 @@
 /*
-Minetest-c55
-Copyright (C) 2012 celeron55, Perttu Ahola <celeron55@gmail.com>
-Copyright (C) 2012 Kahrl <kahrl@gmx.net>
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013 Kahrl <kahrl@gmx.net>
 
 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 2 of the License, or
+it under the terms of the GNU Lesser General Public License as published by
+the Free Software Foundation; either version 2.1 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.
+GNU Lesser General Public License for more details.
 
-You should have received a copy of the GNU General Public License along
+You should have received a copy of the GNU Lesser General Public License along
 with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
@@ -125,10 +125,10 @@ class SourceShaderCache
                        const std::string &filename)
        {
                std::string combined = name_of_shader + DIR_DELIM + filename;
-               core::map<std::string, std::string>::Node *n;
+               std::map<std::string, std::string>::iterator n;
                n = m_programs.find(combined);
-               if(n)
-                       return n->getValue();
+               if(n != m_programs.end())
+                       return n->second;
                return "";
        }
        // Primarily fetches from cache, secondarily tries to read from filesystem
@@ -136,10 +136,10 @@ class SourceShaderCache
                        const std::string &filename)
        {
                std::string combined = name_of_shader + DIR_DELIM + filename;
-               core::map<std::string, std::string>::Node *n;
+               std::map<std::string, std::string>::iterator n;
                n = m_programs.find(combined);
-               if(n)
-                       return n->getValue();
+               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 \""
@@ -156,7 +156,7 @@ class SourceShaderCache
                return "";
        }
 private:
-       core::map<std::string, std::string> m_programs;
+       std::map<std::string, std::string> m_programs;
        std::string readFile(const std::string &path)
        {
                std::ifstream is(path.c_str(), std::ios::binary);
@@ -210,8 +210,7 @@ class ShaderCallback : public video::IShaderConstantSetCallBack
 class MainShaderConstantSetter : public IShaderConstantSetter
 {
 public:
-       MainShaderConstantSetter(IrrlichtDevice *device):
-               m_device(device)
+       MainShaderConstantSetter(IrrlichtDevice *device)
        {}
        ~MainShaderConstantSetter() {}
 
@@ -240,16 +239,21 @@ class MainShaderConstantSetter : public IShaderConstantSetter
                        services->setVertexShaderConstant(worldViewProj.pointer(), 4, 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
                core::matrix4 world = driver->getTransform(video::ETS_WORLD);
-               world = world.getTransposed();
                if(is_highlevel)
-                       services->setVertexShaderConstant("mTransWorld", world.pointer(), 16);
+                       services->setVertexShaderConstant("mWorld", world.pointer(), 16);
                else
                        services->setVertexShaderConstant(world.pointer(), 8, 4);
-       }
 
-private:
-       IrrlichtDevice *m_device;
+       }
 };
 
 /*
@@ -332,9 +336,9 @@ class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterR
 
        // A shader id is index in this array.
        // The first position contains a dummy shader.
-       core::array<ShaderInfo> m_shaderinfo_cache;
+       std::vector<ShaderInfo> m_shaderinfo_cache;
        // Maps a shader name to an index in the former.
-       core::map<std::string, u32> m_name_to_id;
+       std::map<std::string, u32> m_name_to_id;
        // The two former containers are behind this mutex
        JMutex m_shaderinfo_cache_mutex;
 
@@ -343,7 +347,7 @@ class ShaderSource : public IWritableShaderSource, public IShaderConstantSetterR
 
        // Global constant setters
        // TODO: Delete these in the destructor
-       core::array<IShaderConstantSetter*> m_global_setters;
+       std::vector<IShaderConstantSetter*> m_global_setters;
 };
 
 IWritableShaderSource* createShaderSource(IrrlichtDevice *device)
@@ -362,7 +366,7 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
        Load shader programs
 */
 void load_shaders(std::string name, SourceShaderCache *sourcecache,
-               video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
+               video::E_DRIVER_TYPE drivertype, bool enable_shaders,
                std::string &vertex_program, std::string &pixel_program,
                std::string &geometry_program, bool &is_highlevel);
 
@@ -373,8 +377,6 @@ ShaderSource::ShaderSource(IrrlichtDevice *device):
 
        m_shader_callback = new ShaderCallback(this, "default");
 
-       m_shaderinfo_cache_mutex.Init();
-
        m_main_thread = get_current_thread_id();
 
        // Add a dummy ShaderInfo as the first index, named ""
@@ -388,6 +390,12 @@ ShaderSource::ShaderSource(IrrlichtDevice *device):
 ShaderSource::~ShaderSource()
 {
        //m_shader_callback->drop();
+
+       for (std::vector<IShaderConstantSetter*>::iterator iter = m_global_setters.begin();
+                       iter != m_global_setters.end(); iter++) {
+               delete *iter;
+       }
+       m_global_setters.clear();
 }
 
 u32 ShaderSource::getShaderId(const std::string &name)
@@ -399,10 +407,10 @@ u32 ShaderSource::getShaderId(const std::string &name)
                        See if shader already exists
                */
                JMutexAutoLock lock(m_shaderinfo_cache_mutex);
-               core::map<std::string, u32>::Node *n;
+               std::map<std::string, u32>::iterator n;
                n = m_name_to_id.find(name);
-               if(n != NULL)
-                       return n->getValue();
+               if(n != m_name_to_id.end())
+                       return n->second;
        }
 
        /*
@@ -411,31 +419,30 @@ u32 ShaderSource::getShaderId(const std::string &name)
        if(get_current_thread_id() == m_main_thread){
                return getShaderIdDirect(name);
        } else {
-               infostream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;
+               /*errorstream<<"getShaderId(): Queued: name=\""<<name<<"\""<<std::endl;*/
 
                // We're gonna ask the result to be put into here
-               ResultQueue<std::string, u32, u8, u8> result_queue;
+
+               static ResultQueue<std::string, u32, u8, u8> result_queue;
 
                // Throw a request in
                m_get_shader_queue.add(name, 0, 0, &result_queue);
 
-               infostream<<"Waiting for shader from main thread, name=\""
-                               <<name<<"\""<<std::endl;
+               /* infostream<<"Waiting for shader from main thread, name=\""
+                               <<name<<"\""<<std::endl;*/
 
-               try{
-                       // Wait result for a second
+               while(true) {
                        GetResult<std::string, u32, u8, u8>
-                                       result = result_queue.pop_front(1000);
-
-                       // Check that at least something worked OK
-                       assert(result.key == name);
+                               result = result_queue.pop_frontNoEx();
 
-                       return result.item;
-               }
-               catch(ItemNotFoundException &e){
-                       infostream<<"Waiting for shader timed out."<<std::endl;
-                       return 0;
+                       if (result.key == name) {
+                               return result.item;
+                       }
+                       else {
+                               errorstream << "Got shader with invalid name: " << result.key << std::endl;
+                       }
                }
+
        }
 
        infostream<<"getShaderId(): Failed"<<std::endl;
@@ -471,12 +478,12 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name)
        {
                JMutexAutoLock lock(m_shaderinfo_cache_mutex);
 
-               core::map<std::string, u32>::Node *n;
+               std::map<std::string, u32>::iterator n;
                n = m_name_to_id.find(name);
-               if(n != NULL){
+               if(n != m_name_to_id.end()){
                        /*infostream<<"getShaderIdDirect(): \""<<name
                                        <<"\" found in cache"<<std::endl;*/
-                       return n->getValue();
+                       return n->second;
                }
        }
 
@@ -494,7 +501,7 @@ u32 ShaderSource::getShaderIdDirect(const std::string &name)
 
        u32 id = m_shaderinfo_cache.size();
        m_shaderinfo_cache.push_back(info);
-       m_name_to_id.insert(name, id);
+       m_name_to_id[name] = id;
 
        /*infostream<<"getShaderIdDirect(): "
                        <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;*/
@@ -531,22 +538,17 @@ void ShaderSource::processQueue()
        /*
                Fetch shaders
        */
-       if(m_get_shader_queue.size() > 0){
+       //NOTE this is only thread safe for ONE consumer thread!
+       if(!m_get_shader_queue.empty()){
                GetRequest<std::string, u32, u8, u8>
                                request = m_get_shader_queue.pop();
 
-               /*infostream<<"ShaderSource::processQueue(): "
+               /**errorstream<<"ShaderSource::processQueue(): "
                                <<"got shader request with "
                                <<"name=\""<<request.key<<"\""
-                               <<std::endl;*/
+                               <<std::endl;**/
 
-               GetResult<std::string, u32, u8, u8>
-                               result;
-               result.key = request.key;
-               result.callers = request.callers;
-               result.item = getShaderIdDirect(request.key);
-
-               request.dest->push_back(result);
+               m_get_shader_queue.pushResult(request,getShaderIdDirect(request.key));
        }
 }
 
@@ -579,8 +581,10 @@ void ShaderSource::rebuildShaders()
        // Recreate shaders
        for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
                ShaderInfo *info = &m_shaderinfo_cache[i];
-               *info = generate_shader(info->name, m_device,
-                               m_shader_callback, &m_sourcecache);
+               if(info->name != ""){
+                       *info = generate_shader(info->name, m_device,
+                                       m_shader_callback, &m_sourcecache);
+               }
        }
 }
 
@@ -592,7 +596,7 @@ void ShaderSource::onSetConstants(video::IMaterialRendererServices *services,
                setter->onSetConstants(services, is_highlevel);
        }
 }
+
 ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
                video::IShaderConstantSetCallBack *callback,
                SourceShaderCache *sourcecache)
@@ -616,9 +620,8 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
                }
        }
 
-       // 0 = off, 1 = assembly shaders only, 2 = highlevel or assembly
-       s32 enable_shaders = g_settings->getS32("enable_shaders");
-       if(enable_shaders <= 0)
+       bool enable_shaders = g_settings->getBool("enable_shaders");
+       if(!enable_shaders)
                return shaderinfo;
 
        video::IVideoDriver* driver = device->getVideoDriver();
@@ -672,6 +675,77 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
        if(vertex_program == "" && pixel_program == "" && geometry_program == "")
                return shaderinfo;
 
+       // Create shaders header
+       std::string shaders_header = "#version 120\n";
+
+       if (g_settings->getBool("generate_normalmaps")){
+               shaders_header += "#define GENERATE_NORMALMAPS\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")){
+               shaders_header += "#define ENABLE_PARALLAX_OCCLUSION\n";
+               shaders_header += "#define PARALLAX_OCCLUSION_SCALE ";
+               shaders_header += ftos(g_settings->getFloat("parallax_occlusion_scale"));
+               shaders_header += "\n";
+               shaders_header += "#define PARALLAX_OCCLUSION_BIAS ";
+               shaders_header += ftos(g_settings->getFloat("parallax_occlusion_bias"));
+               shaders_header += "\n";
+       }
+
+       if (g_settings->getBool("enable_bumpmapping") || g_settings->getBool("enable_parallax_occlusion"))
+               shaders_header += "#define USE_NORMALMAPS\n";
+
+       if (g_settings->getBool("enable_waving_water")){
+               shaders_header += "#define ENABLE_WAVING_WATER\n";
+               shaders_header += "#define WATER_WAVE_HEIGHT ";
+               shaders_header += ftos(g_settings->getFloat("water_wave_height"));
+               shaders_header += "\n";
+               shaders_header += "#define WATER_WAVE_LENGTH ";
+               shaders_header += ftos(g_settings->getFloat("water_wave_length"));
+               shaders_header += "\n";
+               shaders_header += "#define WATER_WAVE_SPEED ";
+               shaders_header += ftos(g_settings->getFloat("water_wave_speed"));
+               shaders_header += "\n";
+       }
+
+       if (g_settings->getBool("enable_waving_leaves"))
+               shaders_header += "#define ENABLE_WAVING_LEAVES\n";
+
+       if (g_settings->getBool("enable_waving_plants"))
+               shaders_header += "#define ENABLE_WAVING_PLANTS\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;
+
        // Call addHighLevelShaderMaterial() or addShaderMaterial()
        const c8* vertex_program_ptr = 0;
        const c8* pixel_program_ptr = 0;
@@ -740,7 +814,7 @@ ShaderInfo generate_shader(std::string name, IrrlichtDevice *device,
 }
 
 void load_shaders(std::string name, SourceShaderCache *sourcecache,
-               video::E_DRIVER_TYPE drivertype, s32 enable_shaders,
+               video::E_DRIVER_TYPE drivertype, bool enable_shaders,
                std::string &vertex_program, std::string &pixel_program,
                std::string &geometry_program, bool &is_highlevel)
 {
@@ -749,7 +823,7 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
        geometry_program = "";
        is_highlevel = false;
 
-       if(enable_shaders >= 2){
+       if(enable_shaders){
                // Look for high level shaders
                if(drivertype == video::EDT_DIRECT3D9){
                        // Direct3D 9: HLSL
@@ -770,24 +844,4 @@ void load_shaders(std::string name, SourceShaderCache *sourcecache,
                }
        }
 
-       if(enable_shaders >= 1){
-               // Look for assembly shaders
-               if(drivertype == video::EDT_DIRECT3D8){
-                       // Direct3D 8 assembly shaders
-                       vertex_program = sourcecache->getOrLoad(name, "d3d8_vertex.asm");
-                       pixel_program = sourcecache->getOrLoad(name, "d3d8_pixel.asm");
-               }
-               else if(drivertype == video::EDT_DIRECT3D9){
-                       // Direct3D 9 assembly shaders
-                       vertex_program = sourcecache->getOrLoad(name, "d3d9_vertex.asm");
-                       pixel_program = sourcecache->getOrLoad(name, "d3d9_pixel.asm");
-               }
-               else if(drivertype == video::EDT_OPENGL){
-                       // OpenGL assembly shaders
-                       vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.asm");
-                       pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.asm");
-               }
-               if(vertex_program != "" || pixel_program != "")
-                       return;
-       }
 }