]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/shader.cpp
Revert "Disable dynamic shadows for the 5.5.0 release" (#12032)
[dragonfireclient.git] / src / client / shader.cpp
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013 Kahrl <kahrl@gmx.net>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include <fstream>
22 #include <iterator>
23 #include "shader.h"
24 #include "irrlichttypes_extrabloated.h"
25 #include "irr_ptr.h"
26 #include "debug.h"
27 #include "filesys.h"
28 #include "util/container.h"
29 #include "util/thread.h"
30 #include "settings.h"
31 #include <ICameraSceneNode.h>
32 #include <IGPUProgrammingServices.h>
33 #include <IMaterialRenderer.h>
34 #include <IMaterialRendererServices.h>
35 #include <IShaderConstantSetCallBack.h>
36 #include "client/renderingengine.h"
37 #include "EShaderTypes.h"
38 #include "log.h"
39 #include "gamedef.h"
40 #include "client/tile.h"
41 #include "config.h"
42
43 #if ENABLE_GLES
44 #ifdef _IRR_COMPILE_WITH_OGLES1_
45 #include <GLES/gl.h>
46 #else
47 #include <GLES2/gl2.h>
48 #endif
49 #else
50 #ifndef __APPLE__
51 #include <GL/gl.h>
52 #else
53 #define GL_SILENCE_DEPRECATION
54 #include <OpenGL/gl.h>
55 #endif
56 #endif
57
58 /*
59         A cache from shader name to shader path
60 */
61 MutexedMap<std::string, std::string> g_shadername_to_path_cache;
62
63 /*
64         Gets the path to a shader by first checking if the file
65           name_of_shader/filename
66         exists in shader_path and if not, using the data path.
67
68         If not found, returns "".
69
70         Utilizes a thread-safe cache.
71 */
72 std::string getShaderPath(const std::string &name_of_shader,
73                 const std::string &filename)
74 {
75         std::string combined = name_of_shader + DIR_DELIM + filename;
76         std::string fullpath;
77         /*
78                 Check from cache
79         */
80         bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
81         if(incache)
82                 return fullpath;
83
84         /*
85                 Check from shader_path
86         */
87         std::string shader_path = g_settings->get("shader_path");
88         if (!shader_path.empty()) {
89                 std::string testpath = shader_path + DIR_DELIM + combined;
90                 if(fs::PathExists(testpath))
91                         fullpath = testpath;
92         }
93
94         /*
95                 Check from default data directory
96         */
97         if (fullpath.empty()) {
98                 std::string rel_path = std::string("client") + DIR_DELIM
99                                 + "shaders" + DIR_DELIM
100                                 + name_of_shader + DIR_DELIM
101                                 + filename;
102                 std::string testpath = porting::path_share + DIR_DELIM + rel_path;
103                 if(fs::PathExists(testpath))
104                         fullpath = testpath;
105         }
106
107         // Add to cache (also an empty result is cached)
108         g_shadername_to_path_cache.set(combined, fullpath);
109
110         // Finally return it
111         return fullpath;
112 }
113
114 /*
115         SourceShaderCache: A cache used for storing source shaders.
116 */
117
118 class SourceShaderCache
119 {
120 public:
121         void insert(const std::string &name_of_shader, const std::string &filename,
122                 const std::string &program, bool prefer_local)
123         {
124                 std::string combined = name_of_shader + DIR_DELIM + filename;
125                 // Try to use local shader instead if asked to
126                 if(prefer_local){
127                         std::string path = getShaderPath(name_of_shader, filename);
128                         if(!path.empty()){
129                                 std::string p = readFile(path);
130                                 if (!p.empty()) {
131                                         m_programs[combined] = p;
132                                         return;
133                                 }
134                         }
135                 }
136                 m_programs[combined] = program;
137         }
138
139         std::string get(const std::string &name_of_shader,
140                 const std::string &filename)
141         {
142                 std::string combined = name_of_shader + DIR_DELIM + filename;
143                 StringMap::iterator n = m_programs.find(combined);
144                 if (n != m_programs.end())
145                         return n->second;
146                 return "";
147         }
148
149         // Primarily fetches from cache, secondarily tries to read from filesystem
150         std::string getOrLoad(const std::string &name_of_shader,
151                 const std::string &filename)
152         {
153                 std::string combined = name_of_shader + DIR_DELIM + filename;
154                 StringMap::iterator n = m_programs.find(combined);
155                 if (n != m_programs.end())
156                         return n->second;
157                 std::string path = getShaderPath(name_of_shader, filename);
158                 if (path.empty()) {
159                         infostream << "SourceShaderCache::getOrLoad(): No path found for \""
160                                 << combined << "\"" << std::endl;
161                         return "";
162                 }
163                 infostream << "SourceShaderCache::getOrLoad(): Loading path \""
164                         << path << "\"" << std::endl;
165                 std::string p = readFile(path);
166                 if (!p.empty()) {
167                         m_programs[combined] = p;
168                         return p;
169                 }
170                 return "";
171         }
172 private:
173         StringMap m_programs;
174
175         std::string readFile(const std::string &path)
176         {
177                 std::ifstream is(path.c_str(), std::ios::binary);
178                 if(!is.is_open())
179                         return "";
180                 std::ostringstream tmp_os;
181                 tmp_os << is.rdbuf();
182                 return tmp_os.str();
183         }
184 };
185
186
187 /*
188         ShaderCallback: Sets constants that can be used in shaders
189 */
190
191 class ShaderCallback : public video::IShaderConstantSetCallBack
192 {
193         std::vector<std::unique_ptr<IShaderConstantSetter>> m_setters;
194
195 public:
196         template <typename Factories>
197         ShaderCallback(const Factories &factories)
198         {
199                 for (auto &&factory : factories)
200                         m_setters.push_back(std::unique_ptr<IShaderConstantSetter>(factory->create()));
201         }
202
203         virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override
204         {
205                 video::IVideoDriver *driver = services->getVideoDriver();
206                 sanity_check(driver != NULL);
207
208                 for (auto &&setter : m_setters)
209                         setter->onSetConstants(services);
210         }
211
212         virtual void OnSetMaterial(const video::SMaterial& material) override
213         {
214                 for (auto &&setter : m_setters)
215                         setter->onSetMaterial(material);
216         }
217 };
218
219
220 /*
221         MainShaderConstantSetter: Set basic constants required for almost everything
222 */
223
224 class MainShaderConstantSetter : public IShaderConstantSetter
225 {
226         CachedVertexShaderSetting<float, 16> m_world_view_proj;
227         CachedVertexShaderSetting<float, 16> m_world;
228
229         // Shadow-related
230         CachedPixelShaderSetting<float, 16> m_shadow_view_proj;
231         CachedPixelShaderSetting<float, 3> m_light_direction;
232         CachedPixelShaderSetting<float> m_texture_res;
233         CachedPixelShaderSetting<float> m_shadow_strength;
234         CachedPixelShaderSetting<float> m_time_of_day;
235         CachedPixelShaderSetting<float> m_shadowfar;
236         CachedPixelShaderSetting<s32> m_shadow_texture;
237
238 #if ENABLE_GLES
239         // Modelview matrix
240         CachedVertexShaderSetting<float, 16> m_world_view;
241         // Texture matrix
242         CachedVertexShaderSetting<float, 16> m_texture;
243         // Normal matrix
244         CachedVertexShaderSetting<float, 9> m_normal;
245 #endif
246
247 public:
248         MainShaderConstantSetter() :
249                   m_world_view_proj("mWorldViewProj")
250                 , m_world("mWorld")
251 #if ENABLE_GLES
252                 , m_world_view("mWorldView")
253                 , m_texture("mTexture")
254                 , m_normal("mNormal")
255 #endif
256                 , m_shadow_view_proj("m_ShadowViewProj")
257                 , m_light_direction("v_LightDirection")
258                 , m_texture_res("f_textureresolution")
259                 , m_shadow_strength("f_shadow_strength")
260                 , m_time_of_day("f_timeofday")
261                 , m_shadowfar("f_shadowfar")
262                 , m_shadow_texture("ShadowMapSampler")
263         {}
264         ~MainShaderConstantSetter() = default;
265
266         virtual void onSetConstants(video::IMaterialRendererServices *services) override
267         {
268                 video::IVideoDriver *driver = services->getVideoDriver();
269                 sanity_check(driver);
270
271                 // Set world matrix
272                 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
273                 m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
274
275                 // Set clip matrix
276                 core::matrix4 worldView;
277                 worldView = driver->getTransform(video::ETS_VIEW);
278                 worldView *= world;
279
280                 core::matrix4 worldViewProj;
281                 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
282                 worldViewProj *= worldView;
283                 m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
284
285 #if ENABLE_GLES
286                 core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0);
287                 m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services);
288                 m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services);
289
290                 core::matrix4 normal;
291                 worldView.getTransposed(normal);
292                 sanity_check(normal.makeInverse());
293                 float m[9] = {
294                         normal[0], normal[1], normal[2],
295                         normal[4], normal[5], normal[6],
296                         normal[8], normal[9], normal[10],
297                 };
298                 m_normal.set(m, services);
299 #endif
300
301                 // Set uniforms for Shadow shader
302                 if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) {
303                         const auto &light = shadow->getDirectionalLight();
304
305                         core::matrix4 shadowViewProj = light.getProjectionMatrix();
306                         shadowViewProj *= light.getViewMatrix();
307                         m_shadow_view_proj.set(shadowViewProj.pointer(), services);
308
309                         float v_LightDirection[3];
310                         light.getDirection().getAs3Values(v_LightDirection);
311                         m_light_direction.set(v_LightDirection, services);
312
313                         float TextureResolution = light.getMapResolution();
314                         m_texture_res.set(&TextureResolution, services);
315
316                         float ShadowStrength = shadow->getShadowStrength();
317                         m_shadow_strength.set(&ShadowStrength, services);
318
319                         float timeOfDay = shadow->getTimeOfDay();
320                         m_time_of_day.set(&timeOfDay, services);
321
322                         float shadowFar = shadow->getMaxShadowFar();
323                         m_shadowfar.set(&shadowFar, services);
324
325                         // I dont like using this hardcoded value. maybe something like
326                         // MAX_TEXTURE - 1 or somthing like that??
327                         s32 TextureLayerID = 3;
328                         m_shadow_texture.set(&TextureLayerID, services);
329                 }
330         }
331 };
332
333
334 class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
335 {
336 public:
337         virtual IShaderConstantSetter* create()
338                 { return new MainShaderConstantSetter(); }
339 };
340
341
342 /*
343         ShaderSource
344 */
345
346 class ShaderSource : public IWritableShaderSource
347 {
348 public:
349         ShaderSource();
350
351         /*
352                 - If shader material specified by name is found from cache,
353                   return the cached id.
354                 - Otherwise generate the shader material, add to cache and return id.
355
356                 The id 0 points to a null shader. Its material is EMT_SOLID.
357         */
358         u32 getShaderIdDirect(const std::string &name,
359                 MaterialType material_type, NodeDrawType drawtype) override;
360
361         /*
362                 If shader specified by the name pointed by the id doesn't
363                 exist, create it, then return id.
364
365                 Can be called from any thread. If called from some other thread
366                 and not found in cache, the call is queued to the main thread
367                 for processing.
368         */
369
370         u32 getShader(const std::string &name,
371                 MaterialType material_type, NodeDrawType drawtype) override;
372
373         ShaderInfo getShaderInfo(u32 id) override;
374
375         // Processes queued shader requests from other threads.
376         // Shall be called from the main thread.
377         void processQueue() override;
378
379         // Insert a shader program into the cache without touching the
380         // filesystem. Shall be called from the main thread.
381         void insertSourceShader(const std::string &name_of_shader,
382                 const std::string &filename, const std::string &program) override;
383
384         // Rebuild shaders from the current set of source shaders
385         // Shall be called from the main thread.
386         void rebuildShaders() override;
387
388         void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) override
389         {
390                 m_setter_factories.push_back(std::unique_ptr<IShaderConstantSetterFactory>(setter));
391         }
392
393 private:
394
395         // The id of the thread that is allowed to use irrlicht directly
396         std::thread::id m_main_thread;
397
398         // Cache of source shaders
399         // This should be only accessed from the main thread
400         SourceShaderCache m_sourcecache;
401
402         // A shader id is index in this array.
403         // The first position contains a dummy shader.
404         std::vector<ShaderInfo> m_shaderinfo_cache;
405         // The former container is behind this mutex
406         std::mutex m_shaderinfo_cache_mutex;
407
408         // Queued shader fetches (to be processed by the main thread)
409         RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
410
411         // Global constant setter factories
412         std::vector<std::unique_ptr<IShaderConstantSetterFactory>> m_setter_factories;
413
414         // Generate shader given the shader name.
415         ShaderInfo generateShader(const std::string &name,
416                         MaterialType material_type, NodeDrawType drawtype);
417 };
418
419 IWritableShaderSource *createShaderSource()
420 {
421         return new ShaderSource();
422 }
423
424 ShaderSource::ShaderSource()
425 {
426         m_main_thread = std::this_thread::get_id();
427
428         // Add a dummy ShaderInfo as the first index, named ""
429         m_shaderinfo_cache.emplace_back();
430
431         // Add main global constant setter
432         addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
433 }
434
435 u32 ShaderSource::getShader(const std::string &name,
436                 MaterialType material_type, NodeDrawType drawtype)
437 {
438         /*
439                 Get shader
440         */
441
442         if (std::this_thread::get_id() == m_main_thread) {
443                 return getShaderIdDirect(name, material_type, drawtype);
444         }
445
446         /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
447
448         // We're gonna ask the result to be put into here
449
450         static ResultQueue<std::string, u32, u8, u8> result_queue;
451
452         // Throw a request in
453         m_get_shader_queue.add(name, 0, 0, &result_queue);
454
455         /* infostream<<"Waiting for shader from main thread, name=\""
456                         <<name<<"\""<<std::endl;*/
457
458         while(true) {
459                 GetResult<std::string, u32, u8, u8>
460                         result = result_queue.pop_frontNoEx();
461
462                 if (result.key == name) {
463                         return result.item;
464                 }
465
466                 errorstream << "Got shader with invalid name: " << result.key << std::endl;
467         }
468
469         infostream << "getShader(): Failed" << std::endl;
470
471         return 0;
472 }
473
474 /*
475         This method generates all the shaders
476 */
477 u32 ShaderSource::getShaderIdDirect(const std::string &name,
478                 MaterialType material_type, NodeDrawType drawtype)
479 {
480         //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
481
482         // Empty name means shader 0
483         if (name.empty()) {
484                 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
485                 return 0;
486         }
487
488         // Check if already have such instance
489         for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
490                 ShaderInfo *info = &m_shaderinfo_cache[i];
491                 if(info->name == name && info->material_type == material_type &&
492                         info->drawtype == drawtype)
493                         return i;
494         }
495
496         /*
497                 Calling only allowed from main thread
498         */
499         if (std::this_thread::get_id() != m_main_thread) {
500                 errorstream<<"ShaderSource::getShaderIdDirect() "
501                                 "called not from main thread"<<std::endl;
502                 return 0;
503         }
504
505         ShaderInfo info = generateShader(name, material_type, drawtype);
506
507         /*
508                 Add shader to caches (add dummy shaders too)
509         */
510
511         MutexAutoLock lock(m_shaderinfo_cache_mutex);
512
513         u32 id = m_shaderinfo_cache.size();
514         m_shaderinfo_cache.push_back(info);
515
516         infostream<<"getShaderIdDirect(): "
517                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
518
519         return id;
520 }
521
522
523 ShaderInfo ShaderSource::getShaderInfo(u32 id)
524 {
525         MutexAutoLock lock(m_shaderinfo_cache_mutex);
526
527         if(id >= m_shaderinfo_cache.size())
528                 return ShaderInfo();
529
530         return m_shaderinfo_cache[id];
531 }
532
533 void ShaderSource::processQueue()
534 {
535
536
537 }
538
539 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
540                 const std::string &filename, const std::string &program)
541 {
542         /*infostream<<"ShaderSource::insertSourceShader(): "
543                         "name_of_shader=\""<<name_of_shader<<"\", "
544                         "filename=\""<<filename<<"\""<<std::endl;*/
545
546         sanity_check(std::this_thread::get_id() == m_main_thread);
547
548         m_sourcecache.insert(name_of_shader, filename, program, true);
549 }
550
551 void ShaderSource::rebuildShaders()
552 {
553         MutexAutoLock lock(m_shaderinfo_cache_mutex);
554
555         /*// Oh well... just clear everything, they'll load sometime.
556         m_shaderinfo_cache.clear();
557         m_name_to_id.clear();*/
558
559         /*
560                 FIXME: Old shader materials can't be deleted in Irrlicht,
561                 or can they?
562                 (This would be nice to do in the destructor too)
563         */
564
565         // Recreate shaders
566         for (ShaderInfo &i : m_shaderinfo_cache) {
567                 ShaderInfo *info = &i;
568                 if (!info->name.empty()) {
569                         *info = generateShader(info->name, info->material_type, info->drawtype);
570                 }
571         }
572 }
573
574
575 ShaderInfo ShaderSource::generateShader(const std::string &name,
576                 MaterialType material_type, NodeDrawType drawtype)
577 {
578         ShaderInfo shaderinfo;
579         shaderinfo.name = name;
580         shaderinfo.material_type = material_type;
581         shaderinfo.drawtype = drawtype;
582         switch (material_type) {
583         case TILE_MATERIAL_OPAQUE:
584         case TILE_MATERIAL_LIQUID_OPAQUE:
585         case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
586                 shaderinfo.base_material = video::EMT_SOLID;
587                 break;
588         case TILE_MATERIAL_ALPHA:
589         case TILE_MATERIAL_PLAIN_ALPHA:
590         case TILE_MATERIAL_LIQUID_TRANSPARENT:
591         case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
592                 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
593                 break;
594         case TILE_MATERIAL_BASIC:
595         case TILE_MATERIAL_PLAIN:
596         case TILE_MATERIAL_WAVING_LEAVES:
597         case TILE_MATERIAL_WAVING_PLANTS:
598         case TILE_MATERIAL_WAVING_LIQUID_BASIC:
599                 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
600                 break;
601         }
602         shaderinfo.material = shaderinfo.base_material;
603
604         bool enable_shaders = g_settings->getBool("enable_shaders");
605         if (!enable_shaders)
606                 return shaderinfo;
607
608         video::IVideoDriver *driver = RenderingEngine::get_video_driver();
609         if (!driver->queryFeature(video::EVDF_ARB_GLSL)) {
610                 errorstream << "Shaders are enabled but GLSL is not supported by the driver\n";
611                 return shaderinfo;
612         }
613         video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
614
615         // Create shaders header
616         bool use_gles = false;
617 #if ENABLE_GLES
618         use_gles = driver->getDriverType() == video::EDT_OGLES2;
619 #endif
620         std::stringstream shaders_header;
621         shaders_header
622                 << std::noboolalpha
623                 << std::showpoint // for GLSL ES
624                 ;
625         std::string vertex_header, fragment_header, geometry_header;
626         if (use_gles) {
627                 shaders_header << R"(
628                         #version 100
629                 )";
630                 vertex_header = R"(
631                         precision mediump float;
632
633                         uniform highp mat4 mWorldView;
634                         uniform highp mat4 mWorldViewProj;
635                         uniform mediump mat4 mTexture;
636                         uniform mediump mat3 mNormal;
637
638                         attribute highp vec4 inVertexPosition;
639                         attribute lowp vec4 inVertexColor;
640                         attribute mediump vec4 inTexCoord0;
641                         attribute mediump vec3 inVertexNormal;
642                         attribute mediump vec4 inVertexTangent;
643                         attribute mediump vec4 inVertexBinormal;
644                 )";
645                 fragment_header = R"(
646                         precision mediump float;
647                 )";
648         } else {
649                 shaders_header << R"(
650                         #version 120
651                         #define lowp
652                         #define mediump
653                         #define highp
654                 )";
655                 vertex_header = R"(
656                         #define mWorldView gl_ModelViewMatrix
657                         #define mWorldViewProj gl_ModelViewProjectionMatrix
658                         #define mTexture (gl_TextureMatrix[0])
659                         #define mNormal gl_NormalMatrix
660
661                         #define inVertexPosition gl_Vertex
662                         #define inVertexColor gl_Color
663                         #define inTexCoord0 gl_MultiTexCoord0
664                         #define inVertexNormal gl_Normal
665                         #define inVertexTangent gl_MultiTexCoord1
666                         #define inVertexBinormal gl_MultiTexCoord2
667                 )";
668         }
669
670         bool use_discard = use_gles;
671 #ifdef __unix__
672         // For renderers that should use discard instead of GL_ALPHA_TEST
673         const char* gl_renderer = (const char*)glGetString(GL_RENDERER);
674         if (strstr(gl_renderer, "GC7000"))
675                 use_discard = true;
676 #endif
677         if (use_discard) {
678                 if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL)
679                         shaders_header << "#define USE_DISCARD 1\n";
680                 else if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
681                         shaders_header << "#define USE_DISCARD_REF 1\n";
682         }
683
684 #define PROVIDE(constant) shaders_header << "#define " #constant " " << (int)constant << "\n"
685
686         PROVIDE(NDT_NORMAL);
687         PROVIDE(NDT_AIRLIKE);
688         PROVIDE(NDT_LIQUID);
689         PROVIDE(NDT_FLOWINGLIQUID);
690         PROVIDE(NDT_GLASSLIKE);
691         PROVIDE(NDT_ALLFACES);
692         PROVIDE(NDT_ALLFACES_OPTIONAL);
693         PROVIDE(NDT_TORCHLIKE);
694         PROVIDE(NDT_SIGNLIKE);
695         PROVIDE(NDT_PLANTLIKE);
696         PROVIDE(NDT_FENCELIKE);
697         PROVIDE(NDT_RAILLIKE);
698         PROVIDE(NDT_NODEBOX);
699         PROVIDE(NDT_GLASSLIKE_FRAMED);
700         PROVIDE(NDT_FIRELIKE);
701         PROVIDE(NDT_GLASSLIKE_FRAMED_OPTIONAL);
702         PROVIDE(NDT_PLANTLIKE_ROOTED);
703
704         PROVIDE(TILE_MATERIAL_BASIC);
705         PROVIDE(TILE_MATERIAL_ALPHA);
706         PROVIDE(TILE_MATERIAL_LIQUID_TRANSPARENT);
707         PROVIDE(TILE_MATERIAL_LIQUID_OPAQUE);
708         PROVIDE(TILE_MATERIAL_WAVING_LEAVES);
709         PROVIDE(TILE_MATERIAL_WAVING_PLANTS);
710         PROVIDE(TILE_MATERIAL_OPAQUE);
711         PROVIDE(TILE_MATERIAL_WAVING_LIQUID_BASIC);
712         PROVIDE(TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
713         PROVIDE(TILE_MATERIAL_WAVING_LIQUID_OPAQUE);
714         PROVIDE(TILE_MATERIAL_PLAIN);
715         PROVIDE(TILE_MATERIAL_PLAIN_ALPHA);
716
717 #undef PROVIDE
718
719         shaders_header << "#define MATERIAL_TYPE " << (int)material_type << "\n";
720         shaders_header << "#define DRAW_TYPE " << (int)drawtype << "\n";
721
722         bool enable_waving_water = g_settings->getBool("enable_waving_water");
723         shaders_header << "#define ENABLE_WAVING_WATER " << enable_waving_water << "\n";
724         if (enable_waving_water) {
725                 shaders_header << "#define WATER_WAVE_HEIGHT " << g_settings->getFloat("water_wave_height") << "\n";
726                 shaders_header << "#define WATER_WAVE_LENGTH " << g_settings->getFloat("water_wave_length") << "\n";
727                 shaders_header << "#define WATER_WAVE_SPEED " << g_settings->getFloat("water_wave_speed") << "\n";
728         }
729
730         shaders_header << "#define ENABLE_WAVING_LEAVES " << g_settings->getBool("enable_waving_leaves") << "\n";
731         shaders_header << "#define ENABLE_WAVING_PLANTS " << g_settings->getBool("enable_waving_plants") << "\n";
732         shaders_header << "#define ENABLE_TONE_MAPPING " << g_settings->getBool("tone_mapping") << "\n";
733
734         shaders_header << "#define FOG_START " << core::clamp(g_settings->getFloat("fog_start"), 0.0f, 0.99f) << "\n";
735
736         if (g_settings->getBool("enable_dynamic_shadows")) {
737                 shaders_header << "#define ENABLE_DYNAMIC_SHADOWS 1\n";
738                 if (g_settings->getBool("shadow_map_color"))
739                         shaders_header << "#define COLORED_SHADOWS 1\n";
740
741                 if (g_settings->getBool("shadow_poisson_filter"))
742                         shaders_header << "#define POISSON_FILTER 1\n";
743
744                 s32 shadow_filter = g_settings->getS32("shadow_filters");
745                 shaders_header << "#define SHADOW_FILTER " << shadow_filter << "\n";
746
747                 float shadow_soft_radius = g_settings->getFloat("shadow_soft_radius");
748                 if (shadow_soft_radius < 1.0f)
749                         shadow_soft_radius = 1.0f;
750                 shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
751         }
752
753         std::string common_header = shaders_header.str();
754
755         std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");
756         std::string fragment_shader = m_sourcecache.getOrLoad(name, "opengl_fragment.glsl");
757         std::string geometry_shader = m_sourcecache.getOrLoad(name, "opengl_geometry.glsl");
758
759         vertex_shader = common_header + vertex_header + vertex_shader;
760         fragment_shader = common_header + fragment_header + fragment_shader;
761         const char *geometry_shader_ptr = nullptr; // optional
762         if (!geometry_shader.empty()) {
763                 geometry_shader = common_header + geometry_header + geometry_shader;
764                 geometry_shader_ptr = geometry_shader.c_str();
765         }
766
767         irr_ptr<ShaderCallback> cb{new ShaderCallback(m_setter_factories)};
768         infostream<<"Compiling high level shaders for "<<name<<std::endl;
769         s32 shadermat = gpu->addHighLevelShaderMaterial(
770                 vertex_shader.c_str(), nullptr, video::EVST_VS_1_1,
771                 fragment_shader.c_str(), nullptr, video::EPST_PS_1_1,
772                 geometry_shader_ptr, nullptr, video::EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
773                 cb.get(), shaderinfo.base_material,  1);
774         if (shadermat == -1) {
775                 errorstream<<"generate_shader(): "
776                                 "failed to generate \""<<name<<"\", "
777                                 "addHighLevelShaderMaterial failed."
778                                 <<std::endl;
779                 dumpShaderProgram(warningstream, "Vertex", vertex_shader);
780                 dumpShaderProgram(warningstream, "Fragment", fragment_shader);
781                 dumpShaderProgram(warningstream, "Geometry", geometry_shader);
782                 return shaderinfo;
783         }
784
785         // Apply the newly created material type
786         shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
787         return shaderinfo;
788 }
789
790 void dumpShaderProgram(std::ostream &output_stream,
791                 const std::string &program_type, const std::string &program)
792 {
793         output_stream << program_type << " shader program:" << std::endl <<
794                 "----------------------------------" << std::endl;
795         size_t pos = 0;
796         size_t prev = 0;
797         s16 line = 1;
798         while ((pos = program.find('\n', prev)) != std::string::npos) {
799                 output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
800                         std::endl;
801                 prev = pos + 1;
802         }
803         output_stream << line << ": " << program.substr(prev) << std::endl <<
804                 "End of " << program_type << " shader program." << std::endl <<
805                 " " << std::endl;
806 }