diff --git a/include/chunkmanager.hpp b/include/chunkmanager.hpp index 9aa2d19..d5fd627 100644 --- a/include/chunkmanager.hpp +++ b/include/chunkmanager.hpp @@ -1,14 +1,14 @@ #ifndef CHUNKMANAGER_H #define CHUNKMANAGER_H -#include "chunk.hpp" - #include #include #include #include +#include "chunk.hpp" #include "globals.hpp" +#include "worldupdatemessage.h" // Seconds to be passed outside of render distance for a chunk to be destroyed #define UNLOAD_TIMEOUT 10 @@ -30,13 +30,12 @@ namespace chunkmanager typedef oneapi::tbb::concurrent_priority_queue ChunkPriorityQueue; void init(); - //void blockpick(bool place); - + void update(); void stop(); void destroy(); + WorldUpdateMsgQueue& getWorldUpdateQueue(); std::array, chunks_volume>& getChunksIndices(); Block getBlockAtPos(int x, int y, int z); - void update(); } #endif diff --git a/include/controls.hpp b/include/controls.hpp index 490dbbd..6703f25 100644 --- a/include/controls.hpp +++ b/include/controls.hpp @@ -4,7 +4,7 @@ #include #include -#define BLOCKPICK_TIMEOUT 0.15f +#define BLOCKPICK_TIMEOUT 0.1f namespace controls{ void init(); diff --git a/include/worldupdatemessage.h b/include/worldupdatemessage.h new file mode 100644 index 0000000..200f8d9 --- /dev/null +++ b/include/worldupdatemessage.h @@ -0,0 +1,21 @@ +#ifndef WORLD_UPDATE_MSG_H +#define WORLD_UPDATE_MSG_H + +#include +#include + +enum class WorldUpdateMsgType{ + BLOCKPICK_PLACE, + BLOCKPICK_BREAK +}; + +typedef struct WorldUpdateMsg{ + WorldUpdateMsgType msg_type; + glm::vec3 cameraPos; + glm::vec3 cameraFront; + float time; +} WorldUpdateMsg; + +typedef oneapi::tbb::concurrent_queue WorldUpdateMsgQueue; + +#endif diff --git a/src/chunkmanager.cpp b/src/chunkmanager.cpp index 1addbce..5983d5b 100644 --- a/src/chunkmanager.cpp +++ b/src/chunkmanager.cpp @@ -20,14 +20,20 @@ namespace chunkmanager { + void blockpick(WorldUpdateMsg& msg); // There's no need of passing by value again (check + // controls.cpp) void generate(); void mesh(); + /* Chunk holding data structures */ // Concurrent hash table of chunks ChunkTable chunks; // Chunk indices. Centered at (0,0,0), going in concentric sphere outwards std::array, chunks_volume> chunks_indices; + /* World Update messaging data structure */ + WorldUpdateMsgQueue WorldUpdateQueue; + /* Multithreading */ std::atomic_bool should_run; std::thread gen_thread, mesh_thread, update_thread; @@ -37,8 +43,11 @@ namespace chunkmanager // Queue of chunks to be meshed ChunkPriorityQueue chunks_to_mesh_queue; + /* Block picking */ int block_to_place{2}; + WorldUpdateMsgQueue& getWorldUpdateQueue(){ return WorldUpdateQueue; } + // Init chunkmanager. Chunk indices and start threads void init(){ int index{0}; @@ -97,15 +106,27 @@ namespace chunkmanager std::atomic_int chunkY=static_cast(theCamera.getAtomicPosY() / CHUNK_SIZE); std::atomic_int chunkZ=static_cast(theCamera.getAtomicPosZ() / CHUNK_SIZE); + /* Process update messages before anything happens */ + WorldUpdateMsg msg; + while(WorldUpdateQueue.try_pop(msg)){ + switch(msg.msg_type){ + case WorldUpdateMsgType::BLOCKPICK_BREAK: + case WorldUpdateMsgType::BLOCKPICK_PLACE: + blockpick(msg); + break; + } + } + + /* Delete old chunks */ // In my head it makes sense to first delete old chunks, then create new ones // I think it's easier for memory allocator to re-use the memory that was freed just // before, but this isn't backed be any evidence and I might be wrong. Anyway this way // works fine so I'm gonna keep it. - int i; + chunk_index_t i; ChunkTable::accessor a; while(chunks_todelete.try_pop(i)){ - const int index = i; + const chunk_index_t index = i; if(chunks.find(a, index)){ Chunk::Chunk* c = a->second; // Use the accessor to erase the element @@ -255,13 +276,12 @@ namespace chunkmanager chunks.clear(); } - /*void blockpick(bool place){ + void blockpick(WorldUpdateMsg& msg){ // cast a ray from the camera in the direction pointed by the camera itself - glm::vec3 pos = glm::vec3(theCamera.getAtomicPosX(), theCamera.getAtomicPosY(), - theCamera.getAtomicPosZ()); + glm::vec3 pos = msg.cameraPos; for(float t = 0.0; t <= 10.0; t += 0.5){ // traverse the ray a block at the time - pos = theCamera.getPos() + t * theCamera.getFront(); + pos = msg.cameraPos + t*msg.cameraFront; // get which chunk and block the ray is at int px = ((int)(pos.x))/CHUNK_SIZE; @@ -286,7 +306,7 @@ namespace chunkmanager if(b != Block::AIR){ // if placing a new block - if(place){ + if(msg.msg_type == WorldUpdateMsgType::BLOCKPICK_PLACE){ // Go half a block backwards on the ray, to check the block where the ray was // coming from // Doing this and not using normal adds the unexpected (and unwanted) ability to @@ -313,8 +333,9 @@ namespace chunkmanager chunks_to_mesh_queue.push(std::make_pair(c1, MESHING_PRIORITY_PLAYER_EDIT)); chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_PLAYER_EDIT)); - debug::window::set_parameter("block_last_action", place); - debug::window::set_parameter("block_last_action_block_type", (int)(Block::STONE)); + debug::window::set_parameter("block_last_action", (int)msg.msg_type); + debug::window::set_parameter("block_last_action_block_type", + (int)(block_to_place)); debug::window::set_parameter("block_last_action_x", px1*CHUNK_SIZE + bx1); debug::window::set_parameter("block_last_action_y", px1*CHUNK_SIZE + by1); debug::window::set_parameter("block_last_action_z", px1*CHUNK_SIZE + bz1); @@ -339,7 +360,7 @@ namespace chunkmanager 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); + debug::window::set_parameter("block_last_action", (int)msg.msg_type); debug::window::set_parameter("block_last_action_block_type", (int) (Block::AIR)); debug::window::set_parameter("block_last_action_x", px*CHUNK_SIZE + bx); debug::window::set_parameter("block_last_action_y", py*CHUNK_SIZE + by); @@ -349,7 +370,7 @@ namespace chunkmanager break; } } - }*/ + } Block getBlockAtPos(int x, int y, int z){ if(x < 0 || y < 0 || z < 0) return Block::NULLBLK; diff --git a/src/controls.cpp b/src/controls.cpp index 235ed96..2b2b82d 100644 --- a/src/controls.cpp +++ b/src/controls.cpp @@ -1,4 +1,8 @@ #include "controls.hpp" + +#include "camera.hpp" +#include "chunkmanager.hpp" +#include "globals.hpp" #include "renderer.hpp" namespace controls{ @@ -12,23 +16,39 @@ namespace controls{ void update(GLFWwindow* window){ float current_time = glfwGetTime(); - // Reset blockpicking timeout has passed + /* BlockPicking */ + // Reset blockpicking if enough time 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; + // Process block picking if a mouse button is pressed + if((glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS || + glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2 == GLFW_PRESS)) && !blockpick){ - 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(); + // Start timeout for next block pick action + blockpick = true; + lastBlockPick = current_time; + + // Construct the message to send to chunkmanager + + // WorldUpdateMsg is allocated on the stack + // unlike ChunkMeshData, the fields of WorldUpdateMsg are few and light, so there's no + // problem in passing them by value each time. + // It also has the advantage of having less memory to manage, since I'm not allocating + // anything on the heap + + WorldUpdateMsg msg{}; + msg.cameraPos = theCamera.getPos(); + msg.cameraFront = theCamera.getFront(); + msg.time = current_time; + msg.msg_type = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS ? + WorldUpdateMsgType::BLOCKPICK_PLACE : WorldUpdateMsgType::BLOCKPICK_BREAK; + + // Send to chunk manager + chunkmanager::getWorldUpdateQueue().push(msg); } + /* SCREENSHOTS */ 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) { diff --git a/src/main.cpp b/src/main.cpp index 7bc51f9..f3f16ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,6 +1,8 @@ #include #include +#include "main.hpp" + #include #include @@ -98,6 +100,8 @@ int main() if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); // the rest of input processing is handled by controls.cpp + + // Input processing controls::update(window); // Camera