aboutsummaryrefslogtreecommitdiff
path: root/src/client/render/render.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/render/render.cc')
-rw-r--r--src/client/render/render.cc147
1 files changed, 130 insertions, 17 deletions
diff --git a/src/client/render/render.cc b/src/client/render/render.cc
index 18aa32a..941988d 100644
--- a/src/client/render/render.cc
+++ b/src/client/render/render.cc
@@ -27,7 +27,7 @@ SDL_Window* const& get_sdl_window() noexcept {
std::bind(SDL_GL_SetAttribute, SDL_GL_CONTEXT_MINOR_VERSION, 2));
SDL_Window* const ret =
- SDL_CreateWindow("blockgame_linux", 0, 0, 0, 0,
+ SDL_CreateWindow("blockgame_linux", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 0, 0,
SDL_WINDOW_OPENGL | SDL_WINDOW_FULLSCREEN_DESKTOP |
SDL_WINDOW_BORDERLESS);
check_sdl_pointer(ret);
@@ -124,6 +124,71 @@ void swap_window() noexcept {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
+// Additional sanity checks for our atlas.
+static void check_atlas(const client::render::texture& texture) {
+ if (texture.width % 6) {
+ throw std::runtime_error("invalid atlas; WIDTH is not divisible by 6");
+ }
+ if (texture.height % (texture.width / 6)) {
+ throw std::runtime_error(
+ "invalid atlas, HEIGHT is not divisible by (WIDTH / 6)");
+ }
+}
+
+GLuint get_texture_atlas() noexcept {
+ static const auto atlas = []() -> GLuint {
+ GLuint texture = 0;
+ glActiveTexture(GL_TEXTURE1);
+ glGenTextures(1, &texture);
+ glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
+
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T,
+ GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR_MIPMAP_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, 16.0f);
+
+ const client::render::texture stbi{"res/textures/atlas.png"};
+ check_atlas(stbi);
+ const int face_size = stbi.width / 6;
+
+ // 2D texture array, where our depth is our block face.
+ glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, face_size, face_size,
+ 6 * (stbi.height / face_size), 0,
+ stbi.channels == 3 ? GL_RGB : GL_RGBA, GL_UNSIGNED_BYTE,
+ nullptr);
+
+ // Fill the 2D texture array.
+ // Because our image has multiple images on the x-axis and opengl
+ // expects a single image per axis, we must fill it in row by row.
+ const auto get_pixel_xy = [&stbi](const int x, const int y) {
+ return stbi.image + 4 * (y * stbi.width + x);
+ };
+ for (int x = 0; x < 6; ++x) {
+ const int x_pixel = x * face_size;
+
+ for (int y = 0; y < stbi.height / face_size; ++y) {
+ const int y_pixel = y * face_size;
+
+ for (auto row = 0; row < face_size; ++row) {
+ glTexSubImage3D(
+ GL_TEXTURE_2D_ARRAY, 0, 0, row, x + y * 6, face_size, 1,
+ 1, GL_RGBA, GL_UNSIGNED_BYTE,
+ get_pixel_xy(x_pixel, row + y_pixel)); // pixel
+ }
+ }
+ }
+
+ glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
+
+ return texture;
+ }();
+ return atlas;
+}
+
void render_cube_outline(const glm::vec3& pos,
const glm::vec4& colour) noexcept {
const auto generate_vbo = [](const auto& vertices) -> GLuint {
@@ -427,6 +492,8 @@ public:
font_atlas(const unsigned size) noexcept
: font_size(size), texture(make_texture_info(size)) {}
~font_atlas() noexcept { glDeleteTextures(1, &this->texture.texture); }
+ font_atlas(font_atlas&&) = delete;
+ font_atlas(const font_atlas&) = delete;
GLint get_height() const noexcept { return this->texture.height; }
GLint get_width() const noexcept { return this->texture.width; }
@@ -578,25 +645,71 @@ void update_uniforms() noexcept {
};
static const GLuint ubo = make_ubo();
- ubo_data upload = {.proj = camera::get_proj(),
- .view = camera::get_view(),
- .frustum = camera::get_frustum(),
- .znear = camera::get_znear(),
- .zfar = camera::get_zfar(),
- .xfov = glm::radians(camera::get_xfov()),
- .yfov = glm::radians(camera::get_yfov()),
- .time = []() -> unsigned {
- static const auto start =
- std::chrono::steady_clock::now();
- const auto now = std::chrono::steady_clock::now();
- return static_cast<unsigned>(
- (now - start) / std::chrono::milliseconds(1));
- }(),
- .xwindow = static_cast<float>(get_window_size().x),
- .ywindow = static_cast<float>(get_window_size().y)};
+ const ubo_data upload = {
+ .proj = camera::get_proj(),
+ .view = camera::get_view(),
+ .frustum = camera::get_frustum(),
+ .znear = camera::get_znear(),
+ .zfar = camera::get_zfar(),
+ .xfov = glm::radians(camera::get_xfov()),
+ .yfov = glm::radians(camera::get_yfov()),
+ .time = []() -> unsigned {
+ static const auto start = std::chrono::steady_clock::now();
+ const auto now = std::chrono::steady_clock::now();
+ return static_cast<unsigned>((now - start) /
+ std::chrono::milliseconds(1));
+ }(),
+ .xwindow = static_cast<float>(get_window_size().x),
+ .ywindow = static_cast<float>(get_window_size().y)};
glBindBuffer(GL_UNIFORM_BUFFER, ubo);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(upload), &upload);
}
+void draw_rectangle(const rectangle_args& ra) noexcept {
+ const glm::vec2 pos = ra.pos.to_vec2();
+ const glm::vec2 size = ra.size.to_vec2();
+
+ // We want to render from the bottom left corner.
+ render::render_rectangle(pos + (size / 2.0f), size, ra.colour);
+}
+
+void draw_colour(const glm::vec4& colour) noexcept {
+ const rectangle_args args = {
+ .pos = {.extent = {0.0f, 0.0f}},
+ .size = {.extent = {1.0f, 1.0f}},
+ .colour = colour,
+ };
+ draw_rectangle(args);
+}
+
+void draw_text(const std::string_view text, const text_args& args) noexcept {
+ const glm::vec2& window = render::get_window_size();
+ const float x = floorf(window.x * args.pos.extent.x + args.pos.offset.x);
+ const float y = floorf(window.y * args.pos.extent.y + args.pos.offset.y);
+ const unsigned height = static_cast<unsigned>(
+ args.extent_height * window.y + args.offset_height);
+
+ const auto make_matrix = [&](const float xo, const float yo) -> glm::mat4 {
+ constexpr auto identity = glm::mat4(1.0f);
+ const auto proj = glm::ortho(0.f, window.x, 0.f, window.y);
+ const auto tran =
+ glm::translate(identity, glm::vec3{x + xo, y + yo, 0.0f});
+ return proj * tran;
+ };
+
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+ if (args.has_backing) {
+ constexpr glm::vec4 black{0.0f, 0.0f, 0.0f, 1.0f};
+ render_text(text, height, args.is_centered, args.is_vcentered, black,
+ make_matrix(2.0f, -2.0f));
+ }
+ glEnable(GL_BLEND);
+
+ render_text(text, height, args.is_centered, args.is_vcentered, args.colour,
+ make_matrix(0, 0));
+ glEnable(GL_DEPTH_TEST);
+}
+
} // namespace render
} // namespace client