images, better shaders, etc

This commit is contained in:
Jay Robson 2024-07-09 17:45:05 +10:00
parent 799fd1c7dc
commit 52dd65b64e
38 changed files with 662 additions and 79 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "src/stb"]
path = src/stb
url = https://github.com/nothings/stb

View File

@ -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)

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

20
assets/shader/atlas.glsl Normal file
View File

@ -0,0 +1,20 @@
layout (binding = TEX_ATLAS) uniform sampler2DArray tex_atlas;
struct AtlasPart {
vec2 uv0;
vec2 uv1;
int zpos;
int edges; // 0 = repeat, 1 = clamp
};
layout(std140, binding = SSBO_ATLAS_BUFFER) readonly buffer AtlasBuffer {
AtlasPart atlas[];
};
vec4 atlas_read_texture(uint tex, vec2 uv) {
AtlasPart a = atlas[tex];
uv = (a.edges == 0 ? fract(uv) : clamp(uv, 0.f, 1.f)) * (a.uv1 - a.uv0) + a.uv0;
return texture(tex_atlas, vec3(uv, a.zpos));
}

View File

@ -0,0 +1,6 @@
#version 430 core
#define SSBO_ATLAS_BUFFER 1
#define TEX_ATLAS 1

View File

@ -1,16 +1,19 @@
#include "version.glsl"
#include "header.glsl"
#include "atlas.glsl"
out vec4 f_colour;
in VS_OUT {
vec4 pos;
vec4 colour;
vec2 uv;
flat uint texid;
} vin;
uniform mat4 u_colour = mat4(1);
void main() {
f_colour = u_colour * vin.colour;
f_colour = u_colour * vin.colour * atlas_read_texture(vin.texid, vin.uv);
}

View File

@ -1,12 +1,16 @@
#include "version.glsl"
#include "header.glsl"
layout (location = 0) in vec3 v_pos;
layout (location = 0) in vec4 v_pos;
layout (location = 1) in vec4 v_colour;
layout (location = 2) in vec2 v_uv;
layout (location = 3) in uint v_texid;
out VS_OUT {
vec4 pos;
vec4 colour;
vec2 uv;
flat uint texid;
} vout;
uniform mat4 u_model = mat4(1);
@ -15,8 +19,10 @@ uniform mat4 u_projection = mat4(1);
void main() {
mat4 mat_mv = u_view * u_model;
vout.pos = mat_mv * vec4(v_pos, 1);
vout.pos = mat_mv * v_pos;
vout.colour = v_colour;
vout.uv = v_uv;
vout.texid = v_texid;
vec4 p = u_projection * vout.pos;
gl_Position = vec4(p.xy, 2 / (p.z + 2) - 1, p.w);
}

View File

@ -1,3 +0,0 @@
#version 430 core

View File

@ -1,6 +1,7 @@
file(GLOB SOURCES ${SOURCES} src/*.cpp)
include(src/stb/files.cmake)
include(src/graphics/files.cmake)
include(src/util/files.cmake)
include(src/world/files.cmake)

View File

@ -10,6 +10,7 @@
using Graphics::Context;
Context::Context(GLFWwindow* window, unsigned int program) {
assert(window);
glUseProgram(program);
m_u_model = glGetUniformLocation(program, "u_model");
m_u_view = glGetUniformLocation(program, "u_view");

View File

@ -1,6 +1,7 @@
file(GLOB SOURCES ${SOURCES} src/graphics/*.cpp)
include(src/graphics/texture/files.cmake)
include(src/graphics/shader/files.cmake)
include(src/graphics/gl/files.cmake)

View File

@ -14,7 +14,17 @@ namespace Graphics {
void add_primitive(const Primitive<VERTICES, INDICES>& 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);

View File

@ -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});
}

View File

@ -3,20 +3,17 @@
#include "vertex.hpp"
#include <glm/matrix.hpp>
#include <optional>
namespace Graphics {
template <int VERTICES, int INDICES>
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<unsigned int> m_texid;
std::optional<glm::vec4> m_colour;
std::optional<glm::vec4> 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;
}
};
};

8
src/graphics/shader.hpp Normal file
View File

@ -0,0 +1,8 @@
#pragma once
namespace Graphics::Shader {
constexpr int SSBO_ATLAS_BUFFER = 1;
constexpr int TEX_ATLAS = 1;
};

View File

@ -105,7 +105,8 @@ static bool parse(std::vector<char>& 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<char> source;
if(!parse(source, path)) {

View File

@ -1,9 +1,7 @@
#pragma once
#include <filesystem>
namespace Graphics::Shader {
void compile(unsigned int shader, const std::filesystem::path& path);
void compile(unsigned int shader, const char* path);
};

15
src/graphics/texture.hpp Normal file
View File

@ -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();
};

View File

@ -0,0 +1,172 @@
#include "atlas.hpp"
#include "image.hpp"
#include <GL/gl.h>
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 = &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 <typename T>
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);
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "image.hpp"
#include <cmath>
#include <cstdint>
#include <glm/matrix.hpp>
#include <vector>
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 <typename T>
constexpr operator glm::vec<N, T>() {
glm::vec<N, T> vec;
for(int i = 0; i < N; i++) {
vec[i] = (T)m_data[i] / 255.0;
}
return vec;
}
template <typename T>
constexpr Pixel& operator = (const glm::vec<N, T>& vec) {
for(int i = 0; i < N; i++) {
m_data[i] = (uint8_t)(std::round(vec[i] * 255));
}
return *this;
}
template <typename T>
constexpr Pixel(const glm::vec<N, T>& vec) {
*this = vec;
}
};
glm::vec<3, int> m_size;
std::vector<Pixel> 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);
};
};

View File

@ -0,0 +1,3 @@
file(GLOB SOURCES ${SOURCES} src/graphics/texture/*.cpp)

View File

@ -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 <iterator>
#include <stb/stb_rect_pack.h>
#include <GL/glew.h>
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<stbrp_rect> rects;
std::vector<UV> 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<stbrp_node> 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);
}

View File

@ -0,0 +1,58 @@
#include "image.hpp"
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <stb/stb_image.h>
#include <GL/gl.h>
#include <string>
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;
}

View File

@ -0,0 +1,34 @@
#pragma once
#include <glm/matrix.hpp>
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;
};
};

View File

@ -0,0 +1,16 @@
#pragma once
#include "image.hpp"
#include <glm/ext/vector_float2.hpp>
namespace Graphics::Texture {
struct UV {
glm::vec2 uv0;
glm::vec2 uv1;
int zpos;
Image::Edge edges;
int padding[2];
};
};

View File

@ -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);
}
}

View File

@ -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();
};

View File

@ -1,11 +1,13 @@
#include "graphics/context.hpp"
#include "graphics/pipeline.hpp"
#include "graphics/texture.hpp"
#include "world/state.hpp"
#include <glm/ext/matrix_clip_space.hpp>
int main() {
Graphics::Pipeline pipeline;
Graphics::Texture::generate_atlas();
World::State state;
while(!pipeline.should_close()) {

1
src/stb Submodule

@ -0,0 +1 @@
Subproject commit 013ac3beddff3dbffafd5177e7972067cd2b5083

9
src/util/math/mod.hpp Normal file
View File

@ -0,0 +1,9 @@
#pragma once
namespace Util::Math {
constexpr auto mod(auto a, auto b) {
return (a % b + b) % b;
}
};

View File

@ -1,9 +1,12 @@
#include "pointer_diff.hpp"
#include <cassert>
#include <cstdint>
#include <cstdlib>
uint64_t Util::pointer_diff(void* a, void* b) {
assert(a);
assert(b);
return std::abs((int64_t)a - (int64_t)b);
}

View File

@ -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 <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/common.hpp>
@ -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<float>(glm::vec3(mpos, -1), ctx.m_view, ctx.m_projection, glm::vec4(0, 0, Graphics::Window::size));
glm::vec<3, double> far = glm::unProject<float>(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<Tile::Empty>());
std::unique_ptr<Tile::TileBase> tile;
switch(m_mode) {
case Mode::SET:
tile = std::make_unique<Tile::Empty>();
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);
}

View File

@ -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();

View File

@ -2,13 +2,13 @@
#include <GL/glew.h>
#include "chunk.hpp"
#include "tile/tile_base.hpp"
#include <glm/detail/qualifier.hpp>
#include "../graphics/texture.hpp"
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/quaternion_transform.hpp>
#include <glm/ext/scalar_constants.hpp>
#include <glm/ext/vector_float3.hpp>
#include <iterator>
#include <memory>
#include <utility>
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<TileBase> 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);
}
}
}

View File

@ -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<Tile::TileBase> 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);

View File

@ -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<TileBase> 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() {

View File

@ -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);
};
};

View File

@ -1,7 +1,10 @@
#include "player.hpp"
#include "../graphics/window.hpp"
#include "../util/math/mod.hpp"
#include <GLFW/glfw3.h>
#include <algorithm>
#include <cmath>
#include <glm/detail/qualifier.hpp>
#include <glm/detail/type_quat.hpp>
#include <glm/ext/matrix_transform.hpp>
@ -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<double>() * 2);
m_yaw = std::clamp(m_yaw - cursor_diff.y * 0.0025, 0.0, glm::pi<double>() * 0.5);
}
m_vel += m_accel;