StapleGL
Header-only C++20 OpenGL wrapper
Loading...
Searching...
No Matches
batches.cpp
Go to the documentation of this file.
1
12#include "glad.h"
13#include "staplegl.hpp"
14
15#include <GLFW/glfw3.h>
16#include <array>
17#include <cmath>
18#include <cstdint>
19#include <cstdio>
20#include <ctime>
21#include <execution>
22#include <iostream>
23#include <optional>
24#include <span>
25#include <utility>
26
27/*
28
29 IMPORTANT: in order for the program to correctly compile, go to
30 include/staplegl/modules/gl_functions.hpp and replace the placeholder
31 with the provided OpenGL loader ("glad.h").
32
33*/
34
35constexpr auto lerp(float a, float b, float f) -> float
36{
37 return a * (1.0F - f) + b * f;
38}
39
40struct vec3 {
41 float x;
42 float y;
43 float z;
44};
45
46void framebuffer_size_callback(GLFWwindow* window, int width, int height);
47void processInput(GLFWwindow* window);
48
49void GLAPIENTRY
50MessageCallback(GLenum source [[maybe_unused]],
51 GLenum type,
52 GLuint id [[maybe_unused]],
53 GLenum severity,
54 GLsizei length [[maybe_unused]],
55 const GLchar* message,
56 const void* userParam [[maybe_unused]])
57{
58 if (type == GL_DEBUG_TYPE_OTHER || type == GL_DEBUG_TYPE_PERFORMANCE)
59 return;
60 // NOLINTNEXTLINE
61 fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x,\nmessage = %s\n", // NOLINT
62 (type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
63 type, severity, message);
64}
65
66// initial window size
67const int32_t SCR_WIDTH = 1600;
68const int32_t SCR_HEIGHT = 900;
69
70auto main() -> int
71{
72 // glfw: initialize and configure
73 // ------------------------------
74 glfwInit();
75 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
76 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
77
78 glfwWindowHint(GLFW_SAMPLES, 4); // MSAA
79 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
80
81#ifdef __APPLE__
82 glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
83#endif
84
85 // glfw window creation
86 // --------------------
87 GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Batched Rendering", nullptr, nullptr);
88 if (window == nullptr) {
89 std::cout << "Failed to create GLFW window" << std::endl;
90 glfwTerminate();
91 return -1;
92 }
93 glfwMakeContextCurrent(window);
94 glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
95
96 // glad: load all OpenGL function pointers
97 // ---------------------------------------
98 if (gladLoadGLLoader(reinterpret_cast<GLADloadproc>(glfwGetProcAddress)) == 0) { // NOLINT (reinterpret-cast)
99 std::cout << "Failed to initialize GLAD" << std::endl;
100 return -1;
101 }
102
103 // During init, enable debug output
104 glEnable(GL_DEBUG_OUTPUT);
105 glDebugMessageCallback(MessageCallback, nullptr);
106
107 // antialiasing and other nice things
108 glEnable(GL_MULTISAMPLE);
109
110 staplegl::shader_program const basic { "batched_shader", "./shaders/batched_shader.glsl" };
111
112 basic.bind();
113
114 // set up vertex data (and buffer(s)) and configure vertex attributes
115 // ------------------------------------------------------------------
116 const std::array<const float, 12> vertices {
117 0.001F, 0.001F, 0.0F, // top right
118 0.001F, -0.001F, 0.0F, // bottom right
119 -0.001F, -0.001F, 0.0F, // bottom left
120 -0.001F, 0.001F, 0.0F, // top left
121 };
122
123 const std::array<const unsigned int, 6> indices {
124 // note that we start from 0!
125 0, 1, 3, // first Triangle
126 1, 2, 3 // second Triangle
127 };
128
129 using namespace staplegl::shader_data_type;
130
131 staplegl::vertex_buffer_layout const layout {
132 { u_type::vec3, "aPos" }
133 };
134
135 staplegl::vertex_buffer_layout const instance_layout {
136 { u_type::vec3, "instancePos" }
137 };
138
140 staplegl::vertex_buffer_inst VBO_inst({}); // empty for now
141
142 VBO_inst.set_layout(instance_layout);
143 VBO.set_layout(layout);
144
145 staplegl::index_buffer EBO { indices };
147
148 VAO.add_vertex_buffer(std::move(VBO));
149 VAO.set_instance_buffer(std::move(VBO_inst));
150 VAO.set_index_buffer(std::move(EBO));
151
152 staplegl::vertex_buffer_layout const UBO_block_layout {
153 { shader_array_type::float32_arr, "u_color", 4 }
154 };
155
156 staplegl::uniform_buffer UBO_block { UBO_block_layout, 1 };
157
158 UBO_block.bind();
159
160 std::array<float, 4> color {};
161
162 VAO.bind();
163
164 const float START = -0.95F;
165 const float END = 0.95F;
166
167 const float Z_START = 0.01F;
168 const float Z_END = 1.00F;
169
170 std::srand(static_cast<unsigned int>(std::time(nullptr)));
171 constexpr int32_t NUM_INSTANCES = 65535;
172
173 for (int i = 0; i < NUM_INSTANCES; i++) {
174 std::array<float, 3> offset = {
175 lerp(START, END,
176 static_cast<float>(rand()) / static_cast<float>(RAND_MAX)),
177 lerp(START, END,
178 static_cast<float>(rand()) / static_cast<float>(RAND_MAX)),
179 lerp(Z_START, Z_END,
180 static_cast<float>(rand()) / static_cast<float>(RAND_MAX))
181 };
182
183 // I know the instance is set since it is in the optional, so I go for direct access
184 VAO.instanced_data()->add_instance(offset);
185 }
186
187 while (glfwWindowShouldClose(window) == 0) {
188
189 processInput(window);
190
191 glClearColor(0.2F, 0.3F, 0.3F, 1.0F);
192 glClear(GL_COLOR_BUFFER_BIT);
193
194 auto timeNow = static_cast<float>(glfwGetTime());
195
196 for (float& col : color) {
197 col = std::sin(timeNow) / 2.0F + 0.5F;
198 }
199 color[3] = 1.0F;
200
201 for (int i = 0; i < 4; ++i) {
202 UBO_block.set_attribute_data(std::span { &color[i], 1 }, "u_color", i);
203 }
204
205// define a struct for the instance data
206#pragma pack(push, 1)
207 struct vec3 {
208 float x;
209 float y;
210 float z;
211 };
212#pragma pack(pop)
213
214 static_assert(sizeof(vec3) == 3 * sizeof(float));
215
216 // apply a lambda to the instance data, reading it as a span of vec3
217 VAO.instanced_data()->apply<vec3>(
218 [](std::span<vec3> data) {
219 // thanks to std::span, we can pass this GPU buffer to a parallel algorithm!
220 std::for_each(std::execution::par_unseq, data.begin(), data.end(),
221 [](vec3& v) { // randomly shift the position of the instance left or right
222 const float speed = ((static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX)) - 0.5F) / 1000.0F;
223 v.x += speed;
224 });
225 });
226
227 // draw the instances
228 glDrawElementsInstanced(GL_TRIANGLES, VAO.index_data().count(), GL_UNSIGNED_INT, nullptr,
229 VAO.instanced_data()->instance_count());
230
231 glfwSwapBuffers(window);
232 glfwPollEvents();
233 }
234
235 glfwTerminate();
236 return 0;
237}
238
239void processInput(GLFWwindow* window)
240{
241 if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
242 glfwSetWindowShouldClose(window, 1);
243 }
244}
245
246void framebuffer_size_callback(GLFWwindow* /*window*/, int width, int height)
247{
248 glViewport(0, 0, width, height);
249}
const int32_t SCR_WIDTH
Definition batches.cpp:67
void processInput(GLFWwindow *window)
Definition batches.cpp:239
void framebuffer_size_callback(GLFWwindow *window, int width, int height)
Definition batches.cpp:246
void GLAPIENTRY MessageCallback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *userParam)
Definition batches.cpp:50
auto main() -> int
Definition batches.cpp:70
const int32_t SCR_HEIGHT
Definition batches.cpp:68
constexpr auto lerp(float a, float b, float f) -> float
Definition batches.cpp:35
Element Buffer Object (EBO) wrapper.
Shader program class.
Definition shader.hpp:77
void bind() const
Bind the shader program.
Definition shader.hpp:367
Uniform Buffer Object (UBO) wrapper.
void bind() const
Bind the uniform to the OpenGL context.
Vertex Array Object (VAO) wrapper.
constexpr auto instanced_data() -> std::optional< vertex_buffer_inst > &
Get the instance buffer object.
void bind() const
Bind the vertex array object.
void set_index_buffer(index_buffer &&ibo)
Set the index buffer object.
void set_instance_buffer(vertex_buffer_inst &&vbo)
Set the instance buffer object.
constexpr auto index_data() -> index_buffer &
Get the index buffer object.
auto add_vertex_buffer(vertex_buffer &&vbo) -> vertex_array::iterator_t
Add a vertex buffer to the vertex array object.
A vertex buffer object for instanced rendering.
Vertex Buffer Object (VBO) wrapper.
void set_layout(const vertex_buffer_layout &layout)
Set the layout object.
StapleGL, a C++20 wrapper for OpenGL.
float x
Definition batches.cpp:41
float y
Definition batches.cpp:42
float z
Definition batches.cpp:43