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