1 //  SuperTuxKart - a fun racing game with go-kart
2 //  Copyright (C) 2018 SuperTuxKart-Team
3 //
4 //  This program is free software; you can redistribute it and/or
5 //  modify it under the terms of the GNU General Public License
6 //  as published by the Free Software Foundation; either version 3
7 //  of the License, or (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program; if not, write to the Free Software
16 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 
18 #include "graphics/sp/sp_shader_manager.hpp"
19 #include "io/file_manager.hpp"
20 #include "io/xml_node.hpp"
21 #include "graphics/central_settings.hpp"
22 #include "graphics/sp/sp_base.hpp"
23 #include "graphics/sp/sp_shader.hpp"
24 #include "graphics/sp/sp_texture.hpp"
25 #include "graphics/sp/sp_texture_manager.hpp"
26 #include "graphics/sp/sp_uniform_assigner.hpp"
27 #include "tracks/track.hpp"
28 #include "utils/string_utils.hpp"
29 #include "utils/log.hpp"
30 
31 #include <algorithm>
32 
33 namespace SP
34 {
35 SPShaderManager* SPShaderManager::m_spsm = NULL;
36 // ----------------------------------------------------------------------------
SPShaderManager()37 SPShaderManager::SPShaderManager()
38 {
39 #ifndef SERVER_ONLY
40     m_official_sampler_types =
41     {
42         { "nearest", ST_NEAREST },
43         { "nearest_clamped", ST_NEAREST_CLAMPED },
44         { "bilinear", ST_BILINEAR },
45         { "bilinear_clamped", ST_BILINEAR_CLAMPED },
46         { "trilinear", ST_TRILINEAR },
47         { "trilinear_clamped", ST_TRILINEAR_CLAMPED },
48         { "semi_trilinear", ST_SEMI_TRILINEAR }
49     };
50 
51     m_official_uniform_assigner_functions =
52     {
53         { "shadowCascadeUniformAssigner", [](SPUniformAssigner* ua)
54             {
55                 ua->setValue(sp_cur_shadow_cascade);
56             }
57         },
58         { "windDirectionUniformAssigner", [](SPUniformAssigner* ua)
59             {
60                 ua->setValue(sp_wind_dir);
61             }
62         },
63         { "isDuringDayUniformAssigner", [](SPUniformAssigner* ua)
64             {
65                 int is_during_day = Track::getCurrentTrack() ?
66                 Track::getCurrentTrack()->getIsDuringDay() ? 1 : 0 : 0;
67                 ua->setValue(is_during_day);
68             }
69         },
70         { "zeroAlphaUniformAssigner", [](SPUniformAssigner* ua)
71             {
72                 ua->setValue(0.0f);
73             }
74         },
75         { "fogUniformAssigner", [](SPUniformAssigner* ua)
76             {
77                 int fog_enable = Track::getCurrentTrack() ?
78                     Track::getCurrentTrack()->isFogEnabled() ? 1 : 0 : 0;
79                 ua->setValue(fog_enable);
80             }
81         },
82         { "ghostAlphaUniformAssigner", [](SPUniformAssigner* ua)
83             {
84                 float alpha = 1.0f;
85                 if (Track::getCurrentTrack())
86                 {
87                     const video::SColor& c = Track::getCurrentTrack()
88                         ->getSunColor();
89                     float y = 0.2126f * c.getRed() + 0.7152f * c.getGreen() +
90                         0.0722f * c.getBlue();
91                     alpha = y > 128.0f ? 0.5f : 0.35f;
92                 }
93                 ua->setValue(alpha);
94            }
95        }
96     };
97 
98     m_official_use_functions =
99     {
100         { "alphaBlendUse", []()
101             {
102                 glEnable(GL_DEPTH_TEST);
103                 glDepthMask(GL_FALSE);
104                 glDisable(GL_CULL_FACE);
105                 glEnable(GL_BLEND);
106                 glBlendEquation(GL_FUNC_ADD);
107                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
108             }
109         },
110         { "additiveUse", []()
111             {
112                 glEnable(GL_DEPTH_TEST);
113                 glDepthMask(GL_FALSE);
114                 glDisable(GL_CULL_FACE);
115                 glEnable(GL_BLEND);
116                 glBlendEquation(GL_FUNC_ADD);
117                 glBlendFunc(GL_ONE, GL_ONE);
118             }
119         },
120         { "ghostUse", []()
121             {
122                 glEnable(GL_DEPTH_TEST);
123                 glDepthMask(GL_TRUE);
124                 glEnable(GL_CULL_FACE);
125                 glEnable(GL_BLEND);
126                 glBlendEquation(GL_FUNC_ADD);
127                 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
128             }
129         }
130     };
131 #endif
132 }   // SPShaderManager
133 
134 // ----------------------------------------------------------------------------
~SPShaderManager()135 SPShaderManager::~SPShaderManager()
136 {
137     m_official_shaders.clear();
138     m_shaders.clear();
139 }   // ~SPShaderManager
140 
141 // ----------------------------------------------------------------------------
loadEachShader(const std::string & file_name)142 void SPShaderManager::loadEachShader(const std::string& file_name)
143 {
144 #ifndef SERVER_ONLY
145     std::unique_ptr<XMLNode> xml(file_manager->createXMLTree(file_name));
146     if (!xml || xml->getName() != "spshader")
147     {
148         Log::error("SPShaderManager", "Invalid SPShader file %s",
149             file_name.c_str());
150         return;
151     }
152 
153     ShaderInfo si;
154 
155     const XMLNode* shader_info = xml->getNode("shader-info");
156     if (!shader_info)
157     {
158         Log::error("SPShaderManager", "Missing shader-info header in file %s",
159             file_name.c_str());
160         return;
161     }
162 
163     shader_info->get("name", &si.m_shader_name);
164     if (si.m_shader_name.empty())
165     {
166         Log::error("SPShaderManager", "Empty shader name in file %s",
167             file_name.c_str());
168         return;
169     }
170     else if (si.m_shader_name.find("_skinned") != std::string::npos)
171     {
172         Log::error("SPShaderManager", "_skinned name is reserved for auto"
173             " skinned mesh shader adding");
174         return;
175     }
176     else if (getSPShader(si.m_shader_name))
177     {
178         Log::error("SPShaderManager", "%s shader already exists",
179             si.m_shader_name.c_str());
180         return;
181     }
182 
183     shader_info->get("fallback-shader", &si.m_fallback_name);
184     shader_info->get("transparent", &si.m_transparent_shader);
185     shader_info->get("drawing-priority", &si.m_drawing_priority);
186     shader_info->get("use-alpha-channel", &si.m_use_alpha_channel);
187     shader_info->get("use-tangents", &si.m_use_tangents);
188     std::string srgb_prop;
189     shader_info->get("srgb", &srgb_prop);
190     std::vector<std::string> srgb_props = StringUtils::split(srgb_prop, ' ');
191     if (srgb_props.size() == 6)
192     {
193         for (unsigned i = 0; i < 6; i++)
194         {
195             si.m_srgb[i] = srgb_props[i] == "Y";
196         }
197     }
198     else if (!srgb_prop.empty())
199     {
200         Log::error("SPShaderManager", "Invalid srgb properties in shader");
201     }
202 
203     std::array<PassInfo, 2> pi;
204     loadPassInfo(xml->getNode("first-pass"), pi[0]);
205     if (!si.m_transparent_shader && CVS->isDeferredEnabled())
206     {
207         loadPassInfo(xml->getNode("shadow-pass"), pi[1]);
208     }
209     if (pi[0].m_vertex_shader.empty())
210     {
211         Log::error("SPShaderManager", "Missing first pass vertex shader in"
212             " file %s", file_name.c_str());
213         return;
214     }
215     if (!si.m_fallback_name.empty() && !CVS->isDeferredEnabled())
216     {
217         std::shared_ptr<SPShader> fallback_shader =
218             getSPShader(si.m_fallback_name);
219         if (!fallback_shader)
220         {
221             Log::error("SPShaderManager", "%s fallback shader missing",
222                 si.m_fallback_name.c_str());
223         }
224         else
225         {
226             addSPShader(si.m_shader_name, fallback_shader);
227             if (!pi[0].m_skinned_mesh_shader.empty())
228             {
229                 std::shared_ptr<SPShader> fallback_skinned_shader =
230                     getSPShader(si.m_fallback_name + "_skinned");
231                 if (!fallback_skinned_shader)
232                 {
233                     Log::error("SPShaderManager", "%s fallback skinned mesh"
234                         " shader missing", si.m_fallback_name.c_str());
235                 }
236                 addSPShader(si.m_shader_name + "_skinned",
237                     fallback_skinned_shader);
238             }
239         }
240         return;
241     }
242 
243     UniformAssigners ua;
244     const XMLNode* uniform_assigners = xml->getNode("uniform-assigners");
245     if (uniform_assigners)
246     {
247         for (unsigned i = 0; i < uniform_assigners->getNumNodes(); i++)
248         {
249             const XMLNode* uniform_assigner = uniform_assigners->getNode(i);
250             if (uniform_assigner->getName() == "uniform-assigner")
251             {
252                 std::string name, function;
253                 uniform_assigner->get("name", &name);
254                 uniform_assigner->get("function", &function);
255                 if (!name.empty() && !function.empty() &&
256                     m_official_uniform_assigner_functions.find(function) !=
257                     m_official_uniform_assigner_functions.end())
258                 {
259                     ua.emplace_back(name, m_official_uniform_assigner_functions
260                         .at(function));
261                 }
262                 else
263                 {
264                     Log::error("SPShaderManager", "Invalid uniform assigner"
265                         " %s", function.c_str());
266                 }
267             }
268         }
269     }
270 
271     addSPShader(si.m_shader_name, buildSPShader(si, pi, ua, false/*skinned*/));
272     if (!pi[0].m_skinned_mesh_shader.empty())
273     {
274         addSPShader(si.m_shader_name + "_skinned", buildSPShader(si, pi, ua,
275             true/*skinned*/));
276     }
277 #endif
278 }   // loadEachShader
279 
280 // ----------------------------------------------------------------------------
loadPassInfo(const XMLNode * pass,PassInfo & pi)281 void SPShaderManager::loadPassInfo(const XMLNode* pass, PassInfo& pi)
282 {
283     if (!pass)
284     {
285         return;
286     }
287 
288     std::string use_function, unuse_function;
289     pass->get("use-function", &use_function);
290     if (!use_function.empty() && m_official_use_functions.find(use_function) !=
291         m_official_use_functions.end())
292     {
293         pi.m_use_function = m_official_use_functions.at(use_function);
294     }
295 
296     pass->get("unuse-function", &unuse_function);
297     if (!unuse_function.empty() &&
298         m_official_unuse_functions.find(unuse_function) !=
299         m_official_unuse_functions.end())
300     {
301         pi.m_unuse_function = m_official_unuse_functions.at(unuse_function);
302     }
303 
304     pass->get("vertex-shader", &pi.m_vertex_shader);
305     pi.m_vertex_shader = getShaderFullPath(pi.m_vertex_shader);
306 
307     pass->get("fragment-shader", &pi.m_fragment_shader);
308     pi.m_fragment_shader = getShaderFullPath(pi.m_fragment_shader);
309 
310     pass->get("skinned-mesh-shader", &pi.m_skinned_mesh_shader);
311     pi.m_skinned_mesh_shader = getShaderFullPath(pi.m_skinned_mesh_shader);
312 
313     const XMLNode* prefilled_textures = pass->getNode("prefilled-textures");
314     if (prefilled_textures)
315     {
316         for (unsigned i = 0; i < prefilled_textures->getNumNodes(); i++)
317         {
318             const XMLNode* prefilled_texture = prefilled_textures->getNode(i);
319             if (prefilled_texture->getName() == "prefilled-texture")
320             {
321                 bool srgb = false;
322                 SamplerType st = ST_TRILINEAR;
323                 std::string name, file, srgb_props, sampler_props;
324                 prefilled_texture->get("name", &name);
325                 prefilled_texture->get("file", &file);
326                 prefilled_texture->get("srgb", &srgb_props);
327 #ifndef SERVER_ONLY
328                 if (!srgb_props.empty())
329                 {
330                     srgb = srgb_props == "Y" && CVS->isDeferredEnabled();
331                 }
332 #endif
333                 prefilled_texture->get("sampler", &sampler_props);
334                 if (!sampler_props.empty() &&
335                     m_official_sampler_types.find(sampler_props) !=
336                     m_official_sampler_types.end())
337                 {
338                     st = m_official_sampler_types.at(sampler_props);
339                 }
340                 if (!name.empty() && !file.empty())
341                 {
342                     pi.m_prefilled_textures.emplace_back(name, file, srgb, st);
343                 }
344                 else
345                 {
346                     Log::error("SPShaderManager", "Invalid prefilled texture");
347                 }
348             }
349         }
350     }
351 }   // loadPassInfo
352 
353 // ----------------------------------------------------------------------------
getShaderFullPath(const std::string & name)354 std::string SPShaderManager::getShaderFullPath(const std::string& name)
355 {
356     if (name.empty())
357     {
358         return "";
359     }
360     std::string cur_location = m_shader_directory + name;
361     if (file_manager->fileExists(cur_location))
362     {
363         return cur_location;
364     }
365     cur_location = file_manager->getAssetChecked(FileManager::SHADER, name);
366     if (cur_location.empty())
367     {
368         return "";
369     }
370     return file_manager->getFileSystem()->getAbsolutePath(cur_location.c_str())
371         .c_str();
372 }   // getShaderFullPath
373 
374 // ----------------------------------------------------------------------------
buildSPShader(const ShaderInfo & si,const std::array<PassInfo,2> & pi,const UniformAssigners & ua,bool skinned)375 std::shared_ptr<SPShader> SPShaderManager::buildSPShader(const ShaderInfo& si,
376     const std::array<PassInfo, 2>& pi, const UniformAssigners& ua,
377     bool skinned)
378 {
379     std::shared_ptr<SPShader> sps;
380 #ifndef SERVER_ONLY
381     sps = std::make_shared<SPShader>(si.m_shader_name,
382         [this, pi, ua, skinned](SPShader* shader)
383         {
384             // First pass
385             assert(!pi[0].m_vertex_shader.empty() ||
386                 (skinned && !pi[0].m_skinned_mesh_shader.empty()));
387 
388             SPPerObjectUniform* pou = static_cast<SPPerObjectUniform*>(shader);
389             for (auto& p : ua)
390             {
391                 pou->addAssignerFunction(p.first, p.second);
392             }
393 
394             shader->addShaderFile(skinned ?
395                 pi[0].m_skinned_mesh_shader : pi[0].m_vertex_shader,
396                 GL_VERTEX_SHADER, RP_1ST);
397             if (!pi[0].m_fragment_shader.empty())
398             {
399                 shader->addShaderFile(pi[0].m_fragment_shader,
400                     GL_FRAGMENT_SHADER, RP_1ST);
401             }
402             shader->linkShaderFiles(RP_1ST);
403             shader->use(RP_1ST);
404             shader->addBasicUniforms(RP_1ST);
405             shader->addAllUniforms(RP_1ST);
406             if (pi[0].m_use_function)
407             {
408                 shader->setUseFunction(pi[0].m_use_function, RP_1ST);
409             }
410             if (pi[0].m_unuse_function)
411             {
412                 shader->setUnuseFunction(pi[0].m_unuse_function, RP_1ST);
413             }
414             addPrefilledTexturesToShader(shader, pi[0].m_prefilled_textures,
415                 RP_1ST);
416             shader->addAllTextures(RP_1ST);
417 
418             if (pi[1].m_vertex_shader.empty())
419             {
420                 return;
421             }
422             // Shadow pass
423             if (skinned && pi[1].m_skinned_mesh_shader.empty())
424             {
425                 Log::warn("SPShader", "Missing skinned mesh vertex shader in"
426                     " shadow pass");
427                 return;
428             }
429             shader->addShaderFile(skinned ?
430                 pi[1].m_skinned_mesh_shader : pi[1].m_vertex_shader,
431                 GL_VERTEX_SHADER, RP_SHADOW);
432             if (!pi[1].m_fragment_shader.empty())
433             {
434                 shader->addShaderFile(pi[1].m_fragment_shader,
435                     GL_FRAGMENT_SHADER, RP_SHADOW);
436             }
437             shader->linkShaderFiles(RP_SHADOW);
438             shader->use(RP_SHADOW);
439             shader->addBasicUniforms(RP_SHADOW);
440             shader->addAllUniforms(RP_SHADOW);
441             if (pi[1].m_use_function)
442             {
443                 shader->setUseFunction(pi[1].m_use_function, RP_SHADOW);
444             }
445             if (pi[1].m_unuse_function)
446             {
447                 shader->setUnuseFunction(pi[1].m_unuse_function, RP_SHADOW);
448             }
449             addPrefilledTexturesToShader(shader, pi[1].m_prefilled_textures,
450                 RP_SHADOW);
451             shader->addAllTextures(RP_SHADOW);
452         }, si.m_transparent_shader, si.m_drawing_priority,
453         si.m_use_alpha_channel, si.m_use_tangents, si.m_srgb);
454 #endif
455     return sps;
456 }   // buildSPShader
457 
458 // ----------------------------------------------------------------------------
addPrefilledTexturesToShader(SPShader * s,const std::vector<std::tuple<std::string,std::string,bool,SamplerType>> & t,RenderPass rp)459 void SPShaderManager::addPrefilledTexturesToShader(SPShader* s,
460     const std::vector<std::tuple<std::string, std::string, bool, SamplerType> >
461     &t, RenderPass rp)
462 {
463 #ifndef SERVER_ONLY
464     for (auto& p : t)
465     {
466         std::string full_path;
467         const std::string& relative_path =
468             file_manager->searchTexture(std::get<1>(p)/*filename*/);
469         if (relative_path.empty())
470         {
471             Log::warn("SPShader", "Cannot determine prefilled texture full"
472                 " path: %s", std::get<1>(p).c_str());
473         }
474         else
475         {
476             full_path = file_manager->getFileSystem()->getAbsolutePath
477                 (relative_path.c_str()).c_str();
478         }
479         if (!full_path.empty())
480         {
481             std::string cid;
482             if (!file_manager->searchTextureContainerId(cid, std::get<1>(p)))
483             {
484                 Log::warn("SPShader", "Missing container id for %s, no texture"
485                     " compression for it will be done.",
486                     std::get<1>(p).c_str());
487             }
488             std::shared_ptr<SPTexture> pt = SPTextureManager::get()
489                 ->getTexture(full_path, NULL/*material*/, std::get<2>(p), cid);
490             s->addCustomPrefilledTextures(std::get<3>(p)/*sampler_type*/,
491                 GL_TEXTURE_2D, std::get<0>(p)/*name_in_shader*/, [pt]()->GLuint
492                 {
493                     return pt->getOpenGLTextureName();
494                 }, rp);
495         }
496     }
497 #endif
498 }   // addPrefilledTexturesToShader
499 
500 // ----------------------------------------------------------------------------
loadSPShaders(const std::string & directory_name)501 void SPShaderManager::loadSPShaders(const std::string& directory_name)
502 {
503     std::set<std::string> shaders;
504     file_manager->listFiles(shaders, directory_name);
505     for (auto it = shaders.begin(); it != shaders.end();)
506     {
507         if ((*it).find("sps") == std::string::npos ||
508             (*it).find(".xml") == std::string::npos)
509         {
510             it = shaders.erase(it);
511         }
512         else
513         {
514             it++;
515         }
516     }
517     if (shaders.empty())
518     {
519         return;
520     }
521 
522     m_shader_directory = file_manager->getFileSystem()->getAbsolutePath
523         (directory_name.c_str()).c_str();
524     for (const std::string& file_name : shaders)
525     {
526         loadEachShader(m_shader_directory + file_name);
527     }
528     m_shader_directory = "";
529 }   // loadSPShaders
530 
531 // ----------------------------------------------------------------------------
unloadAll()532 void SPShaderManager::unloadAll()
533 {
534     for (auto& p : m_shaders)
535     {
536         p.second->unload();
537     }
538 }   // unloadAll
539 
540 // ----------------------------------------------------------------------------
initAll()541 void SPShaderManager::initAll()
542 {
543     for (auto& p : m_shaders)
544     {
545         p.second->init();
546     }
547 }   // initAll
548 
549 // ----------------------------------------------------------------------------
removeUnusedShaders()550 void SPShaderManager::removeUnusedShaders()
551 {
552     for (auto it = m_shaders.begin(); it != m_shaders.end();)
553     {
554         if (it->second.use_count() == 1)
555         {
556             it = m_shaders.erase(it);
557         }
558         else
559         {
560             it++;
561         }
562     }
563 }   // removeUnusedShaders
564 
565 }
566