Brenta Engine 1.2
Loading...
Searching...
No Matches
shader.hpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#pragma once
7
8#include <glad/glad.h>
9
10#include <glm/vec3.hpp>
11#include <glm/mat4x4.hpp>
12
13#include <tenno/vector.hpp>
14
15#include <optional>
16#include <string>
17#include <filesystem>
18
19namespace brenta
20{
21
22//
23// Shader
24// ------
25//
26// Shaders are programs that run on the GPU. They can be written in
27// a high-level language and then compiled into instructions that
28// the GPU understands. OpenGL uses the GLSL language which is similar
29// to C in syntax.
30//
31// Therefore shaders can be compiled into a program, loaded and
32// configured via special variables called "uniforms". They are
33// special variables you can create in the shader that can be set
34// from "outside" the shader, usually to pass som kind of state or
35// configuration.
36//
37// This class provides a simple API to create shaders, load them and
38// set uniforms.
39//
40class Shader
41{
42public:
43
44 using Id = unsigned int;
45
46 enum class Type
47 {
48 Vertex,
49 Fragment,
50 Geometry,
51 Compute,
52 };
53
54 class Object;
55 class Builder;
56
57 // Static API
58
59 // Creates and compiles several shaders (into a shader "program")
60 // Example:
61 // auto my_shader = Shader::create({
62 // { Shader::Type::Vertex, std::filesystem::path("shaders/shader.vs") },
63 // { Shader::Type::Fragment, std::filesystem::path("shaders/shader.fs") }});
64 static std::optional<Shader>
65 create(const tenno::vector<Shader::Object> &objects);
66
67 // Set [feedback_varyings] to an array of CHchar* that specifies
68 // the output attributes we want to capture into a buffer
69 // For example:
70 // const GLchar* feedbackVaryings[] = { "outValue" };
71 //
72 // When using these shaders, you need to surround your draw call
73 // with glBeginTransformFeedback() and glEndTransformFeedback()
74 // like this:
75 //
76 // my_shader.use();
77 // glBeginTransformFeedback(GL_POINTS); // Enter transform feedback mode
78 //
79 // //...
80 // glDrawArrays(GL_POINTS, 0, num_particles);
81 // Gl::check_error();
82 // // ...
83 //
84 // glEndTransformFeedback(); // Exit transform feedback mode
85 static std::optional<Shader>
86 create(const GLchar **feedback_varyings, int num_varyings,
87 const tenno::vector<Shader::Object> &objects);
88
89 // Set uniforms
90 static bool set_bool(Shader::Id id, const std::string &unif_name,
91 bool value);
92 static bool set_int(Shader::Id id, const std::string &unif_name,
93 int value);
94 static bool set_float(Shader::Id id, const std::string &unif_name,
95 float value);
96 static bool set_float2(Shader::Id id, const std::string &unif_name,
97 float v1, float v2);
98 static bool set_float3(Shader::Id id, const std::string &unif_name,
99 float v1, float v2, float v3);
100 static bool set_mat4(Shader::Id id, const std::string &unif_name,
101 glm::mat4 value);
102 static bool set_vec3(Shader::Id id, const std::string &unif_name,
103 float x, float y, float z);
104 static bool set_vec3(Shader::Id id, const std::string &unif_name,
105 glm::vec3 value);
106
107
108 // Non static API
109
110 Shader() = default;
111 Shader(Shader::Id id) : id(id) {}
112 Shader(Shader&& other)
113 { this->id = other.id; other.id = 0; }
114 ~Shader();
115
116 Shader::Id get_id() const;
117
118 // Remember to call use() before setting uniforms and using this
119 // shader
120 bool use();
121
122 // Set uniforms
123 bool set_bool(const std::string &unif_name, bool value);
124 bool set_int(const std::string &unif_name, int value);
125 bool set_float(const std::string &unif_name, float value);
126 bool set_float2(const std::string &unif_name, float v1, float v2);
127 bool set_float3(const std::string &unif_name, float v1, float v2, float v3);
128 bool set_mat4(const std::string &unif_name, glm::mat4 value);
129 bool set_vec3(const std::string &unif_name, float x, float y, float z);
130 bool set_vec3(const std::string &unif_name, glm::vec3 value);
131
132private:
133
134 Shader::Id id = 0;
135
136 static bool
137 compile_shaders([[maybe_unused]] tenno::vector<Shader::Id> &compiled);
138 static bool compile_shaders(tenno::vector<Shader::Id> &compiled,
139 const tenno::vector<Shader::Object> &objects);
140 static std::optional<Shader::Id>
141 link_program(tenno::vector<Shader::Id>& compiled_shaders,
142 const GLchar **feedback_varyings, int num_varyings);
143 static void clean_compilation(tenno::vector<Shader::Id>& compiled_shader);
144 static bool check_compile_errors(Shader::Id shader);
145 static bool check_link_errors(Shader::Id shader);
146
147};
148
150{
151public:
152
153 Type type;
154 std::string src;
155 std::optional<std::filesystem::path> path;
156
157 Object() = default;
158 Object(Type type, const char* src)
159 : type(type), src(std::string(src)), path({}) {}
160 Object(Type type, const std::string &src)
161 : type(type), src(src) {}
162 Object(Type type, const std::filesystem::path &path);
163
164private:
165
166 static std::optional<std::string> read_file(const std::filesystem::path &path);
167
168};
169
171{
172public:
173
174 Builder& object(const Shader::Object &obj);
175 Builder& objects(const tenno::vector<Shader::Object> &objs);
176 Builder& feedback(const GLchar **feedback_varyings, int num_varyings);
177
178 // Add path to be watched for hot-reloading
179 Builder &watch(const std::filesystem::path &path);
180
181 std::optional<Shader> build();
182 tenno::vector<std::filesystem::path> get_watch_paths() const;
183
184private:
185
186 int num_varyings = 0;
187 const GLchar **feedback_varyings;
188 tenno::vector<Object> objs;
189
190 tenno::vector<std::filesystem::path> watch_paths = {};
191
192};
193
194} // namespace brenta