Brenta Engine 1.2
Loading...
Searching...
No Matches
shader.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/renderer/opengl/shader.hpp>
7#include <brenta/logger.hpp>
8
9#include <glm/gtc/type_ptr.hpp>
10
11#include <sstream>
12
13using namespace brenta;
14
15Shader::~Shader()
16{
17 if (this->id == 0) return;
18
19 glDeleteProgram(this->id);
20
21 EVENT(Logger::Event::Lifetime, "Shader: destroyed {}", this->id);
22 this->id = 0;
23 return;
24}
25
26std::optional<Shader>
27Shader::create(const tenno::vector<Shader::Object> &objects)
28{
29 tenno::vector<Shader::Id> compiled_shaders = {};
30 if (!compile_shaders(compiled_shaders, objects))
31 {
32 ERROR("shader: error compiling shader");
33 return {};
34 }
35
36 std::optional<Shader::Id> id =
37 Shader::link_program(compiled_shaders, nullptr, 0);
38 if (!id) return {};
39
40 Shader::clean_compilation(compiled_shaders);
41 EVENT(Logger::Event::Lifetime, "Shader: initialized {}", *id);
42 return Shader(*id);
43}
44
45std::optional<Shader>
46Shader::create(const GLchar **feedback_varyings, int num_varyings,
47 const tenno::vector<Shader::Object> &objects)
48{
49 tenno::vector<Shader::Id> compiled_shaders = {};
50 if (!compile_shaders(compiled_shaders, objects))
51 {
52 ERROR("shader: error compiling shader");
53 return {};
54 }
55
56 std::optional<Shader::Id> id =
57 Shader::link_program(compiled_shaders, feedback_varyings, num_varyings);
58 if (!id) return {};
59
60 Shader::clean_compilation(compiled_shaders);
61 EVENT(Logger::Event::Lifetime, "Shader: initialized {}", *id);
62 return Shader(*id);
63}
64
65bool Shader::compile_shaders(tenno::vector<Shader::Id> &compiled,
66 const tenno::vector<Shader::Object> &objects)
67{
68 for (const auto& obj : objects)
69 {
70 GLenum shader_type_gl;
71 switch(obj.type)
72 {
73 case Shader::Type::Fragment: shader_type_gl = GL_FRAGMENT_SHADER; break;
74 case Shader::Type::Vertex: shader_type_gl = GL_VERTEX_SHADER; break;
75 case Shader::Type::Geometry: shader_type_gl = GL_GEOMETRY_SHADER; break;
76 case Shader::Type::Compute: shader_type_gl = GL_COMPUTE_SHADER; break;
77 default: shader_type_gl = 0; break;
78 }
79
80 unsigned int shader = glCreateShader(shader_type_gl);
81 const GLchar *src = obj.src.c_str();
82 glShaderSource(shader, 1, &src, NULL);
83 glCompileShader(shader);
84 if (!Shader::check_compile_errors(shader))
85 return false;
86
87 compiled.push_back(shader);
88 }
89 return true;
90}
91
92bool Shader::compile_shaders([[maybe_unused]] tenno::vector<Shader::Id> &compiled)
93{
94 return true;
95}
96
97std::optional<Shader::Id>
98Shader::link_program(tenno::vector<Shader::Id>& compiled_shaders,
99 const GLchar **feedback_varyings, int num_varyings)
100{
101 Shader::Id id = glCreateProgram();
102 std::for_each(compiled_shaders.begin(), compiled_shaders.end(),
103 [&id](auto shader) { glAttachShader(id, shader); });
104
105 if (feedback_varyings != nullptr)
106 glTransformFeedbackVaryings(id, num_varyings, feedback_varyings,
107 GL_INTERLEAVED_ATTRIBS);
108
109 glLinkProgram(id);
110 if (!Shader::check_link_errors(id))
111 return {};
112 return id;
113}
114
115void Shader::clean_compilation(tenno::vector<Shader::Id>& compiled_shaders)
116{
117 std::for_each(compiled_shaders.begin(), compiled_shaders.end(),
118 [](auto shader) { glDeleteShader(shader); });
119 return;
120}
121
122Shader::Id Shader::get_id() const
123{
124 return this->id;
125}
126
127// Use/activate the shader
128bool Shader::use()
129{
130 glUseProgram(this->get_id());
131
132 GLenum err;
133 if ((err = glGetError()) != GL_NO_ERROR)
134 {
135 ERROR("Shader::use: error using shader: {}", err);
136 return false;
137 }
138 return true;
139}
140
141bool Shader::set_bool(Shader::Id id, const std::string& unif_name, bool value)
142{
143 GLint location = glGetUniformLocation(id, unif_name.c_str());
144
145 if (location == -1)
146 {
147 ERROR("Shader::set_bool: uniform '{}' not found in shader",
148 unif_name);
149 return false;
150 }
151
152 glUniform1i(location, (int) value);
153
154 GLenum err;
155 if ((err = glGetError()) != GL_NO_ERROR)
156 {
157 ERROR("Shader::set_bool: error setting bool value with name {}: {}",
158 unif_name, err);
159 return false;
160 }
161 return true;
162}
163
164bool Shader::set_int(Shader::Id id, const std::string &unif_name, int value)
165{
166 GLint location = glGetUniformLocation(id, unif_name.c_str());
167
168 if (location == -1)
169 {
170 ERROR("Shader::set_int: uniform '{}' not found in shader",
171 unif_name);
172 return false;
173 }
174
175 glUniform1i(location, value);
176
177 GLenum err;
178 if ((err = glGetError()) != GL_NO_ERROR)
179 {
180 ERROR("Shader::set_int: error setting int value with name '{}'",
181 unif_name, err);
182 return false;
183 }
184 return true;
185}
186
187bool Shader::set_float(Shader::Id id, const std::string &unif_name, float value)
188{
189 GLint location = glGetUniformLocation(id, unif_name.c_str());
190
191 if (location == -1)
192 {
193 ERROR("Shader::set_float: uniform '{}' not found in shader",
194 unif_name);
195 return false;
196 }
197
198 glUniform1f(location, value);
199
200 GLenum err;
201 if ((err = glGetError()) != GL_NO_ERROR)
202 {
203 ERROR("Shader::set_float: error setting float value with name '{}': {}",
204 unif_name, err);
205 return false;
206 }
207 return true;
208}
209
210bool Shader::set_float2(Shader::Id id, const std::string &unif_name, float v1, float v2)
211{
212 GLint location = glGetUniformLocation(id, unif_name.c_str());
213
214 if (location == -1)
215 {
216 ERROR("Shader::set_float2: uniform '{}' not found in shader",
217 unif_name);
218 return false;
219 }
220
221 glUniform2f(location, v1, v2);
222
223 GLenum err;
224 if ((err = glGetError()) != GL_NO_ERROR)
225 {
226 ERROR("Shader::set_float2: error setting float uniform with name '{}': {}",
227 unif_name, err);
228 return false;
229 }
230 return true;
231}
232
233bool Shader::set_float3(Shader::Id id, const std::string &unif_name, float v1, float v2, float v3)
234{
235 GLint location = glGetUniformLocation(id, unif_name.c_str());
236
237 if (location == -1)
238 {
239 ERROR("Shader::set_float3: uniform '{}' not found in shader",
240 unif_name);
241 return false;
242 }
243
244 glUniform3f(location, v1, v2, v3);
245
246 GLenum err;
247 if ((err = glGetError()) != GL_NO_ERROR)
248 {
249 ERROR("Shader::set_float3: error setting float value with name '{}': {}",
250 unif_name, err);
251 return false;
252 }
253 return true;
254}
255
256bool Shader::set_mat4(Shader::Id id, const std::string &unif_name, glm::mat4 value)
257{
258 GLint location = glGetUniformLocation(id, unif_name.c_str());
259
260 if (location == -1)
261 {
262 ERROR("Shader::set_mat4: uniform '{}' not found in shader",
263 unif_name);
264 return false;
265 }
266
267 glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(value));
268
269 GLenum err;
270 if ((err = glGetError()) != GL_NO_ERROR)
271 {
272 ERROR("Shader::set_mat4: error setting mat4 value with name '{}': {}",
273 unif_name, err);
274 return false;
275 }
276 return true;
277}
278
279bool Shader::set_vec3(Shader::Id id, const std::string &unif_name,
280 float x, float y, float z)
281{
282 GLint location = glGetUniformLocation(id, unif_name.c_str());
283
284 if (location == -1)
285 {
286 ERROR("Shader::set_vec3: uniform '{}' not found in shader",
287 unif_name);
288 return false;
289 }
290
291 glUniform3f(location, x, y, z);
292
293 GLenum err;
294 if ((err = glGetError()) != GL_NO_ERROR)
295 {
296 ERROR("Shader::set_vec3: error setting vec3 value with name '{}': {}",
297 unif_name, err);
298 return false;
299 }
300 return true;
301}
302
303bool Shader::set_vec3(Shader::Id id, const std::string& unif_name, glm::vec3 value)
304{
305 GLint location = glGetUniformLocation(id, unif_name.c_str());
306
307 if (location == -1)
308 {
309 ERROR("Shader::set_vec3: uniform '{}' not found in shader",
310 unif_name);
311 return false;
312 }
313
314 glUniform3f(location, value.x, value.y, value.z);
315
316 GLenum err;
317 if ((err = glGetError()) != GL_NO_ERROR)
318 {
319 ERROR("Shader::set_vec3: error setting vec3 value with name '{}': {}",
320 unif_name, err);
321 return false;
322 }
323 return true;
324}
325
326
327bool Shader::set_bool(const std::string &unif_name, bool value)
328{ return Shader::set_bool(this->id, unif_name, value); }
329bool Shader::set_int(const std::string &unif_name, int value)
330{ return Shader::set_int(this->id, unif_name, value); }
331bool Shader::set_float(const std::string &unif_name, float value)
332{ return Shader::set_float(this->id, unif_name, value); }
333bool Shader::set_float2(const std::string &unif_name, float v1, float v2)
334{ return Shader::set_float2(this->id, unif_name, v1, v2); }
335bool Shader::set_float3(const std::string &unif_name, float v1, float v2, float v3)
336{ return Shader::set_float3(this->id, unif_name, v1, v2, v3); }
337bool Shader::set_mat4(const std::string &unif_name, glm::mat4 value)
338{ return Shader::set_mat4(this->id, unif_name, value); }
339bool Shader::set_vec3(const std::string &unif_name, float x, float y, float z)
340{ return Shader::set_vec3(this->id, unif_name, x, y, z); }
341bool Shader::set_vec3(const std::string &unif_name, glm::vec3 value)
342{ return Shader::set_vec3(this->id, unif_name, value); }
343
344bool Shader::check_compile_errors(Shader::Id shader)
345{
346 int success;
347 char infoLog[1024];
348 glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
349 if (success) return true;
350
351 std::stringstream out;
352 glGetShaderInfoLog(shader, 1024, NULL, infoLog);
353 out << "shader: compilation error: " << infoLog;
354 ERROR("{}", out.str());
355 return false;
356}
357
358bool Shader::check_link_errors(Shader::Id shader)
359{
360 int success;
361 char infoLog[1024];
362 glGetProgramiv(shader, GL_LINK_STATUS, &success);
363 if (success) return true;
364
365 std::stringstream out;
366 glGetProgramInfoLog(shader, 1024, NULL, infoLog);
367 out << "shader: program linking error: " << infoLog;
368 ERROR("{}", out.str());
369 return false;
370}
371
372//
373// Object
374//
375
376Shader::Object::Object(Type type, const std::filesystem::path &path)
377{
378 this->path = path;
379 this->type = type;
380 auto src = Object::read_file(path);
381 if (!src)
382 {
383 this->src = "";
384 }
385 else
386 {
387 this->src = *src;
388 }
389 return;
390}
391
392std::optional<std::string>
393Shader::Object::read_file(const std::filesystem::path &path)
394{
395 std::string code;
396 std::ifstream file;
397 file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
398
399 try
400 {
401 file.open(path);
402 if (!file.is_open()) throw "Cannot open file";
403 std::stringstream stream;
404 stream << file.rdbuf();
405 file.close();
406 code = stream.str();
407 }
408 catch (std::ifstream::failure &e)
409 {
410 ERROR("shader: error reading shader file: {}", path.string());
411 return {};
412 }
413
414 if (code.empty())
415 {
416 ERROR("shader: file is empty: {}", path.string());
417 return {};
418 }
419
420 return code;
421}
422
423Shader::Builder &Shader::Builder::object(const Shader::Object &obj)
424{
425 this->objs.push_back(obj);
426 if (obj.path)
427 this->watch_paths.push_back(*obj.path);
428 return *this;
429}
430
431Shader::Builder &Shader::Builder::objects(const tenno::vector<Shader::Object> &objs)
432{
433 for (auto& obj : objs)
434 {
435 this->objs.push_back(obj);
436 if (obj.path)
437 this->watch_paths.push_back(*obj.path);
438 }
439 return *this;
440}
441
442Shader::Builder &Shader::Builder::feedback(const GLchar **feedback_varyings,
443 int num_varyings)
444{
445 this->feedback_varyings = feedback_varyings;
446 this->num_varyings = num_varyings;
447 return *this;
448}
449
450// Add path to be watched for hot-reloading
451Shader::Builder &Shader::Builder::watch(const std::filesystem::path &path)
452{
453 this->watch_paths.push_back(path);
454 return *this;
455}
456
457std::optional<Shader> Shader::Builder::build()
458{
459 if (num_varyings == 0)
460 return Shader::create(this->objs);
461 else
462 return Shader::create(this->feedback_varyings,
463 this->num_varyings,
464 this->objs);
465}
466
467tenno::vector<std::filesystem::path> Shader::Builder::get_watch_paths() const
468{
469 return this->watch_paths;
470}