Properly warn that shader queue is unimplemented
[minetest.git] / src / client / shader.cpp
blob04253f3b4502e76cda57fb8ca667456bf28723b9
1 /*
2 Minetest
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013 Kahrl <kahrl@gmx.net>
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.
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.
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.
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 "gettext.h"
39 #include "log.h"
40 #include "gamedef.h"
41 #include "client/tile.h"
42 #include "config.h"
44 #include <mt_opengl.h>
47 A cache from shader name to shader path
49 MutexedMap<std::string, std::string> g_shadername_to_path_cache;
52 Gets the path to a shader by first checking if the file
53 name_of_shader/filename
54 exists in shader_path and if not, using the data path.
56 If not found, returns "".
58 Utilizes a thread-safe cache.
60 std::string getShaderPath(const std::string &name_of_shader,
61 const std::string &filename)
63 std::string combined = name_of_shader + DIR_DELIM + filename;
64 std::string fullpath;
66 Check from cache
68 bool incache = g_shadername_to_path_cache.get(combined, &fullpath);
69 if(incache)
70 return fullpath;
73 Check from shader_path
75 std::string shader_path = g_settings->get("shader_path");
76 if (!shader_path.empty()) {
77 std::string testpath = shader_path + DIR_DELIM + combined;
78 if(fs::PathExists(testpath))
79 fullpath = testpath;
83 Check from default data directory
85 if (fullpath.empty()) {
86 std::string rel_path = std::string("client") + DIR_DELIM
87 + "shaders" + DIR_DELIM
88 + name_of_shader + DIR_DELIM
89 + filename;
90 std::string testpath = porting::path_share + DIR_DELIM + rel_path;
91 if(fs::PathExists(testpath))
92 fullpath = testpath;
95 // Add to cache (also an empty result is cached)
96 g_shadername_to_path_cache.set(combined, fullpath);
98 // Finally return it
99 return fullpath;
103 SourceShaderCache: A cache used for storing source shaders.
106 class SourceShaderCache
108 public:
109 void insert(const std::string &name_of_shader, const std::string &filename,
110 const std::string &program, bool prefer_local)
112 std::string combined = name_of_shader + DIR_DELIM + filename;
113 // Try to use local shader instead if asked to
114 if(prefer_local){
115 std::string path = getShaderPath(name_of_shader, filename);
116 if(!path.empty()){
117 std::string p = readFile(path);
118 if (!p.empty()) {
119 m_programs[combined] = p;
120 return;
124 m_programs[combined] = program;
127 std::string get(const std::string &name_of_shader,
128 const std::string &filename)
130 std::string combined = name_of_shader + DIR_DELIM + filename;
131 StringMap::iterator n = m_programs.find(combined);
132 if (n != m_programs.end())
133 return n->second;
134 return "";
137 // Primarily fetches from cache, secondarily tries to read from filesystem
138 std::string getOrLoad(const std::string &name_of_shader,
139 const std::string &filename)
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 std::string path = getShaderPath(name_of_shader, filename);
146 if (path.empty()) {
147 infostream << "SourceShaderCache::getOrLoad(): No path found for \""
148 << combined << "\"" << std::endl;
149 return "";
151 infostream << "SourceShaderCache::getOrLoad(): Loading path \""
152 << path << "\"" << std::endl;
153 std::string p = readFile(path);
154 if (!p.empty()) {
155 m_programs[combined] = p;
156 return p;
158 return "";
160 private:
161 StringMap m_programs;
163 std::string readFile(const std::string &path)
165 std::ifstream is(path.c_str(), std::ios::binary);
166 if(!is.is_open())
167 return "";
168 std::ostringstream tmp_os;
169 tmp_os << is.rdbuf();
170 return tmp_os.str();
176 ShaderCallback: Sets constants that can be used in shaders
179 class ShaderCallback : public video::IShaderConstantSetCallBack
181 std::vector<std::unique_ptr<IShaderConstantSetter>> m_setters;
183 public:
184 template <typename Factories>
185 ShaderCallback(const Factories &factories)
187 for (auto &&factory : factories)
188 m_setters.emplace_back(factory->create());
191 virtual void OnSetConstants(video::IMaterialRendererServices *services, s32 userData) override
193 video::IVideoDriver *driver = services->getVideoDriver();
194 sanity_check(driver != NULL);
196 for (auto &&setter : m_setters)
197 setter->onSetConstants(services);
200 virtual void OnSetMaterial(const video::SMaterial& material) override
202 for (auto &&setter : m_setters)
203 setter->onSetMaterial(material);
209 MainShaderConstantSetter: Set basic constants required for almost everything
212 class MainShaderConstantSetter : public IShaderConstantSetter
214 CachedVertexShaderSetting<f32, 16> m_world_view_proj{"mWorldViewProj"};
215 CachedVertexShaderSetting<f32, 16> m_world{"mWorld"};
217 // Shadow-related
218 CachedPixelShaderSetting<f32, 16> m_shadow_view_proj{"m_ShadowViewProj"};
219 CachedPixelShaderSetting<f32, 3> m_light_direction{"v_LightDirection"};
220 CachedPixelShaderSetting<f32> m_texture_res{"f_textureresolution"};
221 CachedPixelShaderSetting<f32> m_shadow_strength{"f_shadow_strength"};
222 CachedPixelShaderSetting<f32> m_time_of_day{"f_timeofday"};
223 CachedPixelShaderSetting<f32> m_shadowfar{"f_shadowfar"};
224 CachedPixelShaderSetting<f32, 4> m_camera_pos{"CameraPos"};
225 CachedPixelShaderSetting<s32> m_shadow_texture{"ShadowMapSampler"};
226 CachedVertexShaderSetting<f32>
227 m_perspective_bias0_vertex{"xyPerspectiveBias0"};
228 CachedPixelShaderSetting<f32>
229 m_perspective_bias0_pixel{"xyPerspectiveBias0"};
230 CachedVertexShaderSetting<f32>
231 m_perspective_bias1_vertex{"xyPerspectiveBias1"};
232 CachedPixelShaderSetting<f32>
233 m_perspective_bias1_pixel{"xyPerspectiveBias1"};
234 CachedVertexShaderSetting<f32>
235 m_perspective_zbias_vertex{"zPerspectiveBias"};
236 CachedPixelShaderSetting<f32> m_perspective_zbias_pixel{"zPerspectiveBias"};
238 // Modelview matrix
239 CachedVertexShaderSetting<float, 16> m_world_view{"mWorldView"};
240 // Texture matrix
241 CachedVertexShaderSetting<float, 16> m_texture{"mTexture"};
243 public:
244 ~MainShaderConstantSetter() = default;
246 virtual void onSetConstants(video::IMaterialRendererServices *services) override
248 video::IVideoDriver *driver = services->getVideoDriver();
249 sanity_check(driver);
251 // Set world matrix
252 core::matrix4 world = driver->getTransform(video::ETS_WORLD);
253 m_world.set(*reinterpret_cast<float(*)[16]>(world.pointer()), services);
255 // Set clip matrix
256 core::matrix4 worldView;
257 worldView = driver->getTransform(video::ETS_VIEW);
258 worldView *= world;
260 core::matrix4 worldViewProj;
261 worldViewProj = driver->getTransform(video::ETS_PROJECTION);
262 worldViewProj *= worldView;
263 m_world_view_proj.set(*reinterpret_cast<float(*)[16]>(worldViewProj.pointer()), services);
265 if (driver->getDriverType() == video::EDT_OGLES2 || driver->getDriverType() == video::EDT_OPENGL3) {
266 core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0);
267 m_world_view.set(*reinterpret_cast<float(*)[16]>(worldView.pointer()), services);
268 m_texture.set(*reinterpret_cast<float(*)[16]>(texture.pointer()), services);
271 // Set uniforms for Shadow shader
272 if (ShadowRenderer *shadow = RenderingEngine::get_shadow_renderer()) {
273 const auto &light = shadow->getDirectionalLight();
275 core::matrix4 shadowViewProj = light.getProjectionMatrix();
276 shadowViewProj *= light.getViewMatrix();
277 m_shadow_view_proj.set(shadowViewProj.pointer(), services);
279 f32 v_LightDirection[3];
280 light.getDirection().getAs3Values(v_LightDirection);
281 m_light_direction.set(v_LightDirection, services);
283 f32 TextureResolution = light.getMapResolution();
284 m_texture_res.set(&TextureResolution, services);
286 f32 ShadowStrength = shadow->getShadowStrength();
287 m_shadow_strength.set(&ShadowStrength, services);
289 f32 timeOfDay = shadow->getTimeOfDay();
290 m_time_of_day.set(&timeOfDay, services);
292 f32 shadowFar = shadow->getMaxShadowFar();
293 m_shadowfar.set(&shadowFar, services);
295 f32 cam_pos[4];
296 shadowViewProj.transformVect(cam_pos, light.getPlayerPos());
297 m_camera_pos.set(cam_pos, services);
299 // I don't like using this hardcoded value. maybe something like
300 // MAX_TEXTURE - 1 or somthing like that??
301 s32 TextureLayerID = 3;
302 m_shadow_texture.set(&TextureLayerID, services);
304 f32 bias0 = shadow->getPerspectiveBiasXY();
305 m_perspective_bias0_vertex.set(&bias0, services);
306 m_perspective_bias0_pixel.set(&bias0, services);
307 f32 bias1 = 1.0f - bias0 + 1e-5f;
308 m_perspective_bias1_vertex.set(&bias1, services);
309 m_perspective_bias1_pixel.set(&bias1, services);
310 f32 zbias = shadow->getPerspectiveBiasZ();
311 m_perspective_zbias_vertex.set(&zbias, services);
312 m_perspective_zbias_pixel.set(&zbias, services);
318 class MainShaderConstantSetterFactory : public IShaderConstantSetterFactory
320 public:
321 virtual IShaderConstantSetter* create()
322 { return new MainShaderConstantSetter(); }
327 ShaderSource
330 class ShaderSource : public IWritableShaderSource
332 public:
333 ShaderSource();
336 - If shader material specified by name is found from cache,
337 return the cached id.
338 - Otherwise generate the shader material, add to cache and return id.
340 The id 0 points to a null shader. Its material is EMT_SOLID.
342 u32 getShaderIdDirect(const std::string &name,
343 MaterialType material_type, NodeDrawType drawtype) override;
346 If shader specified by the name pointed by the id doesn't
347 exist, create it, then return id.
349 Can be called from any thread. If called from some other thread
350 and not found in cache, the call is queued to the main thread
351 for processing.
354 u32 getShader(const std::string &name,
355 MaterialType material_type, NodeDrawType drawtype) override;
357 ShaderInfo getShaderInfo(u32 id) override;
359 // Processes queued shader requests from other threads.
360 // Shall be called from the main thread.
361 void processQueue() override;
363 // Insert a shader program into the cache without touching the
364 // filesystem. Shall be called from the main thread.
365 void insertSourceShader(const std::string &name_of_shader,
366 const std::string &filename, const std::string &program) override;
368 // Rebuild shaders from the current set of source shaders
369 // Shall be called from the main thread.
370 void rebuildShaders() override;
372 void addShaderConstantSetterFactory(IShaderConstantSetterFactory *setter) override
374 m_setter_factories.emplace_back(setter);
377 private:
379 // The id of the thread that is allowed to use irrlicht directly
380 std::thread::id m_main_thread;
382 // Cache of source shaders
383 // This should be only accessed from the main thread
384 SourceShaderCache m_sourcecache;
386 // A shader id is index in this array.
387 // The first position contains a dummy shader.
388 std::vector<ShaderInfo> m_shaderinfo_cache;
389 // The former container is behind this mutex
390 std::mutex m_shaderinfo_cache_mutex;
392 #if 0
393 // Queued shader fetches (to be processed by the main thread)
394 RequestQueue<std::string, u32, u8, u8> m_get_shader_queue;
395 #endif
397 // Global constant setter factories
398 std::vector<std::unique_ptr<IShaderConstantSetterFactory>> m_setter_factories;
400 // Generate shader given the shader name.
401 ShaderInfo generateShader(const std::string &name,
402 MaterialType material_type, NodeDrawType drawtype);
405 IWritableShaderSource *createShaderSource()
407 return new ShaderSource();
410 ShaderSource::ShaderSource()
412 m_main_thread = std::this_thread::get_id();
414 // Add a dummy ShaderInfo as the first index, named ""
415 m_shaderinfo_cache.emplace_back();
417 // Add main global constant setter
418 addShaderConstantSetterFactory(new MainShaderConstantSetterFactory());
421 u32 ShaderSource::getShader(const std::string &name,
422 MaterialType material_type, NodeDrawType drawtype)
425 Get shader
428 if (std::this_thread::get_id() == m_main_thread) {
429 return getShaderIdDirect(name, material_type, drawtype);
432 errorstream << "ShaderSource::getShader(): getting from "
433 "other thread not implemented" << std::endl;
435 #if 0
436 // We're gonna ask the result to be put into here
438 static ResultQueue<std::string, u32, u8, u8> result_queue;
440 // Throw a request in
441 m_get_shader_queue.add(name, 0, 0, &result_queue);
443 /* infostream<<"Waiting for shader from main thread, name=\""
444 <<name<<"\""<<std::endl;*/
446 while(true) {
447 GetResult<std::string, u32, u8, u8>
448 result = result_queue.pop_frontNoEx();
450 if (result.key == name) {
451 return result.item;
454 errorstream << "Got shader with invalid name: " << result.key << std::endl;
457 infostream << "getShader(): Failed" << std::endl;
458 #endif
460 return 0;
464 This method generates all the shaders
466 u32 ShaderSource::getShaderIdDirect(const std::string &name,
467 MaterialType material_type, NodeDrawType drawtype)
469 // Empty name means shader 0
470 if (name.empty()) {
471 infostream<<"getShaderIdDirect(): name is empty"<<std::endl;
472 return 0;
475 // Check if already have such instance
476 for(u32 i=0; i<m_shaderinfo_cache.size(); i++){
477 ShaderInfo *info = &m_shaderinfo_cache[i];
478 if(info->name == name && info->material_type == material_type &&
479 info->drawtype == drawtype)
480 return i;
484 Calling only allowed from main thread
486 if (std::this_thread::get_id() != m_main_thread) {
487 errorstream<<"ShaderSource::getShaderIdDirect() "
488 "called not from main thread"<<std::endl;
489 return 0;
492 ShaderInfo info = generateShader(name, material_type, drawtype);
495 Add shader to caches (add dummy shaders too)
498 MutexAutoLock lock(m_shaderinfo_cache_mutex);
500 u32 id = m_shaderinfo_cache.size();
501 m_shaderinfo_cache.push_back(info);
503 infostream<<"getShaderIdDirect(): "
504 <<"Returning id="<<id<<" for name \""<<name<<"\""<<std::endl;
506 return id;
510 ShaderInfo ShaderSource::getShaderInfo(u32 id)
512 MutexAutoLock lock(m_shaderinfo_cache_mutex);
514 if(id >= m_shaderinfo_cache.size())
515 return ShaderInfo();
517 return m_shaderinfo_cache[id];
520 void ShaderSource::processQueue()
526 void ShaderSource::insertSourceShader(const std::string &name_of_shader,
527 const std::string &filename, const std::string &program)
529 /*infostream<<"ShaderSource::insertSourceShader(): "
530 "name_of_shader=\""<<name_of_shader<<"\", "
531 "filename=\""<<filename<<"\""<<std::endl;*/
533 sanity_check(std::this_thread::get_id() == m_main_thread);
535 m_sourcecache.insert(name_of_shader, filename, program, true);
538 void ShaderSource::rebuildShaders()
540 MutexAutoLock lock(m_shaderinfo_cache_mutex);
542 /*// Oh well... just clear everything, they'll load sometime.
543 m_shaderinfo_cache.clear();
544 m_name_to_id.clear();*/
547 FIXME: Old shader materials can't be deleted in Irrlicht,
548 or can they?
549 (This would be nice to do in the destructor too)
552 // Recreate shaders
553 for (ShaderInfo &i : m_shaderinfo_cache) {
554 ShaderInfo *info = &i;
555 if (!info->name.empty()) {
556 *info = generateShader(info->name, info->material_type, info->drawtype);
562 ShaderInfo ShaderSource::generateShader(const std::string &name,
563 MaterialType material_type, NodeDrawType drawtype)
565 ShaderInfo shaderinfo;
566 shaderinfo.name = name;
567 shaderinfo.material_type = material_type;
568 shaderinfo.drawtype = drawtype;
569 switch (material_type) {
570 case TILE_MATERIAL_OPAQUE:
571 case TILE_MATERIAL_LIQUID_OPAQUE:
572 case TILE_MATERIAL_WAVING_LIQUID_OPAQUE:
573 shaderinfo.base_material = video::EMT_SOLID;
574 break;
575 case TILE_MATERIAL_ALPHA:
576 case TILE_MATERIAL_PLAIN_ALPHA:
577 case TILE_MATERIAL_LIQUID_TRANSPARENT:
578 case TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT:
579 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
580 break;
581 case TILE_MATERIAL_BASIC:
582 case TILE_MATERIAL_PLAIN:
583 case TILE_MATERIAL_WAVING_LEAVES:
584 case TILE_MATERIAL_WAVING_PLANTS:
585 case TILE_MATERIAL_WAVING_LIQUID_BASIC:
586 shaderinfo.base_material = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
587 break;
589 shaderinfo.material = shaderinfo.base_material;
591 bool enable_shaders = g_settings->getBool("enable_shaders");
592 if (!enable_shaders)
593 return shaderinfo;
595 video::IVideoDriver *driver = RenderingEngine::get_video_driver();
596 if (!driver->queryFeature(video::EVDF_ARB_GLSL)) {
597 throw ShaderException(gettext("Shaders are enabled but GLSL is not "
598 "supported by the driver."));
600 video::IGPUProgrammingServices *gpu = driver->getGPUProgrammingServices();
602 // Create shaders header
603 bool fully_programmable = driver->getDriverType() == video::EDT_OGLES2 || driver->getDriverType() == video::EDT_OPENGL3;
604 std::stringstream shaders_header;
605 shaders_header
606 << std::noboolalpha
607 << std::showpoint // for GLSL ES
609 std::string vertex_header, fragment_header, geometry_header;
610 if (fully_programmable) {
611 if (driver->getDriverType() == video::EDT_OPENGL3) {
612 shaders_header << "#version 150\n";
613 } else {
614 shaders_header << "#version 100\n";
616 vertex_header = R"(
617 precision mediump float;
619 uniform highp mat4 mWorldView;
620 uniform highp mat4 mWorldViewProj;
621 uniform mediump mat4 mTexture;
623 attribute highp vec4 inVertexPosition;
624 attribute lowp vec4 inVertexColor;
625 attribute mediump vec4 inTexCoord0;
626 attribute mediump vec3 inVertexNormal;
627 attribute mediump vec4 inVertexTangent;
628 attribute mediump vec4 inVertexBinormal;
630 fragment_header = R"(
631 precision mediump float;
633 } else {
634 shaders_header << R"(
635 #version 120
636 #define lowp
637 #define mediump
638 #define highp
640 vertex_header = R"(
641 #define mWorldView gl_ModelViewMatrix
642 #define mWorldViewProj gl_ModelViewProjectionMatrix
643 #define mTexture (gl_TextureMatrix[0])
645 #define inVertexPosition gl_Vertex
646 #define inVertexColor gl_Color
647 #define inTexCoord0 gl_MultiTexCoord0
648 #define inVertexNormal gl_Normal
649 #define inVertexTangent gl_MultiTexCoord1
650 #define inVertexBinormal gl_MultiTexCoord2
654 // map legacy semantic texture names to texture identifiers
655 fragment_header += R"(
656 #define baseTexture texture0
657 #define normalTexture texture1
658 #define textureFlags texture2
661 // Since this is the first time we're using the GL bindings be extra careful.
662 // This should be removed before 5.6.0 or similar.
663 if (!GL.GetString) {
664 errorstream << "OpenGL procedures were not loaded correctly, "
665 "please open a bug report with details about your platform/OS." << std::endl;
666 abort();
669 bool use_discard = fully_programmable;
670 // For renderers that should use discard instead of GL_ALPHA_TEST
671 const char *renderer = reinterpret_cast<const char*>(GL.GetString(GL.RENDERER));
672 if (strstr(renderer, "GC7000"))
673 use_discard = true;
674 if (use_discard) {
675 if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL)
676 shaders_header << "#define USE_DISCARD 1\n";
677 else if (shaderinfo.base_material == video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF)
678 shaders_header << "#define USE_DISCARD_REF 1\n";
681 #define PROVIDE(constant) shaders_header << "#define " #constant " " << (int)constant << "\n"
683 PROVIDE(NDT_NORMAL);
684 PROVIDE(NDT_AIRLIKE);
685 PROVIDE(NDT_LIQUID);
686 PROVIDE(NDT_FLOWINGLIQUID);
687 PROVIDE(NDT_GLASSLIKE);
688 PROVIDE(NDT_ALLFACES);
689 PROVIDE(NDT_ALLFACES_OPTIONAL);
690 PROVIDE(NDT_TORCHLIKE);
691 PROVIDE(NDT_SIGNLIKE);
692 PROVIDE(NDT_PLANTLIKE);
693 PROVIDE(NDT_FENCELIKE);
694 PROVIDE(NDT_RAILLIKE);
695 PROVIDE(NDT_NODEBOX);
696 PROVIDE(NDT_GLASSLIKE_FRAMED);
697 PROVIDE(NDT_FIRELIKE);
698 PROVIDE(NDT_GLASSLIKE_FRAMED_OPTIONAL);
699 PROVIDE(NDT_PLANTLIKE_ROOTED);
701 PROVIDE(TILE_MATERIAL_BASIC);
702 PROVIDE(TILE_MATERIAL_ALPHA);
703 PROVIDE(TILE_MATERIAL_LIQUID_TRANSPARENT);
704 PROVIDE(TILE_MATERIAL_LIQUID_OPAQUE);
705 PROVIDE(TILE_MATERIAL_WAVING_LEAVES);
706 PROVIDE(TILE_MATERIAL_WAVING_PLANTS);
707 PROVIDE(TILE_MATERIAL_OPAQUE);
708 PROVIDE(TILE_MATERIAL_WAVING_LIQUID_BASIC);
709 PROVIDE(TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT);
710 PROVIDE(TILE_MATERIAL_WAVING_LIQUID_OPAQUE);
711 PROVIDE(TILE_MATERIAL_PLAIN);
712 PROVIDE(TILE_MATERIAL_PLAIN_ALPHA);
714 #undef PROVIDE
716 shaders_header << "#define MATERIAL_TYPE " << (int)material_type << "\n";
717 shaders_header << "#define DRAW_TYPE " << (int)drawtype << "\n";
719 bool enable_waving_water = g_settings->getBool("enable_waving_water");
720 shaders_header << "#define ENABLE_WAVING_WATER " << enable_waving_water << "\n";
721 if (enable_waving_water) {
722 shaders_header << "#define WATER_WAVE_HEIGHT " << g_settings->getFloat("water_wave_height") << "\n";
723 shaders_header << "#define WATER_WAVE_LENGTH " << g_settings->getFloat("water_wave_length") << "\n";
724 shaders_header << "#define WATER_WAVE_SPEED " << g_settings->getFloat("water_wave_speed") << "\n";
727 shaders_header << "#define ENABLE_WAVING_LEAVES " << g_settings->getBool("enable_waving_leaves") << "\n";
728 shaders_header << "#define ENABLE_WAVING_PLANTS " << g_settings->getBool("enable_waving_plants") << "\n";
729 shaders_header << "#define ENABLE_TONE_MAPPING " << g_settings->getBool("tone_mapping") << "\n";
731 if (g_settings->getBool("enable_dynamic_shadows")) {
732 shaders_header << "#define ENABLE_DYNAMIC_SHADOWS 1\n";
733 if (g_settings->getBool("shadow_map_color"))
734 shaders_header << "#define COLORED_SHADOWS 1\n";
736 if (g_settings->getBool("shadow_poisson_filter"))
737 shaders_header << "#define POISSON_FILTER 1\n";
739 s32 shadow_filter = g_settings->getS32("shadow_filters");
740 shaders_header << "#define SHADOW_FILTER " << shadow_filter << "\n";
742 float shadow_soft_radius = g_settings->getFloat("shadow_soft_radius");
743 if (shadow_soft_radius < 1.0f)
744 shadow_soft_radius = 1.0f;
745 shaders_header << "#define SOFTSHADOWRADIUS " << shadow_soft_radius << "\n";
748 if (g_settings->getBool("enable_bloom")) {
749 shaders_header << "#define ENABLE_BLOOM 1\n";
750 if (g_settings->getBool("enable_bloom_debug"))
751 shaders_header << "#define ENABLE_BLOOM_DEBUG 1\n";
754 if (g_settings->getBool("enable_auto_exposure"))
755 shaders_header << "#define ENABLE_AUTO_EXPOSURE 1\n";
757 if (g_settings->get("antialiasing") == "ssaa") {
758 shaders_header << "#define ENABLE_SSAA 1\n";
759 u16 ssaa_scale = MYMAX(2, g_settings->getU16("fsaa"));
760 shaders_header << "#define SSAA_SCALE " << ssaa_scale << ".\n";
763 if (g_settings->getBool("debanding"))
764 shaders_header << "#define ENABLE_DITHERING 1\n";
766 if (g_settings->getBool("enable_volumetric_lighting")) {
767 shaders_header << "#define VOLUMETRIC_LIGHT 1\n";
770 shaders_header << "#line 0\n"; // reset the line counter for meaningful diagnostics
772 std::string common_header = shaders_header.str();
774 std::string vertex_shader = m_sourcecache.getOrLoad(name, "opengl_vertex.glsl");
775 std::string fragment_shader = m_sourcecache.getOrLoad(name, "opengl_fragment.glsl");
776 std::string geometry_shader = m_sourcecache.getOrLoad(name, "opengl_geometry.glsl");
778 vertex_shader = common_header + vertex_header + vertex_shader;
779 fragment_shader = common_header + fragment_header + fragment_shader;
780 const char *geometry_shader_ptr = nullptr; // optional
781 if (!geometry_shader.empty()) {
782 geometry_shader = common_header + geometry_header + geometry_shader;
783 geometry_shader_ptr = geometry_shader.c_str();
786 irr_ptr<ShaderCallback> cb{new ShaderCallback(m_setter_factories)};
787 infostream<<"Compiling high level shaders for "<<name<<std::endl;
788 s32 shadermat = gpu->addHighLevelShaderMaterial(
789 vertex_shader.c_str(), nullptr, video::EVST_VS_1_1,
790 fragment_shader.c_str(), nullptr, video::EPST_PS_1_1,
791 geometry_shader_ptr, nullptr, video::EGST_GS_4_0, scene::EPT_TRIANGLES, scene::EPT_TRIANGLES, 0,
792 cb.get(), shaderinfo.base_material, 1);
793 if (shadermat == -1) {
794 errorstream<<"generate_shader(): "
795 "failed to generate \""<<name<<"\", "
796 "addHighLevelShaderMaterial failed."
797 <<std::endl;
798 dumpShaderProgram(warningstream, "Vertex", vertex_shader);
799 dumpShaderProgram(warningstream, "Fragment", fragment_shader);
800 dumpShaderProgram(warningstream, "Geometry", geometry_shader);
801 throw ShaderException(
802 fmtgettext("Failed to compile the \"%s\" shader.", name.c_str()) +
803 strgettext("\nCheck debug.txt for details."));
806 // Apply the newly created material type
807 shaderinfo.material = (video::E_MATERIAL_TYPE) shadermat;
808 return shaderinfo;
811 void dumpShaderProgram(std::ostream &output_stream,
812 const std::string &program_type, std::string_view program)
814 output_stream << program_type << " shader program:" << std::endl <<
815 "----------------------------------" << std::endl;
816 size_t pos = 0;
817 size_t prev = 0;
818 s16 line = 1;
819 while ((pos = program.find('\n', prev)) != std::string::npos) {
820 output_stream << line++ << ": "<< program.substr(prev, pos - prev) <<
821 std::endl;
822 prev = pos + 1;
824 output_stream << line << ": " << program.substr(prev) << std::endl <<
825 "End of " << program_type << " shader program." << std::endl <<
826 " " << std::endl;