From 52dd65b64e7818a768c487c77a5febad8fde4c5d Mon Sep 17 00:00:00 2001 From: Jay Robson Date: Tue, 9 Jul 2024 17:45:05 +1000 Subject: [PATCH] images, better shaders, etc --- .gitmodules | 3 + CMakeLists.txt | 2 +- assets/image/yellow_brick_wall.png | Bin 0 -> 927 bytes assets/shader/atlas.glsl | 20 +++ assets/shader/header.glsl | 6 + assets/shader/model.frag | 7 +- assets/shader/model.vert | 12 +- assets/shader/version.glsl | 3 - src/files.cmake | 1 + src/graphics/context.cpp | 1 + src/graphics/files.cmake | 1 + src/graphics/mesh.hpp | 12 +- src/graphics/pipeline.cpp | 4 +- src/graphics/primitive.hpp | 21 +-- src/graphics/shader.hpp | 8 ++ src/graphics/shader/compile.cpp | 3 +- src/graphics/shader/compile.hpp | 4 +- src/graphics/texture.hpp | 15 +++ src/graphics/texture/atlas.cpp | 172 ++++++++++++++++++++++++ src/graphics/texture/atlas.hpp | 63 +++++++++ src/graphics/texture/files.cmake | 3 + src/graphics/texture/generate_atlas.cpp | 90 +++++++++++++ src/graphics/texture/image.cpp | 58 ++++++++ src/graphics/texture/image.hpp | 34 +++++ src/graphics/texture/uv.hpp | 16 +++ src/graphics/vertex.cpp | 6 +- src/graphics/vertex.hpp | 4 +- src/main.cpp | 2 + src/stb | 1 + src/util/math/mod.hpp | 9 ++ src/util/pointer_diff.cpp | 3 + src/world/builder.cpp | 57 +++++--- src/world/builder.hpp | 6 + src/world/chunk.cpp | 24 +++- src/world/chunk.hpp | 29 ++-- src/world/map.cpp | 31 ++++- src/world/map.hpp | 4 + src/world/player.cpp | 6 +- 38 files changed, 662 insertions(+), 79 deletions(-) create mode 100644 .gitmodules create mode 100644 assets/image/yellow_brick_wall.png create mode 100644 assets/shader/atlas.glsl create mode 100644 assets/shader/header.glsl delete mode 100644 assets/shader/version.glsl create mode 100644 src/graphics/shader.hpp create mode 100644 src/graphics/texture.hpp create mode 100644 src/graphics/texture/atlas.cpp create mode 100644 src/graphics/texture/atlas.hpp create mode 100644 src/graphics/texture/files.cmake create mode 100644 src/graphics/texture/generate_atlas.cpp create mode 100644 src/graphics/texture/image.cpp create mode 100644 src/graphics/texture/image.hpp create mode 100644 src/graphics/texture/uv.hpp create mode 160000 src/stb create mode 100644 src/util/math/mod.hpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..ee7ce76 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/stb"] + path = src/stb + url = https://github.com/nothings/stb diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b27682..c8b908d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.25) -project(NuclearPlantSim VERSION 1.0) +project(NuclearPlantSim LANGUAGES C CXX) set(CMAKE_CXX_STANDARD 20) diff --git a/assets/image/yellow_brick_wall.png b/assets/image/yellow_brick_wall.png new file mode 100644 index 0000000000000000000000000000000000000000..2c572907088bce7d9c2488642755ff37dca77f49 GIT binary patch literal 927 zcmV;Q17Q4#P)EX>4Tx04R}tkv&MmKpe$iQ?;d39PA*{AwzYti;6hbDionYs1;guFuC*#nzSS- zE{=k0!NHHks)LKOt`4q(Aou~|?BJy6A|?JWDYS_3;J6>}?mh0_0YaDJc?d{()o&J6RF*0(D@A>EC00009a7bBm001r{001r{0eGc9b^rhX2XskI zMF;2y2nP)@plGi`0000PbVXQnLvL+uWo~o;Lvm$dbY)~9cWHEJAV*0}P*;Ht7XSbO ziAh93R7l6&mc44kFbv0)L`%ql;cm-tOX-_;?_(7D0EH5|WboF4AX-BQamHs$j+>I9 z)pU1>K5YH;*ER2N&j3Kg&J6(I+z6S?^8|*ShIZi<0K`2vyhZdOM{d{aWBirK6R|`; zUmngEC|C=7O`>nUwm(6oA(59)&J9Ei;MF#$7Wt0q?~;J;pD}>1iN5d(z&9_G0!$i9 z#7WAsSzH8UFuM?6cE}Qp`MpVr@(4E_LSiCLc&$0v;S?Q@X{9inhMlFL6|TcR?yv|> zL6m~11PEms8WFquIZ$Na>+_>L6G8PXu?yYT%Ph!_& p) { unsigned int index_at = m_vertices.size(); for(int i = 0; i < VERTICES; i++) { - m_vertices.push_back(p.m_vertices[i]); + Vertex v = p.m_vertices[i]; + if(p.m_colour.has_value()) { + v.m_colour *= p.m_colour.value(); + } + if(p.m_offset.has_value()) { + v.m_pos += p.m_offset.value(); + } + if(p.m_texid.has_value()) { + v.m_texid = p.m_texid.value(); + } + m_vertices.push_back(v); } for(int i = 0; i < INDICES; i++) { m_indices.push_back(p.m_indices[i] + index_at); diff --git a/src/graphics/pipeline.cpp b/src/graphics/pipeline.cpp index e9d53a5..f878823 100644 --- a/src/graphics/pipeline.cpp +++ b/src/graphics/pipeline.cpp @@ -12,8 +12,8 @@ using Graphics::Pipeline; Pipeline::Pipeline() { Graphics::GL::Shader model_vert(GL_VERTEX_SHADER); Graphics::GL::Shader model_frag(GL_FRAGMENT_SHADER); - Graphics::Shader::compile(model_vert, "../assets/shader/model.vert"); - Graphics::Shader::compile(model_frag, "../assets/shader/model.frag"); + Graphics::Shader::compile(model_vert, "model.vert"); + Graphics::Shader::compile(model_frag, "model.frag"); Graphics::Shader::link(m_program_model, {model_vert, model_frag}); } diff --git a/src/graphics/primitive.hpp b/src/graphics/primitive.hpp index 746a1f2..dc72d80 100644 --- a/src/graphics/primitive.hpp +++ b/src/graphics/primitive.hpp @@ -3,20 +3,17 @@ #include "vertex.hpp" #include +#include namespace Graphics { template struct Primitive { Vertex m_vertices[VERTICES]; - unsigned int m_indices[INDICES]; + int m_indices[INDICES]; - constexpr Primitive with_colour(glm::vec4 colour) const { - Primitive p = *this; - for(Vertex& v : p.m_vertices) { - v.m_colour *= colour; - } - return p; - } + std::optional m_texid; + std::optional m_colour; + std::optional m_offset; constexpr Primitive with_matrix(glm::mat4 mat) const { Primitive p = *this; @@ -25,14 +22,6 @@ namespace Graphics { } return p; } - - constexpr Primitive with_translation(glm::vec3 vec) const { - Primitive p = *this; - for(Vertex& v : p.m_vertices) { - v.m_pos += glm::vec4(vec, 0); - } - return p; - } }; }; diff --git a/src/graphics/shader.hpp b/src/graphics/shader.hpp new file mode 100644 index 0000000..f827b4f --- /dev/null +++ b/src/graphics/shader.hpp @@ -0,0 +1,8 @@ + +#pragma once + +namespace Graphics::Shader { + constexpr int SSBO_ATLAS_BUFFER = 1; + constexpr int TEX_ATLAS = 1; +}; + diff --git a/src/graphics/shader/compile.cpp b/src/graphics/shader/compile.cpp index 6a1a786..19d7a23 100644 --- a/src/graphics/shader/compile.cpp +++ b/src/graphics/shader/compile.cpp @@ -105,7 +105,8 @@ static bool parse(std::vector& source, const std::filesystem::path& path) return true; } -void Graphics::Shader::compile(unsigned int shader, const std::filesystem::path& path) { +void Graphics::Shader::compile(unsigned int shader, const char* filename) { + std::filesystem::path path = std::filesystem::path("../assets/shader/").append(filename); std::vector source; if(!parse(source, path)) { diff --git a/src/graphics/shader/compile.hpp b/src/graphics/shader/compile.hpp index 8064ce4..8c05bd2 100644 --- a/src/graphics/shader/compile.hpp +++ b/src/graphics/shader/compile.hpp @@ -1,9 +1,7 @@ #pragma once -#include - namespace Graphics::Shader { - void compile(unsigned int shader, const std::filesystem::path& path); + void compile(unsigned int shader, const char* path); }; diff --git a/src/graphics/texture.hpp b/src/graphics/texture.hpp new file mode 100644 index 0000000..7f2be15 --- /dev/null +++ b/src/graphics/texture.hpp @@ -0,0 +1,15 @@ + +#pragma once + +#include "texture/image.hpp" + +namespace Graphics::Texture { + inline Image YELLOW_BRICK_WALL {"yellow_brick_wall.png", Image::Edge::CLAMP}; + + inline Image* const IMAGES[] { + &YELLOW_BRICK_WALL, + }; + + void generate_atlas(); +}; + diff --git a/src/graphics/texture/atlas.cpp b/src/graphics/texture/atlas.cpp new file mode 100644 index 0000000..57299e2 --- /dev/null +++ b/src/graphics/texture/atlas.cpp @@ -0,0 +1,172 @@ + +#include "atlas.hpp" +#include "image.hpp" +#include + +using Graphics::Texture::Atlas; +using Graphics::Texture::Image; + +Atlas::Pixel::Pixel(const Pixel& p) { + for(int i = 0; i < N; i++) { + m_data[i] = p.m_data[i]; + } +} + +Atlas::Pixel::Pixel(const uint8_t* p) { + for(int i = 0; i < N; i++) { + m_data[i] = p[i]; + } +} + +bool Atlas::Pixel::operator == (const Pixel& p) const { + for(int i = 0; i < N; i++) { + if(m_data[i] != p.m_data[i]) { + return false; + } + } + return true; +} + +bool Atlas::Pixel::operator != (const Pixel& p) const { + return !(*this == p); +} + +Atlas::Pixel& Atlas::Pixel::operator = (const Pixel& p) { + for(int i = 0; i < N; i++) { + m_data[i] = p.m_data[i]; + } + return *this; +} + +Atlas::Pixel& Atlas::Pixel::operator = (const uint8_t* p) { + for(int i = 0; i < N; i++) { + m_data[i] = p[i]; + } + return *this; +} + +uint8_t& Atlas::Pixel::operator [] (int i) { + return m_data[i]; +} + +const uint8_t& Atlas::Pixel::operator [] (int i) const { + return m_data[i]; +} + +Atlas::Atlas(glm::vec<2, int> size) : m_size({size, 1}), m_data(size.x * size.y) { +} + +Atlas::Atlas(glm::vec<3, int> size) : m_size(size), m_data(size.x * size.y * size.z) { +} + +Atlas::Pixel& Atlas::get(glm::vec<3, int> p) { + int z_off = p.z * m_size.x * m_size.y; + + if(z_off >= m_data.size()) { + m_size.z = p.z + 1; + m_data.resize(m_size.x * m_size.y * m_size.z); + } + + return m_data[z_off + p.y * m_size.x + p.x]; +} + +const Atlas::Pixel& Atlas::get(glm::vec<3, int> p) const { + static const Atlas::Pixel empty; + int z_off = p.z * m_size.x * m_size.y; + + if(z_off >= m_data.size()) { + return empty; + } + + return m_data[z_off + p.y * m_size.x + p.x]; +} + +struct DrawSrcImage { + glm::vec<2, int> m_size; + const Image* m_image; + + DrawSrcImage(const Image& image) { + m_size = image.m_size; + m_image = ℑ + } + + Atlas::Pixel get(glm::vec<2, int> pos) const { + const uint8_t* pixel_ptr = m_image->get(pos); + Atlas::Pixel pixel; + + for(int i = 0; i < 4; i++) { + switch(m_image->m_mapping[i]) { + case GL_ZERO: + pixel[i] = 0; + break; + case GL_ONE: + pixel[i] = 255; + break; + case GL_RED: + pixel[i] = pixel_ptr[0]; + break; + case GL_GREEN: + pixel[i] = pixel_ptr[1]; + break; + case GL_BLUE: + pixel[i] = pixel_ptr[2]; + break; + case GL_ALPHA: + pixel[i] = pixel_ptr[3]; + break; + } + } + + return pixel; + } +}; + +struct DrawSrcAtlas { + glm::vec<2, int> m_size; + const Atlas* m_atlas; + + DrawSrcAtlas(const Atlas& atlas) { + m_size = atlas.m_size; + m_atlas = &atlas; + } + + Atlas::Pixel get(glm::vec<2, int> pos) const { + return m_atlas->get({pos, 0}); + } +}; + +template +static void draw_to(const T& src, Atlas& dst, glm::vec<3, int> p) { + + for(int i = 0; i < src.m_size.y; i++) { + for(int j = 0; j < src.m_size.x; j++) { + dst.get({p.x + j, p.y + i, p.z}) = src.get({j, i}); + } + } + + if(dst.m_padding) { + for(int i = 0; i < src.m_size.y; i++) { + dst.get({p.x - 1, p.y + i, p.z}) = src.get({0, i}); + dst.get({p.x + src.m_size.x, p.y + i, p.z}) = src.get({src.m_size.x - 1, i}); + } + + for(int i = 0; i < src.m_size.x; i++) { + dst.get({p.x + i, p.y - 1, p.z}) = src.get({i, 0}); + dst.get({p.x + i, p.y + src.m_size.y, p.z}) = src.get({i, src.m_size.y - 1}); + } + + dst.get({p.x - 1, p.y - 1, p.z}) = src.get({0, 0}); + dst.get({p.x + src.m_size.x, p.y - 1, p.z}) = src.get({src.m_size.x - 1, 0}); + dst.get({p.x - 1, p.y + src.m_size.y, p.z}) = src.get({0, src.m_size.y - 1}); + dst.get({p.x + src.m_size.x, p.y + src.m_size.y, p.z}) = src.get({src.m_size.x - 1, src.m_size.y - 1}); + } +} + +void Atlas::draw(const Atlas& src, glm::vec<3, int> p) { + draw_to(DrawSrcAtlas(src), *this, p); +} + +void Atlas::draw(const Image& src, glm::vec<3, int> p) { + draw_to(DrawSrcImage(src), *this, p); +} + diff --git a/src/graphics/texture/atlas.hpp b/src/graphics/texture/atlas.hpp new file mode 100644 index 0000000..1a0ed8c --- /dev/null +++ b/src/graphics/texture/atlas.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "image.hpp" +#include +#include +#include +#include + +namespace Graphics::Texture { + struct Atlas { + static constexpr int N = 4; + struct Pixel { + uint8_t m_data[N] = {0}; + + Pixel() = default; + Pixel(const Pixel& p); + Pixel(const uint8_t* p); + + bool operator == (const Pixel& p) const; + bool operator != (const Pixel& p) const; + Pixel& operator = (const Pixel& p); + Pixel& operator = (const uint8_t* p); + uint8_t& operator [] (int i); + const uint8_t& operator [] (int i) const; + + template + constexpr operator glm::vec() { + glm::vec vec; + for(int i = 0; i < N; i++) { + vec[i] = (T)m_data[i] / 255.0; + } + return vec; + } + + template + constexpr Pixel& operator = (const glm::vec& vec) { + for(int i = 0; i < N; i++) { + m_data[i] = (uint8_t)(std::round(vec[i] * 255)); + } + return *this; + } + + template + constexpr Pixel(const glm::vec& vec) { + *this = vec; + } + }; + + glm::vec<3, int> m_size; + std::vector m_data; + bool m_padding = false; + + Atlas() = default; + Atlas(glm::vec<2, int> size); + Atlas(glm::vec<3, int> size); + Pixel& get(glm::vec<3, int> pos); + const Pixel& get(glm::vec<3, int> pos) const; + void draw(const Atlas& src, glm::vec<3, int> pos); + void draw(const Image& src, glm::vec<3, int> pos); + }; +}; + diff --git a/src/graphics/texture/files.cmake b/src/graphics/texture/files.cmake new file mode 100644 index 0000000..7faa933 --- /dev/null +++ b/src/graphics/texture/files.cmake @@ -0,0 +1,3 @@ + +file(GLOB SOURCES ${SOURCES} src/graphics/texture/*.cpp) + diff --git a/src/graphics/texture/generate_atlas.cpp b/src/graphics/texture/generate_atlas.cpp new file mode 100644 index 0000000..afc9f89 --- /dev/null +++ b/src/graphics/texture/generate_atlas.cpp @@ -0,0 +1,90 @@ + +#include "../texture.hpp" +#include "../shader.hpp" +#include "atlas.hpp" +#include "image.hpp" +#include "uv.hpp" +#include "../gl/array_buffer.hpp" +#include "../gl/texture.hpp" +#include +#include +#include + +void Graphics::Texture::generate_atlas() { + int total_area = 0; + int min_size = 1; + int padding = 0; + int offset = 0; + + for(const Image* i : IMAGES) { + int w = i->m_size.x + padding; + int h = i->m_size.y + padding; + if(w > min_size) min_size = w; + if(h > min_size) min_size = h; + total_area += w * h; + } + + // find an approximate size for the atlas that can hold all the textures + int size = std::pow(2, std::ceil(std::log2(std::max((double)min_size, std::pow(total_area, 1.0/3.0))))); + + std::vector rects; + std::vector uvs(std::size(IMAGES)); + rects.reserve(std::size(IMAGES)); + + for(int i = 0; i < std::size(IMAGES); i++) { + const Image* a = IMAGES[i]; + stbrp_rect rect; + rect.id = i; + rect.w = a->m_size.x + padding; + rect.h = a->m_size.y + padding; + rects.push_back(rect); + } + + stbrp_context context; + std::vector nodes(size); + Atlas atlas({size, size}); + int zpos; + + for(zpos = 0; rects.size() > 0; zpos++) { + stbrp_init_target(&context, size, size, nodes.data(), nodes.size()); + stbrp_pack_rects(&context, rects.data(), rects.size()); + + for(auto it = rects.begin(); it != rects.end();) { + if(!it->was_packed) { + it++; + continue; + } + + Image* src = IMAGES[it->id]; + atlas.draw(*src, {it->x + offset, it->y + offset, zpos}); + src->m_id = it->id; + src->clear_host(); + + uvs[it->id] = { + .uv0={(it->x + offset + 0.5f) / size, (it->y + offset + 0.5f) / size}, + .uv1={(it->x + offset + src->m_size.x - 0.5f) / size, (it->y + offset + src->m_size.y - 0.5f) / size}, + .zpos=zpos, + .edges=src->m_edges, + }; + + it = rects.erase(it); + } + } + + static GL::ArrayBuffer ssbo_id; + static GL::Texture tex_id; + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo_id); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(UV) * uvs.size(), uvs.data(), GL_STATIC_DRAW); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, Graphics::Shader::SSBO_ATLAS_BUFFER, ssbo_id); + + glActiveTexture(GL_TEXTURE0 + Graphics::Shader::TEX_ATLAS); + glBindTexture(GL_TEXTURE_2D_ARRAY, tex_id); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, atlas.m_size.x, atlas.m_size.y, atlas.m_size.z, 0, GL_RGBA, GL_UNSIGNED_BYTE, atlas.m_data.data()); + 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_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glGenerateMipmap(GL_TEXTURE_2D_ARRAY); +} + diff --git a/src/graphics/texture/image.cpp b/src/graphics/texture/image.cpp new file mode 100644 index 0000000..9d7d6d1 --- /dev/null +++ b/src/graphics/texture/image.cpp @@ -0,0 +1,58 @@ + +#include "image.hpp" +#include +#include +#include +#include +#include +#include + +using Graphics::Texture::Image; + +Image::Image(const char* filename, Edge edge_behaviour) { + std::string path = std::string("../assets/image/") + filename; + m_data = stbi_load(path.c_str(), &m_size.x, &m_size.y, &m_channels, 0); + m_edges = edge_behaviour; + std::cout << "Loaded " << filename << std::endl; + + switch(m_channels) { + case 1: + m_mapping = {GL_RED, GL_RED, GL_RED, GL_ONE}; + break; + case 2: + m_mapping = {GL_RED, GL_GREEN, GL_ZERO, GL_ONE}; + break; + case 3: + m_mapping = {GL_RED, GL_GREEN, GL_BLUE, GL_ONE}; + break; + case 4: + m_mapping = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA}; + break; + } +} + +Image::Image(Image&& o) : Image(o) { + o.m_data = nullptr; +} + +Image::~Image() { + if(m_data) { + free(m_data); + } +} + +unsigned char* Image::get(glm::vec<2, int> pos) const { + return &m_data[(pos.y * m_size.x + pos.x) * m_channels]; +} + +void Image::clear_host() { + assert(m_data); + free(m_data); + m_data = nullptr; +} + +Image::operator unsigned int() { + assert(m_id != -1); + return m_id; +} + diff --git a/src/graphics/texture/image.hpp b/src/graphics/texture/image.hpp new file mode 100644 index 0000000..8b8b93f --- /dev/null +++ b/src/graphics/texture/image.hpp @@ -0,0 +1,34 @@ + +#pragma once + +#include + +namespace Graphics::Texture { + struct Image { + enum Edge : int { + CLAMP = 0, + REPEATING = 1, + }; + + unsigned char* m_data; + glm::vec<2, int> m_size; + glm::vec<4, int> m_mapping; + int m_channels; + Edge m_edges; + int m_id = -1; + + Image(const char* path, Edge edge_behaviour); + Image(Image&&); + ~Image(); + + unsigned char* get(glm::vec<2, int> pos) const; + void clear_host(); + + operator unsigned int(); + + private: + + Image(const Image&) = default; + }; +}; + diff --git a/src/graphics/texture/uv.hpp b/src/graphics/texture/uv.hpp new file mode 100644 index 0000000..087c443 --- /dev/null +++ b/src/graphics/texture/uv.hpp @@ -0,0 +1,16 @@ + +#pragma once + +#include "image.hpp" +#include + +namespace Graphics::Texture { + struct UV { + glm::vec2 uv0; + glm::vec2 uv1; + int zpos; + Image::Edge edges; + int padding[2]; + }; +}; + diff --git a/src/graphics/vertex.cpp b/src/graphics/vertex.cpp index 61a6bcf..ff786ff 100644 --- a/src/graphics/vertex.cpp +++ b/src/graphics/vertex.cpp @@ -8,10 +8,12 @@ using Graphics::Vertex; void Vertex::set_vertex_attribs() { Vertex v; - glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(v), (void*)Util::pointer_diff(&v, &v.m_pos)); + glVertexAttribPointer(0, 4, GL_FLOAT, false, sizeof(v), (void*)Util::pointer_diff(&v, &v.m_pos)); glVertexAttribPointer(1, 4, GL_FLOAT, false, sizeof(v), (void*)Util::pointer_diff(&v, &v.m_colour)); + glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(v), (void*)Util::pointer_diff(&v, &v.m_uv)); + glVertexAttribIPointer(3, 1, GL_UNSIGNED_INT, sizeof(v), (void*)Util::pointer_diff(&v, &v.m_texid)); - for(int i = 0; i < 2; i++) { + for(int i = 0; i < 4; i++) { glEnableVertexAttribArray(i); } } diff --git a/src/graphics/vertex.hpp b/src/graphics/vertex.hpp index d4f1668..9b6e2b3 100644 --- a/src/graphics/vertex.hpp +++ b/src/graphics/vertex.hpp @@ -6,7 +6,9 @@ namespace Graphics { struct Vertex { glm::vec4 m_pos; - glm::vec4 m_colour; + glm::vec4 m_colour = {1, 1, 1, 1}; + glm::vec2 m_uv; + unsigned int m_texid = 0; static void set_vertex_attribs(); }; diff --git a/src/main.cpp b/src/main.cpp index 012402b..7594bed 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,13 @@ #include "graphics/context.hpp" #include "graphics/pipeline.hpp" +#include "graphics/texture.hpp" #include "world/state.hpp" #include int main() { Graphics::Pipeline pipeline; + Graphics::Texture::generate_atlas(); World::State state; while(!pipeline.should_close()) { diff --git a/src/stb b/src/stb new file mode 160000 index 0000000..013ac3b --- /dev/null +++ b/src/stb @@ -0,0 +1 @@ +Subproject commit 013ac3beddff3dbffafd5177e7972067cd2b5083 diff --git a/src/util/math/mod.hpp b/src/util/math/mod.hpp new file mode 100644 index 0000000..53363d7 --- /dev/null +++ b/src/util/math/mod.hpp @@ -0,0 +1,9 @@ + +#pragma once + +namespace Util::Math { + constexpr auto mod(auto a, auto b) { + return (a % b + b) % b; + } +}; + diff --git a/src/util/pointer_diff.cpp b/src/util/pointer_diff.cpp index 73e6bbb..3c9c0df 100644 --- a/src/util/pointer_diff.cpp +++ b/src/util/pointer_diff.cpp @@ -1,9 +1,12 @@ #include "pointer_diff.hpp" +#include #include #include uint64_t Util::pointer_diff(void* a, void* b) { + assert(a); + assert(b); return std::abs((int64_t)a - (int64_t)b); } diff --git a/src/world/builder.cpp b/src/world/builder.cpp index f9a3252..c06240b 100644 --- a/src/world/builder.cpp +++ b/src/world/builder.cpp @@ -1,9 +1,11 @@ #include "builder.hpp" #include "../graphics/window.hpp" +#include "../graphics/texture.hpp" #include "chunk.hpp" #include "map.hpp" #include "tile/empty.hpp" +#include "tile/tile_base.hpp" #include #include #include @@ -15,10 +17,14 @@ using World::Builder; Builder::Builder() { Graphics::Mesh mesh; - mesh.add_primitive(Chunk::PRIMITIVE_0); + auto prim = Chunk::PRIMITIVE_B; + prim.m_texid = Graphics::Texture::YELLOW_BRICK_WALL; + mesh.add_primitive(prim); for(int i = 0; i < 4; i++) { - mesh.add_primitive(Chunk::PRIMITIVE_S[i]); + prim = Chunk::PRIMITIVE_S[i]; + prim.m_texid = Graphics::Texture::YELLOW_BRICK_WALL; + mesh.add_primitive(prim); } m_model.bind(); @@ -38,32 +44,35 @@ void Builder::update(Map& map, const Graphics::Context& ctx) { glm::vec<3, double> near = glm::unProject(glm::vec3(mpos, -1), ctx.m_view, ctx.m_projection, glm::vec4(0, 0, Graphics::Window::size)); glm::vec<3, double> far = glm::unProject(glm::vec3(mpos, 1), ctx.m_view, ctx.m_projection, glm::vec4(0, 0, Graphics::Window::size)); glm::vec<3, double> direction = far - near; + const glm::vec<2, double> CHECK_TILES[] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; + bool found = false; if(direction.z >= 0) { return; } m_intersect = glm::round(glm::vec<2, double>(near - direction / direction.z * near.z) + ctx.m_transform); + m_has_intersect = true; if(map.get_tile(m_intersect)) { + m_mode = Mode::CLEAR; + } else { + m_mode = Mode::SET; + } + + if(!m_has_intersect || !Graphics::Window::clicked) { return; } - const glm::vec<2, double> CHECK_TILES[] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; - bool found = false; - - for(const glm::vec<2, double>& off : CHECK_TILES) { - if(map.get_tile(m_intersect + off)) { - m_has_intersect = true; - break; - } - } - if(!m_has_intersect) { - return; - } - if(Graphics::Window::clicked) { - map.set_tile(m_intersect, std::make_unique()); + std::unique_ptr tile; + switch(m_mode) { + case Mode::SET: + tile = std::make_unique(); + break; + default: + tile = nullptr; } + map.set_tile(m_intersect, std::move(tile)); } void Builder::render(const Graphics::Context& ctx) const { @@ -73,12 +82,16 @@ void Builder::render(const Graphics::Context& ctx) const { glm::vec<2, double> pos = glm::round(m_intersect); ctx.set_model_matrix(glm::translate(glm::mat4(1), {pos - ctx.m_transform, 0})); - ctx.set_colour_matrix({ - 0.5, 0.5, 0, 0, - 0, 1, 0, 0, - 0, 0.5, 0.5, 0, - 0, 0, 0, 1, - }); + + switch(m_mode) { + case Mode::CLEAR: + ctx.set_colour_matrix(glm::mat4(1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1)); + break; + case Mode::SET: + ctx.set_colour_matrix(glm::mat4(0.5, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0.5, 0, 0, 0, 0, 1)); + break; + } + m_model.bind(); m_model.render(GL_TRIANGLES); } diff --git a/src/world/builder.hpp b/src/world/builder.hpp index e8da2d0..6612a0b 100644 --- a/src/world/builder.hpp +++ b/src/world/builder.hpp @@ -7,9 +7,15 @@ namespace World { struct Builder { + enum Mode { + SET, + CLEAR, + }; + Graphics::GL::Model m_model; glm::vec<2, double> m_intersect; bool m_has_intersect = false; + Mode m_mode; Builder(); diff --git a/src/world/chunk.cpp b/src/world/chunk.cpp index 9665119..c31b8a9 100644 --- a/src/world/chunk.cpp +++ b/src/world/chunk.cpp @@ -2,13 +2,13 @@ #include #include "chunk.hpp" #include "tile/tile_base.hpp" -#include +#include "../graphics/texture.hpp" #include #include #include +#include #include #include -#include using World::Chunk; using World::Tile::TileBase; @@ -36,7 +36,15 @@ const TileBase* Chunk::get(glm::vec<2, int> p) const { TileBase* Chunk::set(glm::vec<2, int> p, std::unique_ptr v) { TileBase* r = v.get(); p = get_pos_mod(p); - m_tiles[p.x * N + p.y] = std::move(v); + m_tiles[p.x * N + p.y].swap(v); + + if(r && !v) { + m_used_tiles += 1; + } + else if(!r && v) { + m_used_tiles -= 1; + } + m_dirty = true; return r; } @@ -71,7 +79,10 @@ void Chunk::update(Map& map) { continue; } - mesh.add_primitive(PRIMITIVE_B.with_translation({t_off, 0})); + auto prim = PRIMITIVE_B; + prim.m_offset = {t_off, 0, 0}; + prim.m_texid = Graphics::Texture::YELLOW_BRICK_WALL; + mesh.add_primitive(prim); for(int i = 0; i < std::size(neighbours); i++) { glm::vec<2, int> n_off = neighbours[i] + t_off; @@ -86,7 +97,10 @@ void Chunk::update(Map& map) { chunk_check = chunks[3]; } if(!chunk_check || !chunk_check->get(n_off)) { - mesh.add_primitive(PRIMITIVE_S[i].with_translation({t_off, 0})); + prim = PRIMITIVE_S[i]; + prim.m_offset = {t_off, 0, 0}; + prim.m_texid = Graphics::Texture::YELLOW_BRICK_WALL; + mesh.add_primitive(prim); } } } diff --git a/src/world/chunk.hpp b/src/world/chunk.hpp index 56e4f98..dbd7bcf 100644 --- a/src/world/chunk.hpp +++ b/src/world/chunk.hpp @@ -17,26 +17,26 @@ namespace World { struct Chunk { static constexpr int N = 16; - static constexpr Graphics::Primitive<4, 6> PRIMITIVE_B = { - .m_vertices = { - {.m_pos = {-0.5, -0.5, 0, 1}, .m_colour = {1, 0, 0, 1}}, - {.m_pos = {-0.5, +0.5, 0, 1}, .m_colour = {1, 1, 0, 1}}, - {.m_pos = {+0.5, -0.5, 0, 1}, .m_colour = {0, 1, 0, 1}}, - {.m_pos = {+0.5, +0.5, 0, 1}, .m_colour = {0, 0, 1, 1}}, + static const inline Graphics::Primitive<4, 6> PRIMITIVE_B = { + .m_vertices={ + {.m_pos={-0.5, -0.5, 0, 1}, .m_uv={0, 0}}, + {.m_pos={-0.5, +0.5, 0, 1}, .m_uv={0, 2}}, + {.m_pos={+0.5, -0.5, 0, 1}, .m_uv={2, 0}}, + {.m_pos={+0.5, +0.5, 0, 1}, .m_uv={2, 2}}, }, - .m_indices = { + .m_indices={ 0, 2, 3, 0, 3, 1, }, }; - static constexpr Graphics::Primitive<4, 6> PRIMITIVE_0 = { - .m_vertices = { - {.m_pos = {-0.5, -0.5, 0.5, 1}, .m_colour = {1, 0, 0, 1}}, - {.m_pos = {-0.5, -0.5, 0, 1}, .m_colour = {1, 1, 0, 1}}, - {.m_pos = {+0.5, -0.5, 0.5, 1}, .m_colour = {0, 1, 0, 1}}, - {.m_pos = {+0.5, -0.5, 0, 1}, .m_colour = {0, 0, 1, 1}}, + static const inline Graphics::Primitive<4, 6> PRIMITIVE_0 = { + .m_vertices={ + {.m_pos={-0.5, -0.5, 0.75, 1}, .m_uv={0, 0}}, + {.m_pos={-0.5, -0.5, 0, 1}, .m_uv={0, 2}}, + {.m_pos={+0.5, -0.5, 0.75, 1}, .m_uv={2, 0}}, + {.m_pos={+0.5, -0.5, 0, 1}, .m_uv={2, 2}}, }, - .m_indices = { + .m_indices={ 0, 2, 3, 0, 3, 1, }, @@ -52,6 +52,7 @@ namespace World { std::unique_ptr m_tiles[N*N]; Graphics::GL::Model m_model; glm::vec<2, int> m_pos; + int m_used_tiles = 0; bool m_dirty = false; Chunk(glm::vec<2, int> pos); diff --git a/src/world/map.cpp b/src/world/map.cpp index 92516fa..c671b36 100644 --- a/src/world/map.cpp +++ b/src/world/map.cpp @@ -49,6 +49,13 @@ const Chunk* Map::get_chunk(glm::vec<2, int> pos) const { return &it->second; } +void Map::mark_chunk_dirty(glm::vec<2, int> pos) { + Chunk* c = get_chunk(pos); + if(c) { + c->m_dirty = true; + } +} + TileBase* Map::get_tile(glm::vec<2, int> pos) { Chunk* c = get_chunk(pos); return c ? c->get(pos) : nullptr; @@ -60,8 +67,28 @@ const TileBase* Map::get_tile(glm::vec<2, int> pos) const { } TileBase* Map::set_tile(glm::vec<2, int> pos, std::unique_ptr tile) { - tile->m_pos = pos; - return get_or_generate_chunk(pos)->set(pos, std::move(tile)); + uint64_t cid = get_chunk_id(pos); + auto it = m_chunks.find(cid); + if(tile) { + tile->m_pos = pos; + if(it == m_chunks.end()) { + it = m_chunks.try_emplace(cid, pos - get_pos_mod(pos)).first; + } + } else if(it == m_chunks.end()) { + return nullptr; + } + Chunk* c = &it->second; + TileBase* tile_p = c->set(pos, std::move(tile)); + mark_chunk_dirty(pos + glm::vec<2, int>(1, 0)); + mark_chunk_dirty(pos + glm::vec<2, int>(0, 1)); + mark_chunk_dirty(pos + glm::vec<2, int>(-1, 0)); + mark_chunk_dirty(pos + glm::vec<2, int>(0, -1)); + + if(!tile_p && c->m_used_tiles == 0) { + m_chunks.erase(it); + } + + return tile_p; } void Map::update() { diff --git a/src/world/map.hpp b/src/world/map.hpp index 1e830f2..5064515 100644 --- a/src/world/map.hpp +++ b/src/world/map.hpp @@ -28,6 +28,10 @@ namespace World { void update(); void render(const Graphics::Context& ctx) const; + + private: + + void mark_chunk_dirty(glm::vec<2, int> pos); }; }; diff --git a/src/world/player.cpp b/src/world/player.cpp index 0c09108..c76c9a7 100644 --- a/src/world/player.cpp +++ b/src/world/player.cpp @@ -1,7 +1,10 @@ #include "player.hpp" #include "../graphics/window.hpp" +#include "../util/math/mod.hpp" #include +#include +#include #include #include #include @@ -54,7 +57,8 @@ void Player::update() { m_cursor_last = cursor; if(Graphics::Window::mouse_locked) { - m_pitch += cursor_diff.x * 0.0025; + m_pitch = std::fmod(m_pitch + cursor_diff.x * 0.0025, glm::pi() * 2); + m_yaw = std::clamp(m_yaw - cursor_diff.y * 0.0025, 0.0, glm::pi() * 0.5); } m_vel += m_accel;