initial commit

This commit is contained in:
Jay Robson 2024-07-07 18:30:53 +10:00
commit 4e3ab6460b
66 changed files with 1443 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/build/

38
CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 3.25)
project(NuclearPlantSim VERSION 1.0)
set(CMAKE_CXX_STANDARD 20)
if(NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -I/usr/include/freetype2")
endif()
if(NOT WIN32)
if(NOT DEFINED SIGFPE_ENABLED)
set(SIGFPE_ENABLED 1)
endif()
set(libs stdc++ m GLEW glfw GL freetype assimp jsoncpp openal vorbisfile alut atomic)
else()
set(libs stdc++ m brotlidec assimp-5 glew32 opengl32 glfw3 freetype jsoncpp zlibstatic soft_oal vorbisfile vorbis ogg alut atomic)
endif()
if(SIGFPE_ENABLED)
add_compile_definitions(SIGFPE_ENABLED=1)
endif()
if(DEBUG_ENABLED)
message("-- Enabled: Runtime debug checks")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
add_compile_definitions(NO_DEBUG)
endif()
message("-- Using cmake flags: ${CMAKE_CXX_FLAGS}")
message("-- Using libs: ${libs}")
include(src/files.cmake)
add_executable(NuclearPlantSim ${SOURCES})
target_link_libraries(NuclearPlantSim PUBLIC ${libs})

5
assets/shader/math.glsl Normal file
View File

@ -0,0 +1,5 @@
float Map(float x, float i_min, float i_max, float o_min, float o_max) {
return (x - i_min) * (o_max - o_min) / (i_max - i_min) + o_min;
}

16
assets/shader/model.frag Normal file
View File

@ -0,0 +1,16 @@
#include "version.glsl"
out vec4 f_colour;
in VS_OUT {
vec4 pos;
vec4 colour;
} vin;
uniform mat4 u_colour = mat4(1);
void main() {
f_colour = u_colour * vin.colour;
}

22
assets/shader/model.vert Normal file
View File

@ -0,0 +1,22 @@
#include "version.glsl"
layout (location = 0) in vec3 v_pos;
layout (location = 1) in vec4 v_colour;
out VS_OUT {
vec4 pos;
vec4 colour;
} vout;
uniform mat4 u_model = mat4(1);
uniform mat4 u_view = mat4(1);
uniform mat4 u_projection = mat4(1);
void main() {
mat4 mat_mv = u_view * u_model;
vout.pos = mat_mv * vec4(v_pos, 1);
vout.colour = v_colour;
gl_Position = u_projection * vout.pos;
}

View File

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

7
src/files.cmake Normal file
View File

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

30
src/graphics/context.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "context.hpp"
#include "gl/uniform.hpp"
#include <GL/glew.h>
#include <glm/ext/matrix_float4x4.hpp>
using Graphics::Context;
Context::Context(unsigned int program) {
glUseProgram(program);
m_u_model = glGetUniformLocation(program, "u_model");
m_u_view = glGetUniformLocation(program, "u_view");
m_u_projection = glGetUniformLocation(program, "u_projection");
m_u_colour = glGetUniformLocation(program, "u_colour");
m_program = program;
}
void Context::set_model_matrix(glm::mat4 mat) {
GL::Uniform::set_mat4(m_u_model, false, mat);
}
void Context::set_view_matrix(glm::mat4 mat) {
GL::Uniform::set_mat4(m_u_view, false, mat);
}
void Context::set_projection_matrix(glm::mat4 mat) {
GL::Uniform::set_mat4(m_u_projection, false, mat);
}
void Context::set_colour_matrix(glm::mat4 mat) {
GL::Uniform::set_mat4(m_u_colour, false, mat);
}

22
src/graphics/context.hpp Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <glm/ext/matrix_float4x4.hpp>
#include <glm/ext/vector_float4.hpp>
namespace Graphics {
struct Context {
unsigned int m_program;
unsigned int m_u_model;
unsigned int m_u_view;
unsigned int m_u_projection;
unsigned int m_u_colour;
Context(unsigned int program);
void set_model_matrix(glm::mat4 mat);
void set_view_matrix(glm::mat4 mat);
void set_projection_matrix(glm::mat4 mat);
void set_colour_matrix(glm::mat4 mat);
};
};

6
src/graphics/files.cmake Normal file
View File

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

View File

@ -0,0 +1,21 @@
#include "array_buffer.hpp"
#include "buffer_base.hpp"
#include <GL/glew.h>
#include <utility>
using Graphics::GL::ArrayBuffer;
ArrayBuffer::ArrayBuffer() {
glGenBuffers(1, &m_handle);
}
ArrayBuffer::ArrayBuffer(ArrayBuffer&& o) : BufferBase(std::move(o)) {
}
ArrayBuffer::~ArrayBuffer() {
if(m_handle) {
glDeleteBuffers(1, &m_handle);
}
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct ArrayBuffer : BufferBase {
ArrayBuffer();
ArrayBuffer(ArrayBuffer&& o);
~ArrayBuffer();
};
};

View File

@ -0,0 +1,14 @@
#include "buffer_base.hpp"
using Graphics::GL::BufferBase;
BufferBase::BufferBase(BufferBase&& o) {
m_handle = o.m_handle;
o.m_handle = 0;
}
BufferBase::operator unsigned int() const {
return m_handle;
}

View File

@ -0,0 +1,16 @@
#pragma once
namespace Graphics::GL {
struct BufferBase {
unsigned int m_handle;
BufferBase() = default;
BufferBase(const BufferBase& o) = delete;
operator unsigned int() const;
protected:
BufferBase(BufferBase&& o);
};
};

View File

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

View File

@ -0,0 +1,20 @@
#include "frame_buffer.hpp"
#include <GL/glew.h>
#include <utility>
using Graphics::GL::FrameBuffer;
FrameBuffer::FrameBuffer() {
glGenFramebuffers(1, &m_handle);
}
FrameBuffer::FrameBuffer(FrameBuffer&& o) : BufferBase(std::move(o)) {
}
FrameBuffer::~FrameBuffer() {
if(m_handle) {
glDeleteFramebuffers(1, &m_handle);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct FrameBuffer : BufferBase {
FrameBuffer();
FrameBuffer(FrameBuffer&& o);
~FrameBuffer();
};
};

34
src/graphics/gl/model.cpp Normal file
View File

@ -0,0 +1,34 @@
#include <GL/glew.h>
#include <iostream>
#include "model.hpp"
#include "../../util/stream/vector.hpp"
using Graphics::GL::Model;
void Model::set(const Mesh& mesh, int usage) {
glBufferData(GL_ARRAY_BUFFER, mesh.m_vertices.size() * sizeof(mesh.m_vertices[0]), mesh.m_vertices.data(), usage);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, mesh.m_indices.size() * sizeof(mesh.m_indices[0]), mesh.m_indices.data(), usage);
m_size = mesh.m_indices.size();
std::cout << "Indices: ";
for(int i : mesh.m_indices) {
std::cout << i << ", ";
}
std::cout << "\nVertices:\n";
for(const Vertex& v : mesh.m_vertices) {
std::cout << Util::Stream::Vector(v.m_pos) << ", ";
std::cout << Util::Stream::Vector(v.m_colour) << "\n";
}
}
void Model::bind() const {
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
}
void Model::render(int mode) const {
glDrawElements(mode, m_size, GL_UNSIGNED_INT, 0);
}

20
src/graphics/gl/model.hpp Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "array_buffer.hpp"
#include "vertex_array.hpp"
#include "../mesh.hpp"
namespace Graphics::GL {
struct Model {
VertexArray m_vao;
ArrayBuffer m_vbo;
ArrayBuffer m_ebo;
unsigned int m_size;
void bind() const;
void set(const Mesh& mesh, int usage);
void render(int mode) const;
};
};

View File

@ -0,0 +1,20 @@
#include <GL/glew.h>
#include <utility>
#include "program.hpp"
using Graphics::GL::Program;
Program::Program() {
m_handle = glCreateProgram();
}
Program::Program(Program&& o) : BufferBase(std::move(o)) {
}
Program::~Program() {
if(m_handle) {
glDeleteProgram(m_handle);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct Program : BufferBase {
Program();
Program(Program&& o);
~Program();
};
};

View File

@ -0,0 +1,20 @@
#include "render_buffer.hpp"
#include <GL/glew.h>
#include <utility>
using Graphics::GL::RenderBuffer;
RenderBuffer::RenderBuffer() {
glGenRenderbuffers(1, &m_handle);
}
RenderBuffer::RenderBuffer(RenderBuffer&& o) : BufferBase(std::move(o)) {
}
RenderBuffer::~RenderBuffer() {
if(m_handle) {
glDeleteRenderbuffers(1, &m_handle);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct RenderBuffer : BufferBase {
RenderBuffer();
RenderBuffer(RenderBuffer&& o);
~RenderBuffer();
};
};

View File

@ -0,0 +1,20 @@
#include <GL/glew.h>
#include <utility>
#include "shader.hpp"
using Graphics::GL::Shader;
Shader::Shader(int type) {
m_handle = glCreateShader(type);
}
Shader::Shader(Shader&& o) : BufferBase(std::move(o)) {
}
Shader::~Shader() {
if(m_handle) {
glDeleteShader(m_handle);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct Shader : BufferBase {
Shader(int type);
Shader(Shader&& o);
~Shader();
};
};

View File

@ -0,0 +1,20 @@
#include "texture.hpp"
#include <GL/glew.h>
#include <utility>
using Graphics::GL::Texture;
Texture::Texture() {
glGenTextures(1, &m_handle);
}
Texture::Texture(Texture&& o) : BufferBase(std::move(o)) {
}
Texture::~Texture() {
if(m_handle) {
glDeleteTextures(1, &m_handle);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct Texture : BufferBase {
Texture();
Texture(Texture&& o);
~Texture();
};
};

View File

@ -0,0 +1,30 @@
#include "uniform.hpp"
#include <GL/glew.h>
using namespace Graphics::GL;
void Uniform::set_mat4(unsigned int loc, bool transpose, glm::mat4 v) {
glUniformMatrix4fv(loc, 1, transpose, &v[0][0]);
}
void Uniform::set_mat3(unsigned int loc, bool transpose, glm::mat3 v) {
glUniformMatrix3fv(loc, 1, transpose, &v[0][0]);
}
void Uniform::set_mat2(unsigned int loc, bool transpose, glm::mat2 v) {
glUniformMatrix2fv(loc, 1, transpose, &v[0][0]);
}
void Uniform::set_vec4(unsigned int loc, glm::vec4 v) {
glUniform4fv(loc, 1, &v[0]);
}
void Uniform::set_vec3(unsigned int loc, glm::vec3 v) {
glUniform3fv(loc, 1, &v[0]);
}
void Uniform::set_vec2(unsigned int loc, glm::vec2 v) {
glUniform2fv(loc, 1, &v[0]);
}

View File

@ -0,0 +1,14 @@
#pragma once
#include <glm/matrix.hpp>
namespace Graphics::GL::Uniform {
void set_mat4(unsigned int loc, bool transpose, glm::mat4 v);
void set_mat3(unsigned int loc, bool transpose, glm::mat3 v);
void set_mat2(unsigned int loc, bool transpose, glm::mat2 v);
void set_vec4(unsigned int loc, glm::vec4 v);
void set_vec3(unsigned int loc, glm::vec3 v);
void set_vec2(unsigned int loc, glm::vec2 v);
};

View File

@ -0,0 +1,20 @@
#include "vertex_array.hpp"
#include <GL/glew.h>
#include <utility>
using Graphics::GL::VertexArray;
VertexArray::VertexArray() {
glGenVertexArrays(1, &m_handle);
}
VertexArray::VertexArray(VertexArray&& o) : BufferBase(std::move(o)) {
}
VertexArray::~VertexArray() {
if(m_handle) {
glDeleteVertexArrays(1, &m_handle);
}
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "buffer_base.hpp"
namespace Graphics::GL {
struct VertexArray : BufferBase {
VertexArray();
VertexArray(VertexArray&& o);
~VertexArray();
};
};

19
src/graphics/init.cpp Normal file
View File

@ -0,0 +1,19 @@
#include <GLFW/glfw3.h>
#include <cassert>
#include "init.hpp"
static bool is_initialized = false;
void Graphics::init() {
if(is_initialized) {
return;
}
assert(glfwInit());
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
is_initialized = true;
}

7
src/graphics/init.hpp Normal file
View File

@ -0,0 +1,7 @@
#pragma once
namespace Graphics {
void init();
}

25
src/graphics/mesh.hpp Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "vertex.hpp"
#include "primitive.hpp"
#include <vector>
namespace Graphics {
struct Mesh {
std::vector<Vertex> m_vertices;
std::vector<unsigned int> m_indices;
template <int VERTICES, int INDICES>
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]);
}
for(int i = 0; i < INDICES; i++) {
m_indices.push_back(p.m_indices[i] + index_at);
}
}
};
};

View File

@ -0,0 +1,38 @@
#pragma once
#include "vertex.hpp"
#include <glm/matrix.hpp>
namespace Graphics {
template <int VERTICES, int INDICES>
struct Primitive {
Vertex m_vertices[VERTICES];
unsigned 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;
}
constexpr Primitive with_matrix(glm::mat4 mat) const {
Primitive p = *this;
for(Vertex& v : p.m_vertices) {
v.m_pos = mat * v.m_pos;
}
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;
}
};
};

View File

@ -0,0 +1,146 @@
#include "compile.hpp"
#include <filesystem>
#include <format>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>
#include <GL/glew.h>
static constexpr std::string KEYWORD = "#include";
enum include_mode_t {
WHITESPACE,
READING,
ENDING,
};
static bool parse(std::vector<char>& source, const std::filesystem::path& path) {
std::ifstream file(path);
std::vector<char> line;
int n_line = 1;
int n_col = 0;
int keyword_at = 0;
include_mode_t mode = include_mode_t::WHITESPACE;
if(!file.is_open()) {
return false;
}
for(auto it = std::istreambuf_iterator<char>(file); it != std::istreambuf_iterator<char>(); it++) {
char c = *it;
if(c == '\0') {
break;
}
if(c == '\n') {
n_line++;
n_col = 0;
} else {
n_col++;
}
if(keyword_at < KEYWORD.size()) {
if(keyword_at == n_col - 1 && KEYWORD[keyword_at] == c) {
keyword_at++;
continue;
}
else if(keyword_at > 0) {
source.insert(source.end(), &KEYWORD[0], &KEYWORD[keyword_at]);
keyword_at = 0;
}
source.push_back(c);
continue;
}
switch(mode) {
case include_mode_t::WHITESPACE: {
if(c != ' ' && c != '\t' && c != '\r' && c != '"') {
throw std::runtime_error(std::format("At {}:{}: Syntax error", n_line, n_col));
}
if(c == '"') {
mode = include_mode_t::READING;
}
break;
}
case include_mode_t::READING: {
if(c != '"') {
line.push_back(c);
break;
}
mode = include_mode_t::ENDING;
std::filesystem::path path_next = path.parent_path().append(line.begin(), line.end());
std::string comment = std::format("//'{}' {}\n", path_next.string(), '{');
source.insert(source.end(), comment.begin(), comment.end());
if(!parse(source, path_next)) {
throw std::runtime_error(std::format("At {}:{} in '{}': File '{}' not found", n_line, n_col, path.string(), path_next.string()));
}
comment = "//}\n";
source.insert(source.end(), comment.begin(), comment.end());
line.clear();
break;
}
case include_mode_t::ENDING: {
if(c == '\r') {
break;
}
if(c != '\n') {
throw std::runtime_error(std::format("At {}:{} in '{}': Syntax error", n_line, n_col, path.string()));
}
mode = include_mode_t::WHITESPACE;
keyword_at = 0;
break;
}}
}
if(keyword_at > 0) {
source.insert(source.end(), &KEYWORD[0], &KEYWORD[keyword_at]);
}
return true;
}
Graphics::GL::Shader Graphics::Shader::compile(int type, const std::filesystem::path& path) {
std::vector<char> source;
GL::Shader shader(type);
if(!parse(source, path)) {
throw std::runtime_error(std::format("File '{}' not found", path.string()));
}
const char* data = source.data();
int data_len = source.size();
glShaderSource(shader, 1, &data, &data_len);
glCompileShader(shader);
int success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if(!success) {
char info_log[1024];
glGetShaderInfoLog(shader, sizeof(info_log), nullptr, info_log);
std::ostringstream source_processed;
int n_line = 1;
source_processed << n_line << '\t';
for(char c : source) {
if(c == '\n') {
source_processed << '\n' << ++n_line << '\t';
} else {
source_processed << c;
}
}
throw std::runtime_error(std::format("OpenGL shader compilation error\n{}\nIn expanded source file '{}'\n{}", source_processed.str(), path.string(), info_log));
}
return shader;
}

View File

@ -0,0 +1,10 @@
#pragma once
#include "../gl/shader.hpp"
#include <filesystem>
namespace Graphics::Shader {
GL::Shader compile(int type, const std::filesystem::path& path);
};

View File

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

View File

@ -0,0 +1,26 @@
#include "link.hpp"
#include <GL/glew.h>
#include <format>
#include <stdexcept>
Graphics::GL::Program Graphics::Shader::link(const std::vector<unsigned int>& shaders) {
GL::Program program;
int success;
for(unsigned int shader : shaders) {
glAttachShader(program, shader);
}
glLinkProgram(program);
glGetProgramiv(program, GL_LINK_STATUS, &success);
if(!success) {
char buff[1024];
glGetProgramInfoLog(program, sizeof(buff), nullptr, buff);
throw std::runtime_error(std::format("OpenGL link error: {}", buff));
}
return program;
}

View File

@ -0,0 +1,10 @@
#pragma once
#include "../gl/program.hpp"
#include <vector>
namespace Graphics::Shader {
GL::Program link(const std::vector<unsigned int>& shaders);
}

18
src/graphics/vertex.cpp Normal file
View File

@ -0,0 +1,18 @@
#include <GL/glew.h>
#include "vertex.hpp"
#include "../util/pointer_diff.hpp"
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(1, 4, GL_FLOAT, false, sizeof(v), (void*)Util::pointer_diff(&v, &v.m_colour));
for(int i = 0; i < 2; i++) {
glEnableVertexAttribArray(i);
}
}

14
src/graphics/vertex.hpp Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <glm/glm.hpp>
namespace Graphics {
struct Vertex {
glm::vec4 m_pos;
glm::vec4 m_colour;
static void set_vertex_attribs();
};
};

61
src/graphics/window.cpp Normal file
View File

@ -0,0 +1,61 @@
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <cassert>
#include <iostream>
#include <stdexcept>
#include "window.hpp"
#include "init.hpp"
static void GLAPIENTRY cb_debug_message(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) {
if(severity == GL_DEBUG_SEVERITY_HIGH) {
throw std::runtime_error(message);
}
else if(severity != GL_DEBUG_SEVERITY_NOTIFICATION) {
std::cout << "GL CALLBACK: " << message << "\n";
}
}
Graphics::Window::Window() {
init();
m_handle = glfwCreateWindow(800, 600, "Window", nullptr, nullptr);
glfwMakeContextCurrent(m_handle);
glfwSwapInterval(1);
assert(glewInit() == GLEW_OK);
glDebugMessageCallback(cb_debug_message, nullptr);
glEnable(GL_DEBUG_OUTPUT);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
}
Graphics::Window::Window(Window&& o) {
m_handle = o.m_handle;
o.m_handle = nullptr;
}
Graphics::Window::~Window() {
if(m_handle) {
glfwDestroyWindow(m_handle);
}
}
void Graphics::Window::poll_events() {
glfwPollEvents();
}
void Graphics::Window::swap_buffers() {
glfwSwapBuffers(m_handle);
}
bool Graphics::Window::should_close() {
return glfwWindowShouldClose(m_handle);
}
void Graphics::Window::make_current() {
glfwMakeContextCurrent(m_handle);
}

19
src/graphics/window.hpp Normal file
View File

@ -0,0 +1,19 @@
struct GLFWwindow;
namespace Graphics {
struct Window {
GLFWwindow* m_handle;
Window();
Window(const Window& o) = delete;
Window(Window&& o);
~Window();
void make_current();
bool should_close();
void swap_buffers();
void poll_events();
};
};

62
src/main.cpp Normal file
View File

@ -0,0 +1,62 @@
#include <GL/glew.h>
#include <glm/ext.hpp>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/ext/matrix_float2x3.hpp>
#include <glm/ext/matrix_projection.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/trigonometric.hpp>
#include <memory>
#include "graphics/context.hpp"
#include "graphics/gl/program.hpp"
#include "graphics/gl/shader.hpp"
#include "graphics/shader/compile.hpp"
#include "graphics/shader/link.hpp"
#include "graphics/window.hpp"
#include "world/map.hpp"
#include "world/player.hpp"
#include "world/tile/empty.hpp"
int main() {
Graphics::Window window;
Graphics::GL::Shader model_vert = Graphics::Shader::compile(GL_VERTEX_SHADER, "../assets/shader/model.vert");
Graphics::GL::Shader model_frag = Graphics::Shader::compile(GL_FRAGMENT_SHADER, "../assets/shader/model.frag");
Graphics::GL::Program model_p = Graphics::Shader::link({model_vert, model_frag});
Graphics::Context context(model_p);
World::Player player;
World::Map map;
map.set_tile({0, 0}, std::make_unique<World::Tile::Empty>());
map.set_tile({1, 0}, std::make_unique<World::Tile::Empty>());
map.set_tile({2, 1}, std::make_unique<World::Tile::Empty>());
map.set_tile({2, 2}, std::make_unique<World::Tile::Empty>());
map.set_tile({3, 2}, std::make_unique<World::Tile::Empty>());
map.set_tile({-1, -1}, std::make_unique<World::Tile::Empty>());
map.set_tile({-1, -2}, std::make_unique<World::Tile::Empty>());
map.set_tile({-2, -3}, std::make_unique<World::Tile::Empty>());
map.set_tile({-1, 0}, std::make_unique<World::Tile::Empty>());
map.set_tile({-3, 0}, std::make_unique<World::Tile::Empty>());
map.set_tile({-4, 0}, std::make_unique<World::Tile::Empty>());
map.set_tile({-5, 0}, std::make_unique<World::Tile::Empty>());
map.set_tile({-4, 1}, std::make_unique<World::Tile::Empty>());
map.set_tile({-4, -1}, std::make_unique<World::Tile::Empty>());
map.set_tile({0, -1}, std::make_unique<World::Tile::Empty>());
glUseProgram(model_p);
context.set_projection_matrix(glm::perspective(glm::radians(90.0), 800.0 / 600.0, 0.1, 100.0));
context.set_view_matrix(player.get_view_matrix());
while(!window.should_close()) {
map.update();
window.poll_events();
glUseProgram(model_p);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glClearColor(0.1, 0.1, 0.12, 1);
map.render(context);
window.swap_buffers();
}
}

View File

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

17
src/util/file/load.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "load.hpp"
#include <format>
#include <fstream>
#include <iterator>
#include <stdexcept>
void Util::File::load(std::vector<char>& out, const std::filesystem::path& path) {
std::ifstream file(path);
if(!file.is_open()) {
throw std::runtime_error(std::runtime_error(std::format("file '{}' not found", path.string())));
}
out.insert(out.end(), std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
}

10
src/util/file/load.hpp Normal file
View File

@ -0,0 +1,10 @@
#pragma once
#include <filesystem>
#include <vector>
namespace Util::File {
void load(std::vector<char>& out, const std::filesystem::path& path);
};

7
src/util/files.cmake Normal file
View File

@ -0,0 +1,7 @@
file(GLOB SOURCES ${SOURCES} src/util/*.cpp)
include(src/util/file/files.cmake)
include(src/util/math/files.cmake)
include(src/util/stream/files.cmake)

View File

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

View File

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

View File

@ -0,0 +1,7 @@
#include <cstdint>
namespace Util {
uint64_t pointer_diff(void* a, void* b);
};

View File

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

View File

@ -0,0 +1,27 @@
#pragma once
#include "vector.hpp"
#include <glm/matrix.hpp>
#include <ostream>
namespace Util::Stream {
template <int C, int R, typename T>
struct Matrix {
glm::mat<C, R, T> m_mat;
constexpr Matrix(glm::mat<C, R, T> mat): m_mat(mat) {
}
friend std::ostream& operator << (std::ostream& o, const Matrix& v) {
o << "{";
int i;
for(i = 0; i < C - 1; i++) {
o << Vector(v.m_mat[i]) << ", ";
}
o << Vector(v.m_mat[i]) << "}";
return o;
}
};
};

View File

@ -0,0 +1,26 @@
#pragma once
#include <glm/matrix.hpp>
#include <ostream>
namespace Util::Stream {
template <int L, typename T>
struct Vector {
glm::vec<L, T> m_vec;
constexpr Vector(glm::vec<L, T> vec) : m_vec(vec) {
}
friend std::ostream& operator << (std::ostream& o, const Vector& v) {
o << "{";
int i;
for(i = 0; i < L - 1; i++) {
o << v.m_vec[i] << ", ";
}
o << v.m_vec[i] << "}";
return o;
}
};
};

139
src/world/chunk.cpp Normal file
View File

@ -0,0 +1,139 @@
#include <GL/glew.h>
#include "chunk.hpp"
#include "tile/tile_base.hpp"
#include <glm/detail/qualifier.hpp>
#include <glm/ext/matrix_transform.hpp>
#include <glm/ext/quaternion_transform.hpp>
#include <glm/ext/scalar_constants.hpp>
#include <iterator>
#include <memory>
#include <utility>
using World::Chunk;
using World::Tile::TileBase;
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}},
},
.m_indices = {
0, 2, 3,
0, 3, 1,
},
};
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}},
},
.m_indices = {
0, 2, 3,
0, 3, 1,
},
};
static const Graphics::Primitive<4, 6> PRIMITIVE_S[4] = {
PRIMITIVE_0.with_matrix(glm::rotate(glm::mat4(1), glm::pi<float>() * 0.5f, {0, 0, 1})),
PRIMITIVE_0.with_matrix(glm::rotate(glm::mat4(1), glm::pi<float>() * 1.0f, {0, 0, 1})),
PRIMITIVE_0.with_matrix(glm::rotate(glm::mat4(1), glm::pi<float>() * 1.5f, {0, 0, 1})),
PRIMITIVE_0,
};
constexpr glm::vec<2, int> get_pos_mod(glm::vec<2, int> pos) {
return (pos % Chunk::N + Chunk::N) % Chunk::N;
}
Chunk::Chunk(glm::vec<2, int> pos) {
m_pos = pos;
}
TileBase* Chunk::get(glm::vec<2, int> p) {
p = get_pos_mod(p);
return m_tiles[p.x * N + p.y].get();
}
const TileBase* Chunk::get(glm::vec<2, int> p) const {
p = get_pos_mod(p);
return m_tiles[p.x * N + p.y].get();
}
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_dirty = true;
return r;
}
void Chunk::update(Map& map) {
for(auto& tile : m_tiles) {
if(tile) {
tile->update();
}
}
if(!m_dirty) {
return;
}
Graphics::Mesh mesh;
const Chunk* chunks[4] = {
map.get_chunk(m_pos + glm::vec<2, int>(N, 0)),
map.get_chunk(m_pos + glm::vec<2, int>(0, N)),
map.get_chunk(m_pos + glm::vec<2, int>(-N, 0)),
map.get_chunk(m_pos + glm::vec<2, int>(0, -N)),
};
const glm::vec<2, int> neighbours[4] = {
{1, 0},
{0, 1},
{-1, 0},
{0, -1},
};
for(int x = 0; x < N; x++) {
for(int y = 0; y < N; y++) {
if(!get({x, y})) {
continue;
}
glm::vec<2, int> t_off(m_pos.x + x, m_pos.y + y);
mesh.add_primitive(PRIMITIVE_B.with_translation({t_off, 0}));
for(int i = 0; i < std::size(neighbours); i++) {
glm::vec<2, int> n_off = neighbours[i] + glm::vec<2, int>(x, y);
const Chunk* chunk_check = this;
if(n_off.x == N) {
chunk_check = chunks[0];
} else if(n_off.y == N) {
chunk_check = chunks[1];
} else if(n_off.x == -1) {
chunk_check = chunks[2];
} else if(n_off.y == -1) {
chunk_check = chunks[3];
}
if(!chunk_check || !chunk_check->get(n_off)) {
mesh.add_primitive(PRIMITIVE_S[i].with_translation({t_off, 0}));
}
}
}
}
m_model.bind();
m_model.set(mesh, GL_DYNAMIC_DRAW);
Graphics::Vertex::set_vertex_attribs();
m_dirty = false;
}
void Chunk::render(Graphics::Context& ctx) const {
m_model.bind();
m_model.render(GL_TRIANGLES);
for(auto& tile : m_tiles) {
if(tile) {
tile->render();
}
}
}

33
src/world/chunk.hpp Normal file
View File

@ -0,0 +1,33 @@
#pragma once
namespace World {
struct Chunk;
};
#include "map.hpp"
#include "../graphics/context.hpp"
#include "tile/tile_base.hpp"
#include "../graphics/gl/model.hpp"
#include <glm/gtc/integer.hpp>
#include <memory>
namespace World {
struct Chunk {
static constexpr int N = 16;
std::unique_ptr<Tile::TileBase> m_tiles[N*N];
Graphics::GL::Model m_model;
glm::vec<2, int> m_pos;
bool m_dirty = false;
Chunk(glm::vec<2, int> pos);
void update(Map& map);
void render(Graphics::Context& ctx) const;
Tile::TileBase* get(glm::vec<2, int> p);
const Tile::TileBase* get(glm::vec<2, int> p) const;
Tile::TileBase* set(glm::vec<2, int> p, std::unique_ptr<Tile::TileBase> v);
};
};

5
src/world/files.cmake Normal file
View File

@ -0,0 +1,5 @@
file(GLOB SOURCES ${SOURCES} src/world/*.cpp)
include(src/world/tile/files.cmake)

78
src/world/map.cpp Normal file
View File

@ -0,0 +1,78 @@
#include "map.hpp"
#include "chunk.hpp"
#include "tile/tile_base.hpp"
#include <glm/gtc/integer.hpp>
#include <utility>
using World::Tile::TileBase;
using World::Chunk;
using World::Map;
constexpr glm::vec<2, int> get_pos_mod(glm::vec<2, int> pos) {
return (pos % Map::N + Map::N) % Map::N;
}
constexpr uint64_t get_chunk_id(glm::vec<2, int> pos) {
glm::vec<2, int> pos_mod = get_pos_mod(pos);
glm::vec<2, uint32_t> cpos = (pos - pos_mod) / Map::N;
return ((uint64_t)cpos.x << 32) | cpos.y;
}
Chunk* Map::get_or_generate_chunk(glm::vec<2, int> pos) {
uint64_t cid = get_chunk_id(pos);
auto it = m_chunks.find(cid);
if(it == m_chunks.end()) {
it = m_chunks.try_emplace(cid, pos - get_pos_mod(pos)).first;
}
return &it->second;
}
Chunk* Map::get_chunk(glm::vec<2, int> pos) {
uint64_t cid = get_chunk_id(pos);
auto it = m_chunks.find(cid);
if(it == m_chunks.end()) {
return nullptr;
}
return &it->second;
}
const Chunk* Map::get_chunk(glm::vec<2, int> pos) const {
uint64_t cid = get_chunk_id(pos);
auto it = m_chunks.find(cid);
if(it == m_chunks.end()) {
return nullptr;
}
return &it->second;
}
TileBase* Map::get_tile(glm::vec<2, int> pos) {
Chunk* c = get_chunk(pos);
return c ? c->get(pos) : nullptr;
}
const TileBase* Map::get_tile(glm::vec<2, int> pos) const {
const Chunk* c = get_chunk(pos);
return c ? c->get(pos) : nullptr;
}
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));
}
void Map::update() {
for(auto& [cid, chunk] : m_chunks) {
chunk.update(*this);
}
}
void Map::render(Graphics::Context& ctx) const {
for(auto& [cid, chunk] : m_chunks) {
chunk.render(ctx);
}
}

33
src/world/map.hpp Normal file
View File

@ -0,0 +1,33 @@
#pragma once
namespace World {
struct Map;
};
#include "chunk.hpp"
#include "tile/tile_base.hpp"
#include "../graphics/context.hpp"
#include <glm/gtc/integer.hpp>
#include <cstdint>
#include <map>
#include <memory>
namespace World {
struct Map {
static constexpr int N = 16;
std::map<uint64_t, Chunk> m_chunks;
const Chunk* get_chunk(glm::vec<2, int> pos) const;
const Tile::TileBase* get_tile(glm::vec<2, int> pos) const;
Chunk* get_chunk(glm::vec<2, int> pos);
Chunk* get_or_generate_chunk(glm::vec<2, int> pos);
Tile::TileBase* get_tile(glm::vec<2, int> pos);
Tile::TileBase* set_tile(glm::vec<2, int> pos, std::unique_ptr<Tile::TileBase> tile);
void update();
void render(Graphics::Context& ctx) const;
};
};

20
src/world/player.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "player.hpp"
#include <glm/ext/matrix_transform.hpp>
using World::Player;
void Player::update() {
m_vel += m_accel;
m_pos += m_vel;
m_accel -= m_vel * 0.01;
}
glm::mat4 Player::get_view_matrix() const {
glm::mat4 mat(1);
mat = glm::translate(mat, {0, 0, -m_distance});
// mat = glm::rotate(mat, (float)m_pitch, {0, 0, 1});
mat = glm::rotate(mat, (float)m_yaw, {-1, 0, 0});
return mat;
}

21
src/world/player.hpp Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <glm/ext/matrix_float4x4.hpp>
#include <glm/ext/scalar_constants.hpp>
#include <glm/matrix.hpp>
namespace World {
struct Player {
glm::vec<2, double> m_pos;
glm::vec<2, double> m_vel;
glm::vec<2, double> m_accel;
double m_distance = 4;
double m_pitch = 0;
double m_yaw = glm::pi<double>() * 0.25;
void update();
glm::mat4 get_view_matrix() const;
};
};

11
src/world/tile/empty.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "empty.hpp"
using World::Tile::Empty;
void Empty::update() {
}
void Empty::render() const {
}

11
src/world/tile/empty.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "tile_base.hpp"
namespace World::Tile {
struct Empty : TileBase {
void update() override;
void render() const override;
};
};

View File

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

View File

@ -0,0 +1,17 @@
#pragma once
#include <glm/gtc/integer.hpp>
namespace World::Tile {
struct TileBase {
int m_facing = 0;
glm::vec<2, int> m_pos;
virtual ~TileBase() = default;
virtual void update() = 0;
virtual void render() const = 0;
};
};