]> git.lizzy.rs Git - dragonfireclient.git/blob - src/client/shader.cpp
Fall back to default when rendering mode (3d_mode) is set invalid (#10922)
[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 #if ENABLE_GLES
229         // Modelview matrix
230         CachedVertexShaderSetting<float, 16> m_world_view;
231         // Texture matrix
232         CachedVertexShaderSetting<float, 16> m_texture;
233         // Normal matrix
234         CachedVertexShaderSetting<float, 9> m_normal;
235 #endif
236
237 public:
238         MainShaderConstantSetter() :
239                   m_world_view_proj("mWorldViewProj")
240                 , m_world("mWorld")
241 #if ENABLE_GLES
242                 , m_world_view("mWorldView")
243                 , m_texture("mTexture")
244                 , m_normal("mNormal")
245 #endif
246         {}
247         ~MainShaderConstantSetter() = default;
248
249         virtual void onSetConstants(video::IMaterialRendererServices *services) override
250         {
251                 video::IVideoDriver *driver = services->getVideoDriver();
252                 sanity_check(driver);
253
254                 // Set world matrix
255                 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
256                 m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
257
258                 // Set clip matrix
259                 core::matrix4 worldView;
260                 worldView = driver->getTransform(video::ETS_VIEW);
261                 worldView *= world;
262
263                 core::matrix4 worldViewProj;
264                 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
265                 worldViewProj *= worldView;
266                 m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
267
268 #if ENABLE_GLES
269                 core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0);
270                 m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services);
271                 m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services);
272
273                 core::matrix4 normal;
274                 worldView.getTransposed(normal);
275                 sanity_check(normal.makeInverse());
276                 float m[9] = {
277                         normal[0], normal[1], normal[2],
278                         normal[4], normal[5], normal[6],
279                         normal[8], normal[9], normal[10],
280                 };
281                 m_normal.set(m, services);
282 #endif
283         }
284 };
285
286
287 class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
288 {
289 public:
290         virtual IShaderConstantSetter* create()
291                 { return new MainShaderConstantSetter(); }
292 };
293
294
295 /*
296         ShaderSource
297 */
298
299 class ShaderSource : public IWritableShaderSource
300 {
301 public:
302         ShaderSource();
303
304         /*
305                 - If shader material specified by name is found from cache,
306                   return the cached id.
307                 - Otherwise generate the shader material, add to cache and return id.
308
309                 The id 0 points to a null shader. Its material is EMT_SOLID.
310         */
311         u32 getShaderIdDirect(const std::string &name,
312                 MaterialType material_type, NodeDrawType drawtype) override;
313
314         /*
315                 If shader specified by the name pointed by the id doesn't
316                 exist, create it, then return id.
317
318                 Can be called from any thread. If called from some other thread
319                 and not found in cache, the call is queued to the main thread
320                 for processing.
321         */
322
323         u32 getShader(const std::string &name,
324                 MaterialType material_type, NodeDrawType drawtype) override;
325
326         ShaderInfo getShaderInfo(u32 id) override;
327
328         // Processes queued shader requests from other threads.
329         // Shall be called from the main thread.
330         void processQueue() override;
331
332         // Insert a shader program into the cache without touching the
333         // filesystem. Shall be called from the main thread.
334         void insertSourceShader(const std::string &name_of_shader,
335                 const std::string &filename, const std::string &program) override;
336
337         // Rebuild shaders from the current set of source shaders
338         // Shall be called from the main thread.
339         void rebuildShaders() override;
340
341         void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) override
342         {
343                 m_setter_factories.push_back(std::unique_ptr<IShaderConstantSetterFactory>(setter));
344         }
345
346 private:
347
348         // The id of the thread that is allowed to use irrlicht directly
349         std::thread::id m_main_thread;
350
351         // Cache of source shaders
352         // This should be only accessed from the main thread
353         SourceShaderCache m_sourcecache;
354
355         // A shader id is index in this array.
356         // The first position contains a dummy shader.
357         std::vector<ShaderInfo> m_shaderinfo_cache;
358         // The former container is behind this mutex
359         std::mutex m_shaderinfo_cache_mutex;
360
361         // Queued shader fetches (to be processed by the main thread)
362         RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
363
364         // Global constant setter factories
365         std::vector<std::unique_ptr<IShaderConstantSetterFactory>> m_setter_factories;
366
367         // Generate shader given the shader name.
368         ShaderInfo generateShader(const std::string &name,
369                         MaterialType material_type, NodeDrawType drawtype);
370 };
371
372 IWritableShaderSource *createShaderSource()
373 {
374         return new ShaderSource();
375 }
376
377 ShaderSource::ShaderSource()
378 {
379         m_main_thread = std::this_thread::get_id();
380
381         // Add a dummy ShaderInfo as the first index, named ""
382         m_shaderinfo_cache.emplace_back();
383
384         // Add main global constant setter
385         addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
386 }
387
388 u32 ShaderSource::getShader(const std::string &name,
389                 MaterialType material_type, NodeDrawType drawtype)
390 {
391         /*
392                 Get shader
393         */
394
395         if (std::this_thread::get_id() == m_main_thread) {
396                 return getShaderIdDirect(name, material_type, drawtype);
397         }
398
399         /*errorstream<<"getShader(): Queued: name=\""<<name<<"\""<<std::endl;*/
400
401         // We're gonna ask the result to be put into here
402
403         static ResultQueue<std::string, u32, u8, u8> result_queue;
404
405         // Throw a request in
406         m_get_shader_queue.add(name, 0, 0, &result_queue);
407
408         /* infostream<<"Waiting for shader from main thread, name=\""
409                         <<name<<"\""<<std::endl;*/
410
411         while(true) {
412                 GetResult<std::string, u32, u8, u8>
413                         result = result_queue.pop_frontNoEx();
414
415                 if (result.key == name) {
416                         return result.item;
417                 }
418
419                 errorstream << "Got shader with invalid name: " << result.key << std::endl;
420         }
421
422         infostream << "getShader(): Failed" << std::endl;
423
424         return 0;
425 }
426
427 /*
428         This method generates all the shaders
429 */
430 u32 ShaderSource::getShaderIdDirect(const std::string &name,
431                 MaterialType material_type, NodeDrawType drawtype)
432 {
433         //infostream<<"getShaderIdDirect(): name=\""<<name<<"\""<<std::endl;
434
435         // Empty name means shader 0
436         if (name.empty()) {
437                 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
438                 return 0;
439         }
440
441         // Check if already have such instance
442         for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
443                 ShaderInfo *info = &m_shaderinfo_cache[i];
444                 if(info->name == name && info->material_type == material_type &&
445                         info->drawtype == drawtype)
446                         return i;
447         }
448
449         /*
450                 Calling only allowed from main thread
451         */
452         if (std::this_thread::get_id() != m_main_thread) {
453                 errorstream<<"ShaderSource::getShaderIdDirect() "
454                                 "called not from main thread"<<std::endl;
455                 return 0;
456         }
457
458         ShaderInfo info = generateShader(name, material_type, drawtype);
459
460         /*
461                 Add shader to caches (add dummy shaders too)
462         */
463
464         MutexAutoLock lock(m_shaderinfo_cache_mutex);
465
466         u32 id = m_shaderinfo_cache.size();
467         m_shaderinfo_cache.push_back(info);
468
469         infostream<<"getShaderIdDirect(): "
470                         <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
471
472         return id;
473 }
474
475
476 ShaderInfo ShaderSource::getShaderInfo(u32 id)
477 {
478         MutexAutoLock lock(m_shaderinfo_cache_mutex);
479
480         if(id >= m_shaderinfo_cache.size())
481                 return ShaderInfo();
482
483         return m_shaderinfo_cache[id];
484 }
485
486 void ShaderSource::processQueue()
487 {
488
489
490 }
491
492 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
493                 const std::string &filename, const std::string &program)
494 {
495         /*infostream<<"ShaderSource::insertSourceShader(): "
496                         "name_of_shader=\""<<name_of_shader<<"\", "
497                         "filename=\""<<filename<<"\""<<std::endl;*/
498
499         sanity_check(std::this_thread::get_id() == m_main_thread);
500
501         m_sourcecache.insert(name_of_shader, filename, program, true);
502 }
503
504 void ShaderSource::rebuildShaders()
505 {
506         MutexAutoLock lock(m_shaderinfo_cache_mutex);
507
508         /*// Oh well... just clear everything, they'll load sometime.
509         m_shaderinfo_cache.clear();
510         m_name_to_id.clear();*/
511
512         /*
513                 FIXME: Old shader materials can't be deleted in Irrlicht,
514                 or can they?
515                 (This would be nice to do in the destructor too)
516         */
517
518         // Recreate shaders
519         for (ShaderInfo &i : m_shaderinfo_cache) {
520                 ShaderInfo *info = &i;
521                 if (!info->name.empty()) {
522                         *info = generateShader(info->name, info->material_type, info->drawtype);
523                 }
524         }
525 }
526
527
528 ShaderInfo ShaderSource::generateShader(const std::string &name,
529                 MaterialType material_type, NodeDrawType drawtype)
530 {
531         ShaderInfo shaderinfo;
532         shaderinfo.name = name;
533         shaderinfo.material_type = material_type;
534         shaderinfo.drawtype = drawtype;
535         switch (material_type) {
536         case TILE_MATERIAL_OPAQUE:
537         case TILE_MATERIAL_LIQUID_OPAQUE:
538         case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
539                 shaderinfo.base_material = video::EMT_SOLID;
540                 break;
541         case TILE_MATERIAL_ALPHA:
542         case TILE_MATERIAL_PLAIN_ALPHA:
543         case TILE_MATERIAL_LIQUID_TRANSPARENT:
544         case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
545                 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
546                 break;
547         case TILE_MATERIAL_BASIC:
548         case TILE_MATERIAL_PLAIN:
549         case TILE_MATERIAL_WAVING_LEAVES:
550         case TILE_MATERIAL_WAVING_PLANTS:
551         case TILE_MATERIAL_WAVING_LIQUID_BASIC:
552                 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
553                 break;
554         }
555         shaderinfo.material = shaderinfo.base_material;
556
557         bool enable_shaders = g_settings->getBool("enable_shaders");
558         if (!enable_shaders)
559                 return shaderinfo;
560
561         video::IVideoDriver *driver = RenderingEngine::get_video_driver();
562         if (!driver->queryFeature(video::EVDF_ARB_GLSL)) {
563                 errorstream << "Shaders are enabled but GLSL is not supported by the driver\n";
564                 return shaderinfo;
565         }
566         video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
567
568         // Create shaders header
569         bool use_gles = false;
570 #if ENABLE_GLES
571         use_gles = driver->getDriverType() == video::EDT_OGLES2;
572 #endif
573         std::stringstream shaders_header;
574         shaders_header
575                 << std::noboolalpha
576                 << std::showpoint // for GLSL ES
577                 ;
578         std::string vertex_header, fragment_header, geometry_header;
579         if (use_gles) {
580                 shaders_header << R"(
581                         #version 100
582                         )";
583                 vertex_header = R"(
584                         uniform highp mat4 mWorldView;
585                         uniform highp mat4 mWorldViewProj;
586                         uniform mediump mat4 mTexture;
587                         uniform mediump mat3 mNormal;
588
589                         attribute highp vec4 inVertexPosition;
590                         attribute lowp vec4 inVertexColor;
591                         attribute mediump vec4 inTexCoord0;
592                         attribute mediump vec3 inVertexNormal;
593                         attribute mediump vec4 inVertexTangent;
594                         attribute mediump vec4 inVertexBinormal;
595                         )";
596                 fragment_header = R"(
597                         precision mediump float;
598                         )";
599         } else {
600                 shaders_header << R"(
601                         #version 120
602                         #define lowp
603                         #define mediump
604                         #define highp
605                         )";
606                 vertex_header = R"(
607                         #define mWorldView gl_ModelViewMatrix
608                         #define mWorldViewProj gl_ModelViewProjectionMatrix
609                         #define mTexture (gl_TextureMatrix[0])
610                         #define mNormal gl_NormalMatrix
611
612                         #define inVertexPosition gl_Vertex
613                         #define inVertexColor gl_Color
614                         #define inTexCoord0 gl_MultiTexCoord0
615                         #define inVertexNormal gl_Normal
616                         #define inVertexTangent gl_MultiTexCoord1
617                         #define inVertexBinormal gl_MultiTexCoord2
618                         )";
619         }
620
621         bool use_discard = use_gles;
622 #ifdef __unix__
623         // For renderers that should use discard instead of GL_ALPHA_TEST
624         const char* gl_renderer = (const char*)glGetString(GL_RENDERER);
625         if (strstr(gl_renderer, "GC7000"))
626                 use_discard = true;
627 #endif
628         if (use_discard && shaderinfo.base_material != video::EMT_SOLID)
629                 shaders_header << "#define USE_DISCARD 1\n";
630
631 #define PROVIDE(constant) shaders_header << "#define " #constant " " << (int)constant << "\n"
632
633         PROVIDE(NDT_NORMAL);
634         PROVIDE(NDT_AIRLIKE);
635         PROVIDE(NDT_LIQUID);
636         PROVIDE(NDT_FLOWINGLIQUID);
637         PROVIDE(NDT_GLASSLIKE);
638         PROVIDE(NDT_ALLFACES);
639         PROVIDE(NDT_ALLFACES_OPTIONAL);
640         PROVIDE(NDT_TORCHLIKE);
641         PROVIDE(NDT_SIGNLIKE);
642         PROVIDE(NDT_PLANTLIKE);
643         PROVIDE(NDT_FENCELIKE);
644         PROVIDE(NDT_RAILLIKE);
645         PROVIDE(NDT_NODEBOX);
646         PROVIDE(NDT_GLASSLIKE_FRAMED);
647         PROVIDE(NDT_FIRELIKE);
648         PROVIDE(NDT_GLASSLIKE_FRAMED_OPTIONAL);
649         PROVIDE(NDT_PLANTLIKE_ROOTED);
650
651         PROVIDE(TILE_MATERIAL_BASIC);
652         PROVIDE(TILE_MATERIAL_ALPHA);
653         PROVIDE(TILE_MATERIAL_LIQUID_TRANSPARENT);
654         PROVIDE(TILE_MATERIAL_LIQUID_OPAQUE);
655         PROVIDE(TILE_MATERIAL_WAVING_LEAVES);
656         PROVIDE(TILE_MATERIAL_WAVING_PLANTS);
657         PROVIDE(TILE_MATERIAL_OPAQUE);
658         PROVIDE(TILE_MATERIAL_WAVING_LIQUID_BASIC);
659         PROVIDE(TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
660         PROVIDE(TILE_MATERIAL_WAVING_LIQUID_OPAQUE);
661         PROVIDE(TILE_MATERIAL_PLAIN);
662         PROVIDE(TILE_MATERIAL_PLAIN_ALPHA);
663
664 #undef PROVIDE
665
666         shaders_header << "#define MATERIAL_TYPE " << (int)material_type << "\n";
667         shaders_header << "#define DRAW_TYPE " << (int)drawtype << "\n";
668
669         bool enable_waving_water = g_settings->getBool("enable_waving_water");
670         shaders_header << "#define ENABLE_WAVING_WATER " << enable_waving_water << "\n";
671         if (enable_waving_water) {
672                 shaders_header << "#define WATER_WAVE_HEIGHT " << g_settings->getFloat("water_wave_height") << "\n";
673                 shaders_header << "#define WATER_WAVE_LENGTH " << g_settings->getFloat("water_wave_length") << "\n";
674                 shaders_header << "#define WATER_WAVE_SPEED " << g_settings->getFloat("water_wave_speed") << "\n";
675         }
676
677         shaders_header << "#define ENABLE_WAVING_LEAVES " << g_settings->getBool("enable_waving_leaves") << "\n";
678         shaders_header << "#define ENABLE_WAVING_PLANTS " << g_settings->getBool("enable_waving_plants") << "\n";
679         shaders_header << "#define ENABLE_TONE_MAPPING " << g_settings->getBool("tone_mapping") << "\n";
680
681         shaders_header << "#define FOG_START " << core::clamp(g_settings->getFloat("fog_start"), 0.0f, 0.99f) << "\n";
682
683         std::string common_header = shaders_header.str();
684
685         std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");
686         std::string fragment_shader = m_sourcecache.getOrLoad(name, "opengl_fragment.glsl");
687         std::string geometry_shader = m_sourcecache.getOrLoad(name, "opengl_geometry.glsl");
688
689         vertex_shader = common_header + vertex_header + vertex_shader;
690         fragment_shader = common_header + fragment_header + fragment_shader;
691         const char *geometry_shader_ptr = nullptr; // optional
692         if (!geometry_shader.empty()) {
693                 geometry_shader = common_header + geometry_header + geometry_shader;
694                 geometry_shader_ptr = geometry_shader.c_str();
695         }
696
697         irr_ptr<ShaderCallback> cb{new ShaderCallback(m_setter_factories)};
698         infostream<<"Compiling high level shaders for "<<name<<std::endl;
699         s32 shadermat = gpu->addHighLevelShaderMaterial(
700                 vertex_shader.c_str(), nullptr, video::EVST_VS_1_1,
701                 fragment_shader.c_str(), nullptr, video::EPST_PS_1_1,
702                 geometry_shader_ptr, nullptr, video::EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
703                 cb.get(), shaderinfo.base_material,  1);
704         if (shadermat == -1) {
705                 errorstream<<"generate_shader(): "
706                                 "failed to generate \""<<name<<"\", "
707                                 "addHighLevelShaderMaterial failed."
708                                 <<std::endl;
709                 dumpShaderProgram(warningstream, "Vertex", vertex_shader);
710                 dumpShaderProgram(warningstream, "Fragment", fragment_shader);
711                 dumpShaderProgram(warningstream, "Geometry", geometry_shader);
712                 return shaderinfo;
713         }
714
715         // Apply the newly created material type
716         shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
717         return shaderinfo;
718 }
719
720 void dumpShaderProgram(std::ostream &output_stream,
721                 const std::string &program_type, const std::string &program)
722 {
723         output_stream << program_type << " shader program:" << std::endl <<
724                 "----------------------------------" << std::endl;
725         size_t pos = 0;
726         size_t prev = 0;
727         s16 line = 1;
728         while ((pos = program.find('\n', prev)) != std::string::npos) {
729                 output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
730                         std::endl;
731                 prev = pos + 1;
732         }
733         output_stream << line << ": " << program.substr(prev) << std::endl <<
734                 "End of " << program_type << " shader program." << std::endl <<
735                 " " << std::endl;
736 }