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