Compare commits
No commits in common. "22f8fedce2655fca9258a8c4cd54b49c11ba710d" and "28227d61058e063b5d60c3baece77e2c9940c19a" have entirely different histories.
22f8fedce2
...
28227d6105
|
@ -58,13 +58,4 @@ if (USE_EXTRAS)
|
||||||
set_target_properties(FinalProject PROPERTIES COMPILE_FLAGS "-pthread")
|
set_target_properties(FinalProject PROPERTIES COMPILE_FLAGS "-pthread")
|
||||||
else()
|
else()
|
||||||
target_link_libraries(FinalProject PRIVATE glfw)
|
target_link_libraries(FinalProject PRIVATE glfw)
|
||||||
endif()
|
|
||||||
|
|
||||||
if(MSVC)
|
|
||||||
#target_compile_options(BLT_TESTS PRIVATE /W4)
|
|
||||||
if(${CMAKE_BUILD_TYPE} MATCHES Debug)
|
|
||||||
target_link_options(FinalProject PRIVATE /DEBUG)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
target_compile_options(FinalProject PRIVATE -Wall -Wextra -Wpedantic)
|
|
||||||
endif()
|
endif()
|
|
@ -36,7 +36,7 @@ namespace fp {
|
||||||
vbo_type type = ARRAY_BUFFER;
|
vbo_type type = ARRAY_BUFFER;
|
||||||
vbo_mem_type mem_type = STATIC;
|
vbo_mem_type mem_type = STATIC;
|
||||||
|
|
||||||
VBO(vbo_type type, void* data, int size, vbo_mem_type mem_type = STATIC): size(size), type(type), mem_type(mem_type) {
|
VBO(vbo_type type, void* data, int size, vbo_mem_type mem_type = STATIC): type(type), size(size), mem_type(mem_type) {
|
||||||
glGenBuffers(1, &vboID);
|
glGenBuffers(1, &vboID);
|
||||||
bind();
|
bind();
|
||||||
glBufferData(type, size, data, mem_type);
|
glBufferData(type, size, data, mem_type);
|
||||||
|
@ -182,36 +182,49 @@ namespace fp {
|
||||||
glUniform4f(getUniformLocation(name), x, y, z, w);
|
glUniform4f(getUniformLocation(name), x, y, z, w);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* a basic computer shader class, contains the functions and resources required to use compute shaders!
|
|
||||||
*/
|
|
||||||
class compute_shader : public shader_base {
|
|
||||||
private:
|
|
||||||
GLuint shaderID = 0;
|
|
||||||
public:
|
|
||||||
explicit compute_shader(const std::string& shader_source, bool loadAsString = true);
|
|
||||||
|
|
||||||
inline void execute(int x, int y, int z) const {
|
|
||||||
bind();
|
|
||||||
glDispatchCompute(x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
~compute_shader();
|
|
||||||
};
|
|
||||||
|
|
||||||
class shader : public shader_base {
|
class shader {
|
||||||
private:
|
private:
|
||||||
GLuint vertexShaderID = 0;
|
struct IntDefaultedToMinusOne {
|
||||||
GLuint fragmentShaderID = 0;
|
GLint i = -1;
|
||||||
|
};
|
||||||
|
// we can have shaders of many types in OpenGL
|
||||||
|
unsigned int programID = 0;
|
||||||
|
// but we will only make use of these two for now
|
||||||
|
unsigned int vertexShaderID = 0;
|
||||||
|
unsigned int fragmentShaderID = 0;
|
||||||
// while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!)
|
// while these will remain unused. (Webgl2 apparently doesn't support them despite being based on GL4.3? that's a TODO!)
|
||||||
GLuint geometryShaderID = 0;
|
unsigned int geometryShaderID = 0;
|
||||||
// this would be very useful however it is highly unlikely webgl will support it
|
// this would be very useful however it is highly unlikely webgl will support it
|
||||||
// im leaving some of this stuff in here because I might expand the native application to use some of it.
|
// im leaving some of this stuff in here because I might expand the native application to use some of it.
|
||||||
// im trying to keep the web and native versions the same though
|
// im trying to keep the web and native versions the same though
|
||||||
GLuint tessellationShaderID = 0;
|
unsigned int tessellationShaderID = 0;
|
||||||
|
std::unordered_map<std::string, IntDefaultedToMinusOne> uniformVars;
|
||||||
|
|
||||||
static unsigned int createShader(const std::string& source, int type);
|
static unsigned int createShader(const std::string& source, int type);
|
||||||
|
|
||||||
|
inline GLint getUniformLocation(const std::string &name) {
|
||||||
|
if (uniformVars[name].i != -1)
|
||||||
|
return uniformVars[name].i;
|
||||||
|
// caching the result is a lot faster since it won't change after the shader is created.
|
||||||
|
// TODO: look into this: https://webglfundamentals.org/webgl/lessons/webgl-qna-how-can-i-get-all-the-uniforms-and-uniformblocks.html
|
||||||
|
int loc = glGetUniformLocation(programID, name.c_str());
|
||||||
|
uniformVars[name].i = loc;
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline std::string removeEmptyFirstLines(const std::string& string){
|
||||||
|
auto lines = blt::string::split(string, "\n");
|
||||||
|
std::string new_source_string;
|
||||||
|
for (const auto& line : lines) {
|
||||||
|
if (!line.empty() && !blt::string::contains(line, "\"")) {
|
||||||
|
new_source_string += line;
|
||||||
|
new_source_string += "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new_source_string;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Creates a shader
|
* Creates a shader
|
||||||
|
@ -230,16 +243,56 @@ namespace fp {
|
||||||
// used to set location of shared UBOs like the perspective and view matrix
|
// used to set location of shared UBOs like the perspective and view matrix
|
||||||
void setUniformBlockLocation(const std::string &name, int location) const;
|
void setUniformBlockLocation(const std::string &name, int location) const;
|
||||||
|
|
||||||
~shader();
|
// set various data-types.
|
||||||
|
inline void setBool(const std::string &name, bool value) {
|
||||||
|
glUniform1i(getUniformLocation(name), (int) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setInt(const std::string &name, int value) {
|
||||||
|
glUniform1i(getUniformLocation(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setFloat(const std::string &name, float value) {
|
||||||
|
glUniform1f(getUniformLocation(name), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setMatrix(const std::string &name, blt::mat4x4 &matrix) {
|
||||||
|
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, matrix.ptr());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec3(const std::string &name, const blt::vec3 &vec) {
|
||||||
|
glUniform3f(getUniformLocation(name), vec.x(), vec.y(), vec.z());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec4(const std::string &name, const blt::vec4 &vec) {
|
||||||
|
// TODO: edit BLT to include a w component
|
||||||
|
glUniform4f(getUniformLocation(name), vec.x(), vec.y(), vec.z(), vec[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec2(const std::string &name, float x, float y) {
|
||||||
|
glUniform2f(getUniformLocation(name), x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec3(const std::string &name, float x, float y, float z) {
|
||||||
|
glUniform3f(getUniformLocation(name), x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void setVec4(const std::string &name, float x, float y, float z, float w) {
|
||||||
|
glUniform4f(getUniformLocation(name), x, y, z, w);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void use() const {
|
||||||
|
glUseProgram(programID);
|
||||||
|
}
|
||||||
|
|
||||||
static void updateProjectionMatrix(const blt::mat4x4& projectionMatrix);
|
static void updateProjectionMatrix(const blt::mat4x4& projectionMatrix);
|
||||||
static void updateOrthographicMatrix(const blt::mat4x4& orthoMatrix);
|
static void updateOrthographicMatrix(const blt::mat4x4& orthoMatrix);
|
||||||
static void updateViewMatrix(const blt::mat4x4& viewMatrix);
|
static void updateViewMatrix(const blt::mat4x4& viewMatrix);
|
||||||
// returns the perspective view matrix which is calculated per frame. (This is for optimization)
|
// returns the perspective view matrix which is calculated per frame. (This is for optimization)
|
||||||
static const blt::mat4x4& getPVM();
|
static const blt::mat4x4& getPVM();
|
||||||
|
|
||||||
|
~shader();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -112,8 +112,8 @@ namespace fp::texture {
|
||||||
int width, int height, GLint bind_type = GL_TEXTURE_2D,
|
int width, int height, GLint bind_type = GL_TEXTURE_2D,
|
||||||
GLint color_mode = GL_RGBA
|
GLint color_mode = GL_RGBA
|
||||||
):
|
):
|
||||||
textureBindType(bind_type), textureColorMode(color_mode), m_width(width),
|
m_width(width), m_height(height), textureBindType(bind_type),
|
||||||
m_height(height) {
|
textureColorMode(color_mode) {
|
||||||
glGenTextures(1, &textureID);
|
glGenTextures(1, &textureID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Created by Brett on 22/04/23.
|
|
||||||
* Licensed under GNU General Public License V3.0
|
|
||||||
* See LICENSE file for license detail
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef FINALPROJECT_THREADPOOL_H
|
|
||||||
#define FINALPROJECT_THREADPOOL_H
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
namespace blt {
|
|
||||||
|
|
||||||
class runnable {
|
|
||||||
public:
|
|
||||||
virtual void run() = 0;
|
|
||||||
virtual ~runnable() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If your runnable functions are small consider using another data structure,
|
|
||||||
* as thread_pool will be slow if many small tasks are needed to be ran.
|
|
||||||
* thread_pool is designed for running large long run tasks
|
|
||||||
*/
|
|
||||||
class thread_pool {
|
|
||||||
private:
|
|
||||||
volatile bool halted;
|
|
||||||
int MAX_THREADS;
|
|
||||||
std::queue<std::function<void()>*> runQueue {};
|
|
||||||
std::vector<std::thread*> threads {};
|
|
||||||
std::mutex queueMutex {};
|
|
||||||
public:
|
|
||||||
explicit thread_pool(int maxThreads);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to start the thread_pool.
|
|
||||||
*/
|
|
||||||
void start();
|
|
||||||
|
|
||||||
void run(std::function<void()>* func);
|
|
||||||
|
|
||||||
void run(runnable* func);
|
|
||||||
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
~thread_pool();
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif //FINALPROJECT_THREADPOOL_H
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <render/gl.h>
|
#include <render/gl.h>
|
||||||
#include <phmap.h>
|
#include <phmap.h>
|
||||||
#include "blt/profiling/profiler.h"
|
#include "blt/profiling/profiler.h"
|
||||||
#include "util/threadpool.h"
|
|
||||||
#include <render/frustum.h>
|
#include <render/frustum.h>
|
||||||
|
|
||||||
namespace fp {
|
namespace fp {
|
||||||
|
@ -152,14 +151,7 @@ namespace fp {
|
||||||
|
|
||||||
class world {
|
class world {
|
||||||
private:
|
private:
|
||||||
// not using the parallel functions of the map, we will do manual concurrency control
|
phmap::flat_hash_map<chunk_pos, chunk*, _static::chunk_pos_hash, _static::chunk_pos_equality> chunk_storage;
|
||||||
// "The parallel hash maps are preferred when you have a few hash maps that will store a very large number of values.
|
|
||||||
// The non-parallel hash maps are preferred if you have a large number of hash maps, each storing a relatively small number of values."
|
|
||||||
phmap::parallel_flat_hash_map<chunk_pos, chunk*, _static::chunk_pos_hash, _static::chunk_pos_equality> chunk_storage;
|
|
||||||
std::mutex chunkInsertMutex;
|
|
||||||
std::thread* chunkGenerationThread;
|
|
||||||
const unsigned int THREAD_COUNT = 16;
|
|
||||||
volatile bool running = true;
|
|
||||||
protected:
|
protected:
|
||||||
void generateChunkMesh(chunk* chunk);
|
void generateChunkMesh(chunk* chunk);
|
||||||
|
|
||||||
|
@ -174,16 +166,9 @@ namespace fp {
|
||||||
neighbours[Z_NEG] = getChunk(chunk_pos{pos.x, pos.y, pos.z - 1});
|
neighbours[Z_NEG] = getChunk(chunk_pos{pos.x, pos.y, pos.z - 1});
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool spawnChunk(chunk* chunk) {
|
inline void insertChunk(chunk* chunk) {
|
||||||
// make sure not to insert null chunks
|
|
||||||
if (chunk == nullptr)
|
if (chunk == nullptr)
|
||||||
return false;
|
return;
|
||||||
// we must lock for the entire insertion including neighbour updates.
|
|
||||||
// otherwise chunk updates might not be properly handled
|
|
||||||
std::scoped_lock<std::mutex> lock(chunkInsertMutex);
|
|
||||||
// or overwrite existing chunks (*memory leak*)
|
|
||||||
if (chunk_storage.find(chunk->getPos()) != chunk_storage.end())
|
|
||||||
return false;
|
|
||||||
chunk_storage.insert({chunk->getPos(), chunk});
|
chunk_storage.insert({chunk->getPos(), chunk});
|
||||||
|
|
||||||
chunk_neighbours chunkNeighbours{};
|
chunk_neighbours chunkNeighbours{};
|
||||||
|
@ -193,18 +178,6 @@ namespace fp {
|
||||||
if (p)
|
if (p)
|
||||||
p->setStatus(NEIGHBOUR_CREATE);
|
p->setStatus(NEIGHBOUR_CREATE);
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline static chunk_pos offsetCameraByChunk(int i, int j, int k) {
|
|
||||||
const auto& pos = fp::camera::getPosition();
|
|
||||||
int x = (int) pos.x();
|
|
||||||
int y = (int) pos.y();
|
|
||||||
int z = (int) pos.z();
|
|
||||||
auto camera_chunk_pos = fp::_static::world_to_chunk({x, y, z});
|
|
||||||
return chunk_pos{camera_chunk_pos.x + i, // chunk x
|
|
||||||
camera_chunk_pos.y + j, // chunk y
|
|
||||||
camera_chunk_pos.z + k}; // chunk z
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline chunk* getChunk(const chunk_pos& pos) {
|
inline chunk* getChunk(const chunk_pos& pos) {
|
||||||
|
@ -219,7 +192,7 @@ namespace fp {
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
world();
|
world() = default;
|
||||||
|
|
||||||
void update();
|
void update();
|
||||||
|
|
||||||
|
|
|
@ -52,20 +52,14 @@ int main() {
|
||||||
|
|
||||||
fp::window::init();
|
fp::window::init();
|
||||||
|
|
||||||
BLT_TRACE("Creating renderer");
|
|
||||||
renderer = new fp::renderer();
|
renderer = new fp::renderer();
|
||||||
BLT_TRACE("Init graphics");
|
|
||||||
fp::graphics::init(*renderer);
|
fp::graphics::init(*renderer);
|
||||||
BLT_TRACE("Creating textures");
|
|
||||||
// textures must come first as blocks will require the IDs
|
// textures must come first as blocks will require the IDs
|
||||||
fp::registry::registerDefaultTextures();
|
fp::registry::registerDefaultTextures();
|
||||||
fp::registry::registerDefaultBlocks();
|
fp::registry::registerDefaultBlocks();
|
||||||
BLT_TRACE("Registered textures!");
|
|
||||||
|
|
||||||
BLT_TRACE("Creating chunk shader!");
|
|
||||||
chunk_shader = renderer->createShader(fp::shader(shader_chunk_vert, shader_chunk_frag));
|
chunk_shader = renderer->createShader(fp::shader(shader_chunk_vert, shader_chunk_frag));
|
||||||
world = new fp::world();
|
world = new fp::world();
|
||||||
BLT_TRACE("World created!");
|
|
||||||
|
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
|
@ -65,18 +65,6 @@ namespace fp::_static {
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace fp {
|
namespace fp {
|
||||||
static inline std::string removeEmptyFirstLines(const std::string& string){
|
|
||||||
auto lines = blt::string::split(string, "\n");
|
|
||||||
std::string new_source_string;
|
|
||||||
for (const auto& line : lines) {
|
|
||||||
if (!line.empty() && !blt::string::contains(line, "\"")) {
|
|
||||||
new_source_string += line;
|
|
||||||
new_source_string += "\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new_source_string;
|
|
||||||
}
|
|
||||||
|
|
||||||
VAO::VAO() {
|
VAO::VAO() {
|
||||||
glGenVertexArrays(1, &vaoID);
|
glGenVertexArrays(1, &vaoID);
|
||||||
}
|
}
|
||||||
|
@ -108,6 +96,7 @@ namespace fp {
|
||||||
VBOs.insert({-1, vbo});
|
VBOs.insert({-1, vbo});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
unsigned int shader::createShader(const std::string& source, int type) {
|
unsigned int shader::createShader(const std::string& source, int type) {
|
||||||
const char* shader_code = source.c_str();
|
const char* shader_code = source.c_str();
|
||||||
// creates a Shader
|
// creates a Shader
|
||||||
|
@ -143,7 +132,7 @@ namespace fp {
|
||||||
}
|
}
|
||||||
|
|
||||||
shader::shader(const std::string& vertex, const std::string& fragment, const std::string& geometry, bool load_as_string) {
|
shader::shader(const std::string& vertex, const std::string& fragment, const std::string& geometry, bool load_as_string) {
|
||||||
// load shader sources
|
// load shader sources
|
||||||
bool load_geometry = !geometry.empty();
|
bool load_geometry = !geometry.empty();
|
||||||
std::string vertex_source = vertex;
|
std::string vertex_source = vertex;
|
||||||
std::string fragment_source = fragment;
|
std::string fragment_source = fragment;
|
||||||
|
@ -164,7 +153,7 @@ namespace fp {
|
||||||
vertexShaderID = createShader(vertex_source, GL_VERTEX_SHADER);
|
vertexShaderID = createShader(vertex_source, GL_VERTEX_SHADER);
|
||||||
fragmentShaderID = createShader(fragment_source, GL_FRAGMENT_SHADER);
|
fragmentShaderID = createShader(fragment_source, GL_FRAGMENT_SHADER);
|
||||||
if (load_geometry)
|
if (load_geometry)
|
||||||
geometryShaderID = createShader(geometry_source, GL_GEOMETRY_SHADER);
|
BLT_ERROR("Unable to load geometry shader because webgl doesn't support it!");
|
||||||
|
|
||||||
// bind them to a program
|
// bind them to a program
|
||||||
programID = glCreateProgram();
|
programID = glCreateProgram();
|
||||||
|
@ -181,10 +170,10 @@ namespace fp {
|
||||||
if (!success) {
|
if (!success) {
|
||||||
int log_length = 0;
|
int log_length = 0;
|
||||||
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &log_length);
|
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &log_length);
|
||||||
|
|
||||||
// scoped buffers will delete their memory when they go out of scope.
|
// scoped buffers will delete their memory when they go out of scope.
|
||||||
blt::scoped_buffer<GLchar> infoLog{static_cast<unsigned long>(log_length + 1)};
|
blt::scoped_buffer<GLchar> infoLog{static_cast<unsigned long>(log_length + 1)};
|
||||||
|
|
||||||
glGetProgramInfoLog(programID, log_length + 1, nullptr, infoLog.buffer);
|
glGetProgramInfoLog(programID, log_length + 1, nullptr, infoLog.buffer);
|
||||||
BLT_ERROR("--- --- --- --- --- --- --- --- ---");
|
BLT_ERROR("--- --- --- --- --- --- --- --- ---");
|
||||||
BLT_ERROR("Unable to link program of ID: %d", programID);
|
BLT_ERROR("Unable to link program of ID: %d", programID);
|
||||||
|
@ -197,18 +186,19 @@ namespace fp {
|
||||||
}
|
}
|
||||||
|
|
||||||
glValidateProgram(programID);
|
glValidateProgram(programID);
|
||||||
bind();
|
use();
|
||||||
setUniformBlockLocation("StandardMatrices", 0);
|
setUniformBlockLocation("StandardMatrices", 0);
|
||||||
glUseProgram(0);
|
glUseProgram(0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void shader::bindAttribute(int attribute, const std::string &name) const {
|
void shader::bindAttribute(int attribute, const std::string &name) const {
|
||||||
bind();
|
use();
|
||||||
glBindAttribLocation(programID, attribute, name.c_str());
|
glBindAttribLocation(programID, attribute, name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void shader::setUniformBlockLocation(const std::string &name, int location) const {
|
void shader::setUniformBlockLocation(const std::string &name, int location) const {
|
||||||
bind();
|
use();
|
||||||
glUniformBlockBinding(programID, glGetUniformBlockIndex(programID, name.c_str()), location);
|
glUniformBlockBinding(programID, glGetUniformBlockIndex(programID, name.c_str()), location);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,11 +8,8 @@
|
||||||
namespace fp {
|
namespace fp {
|
||||||
|
|
||||||
shader* renderer::createShader(shader&& shader) {
|
shader* renderer::createShader(shader&& shader) {
|
||||||
BLT_TRACE("Creating shader");
|
|
||||||
auto s = new class shader(std::move(shader));
|
auto s = new class shader(std::move(shader));
|
||||||
BLT_TRACE("Shader created");
|
|
||||||
shaders.push_back(s);
|
shaders.push_back(s);
|
||||||
BLT_TRACE("returning");
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -122,12 +122,10 @@ namespace fp::graphics {
|
||||||
}
|
}
|
||||||
|
|
||||||
void init(renderer& renderer) {
|
void init(renderer& renderer) {
|
||||||
BLT_TRACE("Init font");
|
|
||||||
if (FT_Init_FreeType(&ft)) {
|
if (FT_Init_FreeType(&ft)) {
|
||||||
BLT_FATAL("Unable to init freetype library!");
|
BLT_FATAL("Unable to init freetype library!");
|
||||||
std::abort();
|
std::abort();
|
||||||
}
|
}
|
||||||
BLT_TRACE("Init face");
|
|
||||||
if (FT_New_Face(ft, "assets/fonts/JetBrains Mono.ttf", 0, &monospaced_face)) {
|
if (FT_New_Face(ft, "assets/fonts/JetBrains Mono.ttf", 0, &monospaced_face)) {
|
||||||
BLT_ERROR("Unable to load default monospaced (JetBrains Mono) font!");
|
BLT_ERROR("Unable to load default monospaced (JetBrains Mono) font!");
|
||||||
std::abort();
|
std::abort();
|
||||||
|
@ -136,7 +134,6 @@ namespace fp::graphics {
|
||||||
// disable alignment restrictions. This might cause issues with WebGL! FIXME: if it does
|
// disable alignment restrictions. This might cause issues with WebGL! FIXME: if it does
|
||||||
// gl requires an alignment of 4. Since we are going to only use a single character of any width/height the alignment must be changed.
|
// gl requires an alignment of 4. Since we are going to only use a single character of any width/height the alignment must be changed.
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
BLT_TRACE("Alignment set, generating characters");
|
|
||||||
|
|
||||||
generateCharacters(FONT_11);
|
generateCharacters(FONT_11);
|
||||||
generateCharacters(FONT_12);
|
generateCharacters(FONT_12);
|
||||||
|
@ -148,20 +145,15 @@ namespace fp::graphics {
|
||||||
generateCharacters(FONT_72);
|
generateCharacters(FONT_72);
|
||||||
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
||||||
BLT_TRACE("Character generation complete!");
|
|
||||||
|
|
||||||
FT_Done_Face(monospaced_face);
|
FT_Done_Face(monospaced_face);
|
||||||
FT_Done_FreeType(ft);
|
FT_Done_FreeType(ft);
|
||||||
|
|
||||||
BLT_TRACE("Creating font shaders and GL objects");
|
|
||||||
// create the GL objects required to render texts
|
// create the GL objects required to render texts
|
||||||
text_shader = renderer.createShader(shader(shader_text_vert, shader_text_frag));
|
text_shader = renderer.createShader(shader(shader_text_vert, shader_text_frag));
|
||||||
BLT_TRACE("text shader created!");
|
|
||||||
plane_shader = renderer.createShader(shader(shader_plane_vert, shader_plane_frag));
|
plane_shader = renderer.createShader(shader(shader_plane_vert, shader_plane_frag));
|
||||||
BLT_TRACE("plane shader created!");
|
|
||||||
quad_vao = new VAO();
|
quad_vao = new VAO();
|
||||||
plane_vao = new VAO();
|
plane_vao = new VAO();
|
||||||
BLT_TRACE("VAOs created!");
|
|
||||||
|
|
||||||
float vertices[6 * 4] = {
|
float vertices[6 * 4] = {
|
||||||
// vertices uvs
|
// vertices uvs
|
||||||
|
@ -175,12 +167,11 @@ namespace fp::graphics {
|
||||||
0, 1.0, 0.0f, 1.0f,
|
0, 1.0, 0.0f, 1.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
BLT_TRACE("Binding VBOs");
|
|
||||||
quad_vao->bindVBO(new VBO(ARRAY_BUFFER, vertices, sizeof(float) * 6 * 4), 0, 4);
|
quad_vao->bindVBO(new VBO(ARRAY_BUFFER, vertices, sizeof(float) * 6 * 4), 0, 4);
|
||||||
// since we will be updating the plane VBO regularly, we should tell the driver of this fact
|
// since we will be updating the plane VBO regularly, we should tell the driver of this fact
|
||||||
plane_vao->bindVBO(new VBO(ARRAY_BUFFER, nullptr, 0, DYNAMIC), 0, 3, GL_FLOAT, sizeof(float) * 3);
|
plane_vao->bindVBO(new VBO(ARRAY_BUFFER, nullptr, 0, DYNAMIC), 0, 3, GL_FLOAT, sizeof(float) * 3);
|
||||||
plane_vao->bindElementVBO(new VBO(ELEMENT_BUFFER, nullptr, 0, DYNAMIC));
|
plane_vao->bindElementVBO(new VBO(ELEMENT_BUFFER, nullptr, 0, DYNAMIC));
|
||||||
BLT_TRACE("Init complete!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void cleanup() {
|
void cleanup() {
|
||||||
|
@ -193,7 +184,7 @@ namespace fp::graphics {
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
// generate planes
|
// generate planes
|
||||||
plane_shader->bind();
|
plane_shader->use();
|
||||||
plane_vao->bind();
|
plane_vao->bind();
|
||||||
glDisable(GL_CULL_FACE);
|
glDisable(GL_CULL_FACE);
|
||||||
while (!plane_render_queue.empty()) {
|
while (!plane_render_queue.empty()) {
|
||||||
|
@ -219,7 +210,7 @@ namespace fp::graphics {
|
||||||
//glDisable(GL_CULL_FACE);
|
//glDisable(GL_CULL_FACE);
|
||||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
|
||||||
text_shader->bind();
|
text_shader->use();
|
||||||
quad_vao->bind();
|
quad_vao->bind();
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
|
||||||
|
|
|
@ -148,7 +148,7 @@ void fp::window::init(int width, int height) {
|
||||||
//glfwSwapInterval(1);
|
//glfwSwapInterval(1);
|
||||||
|
|
||||||
updateWindowViewport(width, height);
|
updateWindowViewport(width, height);
|
||||||
BLT_TRACE("Updated window view port!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fp::window::update() {
|
void fp::window::update() {
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
/*
|
|
||||||
* Created by Brett on 22/04/23.
|
|
||||||
* Licensed under GNU General Public License V3.0
|
|
||||||
* See LICENSE file for license detail
|
|
||||||
*/
|
|
||||||
#include <util/threadpool.h>
|
|
||||||
|
|
||||||
blt::thread_pool::thread_pool(int maxThreads): MAX_THREADS(maxThreads) {
|
|
||||||
halted = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
blt::thread_pool::~thread_pool() {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void blt::thread_pool::start() {
|
|
||||||
for (int i = 0; i < MAX_THREADS; i++) {
|
|
||||||
threads.push_back(
|
|
||||||
new std::thread(
|
|
||||||
[this]() -> void {
|
|
||||||
while (!halted){
|
|
||||||
// acquire a resource from the runnable queue
|
|
||||||
while (runQueue.empty())
|
|
||||||
std::this_thread::yield();
|
|
||||||
std::unique_lock<std::mutex> lock(queueMutex);
|
|
||||||
auto* run = runQueue.front();
|
|
||||||
runQueue.pop();
|
|
||||||
lock.unlock();
|
|
||||||
|
|
||||||
// attempt to run the function
|
|
||||||
if (run)
|
|
||||||
(*run)();
|
|
||||||
|
|
||||||
delete(run);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void blt::thread_pool::stop() {
|
|
||||||
if (halted)
|
|
||||||
return;
|
|
||||||
|
|
||||||
halted = true;
|
|
||||||
|
|
||||||
for (std::thread* thread : threads)
|
|
||||||
thread->join();
|
|
||||||
|
|
||||||
for (std::thread* thread : threads)
|
|
||||||
delete thread;
|
|
||||||
|
|
||||||
std::scoped_lock<std::mutex> lock(queueMutex);
|
|
||||||
while (!runQueue.empty()){
|
|
||||||
delete runQueue.front();
|
|
||||||
runQueue.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void blt::thread_pool::run(std::function<void()>* func) {
|
|
||||||
std::scoped_lock<std::mutex> lock(queueMutex);
|
|
||||||
runQueue.push(func);
|
|
||||||
}
|
|
||||||
|
|
||||||
void blt::thread_pool::run(blt::runnable* func) {
|
|
||||||
std::scoped_lock<std::mutex> lock(queueMutex);
|
|
||||||
runQueue.push(new std::function<void()>([&]() -> void {
|
|
||||||
func->run();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
|
@ -120,22 +120,24 @@ void fp::world::generateChunkMesh(chunk* chunk) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::queue<fp::chunk_pos> chunks_to_generate{};
|
||||||
|
|
||||||
void fp::world::update() {
|
void fp::world::update() {
|
||||||
// auto target_delta = 1000000000 / std::stoi(fp::settings::get("FPS"));
|
auto target_delta = 1000000000 / std::stoi(fp::settings::get("FPS"));
|
||||||
//
|
|
||||||
// while (fp::window::getCurrentDelta() < target_delta) {
|
while (fp::window::getCurrentDelta() < target_delta) {
|
||||||
// if (chunks_to_generate.empty())
|
if (chunks_to_generate.empty())
|
||||||
// break;
|
break;
|
||||||
// const auto& front = chunks_to_generate.front();
|
const auto& front = chunks_to_generate.front();
|
||||||
//
|
|
||||||
// spawnChunk(generateChunk(front));
|
insertChunk(generateChunk(front));
|
||||||
//
|
|
||||||
// chunks_to_generate.pop();
|
chunks_to_generate.pop();
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fp::world::render(fp::shader& shader) {
|
void fp::world::render(fp::shader& shader) {
|
||||||
shader.bind();
|
shader.use();
|
||||||
|
|
||||||
if (fp::window::isKeyPressed(GLFW_KEY_F) && fp::window::keyState())
|
if (fp::window::isKeyPressed(GLFW_KEY_F) && fp::window::keyState())
|
||||||
fp::camera::isFrozen() ? fp::camera::unfreeze() : fp::camera::freeze();
|
fp::camera::isFrozen() ? fp::camera::unfreeze() : fp::camera::freeze();
|
||||||
|
@ -149,11 +151,18 @@ void fp::world::render(fp::shader& shader) {
|
||||||
for (int j = -view_distance; j <= view_distance; j++) {
|
for (int j = -view_distance; j <= view_distance; j++) {
|
||||||
for (int k = -view_distance; k <= view_distance; k++) {
|
for (int k = -view_distance; k <= view_distance; k++) {
|
||||||
// get the chunks around the player's camera
|
// get the chunks around the player's camera
|
||||||
chunk_pos adjusted_chunk_pos = offsetCameraByChunk(i, j, k);
|
const auto& pos = fp::camera::getPosition();
|
||||||
// don't try to render null chunks! A separate thread should handle the generation.
|
int x = (int) pos.x();
|
||||||
|
int y = (int) pos.y();
|
||||||
|
int z = (int) pos.z();
|
||||||
|
auto camera_chunk_pos = fp::_static::world_to_chunk({x, y, z});
|
||||||
|
chunk_pos adjusted_chunk_pos {camera_chunk_pos.x + i, // chunk x
|
||||||
|
camera_chunk_pos.y + j, // chunk y
|
||||||
|
camera_chunk_pos.z + k}; // chunk z
|
||||||
|
// queue a chunk for generation if it doesn't exist. A separate thread should handle the generation.
|
||||||
auto* chunk = this->getChunk(adjusted_chunk_pos);
|
auto* chunk = this->getChunk(adjusted_chunk_pos);
|
||||||
if (!chunk) {
|
if (!chunk) {
|
||||||
// chunks_to_generate.push(adjusted_chunk_pos);
|
chunks_to_generate.push(adjusted_chunk_pos);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,13 +175,12 @@ void fp::world::render(fp::shader& shader) {
|
||||||
chunk->updateChunkMesh();
|
chunk->updateChunkMesh();
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto p_min = blt::vec3{(float) i * CHUNK_SIZE, (float) j * CHUNK_SIZE,
|
const auto p_min = blt::vec3{(float)i * CHUNK_SIZE, (float)j * CHUNK_SIZE, (float)k * CHUNK_SIZE};
|
||||||
(float) k * CHUNK_SIZE};
|
|
||||||
const auto p_max = p_min + blt::vec3{CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE};
|
const auto p_max = p_min + blt::vec3{CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE};
|
||||||
|
|
||||||
const auto& m = camera::getPVM();
|
const auto& m = camera::getPVM();
|
||||||
|
|
||||||
//if (frustum::isInsideFrustum(m, p_min))
|
if (frustum::isInsideFrustum(m, p_min))
|
||||||
chunk->render(shader);
|
chunk->render(shader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,9 +209,7 @@ fp::chunk* fp::world::generateChunk(const fp::chunk_pos& pos) {
|
||||||
float noise_total = 1;
|
float noise_total = 1;
|
||||||
|
|
||||||
for (int j = 1; j <= 8; j++)
|
for (int j = 1; j <= 8; j++)
|
||||||
noise_total += stb_perlin_noise3(
|
noise_total += stb_perlin_noise3(block_x / 256.0f, block_z / 256.0f, (float)j * 5.213953f, 0, 0, 0) * (float)(j);
|
||||||
block_x / 256.0f, block_z / 256.0f, (float) j * 5.213953f, 0, 0, 0
|
|
||||||
) * (float) (j);
|
|
||||||
|
|
||||||
noise_total /= 8;
|
noise_total /= 8;
|
||||||
|
|
||||||
|
@ -212,9 +218,7 @@ fp::chunk* fp::world::generateChunk(const fp::chunk_pos& pos) {
|
||||||
for (int j = 0; j < CHUNK_SIZE; j++) {
|
for (int j = 0; j < CHUNK_SIZE; j++) {
|
||||||
auto block_y = float(pos.y * CHUNK_SIZE + j);
|
auto block_y = float(pos.y * CHUNK_SIZE + j);
|
||||||
|
|
||||||
float noise2 = stb_perlin_fbm_noise3(
|
float noise2 = stb_perlin_fbm_noise3(block_x / 32.0f, block_y / 32.0f, block_z / 32.0f, 2.0, 0.5, 5) + 0.75f;
|
||||||
block_x / 32.0f, block_y / 32.0f, block_z / 32.0f, 2.0, 0.5, 5
|
|
||||||
) + 0.75f;
|
|
||||||
|
|
||||||
if (block_y < world_height && noise2 > 0)
|
if (block_y < world_height && noise2 > 0)
|
||||||
storage->set({i, j, k}, noise2 > 1 ? fp::registry::GRASS : fp::registry::STONE);
|
storage->set({i, j, k}, noise2 > 1 ? fp::registry::GRASS : fp::registry::STONE);
|
||||||
|
@ -235,54 +239,6 @@ fp::world::~world() {
|
||||||
BLT_WRITE_PROFILE(profile, "Chunk Mesh");
|
BLT_WRITE_PROFILE(profile, "Chunk Mesh");
|
||||||
for (auto& chunk : chunk_storage)
|
for (auto& chunk : chunk_storage)
|
||||||
delete (chunk.second);
|
delete (chunk.second);
|
||||||
running = false;
|
|
||||||
// for (int i = 0; i < THREAD_COUNT; i++)
|
|
||||||
// chunkGenerationThread[i].join();
|
|
||||||
chunkGenerationThread->join();
|
|
||||||
delete chunkGenerationThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
fp::world::world() {
|
|
||||||
chunkGenerationThread = new std::thread(
|
|
||||||
[this]() -> void {
|
|
||||||
while (running) {
|
|
||||||
auto view_distance = std::stoi(fp::settings::get("VIEW_DISTANCE")) / 2;
|
|
||||||
// get the chunks around the player's camera
|
|
||||||
for (int i = -view_distance; i <= view_distance; i++) {
|
|
||||||
for (int j = -view_distance; j <= view_distance; j++) {
|
|
||||||
for (int k = -view_distance; k <= view_distance; k++) {
|
|
||||||
chunk_pos adjusted_chunk_pos = offsetCameraByChunk(i, j, k);
|
|
||||||
auto* chunk = this->getChunk(adjusted_chunk_pos);
|
|
||||||
// only generate non-existent chunks
|
|
||||||
if (chunk)
|
|
||||||
continue;
|
|
||||||
auto* generated_chunk = generateChunk(adjusted_chunk_pos);
|
|
||||||
spawnChunk(generated_chunk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
// for (int i = 0; i < THREAD_COUNT; i++){
|
|
||||||
// chunkGenerationThread[i] = std::thread{[this]() -> void {
|
|
||||||
// while (running) {
|
|
||||||
// std::unique_lock<std::mutex> clock(chunkQueueMutex);
|
|
||||||
// if (chunks_to_generate.empty()) {
|
|
||||||
// clock.unlock();
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
// auto chunk_pos = chunks_to_generate.front();
|
|
||||||
// chunks_to_generate.pop();
|
|
||||||
// clock.unlock();
|
|
||||||
//
|
|
||||||
// auto* generated_chunk = generateChunk(chunk_pos);
|
|
||||||
// spawnChunk(generated_chunk);
|
|
||||||
// BLT_TRACE("Generated Chunk! %d, %d, %d", chunk_pos.x, chunk_pos.y,
|
|
||||||
// chunk_pos.z);
|
|
||||||
// }
|
|
||||||
// }};
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fp::chunk::render(fp::shader& shader) {
|
void fp::chunk::render(fp::shader& shader) {
|
||||||
|
|
Loading…
Reference in New Issue