]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/shader.cpp
Allow missing shaders
[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 "debug.h"
26 #include "filesys.h"
27 #include "util/container.h"
28 #include "util/thread.h"
29 #include "settings.h"
30 #include <ICameraSceneNode.h>
31 #include <IGPUProgrammingServices.h>
32 #include <IMaterialRenderer.h>
33 #include <IMaterialRendererServices.h>
34 #include <IShaderConstantSetCallBack.h>
35 #include "client/renderingengine.h"
36 #include "EShaderTypes.h"
37 #include "log.h"
38 #include "gamedef.h"
39 #include "client/tile.h"
40 #include "config.h"
41
42 #if ENABLE_GLES
43 #ifdef _IRR_COMPILE_WITH_OGLES1_
44 #include <GLES/gl.h>
45 #else
46 #include <GLES2/gl2.h>
47 #endif
48 #else
49 #ifndef __APPLE__
50 #include <GL/gl.h>
51 #else
52 #define GL_SILENCE_DEPRECATION
53 #include <OpenGL/gl.h>
54 #endif
55 #endif
56
57 /*
58         A cache from shader name to shader path
59 */
60 MutexedMap<std::string, std::string> g_shadername_to_path_cache;
61
62 /*
63         Gets the path to a shader by first checking if the file
64           name_of_shader/filename
65         exists in shader_path and if not, using the data path.
66
67         If not found, returns "".
68
69         Utilizes a thread-safe cache.
70 */
71 std::string getShaderPath(const std::string &name_of_shader,
72                 const std::string &filename)
73 {
74         std::string combined = name_of_shader + DIR_DELIM + filename;
75         std::string fullpath;
76         /*
77                 Check from cache
78         */
79         bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
80         if(incache)
81                 return fullpath;
82
83         /*
84                 Check from shader_path
85         */
86         std::string shader_path = g_settings->get("shader_path");
87         if (!shader_path.empty()) {
88                 std::string testpath = shader_path + DIR_DELIM + combined;
89                 if(fs::PathExists(testpath))
90                         fullpath = testpath;
91         }
92
93         /*
94                 Check from default data directory
95         */
96         if (fullpath.empty()) {
97                 std::string rel_path = std::string("client") + DIR_DELIM
98                                 + "shaders" + DIR_DELIM
99                                 + name_of_shader + DIR_DELIM
100                                 + filename;
101                 std::string testpath = porting::path_share + DIR_DELIM + rel_path;
102                 if(fs::PathExists(testpath))
103                         fullpath = testpath;
104         }
105
106         // Add to cache (also an empty result is cached)
107         g_shadername_to_path_cache.set(combined, fullpath);
108
109         // Finally return it
110         return fullpath;
111 }
112
113 /*
114         SourceShaderCache: A cache used for storing source shaders.
115 */
116
117 class SourceShaderCache
118 {
119 public:
120         void insert(const std::string &name_of_shader, const std::string &filename,
121                 const std::string &program, bool prefer_local)
122         {
123                 std::string combined = name_of_shader + DIR_DELIM + filename;
124                 // Try to use local shader instead if asked to
125                 if(prefer_local){
126                         std::string path = getShaderPath(name_of_shader, filename);
127                         if(!path.empty()){
128                                 std::string p = readFile(path);
129                                 if (!p.empty()) {
130                                         m_programs[combined] = p;
131                                         return;
132                                 }
133                         }
134                 }
135                 m_programs[combined] = program;
136         }
137
138         std::string get(const std::string &name_of_shader,
139                 const std::string &filename)
140         {
141                 std::string combined = name_of_shader + DIR_DELIM + filename;
142                 StringMap::iterator n = m_programs.find(combined);
143                 if (n != m_programs.end())
144                         return n->second;
145                 return "";
146         }
147
148         // Primarily fetches from cache, secondarily tries to read from filesystem
149         std::string getOrLoad(const std::string &name_of_shader,
150                 const std::string &filename)
151         {
152                 std::string combined = name_of_shader + DIR_DELIM + filename;
153                 StringMap::iterator n = m_programs.find(combined);
154                 if (n != m_programs.end())
155                         return n->second;
156                 std::string path = getShaderPath(name_of_shader, filename);
157                 if (path.empty()) {
158                         infostream << "SourceShaderCache::getOrLoad(): No path found for \""
159                                 << combined << "\"" << std::endl;
160                         return "";
161                 }
162                 infostream << "SourceShaderCache::getOrLoad(): Loading path \""
163                         << path << "\"" << std::endl;
164                 std::string p = readFile(path);
165                 if (!p.empty()) {
166                         m_programs[combined] = p;
167                         return p;
168                 }
169                 return "";
170         }
171 private:
172         StringMap m_programs;
173
174         std::string readFile(const std::string &path)
175         {
176                 std::ifstream is(path.c_str(), std::ios::binary);
177                 if(!is.is_open())
178                         return "";
179                 std::ostringstream tmp_os;
180                 tmp_os << is.rdbuf();
181                 return tmp_os.str();
182         }
183 };
184
185
186 /*
187         ShaderCallback: Sets constants that can be used in shaders
188 */
189
190 class ShaderCallback : public video::IShaderConstantSetCallBack
191 {
192         std::vector<IShaderConstantSetter*> m_setters;
193
194 public:
195         ShaderCallback(const std::vector<IShaderConstantSetterFactory *> &factories)
196         {
197                 for (IShaderConstantSetterFactory *factory : factories)
198                         m_setters.push_back(factory->create());
199         }
200
201         ~ShaderCallback()
202         {
203                 for (IShaderConstantSetter *setter : m_setters)
204                         delete setter;
205         }
206
207         virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override
208         {
209                 video::IVideoDriver *driver = services->getVideoDriver();
210                 sanity_check(driver != NULL);
211
212                 bool is_highlevel = userData;
213
214                 for (IShaderConstantSetter *setter : m_setters)
215                         setter->onSetConstants(services, is_highlevel);
216         }
217
218         virtual void OnSetMaterial(const video::SMaterial& material) override
219         {
220                 for (IShaderConstantSetter *setter : m_setters)
221                         setter->onSetMaterial(material);
222         }
223 };
224
225
226 /*
227         MainShaderConstantSetter: Set basic constants required for almost everything
228 */
229
230 class MainShaderConstantSetter : public IShaderConstantSetter
231 {
232         CachedVertexShaderSetting<float, 16> m_world_view_proj;
233         CachedVertexShaderSetting<float, 16> m_world;
234 #if ENABLE_GLES
235         // Modelview matrix
236         CachedVertexShaderSetting<float, 16> m_world_view;
237         // Texture matrix
238         CachedVertexShaderSetting<float, 16> m_texture;
239         // Normal matrix
240         CachedVertexShaderSetting<float, 9> m_normal;
241 #endif
242
243 public:
244         MainShaderConstantSetter() :
245                   m_world_view_proj("mWorldViewProj")
246                 , m_world("mWorld")
247 #if ENABLE_GLES
248                 , m_world_view("mWorldView")
249                 , m_texture("mTexture")
250                 , m_normal("mNormal")
251 #endif
252         {}
253         ~MainShaderConstantSetter() = default;
254
255         virtual void onSetConstants(video::IMaterialRendererServices *services,
256                         bool is_highlevel)
257         {
258                 video::IVideoDriver *driver = services->getVideoDriver();
259                 sanity_check(driver);
260
261                 // Set world matrix
262                 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
263                 if (is_highlevel)
264                         m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
265                 else
266                         services->setVertexShaderConstant(world.pointer(), 4, 4);
267
268                 // Set clip matrix
269                 core::matrix4 worldView;
270                 worldView = driver->getTransform(video::ETS_VIEW);
271                 worldView *= world;
272                 core::matrix4 worldViewProj;
273                 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
274                 worldViewProj *= worldView;
275                 if (is_highlevel)
276                         m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
277                 else
278                         services->setVertexShaderConstant(worldViewProj.pointer(), 0, 4);
279
280 #if ENABLE_GLES
281                 if (is_highlevel) {
282                         core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0);
283                         m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services);
284                         m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services);
285
286                         core::matrix4 normal;
287                         worldView.getTransposed(normal);
288                         sanity_check(normal.makeInverse());
289                         float m[9] = {
290                                 normal[0], normal[1], normal[2],
291                                 normal[4], normal[5], normal[6],
292                                 normal[8], normal[9], normal[10],
293                         };
294                         m_normal.set(m, services);
295                 }
296 #endif
297         }
298 };
299
300
301 class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
302 {
303 public:
304         virtual IShaderConstantSetter* create()
305                 { return new MainShaderConstantSetter(); }
306 };
307
308
309 /*
310         ShaderSource
311 */
312
313 class ShaderSource : public IWritableShaderSource
314 {
315 public:
316         ShaderSource();
317         ~ShaderSource();
318
319         /*
320                 - If shader material specified by name is found from cache,
321                   return the cached id.
322                 - Otherwise generate the shader material, add to cache and return id.
323
324                 The id 0 points to a null shader. Its material is EMT_SOLID.
325         */
326         u32 getShaderIdDirect(const std::string &name,
327                 const u8 material_type, const u8 drawtype);
328
329         /*
330                 If shader specified by the name pointed by the id doesn't
331                 exist, create it, then return id.
332
333                 Can be called from any thread. If called from some other thread
334                 and not found in cache, the call is queued to the main thread
335                 for processing.
336         */
337
338         u32 getShader(const std::string &name,
339                 const u8 material_type, const u8 drawtype);
340
341         ShaderInfo getShaderInfo(u32 id);
342
343         // Processes queued shader requests from other threads.
344         // Shall be called from the main thread.
345         void processQueue();
346
347         // Insert a shader program into the cache without touching the
348         // filesystem. Shall be called from the main thread.
349         void insertSourceShader(const std::string &name_of_shader,
350                 const std::string &filename, const std::string &program);
351
352         // Rebuild shaders from the current set of source shaders
353         // Shall be called from the main thread.
354         void rebuildShaders();
355
356         void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter)
357         {
358                 m_setter_factories.push_back(setter);
359         }
360
361 private:
362
363         // The id of the thread that is allowed to use irrlicht directly
364         std::thread::id m_main_thread;
365
366         // Cache of source shaders
367         // This should be only accessed from the main thread
368         SourceShaderCache m_sourcecache;
369
370         // A shader id is index in this array.
371         // The first position contains a dummy shader.
372         std::vector<ShaderInfo> m_shaderinfo_cache;
373         // The former container is behind this mutex
374         std::mutex m_shaderinfo_cache_mutex;
375
376         // Queued shader fetches (to be processed by the main thread)
377         RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
378
379         // Global constant setter factories
380         std::vector<IShaderConstantSetterFactory *> m_setter_factories;
381
382         // Shader callbacks
383         std::vector<ShaderCallback *> m_callbacks;
384 };
385
386 IWritableShaderSource *createShaderSource()
387 {
388         return new ShaderSource();
389 }
390
391 /*
392         Generate shader given the shader name.
393 */
394 ShaderInfo generate_shader(const std::string &name,
395                 u8 material_type, u8 drawtype, std::vector<ShaderCallback *> &callbacks,
396                 const std::vector<IShaderConstantSetterFactory *> &setter_factories,
397                 SourceShaderCache *sourcecache);
398
399 /*
400         Load shader programs
401 */
402 void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
403                 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
404                 std::string &vertex_program, std::string &pixel_program,
405                 std::string &geometry_program, bool &is_highlevel);
406
407 ShaderSource::ShaderSource()
408 {
409         m_main_thread = std::this_thread::get_id();
410
411         // Add a dummy ShaderInfo as the first index, named ""
412         m_shaderinfo_cache.emplace_back();
413
414         // Add main global constant setter
415         addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
416 }
417
418 ShaderSource::~ShaderSource()
419 {
420         for (ShaderCallback *callback : m_callbacks) {
421                 delete callback;
422         }
423         for (IShaderConstantSetterFactory *setter_factorie : m_setter_factories) {
424                 delete setter_factorie;
425         }
426 }
427
428 u32 ShaderSource::getShader(const std::string &name,
429                 const u8 material_type, const u8 drawtype)
430 {
431         /*
432                 Get shader
433         */
434
435         if (std::this_thread::get_id() == m_main_thread) {
436                 return getShaderIdDirect(name, material_type, drawtype);
437         }
438
439         /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
440
441         // We're gonna ask the result to be put into here
442
443         static ResultQueue<std::string, u32, u8, u8> result_queue;
444
445         // Throw a request in
446         m_get_shader_queue.add(name, 0, 0, &result_queue);
447
448         /* infostream<<"Waiting for shader from main thread, name=\""
449                         <<name<<"\""<<std::endl;*/
450
451         while(true) {
452                 GetResult<std::string, u32, u8, u8>
453                         result = result_queue.pop_frontNoEx();
454
455                 if (result.key == name) {
456                         return result.item;
457                 }
458
459                 errorstream << "Got shader with invalid name: " << result.key << std::endl;
460         }
461
462         infostream << "getShader(): Failed" << std::endl;
463
464         return 0;
465 }
466
467 /*
468         This method generates all the shaders
469 */
470 u32 ShaderSource::getShaderIdDirect(const std::string &name,
471                 const u8 material_type, const u8 drawtype)
472 {
473         //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
474
475         // Empty name means shader 0
476         if (name.empty()) {
477                 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
478                 return 0;
479         }
480
481         // Check if already have such instance
482         for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
483                 ShaderInfo *info = &m_shaderinfo_cache[i];
484                 if(info->name == name && info->material_type == material_type &&
485                         info->drawtype == drawtype)
486                         return i;
487         }
488
489         /*
490                 Calling only allowed from main thread
491         */
492         if (std::this_thread::get_id() != m_main_thread) {
493                 errorstream<<"ShaderSource::getShaderIdDirect() "
494                                 "called not from main thread"<<std::endl;
495                 return 0;
496         }
497
498         ShaderInfo info = generate_shader(name, material_type, drawtype,
499                         m_callbacks, m_setter_factories, &m_sourcecache);
500
501         /*
502                 Add shader to caches (add dummy shaders too)
503         */
504
505         MutexAutoLock lock(m_shaderinfo_cache_mutex);
506
507         u32 id = m_shaderinfo_cache.size();
508         m_shaderinfo_cache.push_back(info);
509
510         infostream<<"getShaderIdDirect(): "
511                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
512
513         return id;
514 }
515
516
517 ShaderInfo ShaderSource::getShaderInfo(u32 id)
518 {
519         MutexAutoLock lock(m_shaderinfo_cache_mutex);
520
521         if(id >= m_shaderinfo_cache.size())
522                 return ShaderInfo();
523
524         return m_shaderinfo_cache[id];
525 }
526
527 void ShaderSource::processQueue()
528 {
529
530
531 }
532
533 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
534                 const std::string &filename, const std::string &program)
535 {
536         /*infostream<<"ShaderSource::insertSourceShader(): "
537                         "name_of_shader=\""<<name_of_shader<<"\", "
538                         "filename=\""<<filename<<"\""<<std::endl;*/
539
540         sanity_check(std::this_thread::get_id() == m_main_thread);
541
542         m_sourcecache.insert(name_of_shader, filename, program, true);
543 }
544
545 void ShaderSource::rebuildShaders()
546 {
547         MutexAutoLock lock(m_shaderinfo_cache_mutex);
548
549         /*// Oh well... just clear everything, they'll load sometime.
550         m_shaderinfo_cache.clear();
551         m_name_to_id.clear();*/
552
553         /*
554                 FIXME: Old shader materials can't be deleted in Irrlicht,
555                 or can they?
556                 (This would be nice to do in the destructor too)
557         */
558
559         // Recreate shaders
560         for (ShaderInfo &i : m_shaderinfo_cache) {
561                 ShaderInfo *info = &i;
562                 if (!info->name.empty()) {
563                         *info = generate_shader(info->name, info->material_type,
564                                         info->drawtype, m_callbacks,
565                                         m_setter_factories, &m_sourcecache);
566                 }
567         }
568 }
569
570
571 ShaderInfo generate_shader(const std::string &name, u8 material_type, u8 drawtype,
572                 std::vector<ShaderCallback *> &callbacks,
573                 const std::vector<IShaderConstantSetterFactory *> &setter_factories,
574                 SourceShaderCache *sourcecache)
575 {
576         ShaderInfo shaderinfo;
577         shaderinfo.name = name;
578         shaderinfo.material_type = material_type;
579         shaderinfo.drawtype = drawtype;
580         switch (material_type) {
581         case TILE_MATERIAL_OPAQUE:
582         case TILE_MATERIAL_LIQUID_OPAQUE:
583         case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
584                 shaderinfo.base_material = video::EMT_SOLID;
585                 break;
586         case TILE_MATERIAL_ALPHA:
587         case TILE_MATERIAL_PLAIN_ALPHA:
588         case TILE_MATERIAL_LIQUID_TRANSPARENT:
589         case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
590                 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
591                 break;
592         case TILE_MATERIAL_BASIC:
593         case TILE_MATERIAL_PLAIN:
594         case TILE_MATERIAL_WAVING_LEAVES:
595         case TILE_MATERIAL_WAVING_PLANTS:
596         case TILE_MATERIAL_WAVING_LIQUID_BASIC:
597                 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
598                 break;
599         }
600         shaderinfo.material = shaderinfo.base_material;
601
602         bool enable_shaders = g_settings->getBool("enable_shaders");
603         if (!enable_shaders)
604                 return shaderinfo;
605
606         video::IVideoDriver *driver = RenderingEngine::get_video_driver();
607
608         video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
609         if(!gpu){
610                 errorstream<<"generate_shader(): "
611                                 "failed to generate \""<<name<<"\", "
612                                 "GPU programming not supported."
613                                 <<std::endl;
614                 return shaderinfo;
615         }
616
617         // Choose shader language depending on driver type and settings
618         // Then load shaders
619         std::string vertex_program;
620         std::string pixel_program;
621         std::string geometry_program;
622         bool is_highlevel;
623         load_shaders(name, sourcecache, driver->getDriverType(),
624                         enable_shaders, vertex_program, pixel_program,
625                         geometry_program, is_highlevel);
626         // Check hardware/driver support
627         if (!vertex_program.empty() &&
628                         !driver->queryFeature(video::EVDF_VERTEX_SHADER_1_1) &&
629                         !driver->queryFeature(video::EVDF_ARB_VERTEX_PROGRAM_1)){
630                 infostream<<"generate_shader(): vertex shaders disabled "
631                                 "because of missing driver/hardware support."
632                                 <<std::endl;
633                 vertex_program = "";
634         }
635         if (!pixel_program.empty() &&
636                         !driver->queryFeature(video::EVDF_PIXEL_SHADER_1_1) &&
637                         !driver->queryFeature(video::EVDF_ARB_FRAGMENT_PROGRAM_1)){
638                 infostream<<"generate_shader(): pixel shaders disabled "
639                                 "because of missing driver/hardware support."
640                                 <<std::endl;
641                 pixel_program = "";
642         }
643         if (!geometry_program.empty() &&
644                         !driver->queryFeature(video::EVDF_GEOMETRY_SHADER)){
645                 infostream<<"generate_shader(): geometry shaders disabled "
646                                 "because of missing driver/hardware support."
647                                 <<std::endl;
648                 geometry_program = "";
649         }
650
651         // If no shaders are used, don't make a separate material type
652         if (vertex_program.empty() && pixel_program.empty() && geometry_program.empty())
653                 return shaderinfo;
654
655         // Create shaders header
656         bool use_gles = false;
657 #if ENABLE_GLES
658         use_gles = driver->getDriverType() == video::EDT_OGLES2;
659 #endif
660         std::string shaders_header, vertex_header, pixel_header; // geometry shaders aren’t supported in GLES<3
661         if (use_gles) {
662                 shaders_header =
663                         "#version 100\n"
664                         ;
665                 vertex_header = R"(
666                         uniform highp mat4 mWorldView;
667                         uniform highp mat4 mWorldViewProj;
668                         uniform mediump mat4 mTexture;
669                         uniform mediump mat3 mNormal;
670
671                         attribute highp vec4 inVertexPosition;
672                         attribute lowp vec4 inVertexColor;
673                         attribute mediump vec4 inTexCoord0;
674                         attribute mediump vec3 inVertexNormal;
675                         attribute mediump vec4 inVertexTangent;
676                         attribute mediump vec4 inVertexBinormal;
677                         )";
678                 pixel_header = R"(
679                         precision mediump float;
680                         )";
681         } else {
682                 shaders_header = R"(
683                         #version 120
684                         #define lowp
685                         #define mediump
686                         #define highp
687                         )";
688                 vertex_header = R"(
689                         #define mWorldView gl_ModelViewMatrix
690                         #define mWorldViewProj gl_ModelViewProjectionMatrix
691                         #define mTexture (gl_TextureMatrix[0])
692                         #define mNormal gl_NormalMatrix
693
694                         #define inVertexPosition gl_Vertex
695                         #define inVertexColor gl_Color
696                         #define inTexCoord0 gl_MultiTexCoord0
697                         #define inVertexNormal gl_Normal
698                         #define inVertexTangent gl_MultiTexCoord1
699                         #define inVertexBinormal gl_MultiTexCoord2
700                         )";
701         }
702
703         bool use_discard = use_gles;
704 #ifdef __unix__
705         // For renderers that should use discard instead of GL_ALPHA_TEST
706         const char* gl_renderer = (const char*)glGetString(GL_RENDERER);
707         if (strstr(gl_renderer, "GC7000"))
708                 use_discard = true;
709 #endif
710         if (use_discard && shaderinfo.base_material != video::EMT_SOLID)
711                 shaders_header += "#define USE_DISCARD\n";
712
713         static const char* drawTypes[] = {
714                 "NDT_NORMAL",
715                 "NDT_AIRLIKE",
716                 "NDT_LIQUID",
717                 "NDT_FLOWINGLIQUID",
718                 "NDT_GLASSLIKE",
719                 "NDT_ALLFACES",
720                 "NDT_ALLFACES_OPTIONAL",
721                 "NDT_TORCHLIKE",
722                 "NDT_SIGNLIKE",
723                 "NDT_PLANTLIKE",
724                 "NDT_FENCELIKE",
725                 "NDT_RAILLIKE",
726                 "NDT_NODEBOX",
727                 "NDT_GLASSLIKE_FRAMED",
728                 "NDT_FIRELIKE",
729                 "NDT_GLASSLIKE_FRAMED_OPTIONAL",
730                 "NDT_PLANTLIKE_ROOTED",
731         };
732
733         for (int i = 0; i < 14; i++){
734                 shaders_header += "#define ";
735                 shaders_header += drawTypes[i];
736                 shaders_header += " ";
737                 shaders_header += itos(i);
738                 shaders_header += "\n";
739         }
740
741         static const char* materialTypes[] = {
742                 "TILE_MATERIAL_BASIC",
743                 "TILE_MATERIAL_ALPHA",
744                 "TILE_MATERIAL_LIQUID_TRANSPARENT",
745                 "TILE_MATERIAL_LIQUID_OPAQUE",
746                 "TILE_MATERIAL_WAVING_LEAVES",
747                 "TILE_MATERIAL_WAVING_PLANTS",
748                 "TILE_MATERIAL_OPAQUE",
749                 "TILE_MATERIAL_WAVING_LIQUID_BASIC",
750                 "TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT",
751                 "TILE_MATERIAL_WAVING_LIQUID_OPAQUE",
752                 "TILE_MATERIAL_PLAIN",
753                 "TILE_MATERIAL_PLAIN_ALPHA",
754         };
755
756         for (int i = 0; i < 12; i++){
757                 shaders_header += "#define ";
758                 shaders_header += materialTypes[i];
759                 shaders_header += " ";
760                 shaders_header += itos(i);
761                 shaders_header += "\n";
762         }
763
764         shaders_header += "#define MATERIAL_TYPE ";
765         shaders_header += itos(material_type);
766         shaders_header += "\n";
767         shaders_header += "#define DRAW_TYPE ";
768         shaders_header += itos(drawtype);
769         shaders_header += "\n";
770
771         if (g_settings->getBool("enable_waving_water")){
772                 shaders_header += "#define ENABLE_WAVING_WATER 1\n";
773                 shaders_header += "#define WATER_WAVE_HEIGHT ";
774                 shaders_header += std::to_string(g_settings->getFloat("water_wave_height"));
775                 shaders_header += "\n";
776                 shaders_header += "#define WATER_WAVE_LENGTH ";
777                 shaders_header += std::to_string(g_settings->getFloat("water_wave_length"));
778                 shaders_header += "\n";
779                 shaders_header += "#define WATER_WAVE_SPEED ";
780                 shaders_header += std::to_string(g_settings->getFloat("water_wave_speed"));
781                 shaders_header += "\n";
782         } else{
783                 shaders_header += "#define ENABLE_WAVING_WATER 0\n";
784         }
785
786         shaders_header += "#define ENABLE_WAVING_LEAVES ";
787         if (g_settings->getBool("enable_waving_leaves"))
788                 shaders_header += "1\n";
789         else
790                 shaders_header += "0\n";
791
792         shaders_header += "#define ENABLE_WAVING_PLANTS ";
793         if (g_settings->getBool("enable_waving_plants"))
794                 shaders_header += "1\n";
795         else
796                 shaders_header += "0\n";
797
798         if (g_settings->getBool("tone_mapping"))
799                 shaders_header += "#define ENABLE_TONE_MAPPING\n";
800
801         shaders_header += "#define FOG_START ";
802         shaders_header += std::to_string(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
803         shaders_header += "\n";
804
805         // Call addHighLevelShaderMaterial() or addShaderMaterial()
806         const c8* vertex_program_ptr = 0;
807         const c8* pixel_program_ptr = 0;
808         const c8* geometry_program_ptr = 0;
809         if (!vertex_program.empty()) {
810                 vertex_program = shaders_header + vertex_header + vertex_program;
811                 vertex_program_ptr = vertex_program.c_str();
812         }
813         if (!pixel_program.empty()) {
814                 pixel_program = shaders_header + pixel_header + pixel_program;
815                 pixel_program_ptr = pixel_program.c_str();
816         }
817         if (!geometry_program.empty()) {
818                 geometry_program = shaders_header + geometry_program;
819                 geometry_program_ptr = geometry_program.c_str();
820         }
821         ShaderCallback *cb = new ShaderCallback(setter_factories);
822         s32 shadermat = -1;
823         if(is_highlevel){
824                 infostream<<"Compiling high level shaders for "<<name<<std::endl;
825                 shadermat = gpu->addHighLevelShaderMaterial(
826                         vertex_program_ptr,   // Vertex shader program
827                         "vertexMain",         // Vertex shader entry point
828                         video::EVST_VS_1_1,   // Vertex shader version
829                         pixel_program_ptr,    // Pixel shader program
830                         "pixelMain",          // Pixel shader entry point
831                         video::EPST_PS_1_2,   // Pixel shader version
832                         geometry_program_ptr, // Geometry shader program
833                         "geometryMain",       // Geometry shader entry point
834                         video::EGST_GS_4_0,   // Geometry shader version
835                         scene::EPT_TRIANGLES,      // Geometry shader input
836                         scene::EPT_TRIANGLE_STRIP, // Geometry shader output
837                         0,                         // Support maximum number of vertices
838                         cb, // Set-constant callback
839                         shaderinfo.base_material,  // Base material
840                         1                          // Userdata passed to callback
841                         );
842                 if(shadermat == -1){
843                         errorstream<<"generate_shader(): "
844                                         "failed to generate \""<<name<<"\", "
845                                         "addHighLevelShaderMaterial failed."
846                                         <<std::endl;
847                         dumpShaderProgram(warningstream, "Vertex", vertex_program);
848                         dumpShaderProgram(warningstream, "Pixel", pixel_program);
849                         dumpShaderProgram(warningstream, "Geometry", geometry_program);
850                         delete cb;
851                         return shaderinfo;
852                 }
853         }
854         else{
855                 infostream<<"Compiling assembly shaders for "<<name<<std::endl;
856                 shadermat = gpu->addShaderMaterial(
857                         vertex_program_ptr,   // Vertex shader program
858                         pixel_program_ptr,    // Pixel shader program
859                         cb, // Set-constant callback
860                         shaderinfo.base_material,  // Base material
861                         0                     // Userdata passed to callback
862                         );
863
864                 if(shadermat == -1){
865                         errorstream<<"generate_shader(): "
866                                         "failed to generate \""<<name<<"\", "
867                                         "addShaderMaterial failed."
868                                         <<std::endl;
869                         dumpShaderProgram(warningstream, "Vertex", vertex_program);
870                         dumpShaderProgram(warningstream,"Pixel", pixel_program);
871                         delete cb;
872                         return shaderinfo;
873                 }
874         }
875         callbacks.push_back(cb);
876
877         // HACK, TODO: investigate this better
878         // Grab the material renderer once more so minetest doesn't crash on exit
879         driver->getMaterialRenderer(shadermat)->grab();
880
881         // Apply the newly created material type
882         shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
883         return shaderinfo;
884 }
885
886 void load_shaders(const std::string &name, SourceShaderCache *sourcecache,
887                 video::E_DRIVER_TYPE drivertype, bool enable_shaders,
888                 std::string &vertex_program, std::string &pixel_program,
889                 std::string &geometry_program, bool &is_highlevel)
890 {
891         vertex_program = "";
892         pixel_program = "";
893         geometry_program = "";
894         is_highlevel = false;
895
896         if (!enable_shaders)
897                 return;
898
899         // Look for high level shaders
900         switch (drivertype) {
901         case video::EDT_DIRECT3D9:
902                 // Direct3D 9: HLSL
903                 // (All shaders in one file)
904                 vertex_program = sourcecache->getOrLoad(name, "d3d9.hlsl");
905                 pixel_program = vertex_program;
906                 geometry_program = vertex_program;
907                 break;
908
909         case video::EDT_OPENGL:
910 #if ENABLE_GLES
911         case video::EDT_OGLES2:
912 #endif
913                 // OpenGL: GLSL
914                 vertex_program = sourcecache->getOrLoad(name, "opengl_vertex.glsl");
915                 pixel_program = sourcecache->getOrLoad(name, "opengl_fragment.glsl");
916                 geometry_program = sourcecache->getOrLoad(name, "opengl_geometry.glsl");
917                 break;
918
919         default:
920                 // e.g. OpenGL ES 1 (with no shader support)
921                 break;
922         }
923         if (!vertex_program.empty() || !pixel_program.empty() || !geometry_program.empty()){
924                 is_highlevel = true;
925                 return;
926         }
927 }
928
929 void dumpShaderProgram(std::ostream &output_stream,
930                 const std::string &program_type, const std::string &program)
931 {
932         output_stream << program_type << " shader program:" << std::endl <<
933                 "----------------------------------" << std::endl;
934         size_t pos = 0;
935         size_t prev = 0;
936         s16 line = 1;
937         while ((pos = program.find('\n', prev)) != std::string::npos) {
938                 output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
939                         std::endl;
940                 prev = pos + 1;
941         }
942         output_stream << line << ": " << program.substr(prev) << std::endl <<
943                 "End of " << program_type << " shader program." << std::endl <<
944                 " " << std::endl;
945 }