aboutsummaryrefslogtreecommitdiff
path: root/src/client/draw.cc
diff options
context:
space:
mode:
authorNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 21:57:46 +1100
committerNicolas James <Eele1Ephe7uZahRie@tutanota.com>2025-02-12 21:57:46 +1100
commite4483eca01b48b943cd0461e24a74ae1a3139ed4 (patch)
treeed58c3c246e3af1af337697695d780aa31f6ad9a /src/client/draw.cc
parent1cc08c51eb4b0f95c30c0a98ad1fc5ad3459b2df (diff)
Update to most recent version (old initial commit)
Diffstat (limited to 'src/client/draw.cc')
-rw-r--r--src/client/draw.cc432
1 files changed, 0 insertions, 432 deletions
diff --git a/src/client/draw.cc b/src/client/draw.cc
deleted file mode 100644
index c549ffe..0000000
--- a/src/client/draw.cc
+++ /dev/null
@@ -1,432 +0,0 @@
-#include "draw.hh"
-
-namespace client {
-namespace draw {
-
-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::render_text(text, height, args.is_centered, args.is_vcentered,
- black, make_matrix(1.0f, -2.0f));
- }
- glEnable(GL_BLEND);
-
- render::render_text(text, height, args.is_centered, args.is_vcentered,
- args.colour, make_matrix(0, 0));
- glEnable(GL_DEPTH_TEST);
-}
-
-static void draw_state_info(const shared::player& lp) noexcept {
-
- constexpr auto nstr = [](const auto d) -> std::string {
- auto str = std::to_string(d);
- str.erase(std::prev(std::end(str), 4), std::end(str));
- return str;
- };
-
- const auto make_position = [&]() -> std::string {
- const auto chunk_x = lp.chunk_pos.x;
- const auto chunk_z = lp.chunk_pos.z;
- const auto x = static_cast<double>(lp.local_pos.x);
- const auto y = static_cast<double>(lp.local_pos.y);
- const auto z = static_cast<double>(lp.local_pos.z);
- constexpr auto WIDTH = shared::world::chunk::WIDTH;
- const auto abs_x = static_cast<double>(lp.chunk_pos.x) * WIDTH + x;
- const auto abs_z = static_cast<double>(lp.chunk_pos.z) * WIDTH + z;
-
- constexpr auto nstr = [](const double d) -> std::string {
- auto str = std::to_string(d);
- str.erase(std::prev(std::end(str), 4), std::end(str));
- return str;
- };
- const std::string chk_str = '[' + std::to_string(chunk_x) + ", " +
- std::to_string(chunk_z) + ']';
- const std::string abs_str =
- '(' + nstr(abs_x) + ", " + nstr(y) + ", " + nstr(abs_z) + ')';
- return "position: " + abs_str + chk_str;
- };
-
- const auto make_direction = [&]() -> std::string {
- const float yaw = glm::degrees(lp.viewangles.yaw);
- const float pitch = glm::degrees(lp.viewangles.pitch);
-
- const std::string bearing = [&]() -> std::string {
- if (yaw >= 315.0f || yaw < 45.0f) {
- return "North";
- } else if (yaw >= 45.0f && yaw < 135.0f) {
- return "East";
- } else if (yaw >= 135.0f && yaw < 225.0f) {
- return "South";
- } else {
- return "West";
- }
- }();
- const std::string face_str = '(' + nstr(pitch) + ", " + nstr(yaw) + ')';
- // Yaw is between 0 and 360.
- return "perspective: " + face_str + " -> " + bearing;
- };
-
- const auto make_address = []() -> std::string {
- const std::string address = std::string(client::state.address);
- const std::string port = std::string(client::state.port);
- return "address: " + address + ":" + port;
- };
-
- const auto make_seed = []() -> std::string {
- return "seed: " + std::to_string(client::state.seed);
- };
-
- const auto make_players = []() -> std::string {
- const auto index = std::to_string(client::state.localplayer);
- return "index: " + index;
- };
-
- const auto make_chunks = []() -> std::string {
- return "chunks [req, rcv]: " +
- std::to_string(client::state.requested_chunk_count) + ", " +
- std::to_string(client::state.networked_chunk_count) + " @ " +
- std::to_string(client::state.draw_distance) + " draw distance";
- };
-
- const auto make_speed = [&]() -> std::string {
- return "speed: " + nstr(glm::length(lp.velocity)) + " b/s";
- };
-
- const auto make_velocity = [&]() -> std::string {
- return "velocity: (" + nstr(lp.velocity.x) + ", " +
- nstr(lp.velocity.y) + ", " + nstr(lp.velocity.z) + ")";
- };
-
- // Draws all of our strings and works its way down the top of the screen.
- text_args args{.pos = {.extent = {0.0f, 1.0f - 0.015f}},
- .extent_height = 0.0165f,
- .colour = {1.0f, 1.0f, 1.0f, 1.0f},
- .has_backing = true};
- const auto draw_str_trail = [&args](const std::string& str) {
- draw_text(str, args);
- args.pos.extent.y -= 0.02f;
- };
- draw_str_trail("blockgame_linux v0.20");
- draw_str_trail(make_address());
- draw_str_trail(make_seed());
- draw_str_trail(make_players());
- draw_str_trail(make_chunks());
- draw_str_trail(make_position());
- draw_str_trail(make_direction());
- draw_str_trail(make_speed());
- draw_str_trail(make_velocity());
-}
-
-static void draw_fps() noexcept {
- const auto get_fps_colour = [](const int fps) -> glm::vec4 {
- if (fps == 0) {
- return {1.0f, 1.0f, 1.0f, 1.0f};
- } else if (fps < 30) {
- return {1.0f, 0.0f, 0.0f, 1.0f};
- } else if (fps < 60) {
- return {1.0f, 0.5f, 0.0f, 1.0f};
- } else if (fps < 120) {
- return {0.0f, 1.0f, 0.0f, 1.0f};
- }
- return {0.0f, 1.0f, 1.0f, 1.0f};
- };
-
- const int fps = client::render::get_fps();
- const std::string fps_str = fps != 0 ? std::to_string(fps) : "~";
- draw_text("FPS: " + fps_str, {.pos = relative_arg{.offset = {0.0f, 2.0f}},
- .extent_height = 0.0165f,
- .colour = get_fps_colour(fps),
- .has_backing = true});
-}
-
-// Player position, version, fps etc
-// Just don't read past this point, doing this is unbelievably ugly.
-// I don't know why I write this like someone else will read it.
-static void draw_overlay(const shared::player& lp) noexcept {
- draw_state_info(lp);
- draw_fps();
-}
-
-static void draw_main_pass(const shared::player& localplayer,
- client::players& players,
- client::world::chunk::map& chunks) noexcept {
- for (auto& chunk : chunks) {
- if (!chunk.second.has_value()) {
- continue;
- }
- chunk.second->draw(chunks, localplayer,
- client::world::chunk::pass::solid);
- }
-
- // Draw players while ignoring the localplayer, which is always 0'th!
- // This also handles drawing messages.
- for (auto i = 1u; i < std::size(players); ++i) {
- players[i].draw_playermodel(localplayer);
- }
-}
-
-static void draw_water_pass(const shared::player& localplayer,
- client::world::chunk::map& chunks) noexcept {
- for (auto& chunk : chunks) {
- if (!chunk.second.has_value()) {
- continue;
- }
- chunk.second->draw(chunks, localplayer,
- client::world::chunk::pass::water);
- }
-}
-
-static void draw_wts_text(const shared::player& localplayer,
- client::players& players) noexcept {
- for (auto i = 1u; i < std::size(players); ++i) {
- players[i].draw_message(localplayer);
- }
-}
-
-static void draw_hud(const client::player& localplayer,
- const client::world::chunk::map& chunks) noexcept {
- if (client::input::is_key_toggled(SDLK_F3)) {
- draw_overlay(localplayer);
- }
-
- if (const auto& msg = localplayer.get_message(); msg.has_value()) {
- if (msg->receive_time + player::MSG_SHOW_TIME >=
- std::chrono::steady_clock::now()) {
- draw_text(msg->text, {.pos = relative_arg{.extent = {0.5f, 0.0f},
- .offset = {0.0f, 2.0f}},
- .extent_height = 0.0165f,
- .colour = {1.0f, 1.0f, 1.0f, 1.0f},
- .has_backing = true,
- .is_centered = true});
- }
- }
-
- if (client::window::is_open()) {
- return;
- }
-
- // crosshair
- client::draw::draw_rectangle(
- {.pos = {.extent = {0.5f, 0.5f}, .offset = {-3.0f, -3.0f}},
- .size = {.offset = {6.0f, 6.0f}},
- .colour = {1.0f, 1.0f, 1.0f, 1.0f}});
-
- // Draw the outline of the block we're aiming at.
- if (const auto interact = client::movement::interact(
- localplayer, movement::interact_mode::remove, chunks);
- interact.has_value()) {
-
- const auto [world_x, world_z] = [&]() -> std::pair<float, float> {
- const float offset_x =
- static_cast<float>(interact->first.x - localplayer.chunk_pos.x);
- const float offset_z =
- static_cast<float>(interact->first.z - localplayer.chunk_pos.z);
- return {offset_x * shared::world::chunk::WIDTH,
- offset_z * shared::world::chunk::WIDTH};
- }();
-
- const glm::vec3 render_pos = {
- world_x + static_cast<float>(interact->second.x),
- interact->second.y,
- world_z + static_cast<float>(interact->second.z)};
-
- client::render::render_cube_outline(render_pos + 0.5f,
- {0.8f, 0.8f, 0.8f, 1.0f});
- }
-}
-
-static void update_camera(const shared::player& localplayer) noexcept {
- // approximately encompasses everything we could possibly see
-
- // if our zfar is too low, our fog breaks
- // bit of maths for calculating the max distance we could can see
- client::render::camera::get_zfar() = std::hypot(
- static_cast<float>(world::chunk::HEIGHT),
- float(client::state.draw_distance) * std::hypot(16.0f, 16.0f));
- client::render::camera::get_pos() =
- localplayer.local_pos +
- glm::vec3{0.0f, shared::player::EYE_HEIGHT, 0.0f};
- client::render::camera::get_front() =
- shared::math::angle_to_dir(localplayer.viewangles);
- client::render::camera::update();
-}
-
-// We create framebuffer here to enable postprocessing and solve some
-// rendering problems.
-static void draw_buffers(const shared::player& localplayer,
- client::players& players,
- client::world::chunk::map& chunks) noexcept {
- const auto make_vbo = [](const auto& vertices) -> GLuint {
- GLuint vbo = 0;
- glGenBuffers(1, &vbo);
- glBindBuffer(GL_ARRAY_BUFFER, vbo);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), std::data(vertices),
- GL_STATIC_DRAW);
- return vbo;
- };
- const auto make_vao = []() -> GLuint {
- GLuint vao = 0;
- glGenVertexArrays(1, &vao);
- glBindVertexArray(vao);
- // position
- glVertexAttribPointer(0, sizeof(glm::vec2) / sizeof(float), GL_FLOAT,
- GL_FALSE, sizeof(glm::vec2), nullptr);
- glEnableVertexAttribArray(0);
- return vao;
- };
- const auto make_ebo = [](const auto& indices) -> GLuint {
- GLuint ebo = 0;
- glGenBuffers(1, &ebo);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), &indices,
- GL_STATIC_DRAW);
- return ebo;
- };
- const auto make_fbo = []() -> GLuint {
- GLuint fbo;
- glGenFramebuffers(1, &fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, fbo);
- return fbo;
- };
- const auto make_texture = [](const auto& internal_format,
- const auto& format,
- const int texture_offset) -> GLuint {
- GLuint texture;
- glGenTextures(1, &texture);
- glActiveTexture(GL_TEXTURE2 + texture_offset);
- glBindTexture(GL_TEXTURE_2D, texture);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
-
- const glm::vec2& window = client::render::get_window_size();
- glTexImage2D(GL_TEXTURE_2D, 0, internal_format, window.x, window.y, 0,
- internal_format, GL_UNSIGNED_BYTE, nullptr);
- glFramebufferTexture2D(GL_FRAMEBUFFER, format, GL_TEXTURE_2D, texture,
- 0);
- return texture;
- };
-
- static constexpr std::array<glm::vec2, 4> vertices = {
- glm::vec2{-1.0f, -1.0f}, glm::vec2{1.0f, -1.0f}, glm::vec2{1.0f, 1.0f},
- glm::vec2{-1.0f, 1.0f}};
- static constexpr std::array<unsigned, 6> indices = {0, 1, 2, 2, 3, 0};
- static const client::render::program framebuffer_program{
- "res/shaders/framebuffer.vs", "res/shaders/framebuffer.fs"};
- static const GLuint vbo [[maybe_unused]] = make_vbo(vertices);
- static const GLuint vao = make_vao();
- static const GLuint ebo [[maybe_unused]] = make_ebo(indices);
- // Main frame buffer object.
- static const GLuint fbo_main = make_fbo();
- static const GLuint main_colour [[maybe_unused]] =
- make_texture(GL_RGB, GL_COLOR_ATTACHMENT0, 0);
- static const GLuint main_depth [[maybe_unused]] =
- make_texture(GL_DEPTH_COMPONENT, GL_DEPTH_ATTACHMENT, 1);
- // Water frame buffer object.
- static const GLuint fbo_water = make_fbo();
- static const GLuint water_colour [[maybe_unused]] =
- make_texture(GL_RGB, GL_COLOR_ATTACHMENT0, 2);
- static const GLuint water_depth [[maybe_unused]] =
- make_texture(GL_DEPTH_COMPONENT, GL_DEPTH_ATTACHMENT, 3);
- static const GLint u_is_underwater =
- glGetUniformLocation(framebuffer_program, "_u_is_underwater");
-
- // We render our main scene on the main framebuffer.
- glBindFramebuffer(GL_FRAMEBUFFER, fbo_main);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- draw_main_pass(localplayer, players, chunks);
-
- // We render our water scene on the water framebuffer.
- glBindFramebuffer(GL_FRAMEBUFFER, fbo_water);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- const bool is_underwater = [&]() -> bool {
- const glm::ivec3 pos{client::render::camera::get_pos()};
-
- const auto find_it = chunks.find(localplayer.chunk_pos);
- if (find_it == std::end(chunks) || !find_it->second.has_value()) {
- return false;
- }
- if (shared::world::chunk::is_outside_chunk(pos)) {
- return false;
- }
-
- const auto block = find_it->second->get_block(pos);
- return block.type == shared::world::block::type::water;
- }();
- if (is_underwater) {
- glFrontFace(GL_CW);
- draw_water_pass(localplayer, chunks);
- glFrontFace(GL_CCW);
- } else {
- draw_water_pass(localplayer, chunks);
- }
-
- glBindFramebuffer(GL_FRAMEBUFFER, main_colour);
- glDisable(GL_DEPTH_TEST);
- glUseProgram(framebuffer_program);
- glBindVertexArray(vao);
-
- glUniform1i(u_is_underwater, is_underwater);
-
- glDrawElements(GL_TRIANGLES, std::size(indices), GL_UNSIGNED_INT, nullptr);
-
- static client::render::program postprocess_program{
- "res/shaders/framebuffer.vs", "res/shaders/postprocess.fs"};
- glBindFramebuffer(GL_FRAMEBUFFER, 0);
- glUseProgram(postprocess_program);
- glDrawElements(GL_TRIANGLES, std::size(indices), GL_UNSIGNED_INT, nullptr);
-
- glEnable(GL_DEPTH_TEST);
-}
-
-void draw(client::players& players,
- client::world::chunk::map& chunks) noexcept {
- const auto& localplayer = client::get_localplayer(players);
-
- update_camera(localplayer);
- client::render::update_uniforms();
-
- draw_buffers(localplayer, players, chunks);
- draw_hud(localplayer, chunks);
- draw_wts_text(localplayer, players);
-
- client::window::draw();
-
- client::render::swap_window();
-}
-
-} // namespace draw
-} // namespace client