Compare commits

..

20 Commits

Author SHA1 Message Date
EmaMaker e593696af3 restore seamless chunk borders 2023-10-03 20:47:53 +02:00
EmaMaker df933bebdd refactor message-sending system between main and update thread
restores blockpicking with new multithread system
2023-10-03 18:43:33 +02:00
EmaMaker 950e8c68ab separate input handling from main 2023-10-03 18:16:45 +02:00
EmaMaker 4c46811a25 delete meshes relative to deleted chunks 2023-10-03 18:04:06 +02:00
EmaMaker bc3c5587f8 calculateIndex belongs to Chunk namespace 2023-10-03 15:42:01 +02:00
EmaMaker b95ea49c93 gpu printed in debug window 2023-10-03 15:35:56 +02:00
EmaMaker 90fabf73ee add to queues only if completely free 2023-10-03 15:35:34 +02:00
EmaMaker 96bf3c651d player debug variables updated in main 2023-10-03 15:32:49 +02:00
EmaMaker 170deaccf7 eliminate unused variables+stricter "free" condition 2023-10-03 15:28:21 +02:00
EmaMaker da6ceeb605 set generation/meshing queue flags before pushing to queues 2023-10-03 12:31:02 +02:00
EmaMaker ca44c0f284 initial update/mesh/render thread communication refactor
Quirks to iron out:
- Some stuff is rendered wrong/not rendered (chunks missing)
- Still no way to delete stuff from ChunksToRender
2023-10-03 12:27:01 +02:00
EmaMaker a05d019c69 use concurrent_hash_map and parallel_for for chunk update
memory VIRT is higher, RES is the same. memory usage stabilizes after a while
2023-10-01 19:45:51 +02:00
EmaMaker 49edf2de85 move chunk update back to secondary thread 2023-10-01 18:12:14 +02:00
EmaMaker 8a3c963721 solve some memory leaks regarding thread communication queues
also temporarly move chunk update in primary thread
2023-10-01 18:11:54 +02:00
EmaMaker 1ec529fae5 fallimentary attempt at fixing memory management with onetbb 2023-09-30 15:09:26 +02:00
EmaMaker 00cdd22e10 renderer: try frustum culling only if chunk has vertices 2023-09-22 09:06:48 +02:00
EmaMaker a65fc44069 renderer: correct distance check 2023-09-21 17:13:21 +02:00
EmaMaker 3f02ca91db chunkmgr: proper use of int instead of uint in chunk indices 2023-09-21 16:58:18 +02:00
EmaMaker 01f1f9da16 squashed commits
erase chunk buffers only after marking as to be deleted

update chunks around player with a cube instead of concentric spheres

only generate chunks around player, update all chunks use onetbb::parallel_for
2023-09-21 15:49:50 +02:00
EmaMaker 5f396a9801 camera: set atomic position at startup
this avoids the first few chunk update loops recognizing the camera as being positioned at (0,0,0), which in turns avoids wastefully generating chunks out of view at startup
2023-09-21 15:43:51 +02:00
23 changed files with 445 additions and 435 deletions

View File

@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.2)
project(cmake-project-template) project(cmake-project-template)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3") #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3")
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -g")
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}) set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})

View File

@ -8,6 +8,9 @@
#include <atomic> #include <atomic>
#include "chunk.hpp"
#include "debugwindow.hpp"
class Camera class Camera
{ {
@ -52,6 +55,16 @@ public:
cameraFront = glm::normalize(direction); cameraFront = glm::normalize(direction);
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
debug::window::set_parameter("px", cameraPos.x);
debug::window::set_parameter("py", cameraPos.y);
debug::window::set_parameter("pz", cameraPos.z);
debug::window::set_parameter("cx", (int)(cameraPos.x / CHUNK_SIZE));
debug::window::set_parameter("cy", (int)(cameraPos.y / CHUNK_SIZE));
debug::window::set_parameter("cz", (int)(cameraPos.z / CHUNK_SIZE));
debug::window::set_parameter("lx", cameraFront.x);
debug::window::set_parameter("ly", cameraFront.y);
debug::window::set_parameter("lz", cameraFront.z);
} }
void viewPortCallBack(GLFWwindow *window, int width, int height) void viewPortCallBack(GLFWwindow *window, int width, int height)
@ -116,7 +129,7 @@ public:
private: private:
glm::vec3 cameraPos = glm::vec3(512.0, 80.0f, 512.0f); glm::vec3 cameraPos = glm::vec3(512.0, 256.0f, 512.0f);
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.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 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 direction = glm::vec3(0.0f); glm::vec3 direction = glm::vec3(0.0f);

View File

@ -20,47 +20,38 @@
#define CHUNK_VOLUME (CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE) #define CHUNK_VOLUME (CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE)
#define CHUNK_MAX_INDEX (CHUNK_VOLUME - 1) #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;
typedef uint16_t chunk_state_t;
namespace Chunk namespace Chunk
{ {
chunk_index_t calculateIndex(chunk_intcoord_t i, chunk_intcoord_t j, chunk_intcoord_t k); constexpr uint16_t CHUNK_STATE_GENERATED = 1;
chunk_index_t calculateIndex(glm::vec3 pos); constexpr uint16_t CHUNK_STATE_MESHED = 2;
constexpr uint16_t CHUNK_STATE_OUTOFVISION = 4;
constexpr chunk_state_t CHUNK_STATE_GENERATED = 1; constexpr uint16_t CHUNK_STATE_UNLOADED = 8;
constexpr chunk_state_t CHUNK_STATE_MESHED = 2; constexpr uint16_t CHUNK_STATE_EMPTY = 16;
constexpr chunk_state_t CHUNK_STATE_MESH_LOADED = 4; constexpr uint16_t CHUNK_STATE_IN_GENERATION_QUEUE = 32;
constexpr chunk_state_t CHUNK_STATE_LOADED = 8; constexpr uint16_t CHUNK_STATE_IN_MESHING_QUEUE = 64;
constexpr chunk_state_t CHUNK_STATE_OUTOFVISION = 16; constexpr uint16_t CHUNK_STATE_IN_DELETING_QUEUE = 128;
constexpr chunk_state_t CHUNK_STATE_UNLOADED = 32;
constexpr chunk_state_t CHUNK_STATE_EMPTY = 64;
constexpr chunk_state_t CHUNK_STATE_IN_GENERATION_QUEUE = 128;
constexpr chunk_state_t CHUNK_STATE_IN_MESHING_QUEUE = 256;
constexpr chunk_state_t CHUNK_STATE_IN_DELETING_QUEUE = 512;
int coord3DTo1D(int x, int y, int z); int coord3DTo1D(int x, int y, int z);
int32_t calculateIndex(int16_t x, int16_t y, int16_t z);
int32_t calculateIndex(glm::vec3 pos);
class Chunk class Chunk
{ {
public: public:
Chunk(glm::vec3 pos = glm::vec3(0.0f)); // a default value for the argument satisfies the need for a default constructor when using the type in an unordered_map (i.e. in chunkmanager) Chunk(glm::vec3 pos = glm::vec3(0.0f)); // a default value for the argument satisfies the need for a default constructor when using the type in an unordered_map (i.e. in chunkmanager)
~Chunk();
public: public:
void createBuffers();
void deleteBuffers();
glm::vec3 getPosition() { return this->position; } glm::vec3 getPosition() { return this->position; }
void setState(chunk_state_t nstate, bool value); void setState(uint16_t nstate, bool value);
bool getState(chunk_state_t n) { return (this->state & n) == n; } uint16_t getTotalState() { return this->state; }
bool isFree(){ return !( bool getState(uint16_t n) { return (this->state & n) == n; }
this->getState(CHUNK_STATE_IN_GENERATION_QUEUE) || bool isFree(){ return !(getState(CHUNK_STATE_IN_GENERATION_QUEUE) ||
this->getState(CHUNK_STATE_IN_MESHING_QUEUE) || getState(CHUNK_STATE_IN_MESHING_QUEUE)); }
this->getState(CHUNK_STATE_IN_DELETING_QUEUE)
); }
chunk_state_t getTotalState() { return this->state; }
void setBlock(Block b, int x, int y, int z); void setBlock(Block b, int x, int y, int z);
void setBlocks(int start, int end, Block b); void setBlocks(int start, int end, Block b);
@ -69,15 +60,17 @@ namespace Chunk
std::unique_ptr<Block[]> getBlocksArray(int* len) { return (this->blocks.toArray(len)); } std::unique_ptr<Block[]> getBlocksArray(int* len) { return (this->blocks.toArray(len)); }
public: public:
GLuint VAO{0}, VBO{0}, extentsBuffer{0}, texinfoBuffer{0}, numVertices{0};
std::atomic<float> unload_timer{0}; std::atomic<float> unload_timer{0};
chunk_index_t getIndex(){ return this->index; } // uint32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1023). There's actually two spare bits
int32_t getIndex(){ return index; }
private: private:
glm::vec3 position{}; glm::vec3 position{};
IntervalMap<Block> blocks{}; IntervalMap<Block> blocks{};
std::atomic<chunk_state_t> state{0}; int32_t index;
chunk_index_t index; std::atomic_uint16_t state{0};
}; };
}; };

View File

@ -4,6 +4,8 @@
#include <oneapi/tbb/concurrent_hash_map.h> #include <oneapi/tbb/concurrent_hash_map.h>
#include <oneapi/tbb/concurrent_queue.h> #include <oneapi/tbb/concurrent_queue.h>
#include <oneapi/tbb/concurrent_priority_queue.h> #include <oneapi/tbb/concurrent_priority_queue.h>
#include <unordered_map>
#include <thread> #include <thread>
#include "chunk.hpp" #include "chunk.hpp"
@ -11,7 +13,7 @@
#include "worldupdatemessage.h" #include "worldupdatemessage.h"
// Seconds to be passed outside of render distance for a chunk to be destroyed // Seconds to be passed outside of render distance for a chunk to be destroyed
#define UNLOAD_TIMEOUT 10 #define UNLOAD_TIMEOUT 5
#define MESHING_PRIORITY_NORMAL 0 #define MESHING_PRIORITY_NORMAL 0
#define MESHING_PRIORITY_PLAYER_EDIT 10 #define MESHING_PRIORITY_PLAYER_EDIT 10
@ -19,7 +21,10 @@
namespace chunkmanager namespace chunkmanager
{ {
typedef oneapi::tbb::concurrent_hash_map<chunk_index_t, Chunk::Chunk*> ChunkTable; typedef oneapi::tbb::concurrent_hash_map<int32_t, Chunk::Chunk*> ChunkTable;
typedef oneapi::tbb::concurrent_queue<int> IntQueue;
//typedef std::unordered_map<int32_t, Chunk::Chunk*> ChunkTable;
typedef std::pair<Chunk::Chunk*, uint8_t> ChunkPQEntry; typedef std::pair<Chunk::Chunk*, uint8_t> ChunkPQEntry;
// The comparing function to use // The comparing function to use
struct compare_f { struct compare_f {
@ -34,7 +39,7 @@ namespace chunkmanager
void stop(); void stop();
void destroy(); void destroy();
WorldUpdateMsgQueue& getWorldUpdateQueue(); WorldUpdateMsgQueue& getWorldUpdateQueue();
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume>& getChunksIndices(); std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices();
Block getBlockAtPos(int x, int y, int z); Block getBlockAtPos(int x, int y, int z);
} }

View File

@ -1,15 +1,12 @@
#ifndef CHUNK_MESH_DATA_H #ifndef CHUNK_MESH_DATA_H
#define CHUNK_MESH_DATA_H #define CHUNK_MESH_DATA_H
#include <oneapi/tbb/concurrent_queue.h>
#include "chunk.hpp"
enum class ChunkMeshDataType{ enum class ChunkMeshDataType{
MESH_UPDATE MESH_UPDATE
}; };
typedef struct ChunkMeshData{ typedef struct ChunkMeshData{
chunk_index_t index; int32_t index;
glm::vec3 position; glm::vec3 position;
int num_vertices = 0; int num_vertices = 0;
@ -33,4 +30,3 @@ typedef oneapi::tbb::concurrent_queue<ChunkMeshData*> ChunkMeshDataQueue;
#endif #endif

View File

@ -15,15 +15,6 @@
#include "shader.hpp" #include "shader.hpp"
namespace chunkmesher{ namespace chunkmesher{
struct MeshData{
Chunk::Chunk* chunk;
GLuint numVertices{0};
std::vector<GLfloat> vertices;
std::vector<GLfloat> extents;
std::vector<GLfloat> texinfo;
};
ChunkMeshDataQueue& getMeshDataQueue(); ChunkMeshDataQueue& getMeshDataQueue();
void init(); void init();
void mesh(Chunk::Chunk* chunk); void mesh(Chunk::Chunk* chunk);
@ -31,4 +22,3 @@ namespace chunkmesher{
#endif #endif

View File

@ -4,11 +4,15 @@
#include <glad/glad.h> #include <glad/glad.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "worldupdatemessage.h"
#define BLOCKPICK_TIMEOUT 0.1f #define BLOCKPICK_TIMEOUT 0.1f
namespace controls{ namespace controls{
void init(); void init();
void update(GLFWwindow* window); void update(GLFWwindow* window);
WorldUpdateMsgQueue& getWorldUpdateQueue();
}; };
#endif #endif

View File

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

View File

@ -1,9 +1,6 @@
#ifndef MAIN_H #ifndef MAIN_H
#define MAIN_H #define MAIN_H
#include <glad/glad.h>
#include <GLFW/glfw3.h>
void framebuffer_size_callback(GLFWwindow *, int, int); void framebuffer_size_callback(GLFWwindow *, int, int);
void mouse_callback(GLFWwindow *window, double xpos, double ypos); void mouse_callback(GLFWwindow *window, double xpos, double ypos);

View File

@ -1,8 +1,7 @@
#ifndef RENDERER_H #ifndef RENDERER_H
#define RENDERER_H #define RENDERER_H
#include <oneapi/tbb/concurrent_unordered_set.h> #include <glm/glm.hpp>
#include <oneapi/tbb/concurrent_queue.h>
#include "chunk.hpp" #include "chunk.hpp"
#include "chunkmesher.hpp" #include "chunkmesher.hpp"
@ -11,7 +10,7 @@
namespace renderer{ namespace renderer{
typedef struct RenderInfo { typedef struct RenderInfo {
chunk_index_t index; int32_t index;
int num_vertices; int num_vertices;
glm::vec3 position; glm::vec3 position;
bool buffers_allocated=false; bool buffers_allocated=false;
@ -54,8 +53,6 @@ namespace renderer{
ChunkMeshDataQueue& getMeshDataQueue(); ChunkMeshDataQueue& getMeshDataQueue();
IndexQueue& getDeleteIndexQueue(); IndexQueue& getDeleteIndexQueue();
}; };
#endif #endif

View File

@ -4,8 +4,6 @@
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <oneapi/tbb/concurrent_queue.h> #include <oneapi/tbb/concurrent_queue.h>
#include "block.hpp"
enum class WorldUpdateMsgType{ enum class WorldUpdateMsgType{
BLOCKPICK_PLACE, BLOCKPICK_PLACE,
BLOCKPICK_BREAK BLOCKPICK_BREAK
@ -16,7 +14,6 @@ typedef struct WorldUpdateMsg{
glm::vec3 cameraPos; glm::vec3 cameraPos;
glm::vec3 cameraFront; glm::vec3 cameraFront;
float time; float time;
Block block;
} WorldUpdateMsg; } WorldUpdateMsg;
typedef oneapi::tbb::concurrent_queue<WorldUpdateMsg> WorldUpdateMsgQueue; typedef oneapi::tbb::concurrent_queue<WorldUpdateMsg> WorldUpdateMsgQueue;

143
intervalmap.hpp Normal file
View File

@ -0,0 +1,143 @@
#ifndef INTERVALMAP_H
#define INTERVALMAP_H
#include <iostream>
#include <iterator> //std::prev
#include <limits> // std::numeric_limits
#include <memory> //std::shared_ptr
#include <map>
template <typename K, typename V>
class IntervalMap
{
public:
~IntervalMap(){
treemap.clear();
}
void insert(K start, K end, V value)
{
if (start >= end)
return;
// The entry just before the end index
auto tmp = treemap.upper_bound(end);
auto end_prev_entry = tmp == treemap.end() ? tmp : --tmp;
auto added_end = treemap.end();
// If it doesn't exist (empty map)
if(end_prev_entry == treemap.end()){
V v{};
added_end = treemap.insert_or_assign(treemap.begin(), end, v);
}
// Or if it has value different from the insertion
else if(end_prev_entry->second != value)
// Add it back at the end
added_end = treemap.insert_or_assign(end_prev_entry, end, end_prev_entry->second);
// The entry just before the start index
tmp = treemap.upper_bound(start);
auto start_prev_entry = tmp == treemap.end() ? tmp : --tmp;
auto added_start = treemap.end();
// If it has value different from the insertion
if(start_prev_entry == treemap.end() || start_prev_entry->second != value)
// Add the start node of the insertion
added_start = treemap.insert_or_assign(start_prev_entry, start, value);
// Delete everything else inside
// map.erase(start, end) deletes every node with key in the range [start, end)
// The key to start deleting from is the key after the start node we added
// (We don't want to delete a node we just added)
auto del_start = added_start == treemap.end() ? std::next(start_prev_entry) : std::next(added_start);
auto del_end = added_end == treemap.end() ? end_prev_entry : added_end;
auto del_end_next = std::next(del_end);
// If the node after the end is of the same type of the end, delete it
// We cannot just expand del_end (++del_end) otherwise interval limits get messed up
if(del_end != treemap.end() && del_end_next != treemap.end() && del_end->second ==
del_end_next->second) treemap.erase(del_end_next);
// Delete everything in between
if(del_start != treemap.end() && (del_end==treemap.end() || del_start->first <
del_end->first)) treemap.erase(del_start, del_end);
}
void remove(K at)
{
treemap.erase(at);
}
auto at(K index)
{
const auto tmp = treemap.lower_bound(index);
const auto r = tmp != treemap.begin() && tmp->first!=index ? std::prev(tmp) : tmp;
return r;
}
void print()
{
for (auto i = treemap.begin(); i != treemap.end(); i++)
std::cout << i->first << ": " << (int)(i->second) << "\n";
if(!treemap.empty()) std::cout << "end key: " << std::prev(treemap.end())->first << "\n";
}
std::unique_ptr<V[]> toArray(int *length)
{
if (treemap.empty())
{
*length = 0;
return nullptr;
}
const auto &end = std::prev(treemap.end());
*length = end->first;
if(*length == 0) return nullptr;
std::unique_ptr<V[]> arr(new V[*length]);
auto start = treemap.begin();
for (auto i = std::next(treemap.begin()); i != treemap.end(); i++)
{
for (int k = start->first; k < i->first; k++)
arr[k] = start->second;
start = i;
}
return arr;
}
void fromArray(V *arr, int length)
{
treemap.clear();
if (length == 0)
return;
V prev = arr[0];
unsigned int prev_start = 0;
for (unsigned int i = 1; i < length; i++)
{
if (prev != arr[i])
{
insert(prev_start, i, prev);
prev_start = i;
}
prev = arr[i];
}
insert(prev_start, length, prev);
}
auto begin(){
return treemap.begin();
}
auto end(){
return treemap.end();
}
private:
std::map<K, V> treemap{};
};
#endif

View File

@ -26,30 +26,29 @@ void main(){
EmitVertex(); EmitVertex();
if(gs_in[0].Extents.x == 0){ if(gs_in[0].Extents.x == 0){
TexCoord = vec3(0.0, gs_in[0].Extents.z, gs_in[0].BlockType);
TexCoord = vec3(gs_in[0].Extents.z, 0.0, gs_in[0].BlockType);
gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, gs_in[0].Extents.z, 0.0); gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, gs_in[0].Extents.z, 0.0);
FragPos = vec3(gl_Position); FragPos = vec3(gl_Position);
gl_Position = projection * view * gl_Position; gl_Position = projection * view * gl_Position;
EmitVertex(); EmitVertex();
TexCoord = vec3(0.0, gs_in[0].Extents.y, gs_in[0].BlockType); TexCoord = vec3(gs_in[0].Extents.y, 0.0, gs_in[0].BlockType);
gl_Position = gl_in[0].gl_Position + vec4(0.0, gs_in[0].Extents.y, 0.0, 0.0); gl_Position = gl_in[0].gl_Position + vec4(0.0, gs_in[0].Extents.y, 0.0, 0.0);
FragPos = vec3(gl_Position); FragPos = vec3(gl_Position);
gl_Position = projection * view * gl_Position; gl_Position = projection * view * gl_Position;
EmitVertex(); EmitVertex();
TexCoord = vec3(gs_in[0].Extents.z, gs_in[0].Extents.y, gs_in[0].BlockType); TexCoord = vec3(gs_in[0].Extents.y, gs_in[0].Extents.z, gs_in[0].BlockType);
} }
else if(gs_in[0].Extents.y == 0){ else if(gs_in[0].Extents.y == 0){
TexCoord = vec3(gs_in[0].Extents.x, 0.0, gs_in[0].BlockType); TexCoord = vec3(0.0, gs_in[0].Extents.z, gs_in[0].BlockType);
gl_Position = gl_in[0].gl_Position + vec4(gs_in[0].Extents.x, 0.0, 0.0, 0.0); gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, gs_in[0].Extents.z, 0.0);
FragPos = vec3(gl_Position); FragPos = vec3(gl_Position);
gl_Position = projection * view * gl_Position; gl_Position = projection * view * gl_Position;
EmitVertex(); EmitVertex();
TexCoord = vec3(0.0, gs_in[0].Extents.z, gs_in[0].BlockType); TexCoord = vec3(gs_in[0].Extents.x, 0.0, gs_in[0].BlockType);
gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, gs_in[0].Extents.z, 0.0); gl_Position = gl_in[0].gl_Position + vec4(gs_in[0].Extents.x, 0.0, 0.0, 0.0);
FragPos = vec3(gl_Position); FragPos = vec3(gl_Position);
gl_Position = projection * view * gl_Position; gl_Position = projection * view * gl_Position;
EmitVertex(); EmitVertex();

View File

@ -7,4 +7,4 @@ set(SOURCE_FILES main.cpp controls.cpp chunk.cpp chunkmanager.cpp chunkmesher.cp
add_executable(OpenGLTest ${SOURCE_FILES}) add_executable(OpenGLTest ${SOURCE_FILES})
target_link_libraries(OpenGLTest glfw tbb glad glm imgui) target_link_libraries(OpenGLTest glfw tbb glad glm imgui)
install(TARGETS OpenGLTest DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR}) install(TARGETS OpenGLTest DESTINATION)

View File

@ -15,13 +15,12 @@ namespace Chunk
return utils::coord3DTo1D(x, y, z, CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE); return utils::coord3DTo1D(x, y, z, CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE);
} }
chunk_index_t calculateIndex(glm::vec3 pos){ int32_t calculateIndex(glm::vec3 pos){
return calculateIndex(static_cast<chunk_intcoord_t>(pos.x), static_cast<chunk_intcoord_t>(pos.y), return calculateIndex(static_cast<int16_t>(pos.x), static_cast<int16_t>(pos.y),
static_cast<chunk_intcoord_t>(pos.z)); static_cast<int16_t>(pos.z));
} }
int32_t calculateIndex(int16_t x, int16_t y, int16_t z){
chunk_index_t calculateIndex(chunk_intcoord_t i, chunk_intcoord_t j, chunk_intcoord_t k){ return x | (y << 10) | (z << 20);
return i | (j << 10) | (k << 20);
} }
Chunk::Chunk(glm::vec3 pos) Chunk::Chunk(glm::vec3 pos)
@ -32,10 +31,6 @@ namespace Chunk
this->index = calculateIndex(pos); this->index = calculateIndex(pos);
} }
Chunk ::~Chunk()
{
}
Block Chunk::getBlock(int x, int y, int z) Block Chunk::getBlock(int x, int y, int z)
{ {
if(x < 0 || y < 0 || z < 0 || x > CHUNK_SIZE -1 || y > CHUNK_SIZE -1 || z > CHUNK_SIZE-1 || if(x < 0 || y < 0 || z < 0 || x > CHUNK_SIZE -1 || y > CHUNK_SIZE -1 || z > CHUNK_SIZE-1 ||
@ -54,7 +49,7 @@ namespace Chunk
this->blocks.insert(start < 0 ? 0 : start, end >= CHUNK_VOLUME ? CHUNK_VOLUME : end, b); this->blocks.insert(start < 0 ? 0 : start, end >= CHUNK_VOLUME ? CHUNK_VOLUME : end, b);
} }
void Chunk::setState(chunk_state_t nstate, bool value) void Chunk::setState(uint16_t nstate, bool value)
{ {
if (value) if (value)
this->state.fetch_or(nstate); this->state.fetch_or(nstate);

View File

@ -63,9 +63,9 @@ std::array<TreeCellInfo, TREE_LUT_SIZE*TREE_LUT_SIZE> treeLUT;
void generateNoise(Chunk::Chunk *chunk) void generateNoise(Chunk::Chunk *chunk)
{ {
int cx = chunk->getPosition().x * CHUNK_SIZE; int cx = static_cast<int>(chunk->getPosition().x) * CHUNK_SIZE;
int cy = chunk->getPosition().y * CHUNK_SIZE; int cy = static_cast<int>(chunk->getPosition().y) * CHUNK_SIZE;
int cz = chunk->getPosition().z * CHUNK_SIZE; int cz = static_cast<int>(chunk->getPosition().z) * CHUNK_SIZE;
// Precalculate LUTs // Precalculate LUTs

View File

@ -1,16 +1,17 @@
#include "chunkmanager.hpp" #include "chunkmanager.hpp"
#include <oneapi/tbb.h>
#include <atomic> #include <atomic>
#include <math.h> #include <math.h>
#include <vector> #include <queue>
#include <thread> #include <thread>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/gtx/string_cast.hpp> #include <glm/gtx/string_cast.hpp>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <oneapi/tbb/parallel_for.h>
#include "block.hpp" #include "block.hpp"
#include "chunk.hpp" #include "chunk.hpp"
#include "chunkgenerator.hpp" #include "chunkgenerator.hpp"
@ -18,7 +19,6 @@
#include "debugwindow.hpp" #include "debugwindow.hpp"
#include "globals.hpp" #include "globals.hpp"
#include "renderer.hpp" #include "renderer.hpp"
#include "utils.hpp"
namespace chunkmanager namespace chunkmanager
{ {
@ -26,13 +26,14 @@ namespace chunkmanager
// controls.cpp) // controls.cpp)
void generate(); void generate();
void mesh(); void mesh();
void send_to_chunk_meshing_thread(Chunk::Chunk* c, int priority);
/* Chunk holding data structures */ /* Chunk holding data structures */
// Concurrent hash table of chunks // Concurrent hash table of chunks
ChunkTable chunks; ChunkTable chunks;
// Concurrent queue for chunks to be deleted
IntQueue chunks_todelete;
// Chunk indices. Centered at (0,0,0), going in concentric sphere outwards // Chunk indices. Centered at (0,0,0), going in concentric sphere outwards
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume> chunks_indices; std::array<std::array<int16_t, 3>, chunks_volume> chunks_indices;
/* World Update messaging data structure */ /* World Update messaging data structure */
WorldUpdateMsgQueue WorldUpdateQueue; WorldUpdateMsgQueue WorldUpdateQueue;
@ -40,21 +41,26 @@ namespace chunkmanager
/* Multithreading */ /* Multithreading */
std::atomic_bool should_run; std::atomic_bool should_run;
std::thread gen_thread, mesh_thread, update_thread; std::thread gen_thread, mesh_thread, update_thread;
// Queue of chunks to be generated // Queue of chunks to be generated
ChunkPriorityQueue chunks_to_generate_queue; ChunkPriorityQueue chunks_to_generate_queue;
// Queue of chunks to be meshed // Queue of chunks to be meshed
ChunkPriorityQueue chunks_to_mesh_queue; ChunkPriorityQueue chunks_to_mesh_queue;
/* Block picking */
int block_to_place{2};
WorldUpdateMsgQueue& getWorldUpdateQueue(){ return WorldUpdateQueue; } WorldUpdateMsgQueue& getWorldUpdateQueue(){ return WorldUpdateQueue; }
// Init chunkmanager. Chunk indices and start threads // Init chunkmanager. Chunk indices and start threads
void init(){ void init(){
int index{0}; int index{0};
constexpr int rr{RENDER_DISTANCE * RENDER_DISTANCE};
for(chunk_intcoord_t i = -RENDER_DISTANCE; i < RENDER_DISTANCE; i++) bool b = true;
for(chunk_intcoord_t j = -RENDER_DISTANCE; j < RENDER_DISTANCE; j++)
for(chunk_intcoord_t k = -RENDER_DISTANCE; k < RENDER_DISTANCE; k++){ for(int16_t i = -RENDER_DISTANCE; i < RENDER_DISTANCE; i++)
for(int16_t j = -RENDER_DISTANCE; j < RENDER_DISTANCE; j++)
for(int16_t k = -RENDER_DISTANCE; k < RENDER_DISTANCE; k++){
chunks_indices[index][0]=i; chunks_indices[index][0]=i;
chunks_indices[index][1]=j; chunks_indices[index][1]=j;
@ -66,6 +72,8 @@ namespace chunkmanager
update_thread = std::thread(update); update_thread = std::thread(update);
gen_thread = std::thread(generate); gen_thread = std::thread(generate);
mesh_thread = std::thread(mesh); mesh_thread = std::thread(mesh);
debug::window::set_parameter("block_type_return", &block_to_place);
} }
// Method for world generation thread(s) // Method for world generation thread(s)
@ -73,12 +81,11 @@ namespace chunkmanager
while(should_run){ while(should_run){
ChunkPQEntry entry; ChunkPQEntry entry;
if(chunks_to_generate_queue.try_pop(entry)){ if(chunks_to_generate_queue.try_pop(entry)){
Chunk::Chunk* chunk = entry.first; generateChunk(entry.first);
generateChunk(chunk); entry.first->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, false);
chunk->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, false);
} }
} }
chunks_to_generate_queue.clear(); //chunks_to_generate_queue.clear();
} }
// Method for chunk meshing thread(s) // Method for chunk meshing thread(s)
@ -91,18 +98,12 @@ namespace chunkmanager
chunk->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, false); chunk->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, false);
} }
} }
chunks_to_mesh_queue.clear(); //chunks_to_mesh_queue.clear();
} }
void send_to_chunk_meshing_thread(Chunk::Chunk* c, int priority){
c->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, true);
chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_NORMAL));
}
oneapi::tbb::concurrent_queue<chunk_index_t> chunks_todelete;
void update(){ void update(){
while(should_run) { while(should_run) {
/* Setup variables for the whole loop */ // Setup variables for the whole loop
// Atomic is needed by parallel_for // Atomic is needed by parallel_for
std::atomic_int nUnloaded{0}, nMarkUnload{0}, nExplored{0}, nMeshed{0}, nGenerated{0}; std::atomic_int nUnloaded{0}, nMarkUnload{0}, nExplored{0}, nMeshed{0}, nGenerated{0};
std::atomic_int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE); std::atomic_int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE);
@ -121,46 +122,37 @@ namespace chunkmanager
} }
/* Delete old chunks */ // Eventually delete old chunks
// In my head it makes sense to first delete old chunks, then create new ones int i;
// 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.
chunk_index_t i;
ChunkTable::accessor a; ChunkTable::accessor a;
while(chunks_todelete.try_pop(i)){ while(chunks_todelete.try_pop(i)){
const chunk_index_t index = i; const int index = i;
if(chunks.find(a, index)){ if(chunks.find(a, index)){
Chunk::Chunk* c = a->second; Chunk::Chunk* c = a->second;
// Use the accessor to erase the element
// Using the key doesn't work
if(chunks.erase(a)){ if(chunks.erase(a)){
nUnloaded++; nUnloaded++;
renderer::getDeleteIndexQueue().push(index); renderer::getDeleteIndexQueue().push(index);
delete c; delete c;
} else { } else std::cout << "failed to delete " << index << std::endl;
c->setState(Chunk::CHUNK_STATE_IN_DELETING_QUEUE, false);
std::cout << "failed to delete " << index << std::endl;
}
} else std::cout << "no such element found to delete\n"; } else std::cout << "no such element found to delete\n";
} }
/* Create new chunks around the player */ // Eventually create new chunks near the player
for(int i = 0; i < chunks_volume; i++) { for(int i = 0; i < chunks_volume; i++) {
const chunk_intcoord_t x = chunks_indices[i][0] + chunkX; const int16_t x = chunks_indices[i][0] + chunkX;
const chunk_intcoord_t y = chunks_indices[i][1] + chunkY; const int16_t y = chunks_indices[i][1] + chunkY;
const chunk_intcoord_t z = chunks_indices[i][2] + chunkZ; const int16_t z = chunks_indices[i][2] + chunkZ;
if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue; if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue;
nExplored++; nExplored++;
const chunk_index_t index = Chunk::calculateIndex(x, y, z); const int32_t index = Chunk::calculateIndex(x, y, z);
ChunkTable::accessor a; ChunkTable::accessor a;
if(!chunks.find(a, index)) chunks.emplace(a, std::make_pair(index, new if(!chunks.find(a, index)) chunks.emplace(a, std::make_pair(index, new
Chunk::Chunk(glm::vec3(x,y,z)))); Chunk::Chunk(glm::vec3(x,y,z))));
} }
/* Update all the chunks */ // Now update all the chunks
oneapi::tbb::parallel_for(chunks.range(), [&](ChunkTable::range_type &r){ oneapi::tbb::parallel_for(chunks.range(), [&](ChunkTable::range_type &r){
for(ChunkTable::iterator a = r.begin(); a != r.end(); a++){ for(ChunkTable::iterator a = r.begin(); a != r.end(); a++){
Chunk::Chunk* c = a->second; Chunk::Chunk* c = a->second;
@ -172,7 +164,6 @@ namespace chunkmanager
int disty = y - chunkY; int disty = y - chunkY;
int distz = z - chunkZ; int distz = z - chunkZ;
// Local variables avoid continously having to call atomic variables
int gen{0}, mesh{0}, unload{0}; int gen{0}, mesh{0}, unload{0};
if( if(
@ -190,44 +181,38 @@ namespace chunkmanager
if(!c->getState(Chunk::CHUNK_STATE_GENERATED)){ if(!c->getState(Chunk::CHUNK_STATE_GENERATED)){
if(c->isFree()){ if(c->isFree()){
// Generate // Generate
// Mark as present in the queue before sending to avoid strange
// a chunk being marked as in the queue after it was already
// processed
c->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, true); c->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, true);
chunks_to_generate_queue.push(std::make_pair(c, GENERATION_PRIORITY_NORMAL)); chunks_to_generate_queue.push(std::make_pair(c, GENERATION_PRIORITY_NORMAL));
} }
}else{ }else{
gen++; gen++;
// If generated but not yet meshed // If generated but not yet meshed
// TODO: not getting meshed
if(!c->getState(Chunk::CHUNK_STATE_MESHED)){ if(!c->getState(Chunk::CHUNK_STATE_MESHED)){
ChunkTable::accessor a1; ChunkTable::accessor a1;
if(c->isFree() &&
// Checking if nearby chunks have been generated allows for seamless
// borders between chunks
if(c->isFree() &&
(distx+1 >= RENDER_DISTANCE || x + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x+1, y, z)) && (distx+1 >= RENDER_DISTANCE || x + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x+1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) && a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(distx-1 < -RENDER_DISTANCE || x - 1 < 0 || (chunks.find(a1, Chunk::calculateIndex(x-1, y, z)) && (distx-1 < -RENDER_DISTANCE || x - 1 < 0 || (chunks.find(a1, Chunk::calculateIndex(x-1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) && a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(disty+1 >= RENDER_DISTANCE || y + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y+1, z)) && (disty+1 >= RENDER_DISTANCE || y + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y+1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) && a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(disty-1 < -RENDER_DISTANCE || y - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y-1, z)) && (disty-1 < -RENDER_DISTANCE || y - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y-1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) && a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(distz+1 >= RENDER_DISTANCE || z + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y, z+1)) && (distz+1 >= RENDER_DISTANCE || z + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y, z+1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) && a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(distz-1 < -RENDER_DISTANCE || z - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y, z-1)) && (distz-1 < -RENDER_DISTANCE || z - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y, z-1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) a1->second->getState(Chunk::CHUNK_STATE_GENERATED)))
) )
{ {
// Mesh // Mesh
c->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, true);
// Mark as present in the queue before sending to avoid strange chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_NORMAL));
// a chunk being marked as in the queue after it was already
// processed
send_to_chunk_meshing_thread(c, MESHING_PRIORITY_NORMAL);
} }
}else mesh++; }else{
mesh++;
// If generated & meshed, render
}
} }
}else{ }else{
@ -247,56 +232,43 @@ namespace chunkmanager
} }
} }
// Update atomic variables only once at the end
nGenerated += gen; nGenerated += gen;
nMeshed += mesh; nMeshed += mesh;
nMarkUnload += unload; nMarkUnload += unload;
} }
}); });
debug::window::set_parameter("update_chunks_total", (int)chunks.size());
debug::window::set_parameter("update_chunks_generated", (int) nGenerated); std::cout << "time: " << glfwGetTime() << "\ntotal: " << chunks.size() << "\ngenerated: " << nGenerated << "\nmeshed: "
debug::window::set_parameter("update_chunks_meshed", (int) nMeshed); << nMeshed << "\nunloaded from prev loop: " << nUnloaded << "\nnew marked for unload: " << nMarkUnload << std::endl;
debug::window::set_parameter("update_chunks_freed", (int) nUnloaded);
debug::window::set_parameter("update_chunks_explored", (int) nExplored);
} }
} }
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; } std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; }
void stop() { void stop() {
should_run=false; should_run=false;
std::cout << "Waiting for secondary threads to shut down" << std::endl; std::cout << "waiting for secondary threads to finish\n";
update_thread.join(); update_thread.join();
std::cout << "Update thread has terminated" << std::endl; std::cout << "update thread terminated\n";
gen_thread.join(); gen_thread.join();
std::cout << "Generation thread has terminated" << std::endl; std::cout << "generation thread terminated\n";
mesh_thread.join(); mesh_thread.join();
std::cout << "Meshing thread has terminated" << std::endl; std::cout << "meshing thread terminated\n";
} }
void destroy(){ void destroy(){
for(const auto& n : chunks){
delete n.second;
}
chunks.clear();
} }
void blockpick(WorldUpdateMsg& msg){
glm::vec3 ray_intersect(glm::vec3 startposition, glm::vec3 startdir){
int old_bx{0}, old_by{0}, old_bz{0};
int old_px{0}, old_py{0}, old_pz{0};
Chunk::Chunk* old_chunk{nullptr};
glm::vec3 old_pos;
// cast a ray from the camera in the direction pointed by the camera itself // cast a ray from the camera in the direction pointed by the camera itself
glm::vec3 origin = startposition; glm::vec3 pos = msg.cameraPos;
glm::vec3 pos = origin;
glm::vec3 front = startdir;
for(float t = 0.0; t <= 10.0; t += 0.5){ for(float t = 0.0; t <= 10.0; t += 0.5){
// traverse the ray a block at the time // traverse the ray a block at the time
pos = origin + t*front; pos = msg.cameraPos + t*msg.cameraFront;
// get which chunk and block the ray is at // get which chunk and block the ray is at
int px = ((int)(pos.x))/CHUNK_SIZE; int px = ((int)(pos.x))/CHUNK_SIZE;
@ -306,196 +278,85 @@ namespace chunkmanager
int by = pos.y - py*CHUNK_SIZE; int by = pos.y - py*CHUNK_SIZE;
int bz = pos.z - pz*CHUNK_SIZE; int bz = pos.z - pz*CHUNK_SIZE;
if(bx == old_bx && by == old_by && bz == old_bz) continue;
// exit early if the position is invalid or the chunk does not exist // exit early if the position is invalid or the chunk does not exist
if(px < 0 || py < 0 || pz < 0 || px >= 1024 || py >= 1024 || pz >= 1024) continue; if(px < 0 || py < 0 || pz < 0 || px >= 1024 || py >= 1024 || pz >= 1024) continue;
ChunkTable::const_accessor a; ChunkTable::accessor a;
if(!chunks.find(a, Chunk::calculateIndex(px, py, pz))) continue; if(!chunks.find(a, Chunk::calculateIndex(px, py, pz))) continue;
Chunk::Chunk* c = a->second; Chunk::Chunk* c = a->second;
if(!c->isFree() || !c->getState(Chunk::CHUNK_STATE_GENERATED)){ if(!c->getState(Chunk::CHUNK_STATE_GENERATED) || c->getState(Chunk::CHUNK_STATE_EMPTY)) continue;
a.release();
continue;
}
Block b = c->getBlock(bx, by, bz); Block b = c->getBlock(bx, by, bz);
a.release(); a.release();
// if the block is non empty // if the block is non empty
if(b != Block::AIR) return pos; if(b != Block::AIR){
old_chunk = c; // if placing a new block
old_bx = bx; if(msg.msg_type == WorldUpdateMsgType::BLOCKPICK_PLACE){
old_by = by; // Go half a block backwards on the ray, to check the block where the ray was
old_bz = bz; // coming from
old_px = px; // Doing this and not using normal adds the unexpected (and unwanted) ability to
old_py = py; // place blocks diagonally, without faces colliding with the block that has
old_pz = pz; // been clicked
old_pos = pos; pos -= theCamera.getFront()*0.5f;
} int px1 = ((int)(pos.x))/CHUNK_SIZE;
return glm::vec3(-1); int py1 = ((int)(pos.y))/CHUNK_SIZE;
} int pz1 = ((int)(pos.z))/CHUNK_SIZE;
int bx1 = pos.x - px1*CHUNK_SIZE;
int by1 = pos.y - py1*CHUNK_SIZE;
int bz1 = pos.z - pz1*CHUNK_SIZE;
void blockpick(WorldUpdateMsg& msg){ // exit early if the position is invalid or the chunk does not exist
//std::cout << glm::to_string(ray_intersect(msg.cameraPos, msg.cameraFront)) << std::endl; if(px1 < 0 || py1 < 0 || pz1 < 0 || px1 >= 1024 || py1 >= 1024 || pz1 >= 1024) return;
glm::vec3 ray_pos = ray_intersect(msg.cameraPos, msg.cameraFront); ChunkTable::accessor a1;
if(ray_pos == glm::vec3(-1)) 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);
// Chunk in which the blockpick is happening // mark the mesh of the chunk the be updated
int chunkx = (int)(ray_pos.x) / CHUNK_SIZE; chunks_to_mesh_queue.push(std::make_pair(c1, MESHING_PRIORITY_PLAYER_EDIT));
int chunky = (int)(ray_pos.y) / CHUNK_SIZE; chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_PLAYER_EDIT));
int chunkz = (int)(ray_pos.z) / CHUNK_SIZE;
// Block (chunk coord) in which the blockpick is happening
int blockx = ray_pos.x - chunkx*CHUNK_SIZE;
int blocky = ray_pos.y - chunky*CHUNK_SIZE;
int blockz = ray_pos.z - chunkz*CHUNK_SIZE;
// The chunk must exist, otherwise ray_intersect would have returned an error debug::window::set_parameter("block_last_action", (int)msg.msg_type);
// Also, the block must be different from AIR debug::window::set_parameter("block_last_action_block_type",
(int)(block_to_place));
ChunkTable::accessor a; debug::window::set_parameter("block_last_action_x", px1*CHUNK_SIZE + bx1);
if(!chunks.find(a, Chunk::calculateIndex(chunkx, chunky, chunkz))) return; debug::window::set_parameter("block_last_action_y", px1*CHUNK_SIZE + by1);
Chunk::Chunk* c = a->second; debug::window::set_parameter("block_last_action_z", px1*CHUNK_SIZE + bz1);
if(!(c->isFree() && c->getState(Chunk::CHUNK_STATE_GENERATED))) return;
if(msg.msg_type == WorldUpdateMsgType::BLOCKPICK_BREAK){
c->setBlock(Block::AIR, blockx, blocky, blockz);
send_to_chunk_meshing_thread(c, MESHING_PRIORITY_PLAYER_EDIT);
}else{
// Traverse voxel using Amanatides&Woo traversal algorithm
// http://www.cse.yorku.ca/~amana/research/grid.pdf
glm::vec3 pos = msg.cameraPos;
glm::vec3 front = glm::normalize(pos - ray_pos);
// Original chunk in which the blockpick started
const int ochunkX=chunkx, ochunkY = chunky, ochunkZ = chunkz;
// The ray has equation pos + t*front
// Initialize phase
// Origin integer voxel coordinates
// Avoid floating point accuracy errors: work as close to 0 as possible, translate
// everything later
int basex = std::floor(ray_pos.x);
int basey = std::floor(ray_pos.y);
int basez = std::floor(ray_pos.z);
double x = ray_pos.x - basex;
double y = ray_pos.y - basey;
double z = ray_pos.z - basez;
auto sign = [=](double f){ return f > 0 ? 1 : f < 0 ? -1 : 0; };
auto tmax = [=](double p, double dir){
int s = sign(dir);
if(s > 0)
return (1 - p) / dir;
else if(s < 0)
return -(p) / dir;
return 0.0;
};
// Step
int stepX = sign(front.x);
int stepY = sign(front.y);
int stepZ = sign(front.z);
// tMax: the value of t at which the ray crosses the first voxel boundary
double tMaxX = tmax(x, front.x);
double tMaxY = tmax(y, front.y);
double tMaxZ = tmax(z, front.z);
// tDelta: how far along the ray along they ray (in units of t) for the _ component of such
// a movement to equal the width of a voxel
double tDeltaX = stepX / front.x;
double tDeltaY = stepY / front.y;
double tDeltaZ = stepZ / front.z;
for(int i = 0; i < 10; i++){
if(tMaxX < tMaxY){
if(tMaxX < tMaxZ) {
x += stepX;
tMaxX += tDeltaX;
}else{
z += stepZ;
tMaxZ += tDeltaZ;
}
}else{ }else{
if(tMaxY < tMaxZ){ // replace the current block with air to remove it
y += stepY; c->setBlock( Block::AIR, bx, by, bz);
tMaxY += tDeltaY;
}else{ chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_PLAYER_EDIT));
z += stepZ;
tMaxZ += tDeltaZ; // When necessary, also mesh nearby chunks
} ChunkTable::accessor a1, a2, b1, b2, c1, c2;
if(bx == 0 && px - 1 >= 0 && chunks.find(a1, Chunk::calculateIndex(px - 1, py, pz)))
chunks_to_mesh_queue.push(std::make_pair(a1->second, MESHING_PRIORITY_PLAYER_EDIT));
if(by == 0 && py - 1 >= 0 && chunks.find(b1, Chunk::calculateIndex(px, py - 1, pz)))
chunks_to_mesh_queue.push(std::make_pair(a2->second, MESHING_PRIORITY_PLAYER_EDIT));
if(bz == 0 && pz - 1 >= 0 && chunks.find(c1, Chunk::calculateIndex(px, py, pz - 1)))
chunks_to_mesh_queue.push(std::make_pair(b1->second, MESHING_PRIORITY_PLAYER_EDIT));
if(bx == CHUNK_SIZE - 1 && px +1 < 1024 && chunks.find(a2, Chunk::calculateIndex(px +1, py, pz)))
chunks_to_mesh_queue.push(std::make_pair(b2->second, MESHING_PRIORITY_PLAYER_EDIT));
if(by == CHUNK_SIZE - 1 && py +1 < 1024 && chunks.find(b2, Chunk::calculateIndex(px, py +1, pz)))
chunks_to_mesh_queue.push(std::make_pair(c1->second, MESHING_PRIORITY_PLAYER_EDIT));
if(bz == CHUNK_SIZE - 1 && pz +1 < 1024 && chunks.find(c2, Chunk::calculateIndex(px, py, pz +1)))
chunks_to_mesh_queue.push(std::make_pair(c2->second, MESHING_PRIORITY_PLAYER_EDIT));
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);
debug::window::set_parameter("block_last_action_z", pz*CHUNK_SIZE + bz);
} }
int realx = basex + x;
int realy = basey + y;
int realz = basez + z;
chunkx = realx / CHUNK_SIZE;
chunky = realy / CHUNK_SIZE;
chunkz = realz / CHUNK_SIZE;
if(chunkx < 0 || chunky < 0 || chunkz < 0 || chunkx > 1023 || chunky > 1023 ||
chunkz > 1023) continue;
blockx = realx - chunkx*CHUNK_SIZE;
blocky = realy - chunky*CHUNK_SIZE;
blockz = realz - chunkz*CHUNK_SIZE;
Chunk::Chunk* chunk;
ChunkTable::accessor b;
if(chunkx != ochunkX || chunky != ochunkY || chunkz != ochunkZ){
if(!chunks.find(b, Chunk::calculateIndex(chunkx, chunky, chunkz)))
continue;
chunk = b->second;
if(!(chunk->isFree() && chunk->getState(Chunk::CHUNK_STATE_GENERATED)))
continue;
}else{
chunk = c;
}
if(chunk->getBlock(blockx, blocky, blockz) != Block::AIR) continue;
chunk->setBlock(msg.block, blockx, blocky, blockz);
send_to_chunk_meshing_thread(chunk, MESHING_PRIORITY_PLAYER_EDIT);
break; break;
} }
} }
// Release the chunk in which the blockpick started to avoid locks
a.release();
// When necessary, also mesh nearby chunks
ChunkTable::accessor a1, a2, b1, b2, c1, c2;
if(blockx == 0 && chunkx - 1 >= 0 && chunks.find(a1, Chunk::calculateIndex(chunkx - 1, chunky, chunkz)))
send_to_chunk_meshing_thread(a1->second, MESHING_PRIORITY_PLAYER_EDIT);
if(blocky == 0 && chunky - 1 >= 0 && chunks.find(b1, Chunk::calculateIndex(chunkx, chunky - 1, chunkz)))
send_to_chunk_meshing_thread(b1->second, MESHING_PRIORITY_PLAYER_EDIT);
if(blockz == 0 && chunkz - 1 >= 0 && chunks.find(c1, Chunk::calculateIndex(chunkx, chunky, chunkz - 1)))
send_to_chunk_meshing_thread(c1->second, MESHING_PRIORITY_PLAYER_EDIT);
if(blockx == CHUNK_SIZE - 1 && chunkx +1 < 1024 && chunks.find(a2, Chunk::calculateIndex(chunkx +1, chunky, chunkz)))
send_to_chunk_meshing_thread(a2->second, MESHING_PRIORITY_PLAYER_EDIT);
if(blocky == CHUNK_SIZE - 1 && chunky +1 < 1024 && chunks.find(b2, Chunk::calculateIndex(chunkx, chunky +1, chunkz)))
send_to_chunk_meshing_thread(b2->second, MESHING_PRIORITY_PLAYER_EDIT);
if(blockz == CHUNK_SIZE - 1 && chunkz +1 < 1024 && chunks.find(c2, Chunk::calculateIndex(chunkx, chunky, chunkz +1)))
send_to_chunk_meshing_thread(c2->second, MESHING_PRIORITY_PLAYER_EDIT);
// Update debugging information
debug::window::set_parameter("block_last_action", msg.msg_type ==
WorldUpdateMsgType::BLOCKPICK_PLACE);
debug::window::set_parameter("block_last_action_block_type", (int)(msg.msg_type ==
WorldUpdateMsgType::BLOCKPICK_PLACE ? msg.block : Block::AIR));
debug::window::set_parameter("block_last_action_x", chunkx*CHUNK_SIZE+blockx);
debug::window::set_parameter("block_last_action_y", chunky*CHUNK_SIZE+blocky);
debug::window::set_parameter("block_last_action_z", chunkz*CHUNK_SIZE+blockz);
} }
Block getBlockAtPos(int x, int y, int z){ Block getBlockAtPos(int x, int y, int z){
@ -521,4 +382,3 @@ namespace chunkmanager
} }
} }
}; };

View File

@ -11,18 +11,15 @@
#include "spacefilling.hpp" #include "spacefilling.hpp"
#include "utils.hpp" #include "utils.hpp"
#define CHUNK_MESH_DATA_QUANTITY 100 #define CHUNKMESHER_BORDERS 0
#define CHUNK_MESH_WORLD_LIMIT_BORDERS 0
namespace chunkmesher{ namespace chunkmesher{
ChunkMeshDataQueue MeshDataQueue; ChunkMeshDataQueue MeshDataQueue;
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; } ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
void init() void init(){
{ for(int i = 0; i < 10; i++)
for(int i = 0; i < CHUNK_MESH_DATA_QUANTITY; i++)
MeshDataQueue.push(new ChunkMeshData{}); MeshDataQueue.push(new ChunkMeshData{});
} }
@ -52,23 +49,23 @@ void mesh(Chunk::Chunk* chunk)
mesh_data->index = chunk->getIndex(); mesh_data->index = chunk->getIndex();
mesh_data->position = chunk->getPosition(); mesh_data->position = chunk->getPosition();
// convert tree to array since it is easier to work with it
int length{0};
std::unique_ptr<Block[]> blocks; std::unique_ptr<Block[]> blocks;
int length{0};
int k, l, u, v, w, h, n, j, i; int k, l, u, v, w, h, n, j, i;
int x[]{0, 0, 0}; int x[]{0, 0, 0};
int q[]{0, 0, 0}; int q[]{0, 0, 0};
int du[]{0, 0, 0}; int du[]{0, 0, 0};
int dv[]{0, 0, 0}; int dv[]{0, 0, 0};
std::array<Block, CHUNK_SIZE * CHUNK_SIZE> mask;
// Abort if chunk is empty // Abort if chunk is empty
if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)) goto end; if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)) goto end;
// convert tree to array since it is easier to work with it
blocks = chunk->getBlocksArray(&length); blocks = chunk->getBlocksArray(&length);
if(length == 0) goto end; if(length == 0) goto end;
std::array<Block, CHUNK_SIZE * CHUNK_SIZE> mask;
for (bool backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b) for (bool backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b)
{ {
// iterate over 3 dimensions // iterate over 3 dimensions
@ -100,6 +97,7 @@ void mesh(Chunk::Chunk* chunk)
Block b1, b2; Block b1, b2;
if(x[dim] >= 0) b1 = blocks[HILBERT_XYZ_ENCODE[x[0]][x[1]][x[2]]]; if(x[dim] >= 0) b1 = blocks[HILBERT_XYZ_ENCODE[x[0]][x[1]][x[2]]];
else{ else{
// b1 = Block::NULLBLK;
int cx = chunk->getPosition().x*CHUNK_SIZE; int cx = chunk->getPosition().x*CHUNK_SIZE;
int cy = chunk->getPosition().y*CHUNK_SIZE; int cy = chunk->getPosition().y*CHUNK_SIZE;
int cz = chunk->getPosition().z*CHUNK_SIZE; int cz = chunk->getPosition().z*CHUNK_SIZE;
@ -114,6 +112,7 @@ void mesh(Chunk::Chunk* chunk)
if(x[dim] < CHUNK_SIZE - 1) b2 = blocks[HILBERT_XYZ_ENCODE[x[0] + q[0]][x[1] if(x[dim] < CHUNK_SIZE - 1) b2 = blocks[HILBERT_XYZ_ENCODE[x[0] + q[0]][x[1]
+ q[1]][x[2] + q[2]]]; + q[1]][x[2] + q[2]]];
else{ else{
//b2 = Block::NULLBLK;
int cx = chunk->getPosition().x*CHUNK_SIZE; int cx = chunk->getPosition().x*CHUNK_SIZE;
int cy = chunk->getPosition().y*CHUNK_SIZE; int cy = chunk->getPosition().y*CHUNK_SIZE;
int cz = chunk->getPosition().z*CHUNK_SIZE; int cz = chunk->getPosition().z*CHUNK_SIZE;
@ -130,7 +129,7 @@ void mesh(Chunk::Chunk* chunk)
// The else case provides face culling for adjacent solid faces // The else case provides face culling for adjacent solid faces
// Checking for NULLBLK avoids creating empty faces if nearby chunk was not // Checking for NULLBLK avoids creating empty faces if nearby chunk was not
// yet generated // yet generated
#if CHUNK_MESH_WORLD_LIMIT_BORDERS == 1 #if CHUNKMESHER_BORDERS == 1
mask[n++] = b1 == b2 ? Block::NULLBLK mask[n++] = b1 == b2 ? Block::NULLBLK
: backFace ? b1 == Block::NULLBLK || b1 == Block::AIR ? b2 : Block::NULLBLK : backFace ? b1 == Block::NULLBLK || b1 == Block::AIR ? b2 : Block::NULLBLK
: b2 == Block::NULLBLK || b2 == Block::AIR ? b1 : Block::NULLBLK; : b2 == Block::NULLBLK || b2 == Block::AIR ? b1 : Block::NULLBLK;
@ -192,11 +191,12 @@ void mesh(Chunk::Chunk* chunk)
mesh_data->vertices.push_back(x[1]); //bottomLeft.y mesh_data->vertices.push_back(x[1]); //bottomLeft.y
mesh_data->vertices.push_back(x[2]); //bottomLeft.z mesh_data->vertices.push_back(x[2]); //bottomLeft.z
// extents, use normals for now // extents
mesh_data->extents.push_back(du[0] + dv[0]); mesh_data->extents.push_back(du[0] + dv[0]);
mesh_data->extents.push_back(du[1] + dv[1]); mesh_data->extents.push_back(du[1] + dv[1]);
mesh_data->extents.push_back(du[2] + dv[2]); mesh_data->extents.push_back(du[2] + dv[2]);
// texture info (block type, backFace)
mesh_data->texinfo.push_back(backFace ? 0.0 : 1.0); mesh_data->texinfo.push_back(backFace ? 0.0 : 1.0);
mesh_data->texinfo.push_back((int)(mask[n]) - 2); mesh_data->texinfo.push_back((int)(mask[n]) - 2);
mesh_data->num_vertices++; mesh_data->num_vertices++;

View File

@ -2,24 +2,20 @@
#include "camera.hpp" #include "camera.hpp"
#include "chunkmanager.hpp" #include "chunkmanager.hpp"
#include "debugwindow.hpp"
#include "globals.hpp" #include "globals.hpp"
#include "renderer.hpp" #include "renderer.hpp"
namespace controls{ namespace controls{
/* Block picking */ WorldUpdateMsgQueue MsgQueue;
int block_to_place{2};
float lastBlockPick=0.0; float lastBlockPick=0.0;
bool blockpick = false; bool blockpick = false;
/* Cursor */
bool cursor = false; bool cursor = false;
void init(){ void init(){
debug::window::set_parameter("block_type_return", &block_to_place);
} }
void update(GLFWwindow* window){ void update(GLFWwindow* window){
// Reset blockping timeout have passed
float current_time = glfwGetTime(); float current_time = glfwGetTime();
/* BlockPicking */ /* BlockPicking */
@ -49,7 +45,6 @@ namespace controls{
msg.time = current_time; msg.time = current_time;
msg.msg_type = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS ? msg.msg_type = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS ?
WorldUpdateMsgType::BLOCKPICK_PLACE : WorldUpdateMsgType::BLOCKPICK_BREAK; WorldUpdateMsgType::BLOCKPICK_PLACE : WorldUpdateMsgType::BLOCKPICK_BREAK;
msg.block = (Block)(block_to_place);
// Send to chunk manager // Send to chunk manager
chunkmanager::getWorldUpdateQueue().push(msg); chunkmanager::getWorldUpdateQueue().push(msg);
@ -63,4 +58,6 @@ namespace controls{
glfwSetInputMode(window, GLFW_CURSOR, cursor ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED); glfwSetInputMode(window, GLFW_CURSOR, cursor ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
} }
} }
WorldUpdateMsgQueue& getWorldUpdateQueue(){ return MsgQueue; }
}; };

View File

@ -1,3 +1,6 @@
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "debugwindow.hpp" #include "debugwindow.hpp"
#include <imgui/imgui.h> #include <imgui/imgui.h>
@ -62,16 +65,14 @@ namespace debug{
void show_debug_window(){ void show_debug_window(){
ImGui::Begin("Debug Window"); ImGui::Begin("Debug Window");
ImGui::PushItemWidth(ImGui::GetFontSize() * -12); //ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
try{ try{
if (ImGui::CollapsingHeader("Frametimes")){ if (ImGui::CollapsingHeader("Frametimes")){
ImGui::Text("FPS: %d", std::any_cast<int>(parameters.at("fps"))); ImGui::Text("FPS: %d", std::any_cast<int>(parameters.at("fps")));
ImGui::Text("Frametime (ms): %f", ImGui::Text("Frametime (ms): %f",
std::any_cast<float>(parameters.at("frametime"))*1000); std::any_cast<float>(parameters.at("frametime"))*1000);
ImGui::Text("GPU: (%s) %s", ImGui::Text("GPU: %s %s", glGetString(GL_VENDOR), glGetString(GL_RENDERER));
std::any_cast<const GLubyte*>(parameters.at("gpu_vendor")),
std::any_cast<const GLubyte*>(parameters.at("gpu_renderer")));
//ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr); //ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr);
} }
@ -82,11 +83,6 @@ namespace debug{
ImGui::Text("Pointing in direction: %f, %f, %f", ImGui::Text("Pointing in direction: %f, %f, %f",
std::any_cast<float>(parameters.at("lx")),std::any_cast<float>(parameters.at("ly")),std::any_cast<float>(parameters.at("lz")) ); std::any_cast<float>(parameters.at("lx")),std::any_cast<float>(parameters.at("ly")),std::any_cast<float>(parameters.at("lz")) );
ImGui::SliderInt("Crosshair type",
std::any_cast<int*>(parameters.at("crosshair_type_return")), 0, 1);
ImGui::SliderInt("Block to place",
std::any_cast<int*>(parameters.at("block_type_return")), 2, 6);
if(parameters.find("block_last_action") != parameters.end()){ if(parameters.find("block_last_action") != parameters.end()){
ImGui::Text("Last Block action: %s", ImGui::Text("Last Block action: %s",
std::any_cast<bool>(parameters.at("block_last_action")) ? "place" : "destroy"); std::any_cast<bool>(parameters.at("block_last_action")) ? "place" : "destroy");
@ -95,6 +91,11 @@ namespace debug{
ImGui::Text("Last Block action position: X: %d, Y: %d, Z: %d", ImGui::Text("Last Block action position: X: %d, Y: %d, Z: %d",
std::any_cast<int>(parameters.at("block_last_action_x")),std::any_cast<int>(parameters.at("block_last_action_y")),std::any_cast<int>(parameters.at("block_last_action_z")) ); std::any_cast<int>(parameters.at("block_last_action_x")),std::any_cast<int>(parameters.at("block_last_action_y")),std::any_cast<int>(parameters.at("block_last_action_z")) );
} }
ImGui::SliderInt("Crosshair type",
std::any_cast<int*>(parameters.at("crosshair_type_return")), 0, 1);
ImGui::SliderInt("Block to place",
std::any_cast<int*>(parameters.at("block_type_return")), 2, 6);
} }
if(ImGui::CollapsingHeader("Mesh")){ if(ImGui::CollapsingHeader("Mesh")){
@ -115,6 +116,8 @@ namespace debug{
if(ImGui::CollapsingHeader("Chunks")){ if(ImGui::CollapsingHeader("Chunks")){
ImGui::Text("Total chunks present: %d", ImGui::Text("Total chunks present: %d",
std::any_cast<int>(parameters.at("update_chunks_total"))); std::any_cast<int>(parameters.at("update_chunks_total")));
ImGui::Text("Buckets in hash map: %d",
std::any_cast<int>(parameters.at("update_chunks_buckets")));
ImGui::Text("Chunks generated: %d", ImGui::Text("Chunks generated: %d",
std::any_cast<int>(parameters.at("update_chunks_generated"))); std::any_cast<int>(parameters.at("update_chunks_generated")));
ImGui::Text("Chunks meshed: %d", ImGui::Text("Chunks meshed: %d",
@ -125,9 +128,9 @@ namespace debug{
std::any_cast<int>(parameters.at("update_chunks_explored"))); std::any_cast<int>(parameters.at("update_chunks_explored")));
} }
}catch(const std::bad_any_cast& e){ }catch(const std::bad_any_cast& e){
std::cout << e.what() << std::endl; std::cout << e.what();
}catch(const std::out_of_range& e){ }catch(const std::out_of_range& e){
std::cout << e.what() << std::endl; std::cout << e.what();
} }
ImGui::End(); ImGui::End();

View File

@ -1,3 +1,6 @@
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "main.hpp" #include "main.hpp"
#include <iostream> #include <iostream>
@ -10,8 +13,8 @@
#include "controls.hpp" #include "controls.hpp"
#include "debugwindow.hpp" #include "debugwindow.hpp"
#include "renderer.hpp" #include "renderer.hpp"
#include "shader.hpp"
#include "spacefilling.hpp" #include "spacefilling.hpp"
#include "shader.hpp"
float deltaTime = 0.0f; // Time between current frame and last frame float deltaTime = 0.0f; // Time between current frame and last frame
float lastFrame = 0.0f; // Time of last frame float lastFrame = 0.0f; // Time of last frame
@ -55,9 +58,7 @@ int main()
//glEnable(GL_FRAMEBUFFER_SRGB); //gamma correction done in fragment shader //glEnable(GL_FRAMEBUFFER_SRGB); //gamma correction done in fragment shader
//glEnable(GL_CULL_FACE); //GL_BACK GL_CCW by default //glEnable(GL_CULL_FACE); //GL_BACK GL_CCW by default
debug::window::set_parameter("gpu_vendor", glGetString(GL_VENDOR)); wireframe = false;
debug::window::set_parameter("gpu_renderer", glGetString(GL_RENDERER));
for(int i = 0; i < 360; i++){ for(int i = 0; i < 360; i++){
sines[i] = sin(3.14 / 180 * i); sines[i] = sin(3.14 / 180 * i);
cosines[i] = cos(3.14 / 180 * i); cosines[i] = cos(3.14 / 180 * i);
@ -90,26 +91,15 @@ int main()
glClearColor(0.431f, 0.694f, 1.0f, 1.0f); glClearColor(0.431f, 0.694f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Input handling // Only handle window closing here
// Only close event is handles by main
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true); glfwSetWindowShouldClose(window, true);
// the rest of input processing is handled by controls.cpp
// Input processing // Input processing
controls::update(window); controls::update(window);
// Camera // Camera
theCamera.update(window, deltaTime); theCamera.update(window, deltaTime);
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 // Render pass
renderer::render(); renderer::render();
@ -123,8 +113,9 @@ int main()
chunkmanager::stop(); chunkmanager::stop();
// Cleanup allocated memory // Cleanup allocated memory
chunkmanager::destroy(); debug::window::destroy();
renderer::destroy(); renderer::destroy();
chunkmanager::destroy();
glfwTerminate(); glfwTerminate();
return 0; return 0;

View File

@ -2,6 +2,7 @@
#include <glm/ext.hpp> #include <glm/ext.hpp>
#include <glm/gtx/string_cast.hpp> #include <glm/gtx/string_cast.hpp>
#include <oneapi/tbb/concurrent_hash_map.h> #include <oneapi/tbb/concurrent_hash_map.h>
#include "chunkmanager.hpp" #include "chunkmanager.hpp"
@ -13,7 +14,7 @@
#include "stb_image_write.h" #include "stb_image_write.h"
namespace renderer{ namespace renderer{
typedef oneapi::tbb::concurrent_hash_map<chunk_index_t, RenderInfo*> RenderTable; typedef oneapi::tbb::concurrent_hash_map<int32_t, RenderInfo*> RenderTable;
RenderTable ChunksToRender; RenderTable ChunksToRender;
ChunkMeshDataQueue MeshDataQueue; ChunkMeshDataQueue MeshDataQueue;
@ -22,16 +23,16 @@ namespace renderer{
Shader* theShader, *quadShader; Shader* theShader, *quadShader;
GLuint chunkTexture; GLuint chunkTexture;
Shader* getRenderShader() { return theShader; }
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
IndexQueue& getDeleteIndexQueue(){ return MeshDataToDelete; }
GLuint renderTexFrameBuffer, renderTex, renderTexDepthBuffer, quadVAO, quadVBO; GLuint renderTexFrameBuffer, renderTex, renderTexDepthBuffer, quadVAO, quadVBO;
int screenWidth, screenHeight; int screenWidth, screenHeight;
int crosshair_type{0}; int crosshair_type{0};
bool wireframe{false}; bool wireframe{false};
Shader* getRenderShader() { return theShader; }
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
IndexQueue& getDeleteIndexQueue(){ return MeshDataToDelete; }
void init(GLFWwindow* window){ void init(GLFWwindow* window){
// Setup rendering // Setup rendering
// We will render the image to a texture, then display the texture on a quad that fills the // We will render the image to a texture, then display the texture on a quad that fills the
@ -141,7 +142,6 @@ namespace renderer{
theShader->use(); theShader->use();
theShader->setVec3("viewPos", cameraPos); theShader->setVec3("viewPos", cameraPos);
/* Process incoming mesh data */
ChunkMeshData* m; ChunkMeshData* m;
while(MeshDataQueue.try_pop(m)){ while(MeshDataQueue.try_pop(m)){
RenderTable::accessor a; RenderTable::accessor a;
@ -151,11 +151,7 @@ namespace renderer{
render_info = a->second; render_info = a->second;
render_info->position = m->position; render_info->position = m->position;
render_info->num_vertices = m->num_vertices; render_info->num_vertices = m->num_vertices;
std::cout << "index collision on " << render_info->index << std::endl;
// Always updated the mesh, even if it's empty
// This should solve the problem of having floating quads when destroying a block
// near chunk borders
send_chunk_to_gpu(m, render_info);
}else{ }else{
render_info = new RenderInfo(); render_info = new RenderInfo();
render_info->index = m->index; render_info->index = m->index;
@ -163,16 +159,14 @@ namespace renderer{
render_info->num_vertices = m->num_vertices; render_info->num_vertices = m->num_vertices;
ChunksToRender.emplace(a, std::make_pair(render_info->index, render_info)); ChunksToRender.emplace(a, std::make_pair(render_info->index, render_info));
// Only send the mesh to the GPU if it's not empty
if(render_info->num_vertices > 0) send_chunk_to_gpu(m, render_info);
} }
send_chunk_to_gpu(m, render_info);
chunkmesher::getMeshDataQueue().push(m); chunkmesher::getMeshDataQueue().push(m);
} }
/* Process chunks to be removed */ // TODO: implement removal of chunks from rendering
chunk_index_t queue_index; int32_t queue_index;
while(MeshDataToDelete.try_pop(queue_index)){ while(MeshDataToDelete.try_pop(queue_index)){
RenderTable::accessor a; RenderTable::accessor a;
@ -184,11 +178,11 @@ namespace renderer{
} }
} }
/* Render the chunks */ // Render the chunks
// parallel_for cannot be used since all the rendering needs to happen in a single thread // parallel_for cannot be used since all the rendering needs to happen in a single thread
for(RenderTable::iterator i = ChunksToRender.begin(); i != ChunksToRender.end(); i++){ for(RenderTable::iterator i = ChunksToRender.begin(); i != ChunksToRender.end(); i++){
RenderInfo* render_info = i->second; RenderInfo* render_info = i->second;
if(render_info->num_vertices > 0) if(render_info->num_vertices > 0)
{ {
total++; total++;
@ -203,7 +197,7 @@ namespace renderer{
// Check if all the corners of the chunk are outside any of the planes // Check if all the corners of the chunk are outside any of the planes
// TODO (?) implement frustum culling as per (Inigo Quilez)[https://iquilezles.org/articles/frustumcorrect/], and check each // TODO (?) implement frustum culling as per (Inigo Quilez)[https://iquilezles.org/articles/frustumcorrect/], and check each
// plane against each corner of the chunk // plane against each corner of the chunk
bool out=false; bool out=false;
int a{0}; int a{0};
for(int p = 0; p < 6; p++){ for(int p = 0; p < 6; p++){
@ -261,35 +255,37 @@ namespace renderer{
void send_chunk_to_gpu(ChunkMeshData* mesh_data, RenderInfo* render_info) void send_chunk_to_gpu(ChunkMeshData* mesh_data, RenderInfo* render_info)
{ {
if(!render_info->buffers_allocated) render_info->allocateBuffers(); if (render_info->num_vertices > 0)
{
if(!render_info->buffers_allocated) render_info->allocateBuffers();
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s). // bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(render_info->VAO); glBindVertexArray(render_info->VAO);
// TODO: change GL_STATIC_DRAW to the one that means "few redraws and further in between" // TODO: change GL_STATIC_DRAW to the one that means "few redraws and further in between"
// position attribute // position attribute
glBindBuffer(GL_ARRAY_BUFFER, render_info->VBO); glBindBuffer(GL_ARRAY_BUFFER, render_info->VBO);
glBufferData(GL_ARRAY_BUFFER, mesh_data->vertices.size() * sizeof(GLfloat), &(mesh_data->vertices[0]), GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, mesh_data->vertices.size() * sizeof(GLfloat), &(mesh_data->vertices[0]), GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
// normal attribute // normal attribute
glBindBuffer(GL_ARRAY_BUFFER, render_info->extentsBuffer); glBindBuffer(GL_ARRAY_BUFFER, render_info->extentsBuffer);
glBufferData(GL_ARRAY_BUFFER, mesh_data->extents.size() * sizeof(GLfloat), &(mesh_data->extents[0]), GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, mesh_data->extents.size() * sizeof(GLfloat), &(mesh_data->extents[0]), GL_STATIC_DRAW);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)(0)); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void *)(0));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
// texcoords attribute // texcoords attribute
glBindBuffer(GL_ARRAY_BUFFER, render_info->texinfoBuffer); glBindBuffer(GL_ARRAY_BUFFER, render_info->texinfoBuffer);
glBufferData(GL_ARRAY_BUFFER, mesh_data->texinfo.size() * sizeof(GLfloat), &(mesh_data->texinfo[0]), GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, mesh_data->texinfo.size() * sizeof(GLfloat), &(mesh_data->texinfo[0]), GL_STATIC_DRAW);
glEnableVertexAttribArray(2); glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
glBindVertexArray(0); glBindVertexArray(0);
}
} }
void framebuffer_size_callback(GLFWwindow *window, int width, int height){ void framebuffer_size_callback(GLFWwindow *window, int width, int height){
resize_framebuffer(width, height); resize_framebuffer(width, height);
} }

34
test2.cpp Normal file
View File

@ -0,0 +1,34 @@
#include <iostream>
#include <string>
#include <thread>
#include "oneapi/tbb/concurrent_hash_map.h"
#include "oneapi/tbb/tbb_allocator.h"
typedef oneapi::tbb::concurrent_hash_map<int, int, oneapi::tbb::tbb_allocator<int>> CTable;
CTable table;
void f(){
/*while(table.size() > 0){
std::cout << "---------------\n";
oneapi::tbb::parallel_for(table.range(), [=](Table::range_type &r){
for(Table::const_iterator a = r.begin(); a != r.end(); a++){
std::cout << a->first << ": " << a->second << std::endl;
}
});
}*/
}
int main(){
std::thread t = std::thread(f);
//Table::accessor a;
/*table.emplace(a, std::make_pair(0, "zero"));
table.emplace(a, std::make_pair(1, "one"));
table.emplace(a, std::make_pair(2, "two"));
table.emplace(a, std::make_pair(3, "three"));
table.emplace(a, std::make_pair(4, "four"));
table.emplace(a, std::make_pair(5, "five"));*/
t.join();
}