diff options
Diffstat (limited to 'src/client/render/render.cc')
| -rw-r--r-- | src/client/render/render.cc | 147 |
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 |
