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.
24 #include "irrlichttypes_extrabloated.h"
28 #include "util/container.h"
29 #include "util/thread.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"
41 #include "client/tile.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
;
68 bool incache
= g_shadername_to_path_cache
.get(combined
, &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
))
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
90 std::string testpath
= porting::path_share
+ DIR_DELIM
+ rel_path
;
91 if(fs::PathExists(testpath
))
95 // Add to cache (also an empty result is cached)
96 g_shadername_to_path_cache
.set(combined
, fullpath
);
103 SourceShaderCache: A cache used for storing source shaders.
106 class SourceShaderCache
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
115 std::string path
= getShaderPath(name_of_shader
, filename
);
117 std::string p
= readFile(path
);
119 m_programs
[combined
] = p
;
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())
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())
145 std::string path
= getShaderPath(name_of_shader
, filename
);
147 infostream
<< "SourceShaderCache::getOrLoad(): No path found for \""
148 << combined
<< "\"" << std::endl
;
151 infostream
<< "SourceShaderCache::getOrLoad(): Loading path \""
152 << path
<< "\"" << std::endl
;
153 std::string p
= readFile(path
);
155 m_programs
[combined
] = p
;
161 StringMap m_programs
;
163 std::string
readFile(const std::string
&path
)
165 std::ifstream
is(path
.c_str(), std::ios::binary
);
168 std::ostringstream tmp_os
;
169 tmp_os
<< is
.rdbuf();
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
;
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"};
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"};
239 CachedVertexShaderSetting
<float, 16> m_world_view
{"mWorldView"};
241 CachedVertexShaderSetting
<float, 16> m_texture
{"mTexture"};
244 ~MainShaderConstantSetter() = default;
246 virtual void onSetConstants(video::IMaterialRendererServices
*services
) override
248 video::IVideoDriver
*driver
= services
->getVideoDriver();
249 sanity_check(driver
);
252 core::matrix4 world
= driver
->getTransform(video::ETS_WORLD
);
253 m_world
.set(*reinterpret_cast<float(*)[16]>(world
.pointer()), services
);
256 core::matrix4 worldView
;
257 worldView
= driver
->getTransform(video::ETS_VIEW
);
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
);
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
321 virtual IShaderConstantSetter
* create()
322 { return new MainShaderConstantSetter(); }
330 class ShaderSource
: public IWritableShaderSource
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
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
);
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
;
393 // Queued shader fetches (to be processed by the main thread)
394 RequestQueue
<std::string
, u32
, u8
, u8
> m_get_shader_queue
;
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
)
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
;
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;*/
447 GetResult
<std::string
, u32
, u8
, u8
>
448 result
= result_queue
.pop_frontNoEx();
450 if (result
.key
== name
) {
454 errorstream
<< "Got shader with invalid name: " << result
.key
<< std::endl
;
457 infostream
<< "getShader(): Failed" << std::endl
;
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
471 infostream
<<"getShaderIdDirect(): name is empty"<<std::endl
;
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
)
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
;
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
;
510 ShaderInfo
ShaderSource::getShaderInfo(u32 id
)
512 MutexAutoLock
lock(m_shaderinfo_cache_mutex
);
514 if(id
>= m_shaderinfo_cache
.size())
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,
549 (This would be nice to do in the destructor too)
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
;
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
;
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
;
589 shaderinfo
.material
= shaderinfo
.base_material
;
591 bool enable_shaders
= g_settings
->getBool("enable_shaders");
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
;
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";
614 shaders_header
<< "#version 100\n";
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;
634 shaders_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.
664 errorstream
<< "OpenGL procedures were not loaded correctly, "
665 "please open a bug report with details about your platform/OS." << std::endl
;
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"))
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"
684 PROVIDE(NDT_AIRLIKE
);
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
);
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."
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
;
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
;
819 while ((pos
= program
.find('\n', prev
)) != std::string::npos
) {
820 output_stream
<< line
++ << ": "<< program
.substr(prev
, pos
- prev
) <<
824 output_stream
<< line
<< ": " << program
.substr(prev
) << std::endl
<<
825 "End of " << program_type
<< " shader program." << std::endl
<<