Chira Engine
A customizable MIT-licensed game engine.
Shader.cpp
1 #include "Shader.h"
2 
3 #include <regex>
4 #include <core/Logger.h>
5 #include <resource/StringResource.h>
6 #include <utility/String.h>
7 #include "UBO.h"
8 
9 using namespace chira;
10 
11 CHIRA_CREATE_LOG(SHADER);
12 
13 Shader::Shader(std::string identifier_)
14  : Resource(std::move(identifier_)) {}
15 
16 void Shader::compile(const byte buffer[], std::size_t bufferLength) {
17  Serial::loadFromBuffer(this, buffer, bufferLength);
18 
19  const auto shaderModuleVertString = Resource::getUniqueUncachedResource<StringResource>(this->vertexPath);
20  const auto shaderModuleVertData = replaceMacros(shaderModuleVertString->getIdentifier().data(), shaderModuleVertString->getString());
21  const auto shaderModuleFragString = Resource::getUniqueUncachedResource<StringResource>(this->fragmentPath);
22  const auto shaderModuleFragData = replaceMacros(shaderModuleFragString->getIdentifier().data(), shaderModuleFragString->getString());
23  this->handle = Renderer::createShader(shaderModuleVertData, shaderModuleFragData);
24 
25  if (this->usesPV) {
26  PerspectiveViewUBO::get().bindToShader(this->handle);
27  }
28  if (this->lit) {
29  LightsUBO::get().bindToShader(this->handle);
30  }
31 }
32 
33 void Shader::use() const {
34  Renderer::useShader(this->handle);
35 }
36 
37 Shader::~Shader() {
38  Renderer::destroyShader(this->handle);
39 }
40 
41 void Shader::addPreprocessorSymbol(const std::string& name, const std::string& value) {
42  Shader::preprocessorSymbols[name] = value;
43 }
44 
45 void Shader::setPreprocessorPrefix(const std::string& prefix) {
46  Shader::preprocessorPrefix = prefix;
47 }
48 
49 void Shader::setPreprocessorSuffix(const std::string& suffix) {
50  Shader::preprocessorSuffix = suffix;
51 }
52 
53 std::string Shader::replaceMacros(const std::string& ignoredInclude, const std::string& data) { // NOLINT(misc-no-recursion)
54  // WARNING: The following code is HYPER SENSITIVE
55  // If you change ANYTHING it will BREAK HORRIBLY
56  // TEST ALL CHANGES
57 
58  // Includes
59  static const std::regex includes{Shader::preprocessorPrefix + "(include[ \t]+([a-z:\\/.]+))" + Shader::preprocessorSuffix,
60  std::regex_constants::icase | std::regex_constants::optimize};
61  // Add the include as a macro to be expanded
62  // This has the positive side effect of caching previously used includes
63  for (std::sregex_iterator it{data.begin(), data.end(), includes}; it != std::sregex_iterator{}; ++it) {
64  if (it->str(2) != ignoredInclude && !Shader::preprocessorSymbols.count(it->str(2))) {
65  auto contents = Resource::getUniqueUncachedResource<StringResource>(it->str(2));
66  Shader::addPreprocessorSymbol(it->str(1), replaceMacros(it->str(2), contents->getString()));
67  }
68  }
69 
70  // Macros
71  std::string out = data;
72  for (const auto& [macro, contents] : Shader::preprocessorSymbols) {
73  std::string fullKey = Shader::preprocessorPrefix;
74  fullKey += macro;
75  fullKey += Shader::preprocessorSuffix;
76  String::replace(out, fullKey, contents);
77  }
78  return out;
79 }
A chunk of data, usually a file. Is typically cached and shared.
Definition: Resource.h:19