galaxy 1.0.0
Real-Time C++23 Game Programming Framework. Built on data-driven design principles and agile software engineering.
Loading...
Searching...
No Matches
Shader.cpp
Go to the documentation of this file.
1
7
8#include <nlohmann/json.hpp>
9
10#include "galaxy/core/ServiceLocator.hpp"
11#include "galaxy/error/Log.hpp"
13#include "galaxy/utils/Globals.hpp"
14
15#include "Shader.hpp"
16
17#ifdef GALAXY_WIN_PLATFORM
18#pragma warning(push)
19#pragma warning(disable : 26414)
20#endif
21
22namespace galaxy
23{
24 namespace graphics
25 {
27 : m_id {0}
28 {
29 }
30
31 Shader::Shader(const nlohmann::json& json)
32 : m_id {0}
33 {
34 if (json.count("file") > 0)
35 {
36 GALAXY_UNUSED(load(json.at("file")));
37 }
38 else
39 {
40 GALAXY_LOG(GALAXY_ERROR, "No shader file found in JSON constructor.");
41 }
42 }
43
45 {
46 if (this->m_id != 0)
47 {
48 glDeleteProgram(this->m_id);
49 }
50
51 this->m_cache = std::move(s.m_cache);
52 this->m_src = std::move(s.m_src);
53 this->m_id = s.m_id;
54
55 s.m_id = 0;
56 }
57
59 {
60 if (this != &s)
61 {
62 if (this->m_id != 0)
63 {
64 glDeleteProgram(this->m_id);
65 }
66
67 this->m_cache = std::move(s.m_cache);
68 this->m_src = std::move(s.m_src);
69 this->m_id = s.m_id;
70
71 s.m_id = 0;
72 }
73
74 return *this;
75 }
76
78 {
79 destroy();
80 }
81
82 bool Shader::load(const std::string& file)
83 {
84 auto& fs = core::ServiceLocator<fs::VirtualFileSystem>::ref();
85
86 auto data = fs.read(file);
87 return parse(data);
88 }
89
90 bool Shader::load(const std::string& vertex, const std::string& frag)
91 {
92 auto& fs = core::ServiceLocator<fs::VirtualFileSystem>::ref();
93
94 auto v = fs.read(vertex);
95 auto f = fs.read(frag);
96
97 return parse(v, f);
98 }
99
100 bool Shader::parse(const std::string& src)
101 {
102 return preprocess(src);
103 }
104
105 bool Shader::parse(const std::string& vertex, const std::string& frag)
106 {
107 auto result = true;
108
109 if (vertex.empty())
110 {
111 GALAXY_LOG(GALAXY_ERROR, "Shader was passed an empty vertex shader.");
112 result = false;
113 }
114
115 if (frag.empty())
116 {
117 GALAXY_LOG(GALAXY_ERROR, "Shader was passed an empty fragment shader.");
118 result = false;
119 }
120
121 if (result)
122 {
123 m_src.first() = vertex;
124 m_src.second() = frag;
125 }
126
127 return result;
128 }
129
131 {
132 if (!m_src.first().empty() && !m_src.second().empty())
133 {
134 // Error reporting for OpenGL.
135 char info[1024] = {0};
136 auto success = 0;
137 auto v_id = 0u;
138 auto f_id = 0u;
139
140 // Then we need to convert the stream to a c string because OpenGL requires a refernece to a c string.
141 auto v_src = m_src.first().c_str();
142 auto f_src = m_src.second().c_str();
143
144 // Retrieve the ids from opengl when creating the shader, then compile shaders, while checking for errors.
145 v_id = glCreateShader(GL_VERTEX_SHADER);
146 glShaderSource(v_id, 1, &v_src, nullptr);
147 glCompileShader(v_id);
148
149 glGetShaderiv(v_id, GL_COMPILE_STATUS, &success);
150 if (!success)
151 {
152 glGetShaderInfoLog(v_id, 1024, nullptr, info);
153
154 GALAXY_LOG(GALAXY_ERROR, "Failed to compile vertex shader. {0}.", info);
155 }
156
157 // Now do the same for the fragment shader.
158 f_id = glCreateShader(GL_FRAGMENT_SHADER);
159 glShaderSource(f_id, 1, &f_src, nullptr);
160 glCompileShader(f_id);
161
162 glGetShaderiv(f_id, GL_COMPILE_STATUS, &success);
163 if (!success)
164 {
165 glGetShaderInfoLog(f_id, 1024, nullptr, info);
166
167 GALAXY_LOG(GALAXY_ERROR, "Failed to compile fragment shader. {0}.", info);
168 }
169
170 if (success)
171 {
172 // Create and link program.
173 m_id = glCreateProgram();
174 glAttachShader(m_id, v_id);
175 glAttachShader(m_id, f_id);
176 glLinkProgram(m_id);
177
178 glGetProgramiv(m_id, GL_LINK_STATUS, &success);
179 if (!success)
180 {
181 glGetProgramInfoLog(m_id, 1024, nullptr, info);
182
183 GALAXY_LOG(GALAXY_ERROR, "Failed to attach shaders: {0}.", info);
184 }
185
186 GLint uniform_count = 0;
187 glGetProgramiv(m_id, GL_ACTIVE_UNIFORMS, &uniform_count);
188
189 if (uniform_count != 0)
190 {
191 GLint max_name_len = 0;
192 glGetProgramiv(m_id, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_name_len);
193
194 GLsizei length = 0;
195 GLsizei count = 0;
196 GLenum type = GL_NONE;
197
198 for (GLint i = 0; i < uniform_count; ++i)
199 {
200 auto uniform_name = std::make_unique<char[]>(max_name_len);
201 glGetActiveUniform(m_id, i, max_name_len, &length, &count, &type, uniform_name.get());
202
203 m_cache.emplace(std::string(uniform_name.get(), length), glGetUniformLocation(m_id, uniform_name.get()));
204 }
205 }
206 }
207
208 // Cleanup shaders.
209 glDeleteShader(v_id);
210 glDeleteShader(f_id);
211 m_src = {};
212 }
213 else
214 {
215 GALAXY_LOG(GALAXY_ERROR, "Tried to compile empty shader.");
216 }
217 }
218
220 {
221 if (m_id != 0)
222 {
223 glDeleteProgram(m_id);
224 }
225 }
226
227 void Shader::bind() const
228 {
229 glUseProgram(m_id);
230 }
231
232 void Shader::unbind() const
233 {
234 glUseProgram(0);
235 }
236
237 unsigned int Shader::id() const
238 {
239 return m_id;
240 }
241
242 bool Shader::preprocess(const std::string& src)
243 {
244 auto result = true;
245
246 if (!src.empty())
247 {
248 const auto token = "#type";
249 const auto len = std::strlen(token);
250
251 auto pos = src.find(token, 0);
252 while (pos != std::string::npos)
253 {
254 const auto eol = src.find_first_of("\r\n", pos);
255 const auto begin = pos + len + 1;
256
257 auto type = src.substr(begin, eol - begin);
258
259 if (type == "vertex" || type == "fragment")
260 {
261 const auto next_line = src.find_first_not_of("\r\n", eol);
262 pos = src.find(token, next_line);
263
264 if (type == "vertex")
265 {
266 m_src.first() = (pos == std::string::npos) ? src.substr(next_line) : src.substr(next_line, pos - next_line);
267 }
268 else
269 {
270 m_src.second() = (pos == std::string::npos) ? src.substr(next_line) : src.substr(next_line, pos - next_line);
271 }
272 }
273 else
274 {
275 GALAXY_LOG(GALAXY_ERROR, "Failed to parse shader type. Must be 'vertex' or 'fragment'.");
276 result = false;
277 }
278 }
279 }
280 else
281 {
282 GALAXY_LOG(GALAXY_ERROR, "Empty source code provided to shader.");
283 result = false;
284 }
285
286 return result;
287 }
288
289 int Shader::get_uniform_location(const std::string& name)
290 {
291 if (m_cache.contains(name))
292 {
293 return m_cache[name];
294 }
295 else
296 {
297 GALAXY_LOG(GALAXY_WARNING, "Failed to get uniform location for '{0}'.", name);
298 return -1;
299 }
300 }
301 } // namespace graphics
302} // namespace galaxy
303
304#ifdef GALAXY_WIN_PLATFORM
305#pragma warning(pop)
306#endif
#define GALAXY_LOG(level, msg,...)
Definition Log.hpp:28
#define GALAXY_ERROR
Definition Log.hpp:24
#define GALAXY_UNUSED(var)
Pragma.hpp galaxy.
Definition Pragma.hpp:16
OpenGL Shader Program.
Definition Shader.hpp:29
void destroy()
Destroys shader program.
Definition Shader.cpp:219
bool parse(const std::string &src)
Loads a combined raw shader.
Definition Shader.cpp:100
unsigned int m_id
OpenGL handle.
Definition Shader.hpp:175
void compile()
Compiles shader into GPU mem.
Definition Shader.cpp:130
int get_uniform_location(const std::string &name)
Retrieves the location of a shader uniform.
Definition Shader.cpp:289
entt::compressed_pair< std::string, std::string > m_src
Shader source code.
Definition Shader.hpp:185
ankerl::unordered_dense::map< std::string, int > m_cache
Cache of uniforms for better performance.
Definition Shader.hpp:180
unsigned int id() const
Get program id.
Definition Shader.cpp:237
void bind() const
Make active shader.
Definition Shader.cpp:227
~Shader()
Destructor.
Definition Shader.cpp:77
bool preprocess(const std::string &src)
Extract source code from a combined shader.
Definition Shader.cpp:242
void unbind() const
Unbind.
Definition Shader.cpp:232
bool load(const std::string &file)
Loads a combined shader.
Definition Shader.cpp:82
Shader & operator=(Shader &&)
Move assignment operator.
Definition Shader.cpp:58
Shader()
Constructor.
Definition Shader.cpp:26
Animated.cpp galaxy.
Definition Animated.cpp:16