Misc fixes in preparation for multithreading refactor #10

Merged
EmaMaker merged 10 commits from multithread-refactor into main 2023-10-03 22:55:28 +02:00
12 changed files with 170 additions and 152 deletions

View File

@ -18,6 +18,10 @@ public:
// This matrix needs to be also updated in viewPortCallback whenever it is changed
projection = glm::perspective(glm::radians(90.0f), 800.0f / 600.0f, 0.1f, 1200.0f);
posX = cameraPos.x;
posY = cameraPos.y;
posZ = cameraPos.z;
}
void update(GLFWwindow *window, float deltaTime)
@ -112,7 +116,7 @@ public:
private:
glm::vec3 cameraPos = glm::vec3(256.0, 80.0f, 256.0f);
glm::vec3 cameraPos = glm::vec3(512.0, 80.0f, 512.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 direction = glm::vec3(0.0f);

View File

@ -20,9 +20,16 @@
#define CHUNK_VOLUME (CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE)
#define CHUNK_MAX_INDEX (CHUNK_VOLUME - 1)
// int32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1023). There's actually two spare bits
typedef int32_t chunk_index_t;
typedef int16_t chunk_intcoord_t;
namespace Chunk
{
chunk_index_t calculateIndex(chunk_intcoord_t i, chunk_intcoord_t j, chunk_intcoord_t k);
chunk_index_t calculateIndex(glm::vec3 pos);
constexpr uint8_t CHUNK_STATE_GENERATED = 1;
constexpr uint8_t CHUNK_STATE_MESHED = 2;
constexpr uint8_t CHUNK_STATE_MESH_LOADED = 4;
@ -58,12 +65,15 @@ namespace Chunk
public:
GLuint VAO{0}, VBO{0}, extentsBuffer{0}, texinfoBuffer{0}, numVertices{0};
std::atomic<float> unload_timer{0};
chunk_index_t getIndex(){ return this->index; }
private:
glm::vec3 position{};
IntervalMap<Block> blocks{};
std::atomic_uint8_t state{0};
chunk_index_t index;
};
};

View File

@ -19,7 +19,7 @@
namespace chunkmanager
{
typedef oneapi::tbb::concurrent_hash_map<uint32_t, Chunk::Chunk*> ChunkTable;
typedef oneapi::tbb::concurrent_hash_map<chunk_index_t, Chunk::Chunk*> ChunkTable;
typedef std::pair<Chunk::Chunk*, uint8_t> ChunkPQEntry;
// The comparing function to use
struct compare_f {
@ -31,12 +31,11 @@ namespace chunkmanager
void init();
void blockpick(bool place);
uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k);
void stop();
void destroy();
oneapi::tbb::concurrent_queue<Chunk::Chunk*>& getDeleteVector();
std::array<std::array<int, 3>, chunks_volume>& getChunksIndices();
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume>& getChunksIndices();
Block getBlockAtPos(int x, int y, int z);
void update();
}

14
include/controls.hpp Normal file
View File

@ -0,0 +1,14 @@
#ifndef CONTROLS_H
#define CONTROLS_H
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define BLOCKPICK_TIMEOUT 0.15f
namespace controls{
void init();
void update(GLFWwindow* window);
};
#endif

View File

@ -13,7 +13,8 @@
#define RENDER_DISTANCE 16
extr Camera theCamera;
constexpr int chunks_volume = static_cast<int>(1.333333333333*M_PI*(RENDER_DISTANCE*RENDER_DISTANCE*RENDER_DISTANCE));
// the cube spans in both directions, to each axis has to be multiplied by 2. 2^3=8
constexpr int chunks_volume = 8*(RENDER_DISTANCE*RENDER_DISTANCE*RENDER_DISTANCE);
extr bool wireframe;
extr float sines[360];

View File

@ -3,6 +3,5 @@
void framebuffer_size_callback(GLFWwindow *, int, int);
void mouse_callback(GLFWwindow *window, double xpos, double ypos);
void processInput(GLFWwindow *);
#endif

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.2)
project(OpenGLTest)
set(SOURCE_FILES main.cpp chunk.cpp chunkmanager.cpp chunkmesher.cpp chunkgenerator.cpp
set(SOURCE_FILES main.cpp controls.cpp chunk.cpp chunkmanager.cpp chunkmesher.cpp chunkgenerator.cpp
debugwindow.cpp renderer.cpp spacefilling.cpp stb_image.cpp utils.cpp OpenSimplexNoise.cpp)
add_executable(OpenGLTest ${SOURCE_FILES})

View File

@ -15,11 +15,21 @@ namespace Chunk
return utils::coord3DTo1D(x, y, z, CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE);
}
chunk_index_t calculateIndex(glm::vec3 pos){
return calculateIndex(static_cast<chunk_intcoord_t>(pos.x), static_cast<chunk_intcoord_t>(pos.y),
static_cast<chunk_intcoord_t>(pos.z));
}
chunk_index_t calculateIndex(chunk_intcoord_t i, chunk_intcoord_t j, chunk_intcoord_t k){
return i | (j << 10) | (k << 20);
}
Chunk::Chunk(glm::vec3 pos)
{
this->position = pos;
this->setState(CHUNK_STATE_EMPTY, true);
this->setBlocks(0, CHUNK_MAX_INDEX, Block::AIR);
this->index = calculateIndex(pos);
}
Chunk ::~Chunk()

View File

@ -24,7 +24,7 @@ namespace chunkmanager
// Concurrent hash table of chunks
ChunkTable chunks;
// Chunk indices. Centered at (0,0,0), going in concentric sphere outwards
std::array<std::array<int, 3>, chunks_volume> chunks_indices;
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume> chunks_indices;
/* Multithreading */
std::atomic_bool should_run;
@ -37,47 +37,18 @@ namespace chunkmanager
int block_to_place{2};
// Init chunkmanager. Chunk indices and start threads
int chunks_volume_real;
void init(){
int index{0};
int rr{RENDER_DISTANCE * RENDER_DISTANCE};
int xp{0}, x{0};
bool b = true;
for(chunk_intcoord_t i = -RENDER_DISTANCE; i < RENDER_DISTANCE; i++)
for(chunk_intcoord_t j = -RENDER_DISTANCE; j < RENDER_DISTANCE; j++)
for(chunk_intcoord_t k = -RENDER_DISTANCE; k < RENDER_DISTANCE; k++){
// Iterate over all chunks, in concentric spheres starting fron the player and going outwards. Alternate left and right
// Eq. of the sphere (x - a)² + (y - b)² + (z - c)² = r²
while (xp <= RENDER_DISTANCE)
{
// Alternate between left and right
if (b) x = +xp;
else x = -xp;
// Step 1. At current x, get the corresponding y values (2nd degree equation, up to 2
// possible results)
int y1 = static_cast<int>(sqrt((rr) - x*x));
for (int y = -y1 + 1 ; y <= y1; y++)
{
// Step 2. At both y's, get the corresponding z values
int z1 = static_cast<int>(sqrt( rr - x*x - y*y));
for (int z = -z1 + 1; z <= z1; z++){
chunks_indices[index][0] = x;
chunks_indices[index][1] = y;
chunks_indices[index][2] = z;
chunks_indices[index][0]=i;
chunks_indices[index][1]=j;
chunks_indices[index][2]=k;
index++;
}
}
if (!b)
{
xp++;
b = true;
}
else b = false;
}
chunks_volume_real = index;
// Also init mesh data queue
for(int i = 0; i < 10; i++)
@ -95,8 +66,9 @@ namespace chunkmanager
void generate(){
while(should_run){
ChunkPQEntry entry;
while(chunks_to_generate_queue.try_pop(entry)) generateChunk(entry.first);
if(chunks_to_generate_queue.try_pop(entry)) generateChunk(entry.first);
}
chunks_to_generate_queue.clear();
}
// Method for chunk meshing thread(s)
@ -111,6 +83,7 @@ namespace chunkmanager
}
}
}
chunks_to_mesh_queue.clear();
}
oneapi::tbb::concurrent_queue<Chunk::Chunk*> chunks_todelete;
@ -121,24 +94,14 @@ namespace chunkmanager
int chunkY=static_cast<int>(theCamera.getAtomicPosY() / CHUNK_SIZE);
int chunkZ=static_cast<int>(theCamera.getAtomicPosZ() / CHUNK_SIZE);
debug::window::set_parameter("px", theCamera.getAtomicPosX());
debug::window::set_parameter("py", theCamera.getAtomicPosY());
debug::window::set_parameter("pz", theCamera.getAtomicPosZ());
debug::window::set_parameter("cx", chunkX);
debug::window::set_parameter("cy", chunkY);
debug::window::set_parameter("cz", chunkZ);
debug::window::set_parameter("lx", theCamera.getFront().x);
debug::window::set_parameter("ly", theCamera.getFront().y);
debug::window::set_parameter("lz", theCamera.getFront().z);
// Update other chunks
for(int i = 0; i < chunks_volume_real; i++) {
const uint16_t x = chunks_indices[i][0] + chunkX;
const uint16_t y = chunks_indices[i][1] + chunkY;
const uint16_t z = chunks_indices[i][2] + chunkZ;
const uint32_t index = calculateIndex(x, y, z);
for(int i = 0; i < chunks_volume; i++) {
const chunk_intcoord_t x = chunks_indices[i][0] + chunkX;
const chunk_intcoord_t y = chunks_indices[i][1] + chunkY;
const chunk_intcoord_t z = chunks_indices[i][2] + chunkZ;
if(x > 1023 || y > 1023 || z > 1023) continue;
if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue;
const chunk_index_t index = Chunk::calculateIndex(x, y, z);
ChunkTable::accessor a, a1, a2, b1, b2, c1, c2;
if(!chunks.find(a, index)) chunks.emplace(a, std::make_pair(index, new Chunk::Chunk(glm::vec3(x,y,z))));
@ -147,17 +110,17 @@ namespace chunkmanager
chunks_to_generate_queue.push(std::make_pair(a->second, GENERATION_PRIORITY_NORMAL));
}else if(! (a->second->getState(Chunk::CHUNK_STATE_MESHED))){
if(
(x + 1 > 1023 || (chunks.find(a1, calculateIndex(x+1, y, z)) &&
(x + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x+1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(x - 1 < 0|| (chunks.find(a1, calculateIndex(x-1, y, z)) &&
(x - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x-1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y+1, z)) &&
(y + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y+1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y - 1 < 0|| (chunks.find(a1, calculateIndex(x, y-1, z)) &&
(y - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y-1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y, z+1)) &&
(z + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y, z+1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z - 1 < 0|| (chunks.find(a1, calculateIndex(x, y, z-1)) &&
(z - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y, z-1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED)))
)
chunks_to_mesh_queue.push(std::make_pair(a->second, MESHING_PRIORITY_NORMAL));
@ -172,11 +135,11 @@ namespace chunkmanager
Chunk::Chunk* n;
nUnloaded = 0;
while(chunks_todelete.try_pop(n)){
int x = static_cast<uint16_t>(n->getPosition().x);
int y = static_cast<uint16_t>(n->getPosition().y);
int z = static_cast<uint16_t>(n->getPosition().z);
chunk_intcoord_t x = static_cast<chunk_intcoord_t>(n->getPosition().x);
chunk_intcoord_t y = static_cast<chunk_intcoord_t>(n->getPosition().y);
chunk_intcoord_t z = static_cast<chunk_intcoord_t>(n->getPosition().z);
if(x > 1023 || y > 1023 || z > 1023) continue;
const uint32_t index = calculateIndex(x, y, z);
const uint32_t index = Chunk::calculateIndex(x, y, z);
chunks.erase(index);
//delete n;
@ -185,20 +148,21 @@ namespace chunkmanager
}
}
// uint32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1023). There's actually two spare bits
uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k){
return i | (j << 10) | (k << 20);
}
oneapi::tbb::concurrent_queue<Chunk::Chunk*>& getDeleteVector(){ return chunks_todelete; }
std::array<std::array<int, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; }
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; }
void stop() {
should_run=false;
std::cout << "Waiting for secondary threads to shut down" << std::endl;
update_thread.join();
std::cout << "Update thread has terminated" << std::endl;
gen_thread.join();
std::cout << "Generation thread has terminated" << std::endl;
mesh_thread.join();
std::cout << "Meshing thread has terminated" << std::endl;
}
void destroy(){
/*for(const auto& n : chunks){
delete n.second;
@ -225,7 +189,7 @@ namespace chunkmanager
if(px < 0 || py < 0 || pz < 0 || px >= 1024 || py >= 1024 || pz >= 1024) continue;
ChunkTable::accessor a;
if(!chunks.find(a, calculateIndex(px, py, pz))) continue;
if(!chunks.find(a, Chunk::calculateIndex(px, py, pz))) continue;
Chunk::Chunk* c = a->second;
if(!c->getState(Chunk::CHUNK_STATE_GENERATED) || c->getState(Chunk::CHUNK_STATE_EMPTY)) continue;
@ -254,7 +218,7 @@ namespace chunkmanager
// exit early if the position is invalid or the chunk does not exist
if(px1 < 0 || py1 < 0 || pz1 < 0 || px1 >= 1024 || py1 >= 1024 || pz1 >= 1024) return;
ChunkTable::accessor a1;
if(!chunks.find(a1, calculateIndex(px1, py1, pz1))) return;
if(!chunks.find(a1, Chunk::calculateIndex(px1, py1, pz1))) return;
Chunk::Chunk* c1 = a1->second;
// place the new block (only stone for now)
c1->setBlock((Block)block_to_place, bx1, by1, bz1);
@ -276,17 +240,17 @@ namespace chunkmanager
// When necessary, also mesh nearby chunks
ChunkTable::accessor a1, a2, b1, b2, c1, c2;
if(bx == 0 && px - 1 >= 0 && chunks.find(a1, calculateIndex(px - 1, py, pz)))
if(bx == 0 && px - 1 >= 0 && chunks.find(a1, Chunk::calculateIndex(px - 1, py, pz)))
chunkmesher::mesh(a1->second);
if(by == 0 && py - 1 >= 0 && chunks.find(b1, calculateIndex(px, py - 1, pz)))
if(by == 0 && py - 1 >= 0 && chunks.find(b1, Chunk::calculateIndex(px, py - 1, pz)))
chunkmesher::mesh(b1->second);
if(bz == 0 && pz - 1 >= 0 && chunks.find(c1, calculateIndex(px, py, pz - 1)))
if(bz == 0 && pz - 1 >= 0 && chunks.find(c1, Chunk::calculateIndex(px, py, pz - 1)))
chunkmesher::mesh(c1->second);
if(bx == CHUNK_SIZE - 1 && px +1 < 1024 && chunks.find(a2, calculateIndex(px +1, py, pz)))
if(bx == CHUNK_SIZE - 1 && px +1 < 1024 && chunks.find(a2, Chunk::calculateIndex(px +1, py, pz)))
chunkmesher::mesh(a2->second);
if(by == CHUNK_SIZE - 1 && py +1 < 1024 && chunks.find(b2, calculateIndex(px, py +1, pz)))
if(by == CHUNK_SIZE - 1 && py +1 < 1024 && chunks.find(b2, Chunk::calculateIndex(px, py +1, pz)))
chunkmesher::mesh(b2->second);
if(bz == CHUNK_SIZE - 1 && pz +1 < 1024 && chunks.find(c2, calculateIndex(px, py, pz +1)))
if(bz == CHUNK_SIZE - 1 && pz +1 < 1024 && chunks.find(c2, Chunk::calculateIndex(px, py, pz +1)))
chunkmesher::mesh(c2->second);
debug::window::set_parameter("block_last_action", place);
@ -312,7 +276,7 @@ namespace chunkmanager
//std::cout << "Block at " << x << ", " << y << ", " << z << " is in chunk " << cx << "," << cy << "," << cz << "\n";
ChunkTable::accessor a;
if(!chunks.find(a, calculateIndex(cx, cy, cz))) return Block::NULLBLK;
if(!chunks.find(a, Chunk::calculateIndex(cx, cy, cz))) return Block::NULLBLK;
else {
int bx = x % CHUNK_SIZE;
int by = y % CHUNK_SIZE;
@ -324,3 +288,4 @@ namespace chunkmanager
}
}
};

39
src/controls.cpp Normal file
View File

@ -0,0 +1,39 @@
#include "controls.hpp"
#include "renderer.hpp"
namespace controls{
float lastBlockPick=0.0;
bool blockpick = false;
bool cursor = false;
void init(){
}
void update(GLFWwindow* window){
float current_time = glfwGetTime();
// Reset blockpicking timeout has passed
if(current_time - lastBlockPick > BLOCKPICK_TIMEOUT) blockpick = false;
// Reset blockpicking if both mouse buttons are released
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_RELEASE && glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_RELEASE) blockpick = false;
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS && !blockpick){
//chunkmanager::blockpick(false);
blockpick=true;
lastBlockPick=glfwGetTime();
}
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS && !blockpick){
//chunkmanager::blockpick(true);
blockpick=true;
lastBlockPick=glfwGetTime();
}
if(glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS) renderer::saveScreenshot();
if(glfwGetKey(window, GLFW_KEY_F3) == GLFW_PRESS) renderer::saveScreenshot(true);
if(glfwGetKey(window, GLFW_KEY_M) == GLFW_PRESS) {
cursor = !cursor;
glfwSetInputMode(window, GLFW_CURSOR, cursor ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
}
}
};

View File

@ -4,26 +4,23 @@
#include <iostream>
#include <thread>
#include "main.hpp"
#define GLOBALS_DEFINER
#include "globals.hpp"
#undef GLOBALS_DEFINER
#include "chunkmanager.hpp"
#include "main.hpp"
#include "controls.hpp"
#include "debugwindow.hpp"
#include "renderer.hpp"
#include "spacefilling.hpp"
#include "shader.hpp"
#include "spacefilling.hpp"
float deltaTime = 0.0f; // Time between current frame and last frame
float lastFrame = 0.0f; // Time of last frame
float lastFPSFrame = 0.0f;
int frames = 0;
float lastBlockPick=0.0;
bool blockpick = false;
bool cursor = false;
int main()
{
@ -70,6 +67,7 @@ int main()
}
SpaceFilling::initLUT();
controls::init();
chunkmanager::init();
debug::window::init(window);
renderer::init(window);
@ -94,14 +92,24 @@ int main()
glClearColor(0.431f, 0.694f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Input processing
processInput(window);
// Input handling
// Only close event is handles by main
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
// the rest of input processing is handled by controls.cpp
controls::update(window);
// Camera
theCamera.update(window, deltaTime);
// Reset blockping timeout if 200ms have passed
if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false;
debug::window::set_parameter("px", theCamera.getPos().x);
debug::window::set_parameter("py", theCamera.getPos().y);
debug::window::set_parameter("pz", theCamera.getPos().z);
debug::window::set_parameter("cx", (int)(theCamera.getPos().x / CHUNK_SIZE));
debug::window::set_parameter("cy", (int)(theCamera.getPos().y / CHUNK_SIZE));
debug::window::set_parameter("cz", (int)(theCamera.getPos().z / CHUNK_SIZE));
debug::window::set_parameter("lx", theCamera.getFront().x);
debug::window::set_parameter("ly", theCamera.getFront().y);
debug::window::set_parameter("lz", theCamera.getFront().z);
// Render pass
renderer::render();
@ -134,33 +142,3 @@ void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{
theCamera.mouseCallback(window, xpos, ypos);
}
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS && !blockpick){
chunkmanager::blockpick(false);
blockpick=true;
lastBlockPick=glfwGetTime();
}
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS && !blockpick){
chunkmanager::blockpick(true);
blockpick=true;
lastBlockPick=glfwGetTime();
}
// Reset blockpicking if enough time has passed
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_RELEASE && glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_RELEASE) blockpick = false;
if(glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS) renderer::saveScreenshot();
if(glfwGetKey(window, GLFW_KEY_F3) == GLFW_PRESS) renderer::saveScreenshot(true);
if(glfwGetKey(window, GLFW_KEY_M) == GLFW_PRESS) {
cursor = !cursor;
glfwSetInputMode(window, GLFW_CURSOR, cursor ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
}
}

View File

@ -149,6 +149,7 @@ namespace renderer{
if(dist <= static_cast<float>(RENDER_DISTANCE)){
if(!c->getState(Chunk::CHUNK_STATE_MESH_LOADED)) continue;
if(c->numVertices > 0){
// Increase total vertex count
vertices += c->numVertices;
@ -178,8 +179,6 @@ namespace renderer{
}
if (!out)
{
if(c->numVertices > 0)
{
theShader->setMat4("model", model);
theShader->setMat4("view", theCamera.getView());