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
21 changed files with 638 additions and 396 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
{ {
@ -18,6 +21,10 @@ public:
// This matrix needs to be also updated in viewPortCallback whenever it is changed // 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); 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) void update(GLFWwindow *window, float deltaTime)
@ -48,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)
@ -112,7 +129,7 @@ public:
private: private:
glm::vec3 cameraPos = glm::vec3(256.0, 80.0f, 256.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

@ -23,31 +23,35 @@
namespace Chunk namespace Chunk
{ {
constexpr uint8_t CHUNK_STATE_GENERATED = 1; constexpr uint16_t CHUNK_STATE_GENERATED = 1;
constexpr uint8_t CHUNK_STATE_MESHED = 2; constexpr uint16_t CHUNK_STATE_MESHED = 2;
constexpr uint8_t CHUNK_STATE_MESH_LOADED = 4; constexpr uint16_t CHUNK_STATE_OUTOFVISION = 4;
constexpr uint8_t CHUNK_STATE_LOADED = 8; constexpr uint16_t CHUNK_STATE_UNLOADED = 8;
constexpr uint8_t CHUNK_STATE_OUTOFVISION = 16; constexpr uint16_t CHUNK_STATE_EMPTY = 16;
constexpr uint8_t CHUNK_STATE_UNLOADED = 32; constexpr uint16_t CHUNK_STATE_IN_GENERATION_QUEUE = 32;
constexpr uint8_t CHUNK_STATE_EMPTY = 64; constexpr uint16_t CHUNK_STATE_IN_MESHING_QUEUE = 64;
constexpr uint16_t CHUNK_STATE_IN_DELETING_QUEUE = 128;
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 createBuffers();
void deleteBuffers(); void deleteBuffers();
glm::vec3 getPosition() { return this->position; } glm::vec3 getPosition() { return this->position; }
uint8_t getTotalState() { return this->state; } void setState(uint16_t nstate, bool value);
bool getState(uint8_t n) { return (this->state & n) == n; } uint16_t getTotalState() { return this->state; }
void setState(uint8_t nstate, bool value); bool getState(uint16_t n) { return (this->state & n) == n; }
bool isFree(){ return !(getState(CHUNK_STATE_IN_GENERATION_QUEUE) ||
getState(CHUNK_STATE_IN_MESHING_QUEUE)); }
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);
@ -58,12 +62,15 @@ namespace Chunk
public: public:
GLuint VAO{0}, VBO{0}, extentsBuffer{0}, texinfoBuffer{0}, numVertices{0}; GLuint VAO{0}, VBO{0}, extentsBuffer{0}, texinfoBuffer{0}, numVertices{0};
std::atomic<float> unload_timer{0}; std::atomic<float> unload_timer{0};
// 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_uint8_t state{0}; int32_t index;
std::atomic_uint16_t state{0};
}; };
}; };

View File

@ -1,17 +1,19 @@
#ifndef CHUNKMANAGER_H #ifndef CHUNKMANAGER_H
#define CHUNKMANAGER_H #define CHUNKMANAGER_H
#include "chunk.hpp"
#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 "globals.hpp" #include "globals.hpp"
#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<uint32_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 {
@ -30,15 +35,12 @@ namespace chunkmanager
typedef oneapi::tbb::concurrent_priority_queue<ChunkPQEntry, compare_f> ChunkPriorityQueue; typedef oneapi::tbb::concurrent_priority_queue<ChunkPQEntry, compare_f> ChunkPriorityQueue;
void init(); void init();
void blockpick(bool place); void update();
uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k);
void stop(); void stop();
void destroy(); void destroy();
oneapi::tbb::concurrent_queue<Chunk::Chunk*>& getDeleteVector(); WorldUpdateMsgQueue& getWorldUpdateQueue();
std::array<std::array<int, 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);
void update();
} }
#endif #endif

32
include/chunkmeshdata.hpp Normal file
View File

@ -0,0 +1,32 @@
#ifndef CHUNK_MESH_DATA_H
#define CHUNK_MESH_DATA_H
enum class ChunkMeshDataType{
MESH_UPDATE
};
typedef struct ChunkMeshData{
int32_t index;
glm::vec3 position;
int num_vertices = 0;
std::vector<GLfloat> vertices;
std::vector<GLfloat> extents;
std::vector<GLfloat> texinfo;
ChunkMeshDataType message_type;
void clear(){
vertices.clear();
texinfo.clear();
extents.clear();
index = 0;
position = glm::vec3(0);
num_vertices = 0;
}
}ChunkMeshData;
typedef oneapi::tbb::concurrent_queue<ChunkMeshData*> ChunkMeshDataQueue;
#endif

View File

@ -5,28 +5,19 @@
#include <vector> #include <vector>
#include <glad/glad.h> #include <glad/glad.h>
#include <glm/glm.hpp>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <oneapi/tbb/concurrent_queue.h> #include <oneapi/tbb/concurrent_queue.h>
#include "chunk.hpp" #include "chunk.hpp"
#include "chunkmeshdata.hpp"
#include "globals.hpp" #include "globals.hpp"
#include "shader.hpp" #include "shader.hpp"
namespace chunkmesher{ namespace chunkmesher{
struct MeshData{ ChunkMeshDataQueue& getMeshDataQueue();
Chunk::Chunk* chunk; void init();
GLuint numVertices{0};
std::vector<GLfloat> vertices;
std::vector<GLfloat> extents;
std::vector<GLfloat> texinfo;
};
oneapi::tbb::concurrent_queue<MeshData*>& getMeshDataQueue();
void mesh(Chunk::Chunk* chunk); void mesh(Chunk::Chunk* chunk);
void sendtogpu(MeshData* mesh_data);
void quad(MeshData* mesh_data, glm::vec3 bottomLeft, glm::vec3 topLeft, glm::vec3 topRight,
glm::vec3 bottomRight, glm::vec3 normal, Block block, int dim, bool backFace);
} }

18
include/controls.hpp Normal file
View File

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

View File

@ -10,10 +10,11 @@
#define extr extern #define extr extern
#endif #endif
#define RENDER_DISTANCE 16 #define RENDER_DISTANCE 8
extr Camera theCamera; extr Camera theCamera;
constexpr int chunks_volume = static_cast<int>(1.333333333333*M_PI*(RENDER_DISTANCE*RENDER_DISTANCE*RENDER_DISTANCE)); //constexpr int chunks_volume = static_cast<int>(1.333333333333*M_PI*(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

@ -3,6 +3,5 @@
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);
void processInput(GLFWwindow *);
#endif #endif

View File

@ -1,17 +1,47 @@
#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"
#include "chunkmeshdata.hpp"
#include "shader.hpp" #include "shader.hpp"
namespace renderer{ namespace renderer{
typedef oneapi::tbb::concurrent_unordered_set<Chunk::Chunk*> RenderSet; typedef struct RenderInfo {
int32_t index;
int num_vertices;
glm::vec3 position;
bool buffers_allocated=false;
GLuint VAO, VBO, extentsBuffer, texinfoBuffer;
void allocateBuffers(){
// Allocate buffers
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &extentsBuffer);
glGenBuffers(1, &texinfoBuffer);
buffers_allocated=true;
}
void deallocateBuffers(){
// Allocate buffers
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &extentsBuffer);
glDeleteBuffers(1, &texinfoBuffer);
glDeleteVertexArrays(1, &VAO);
buffers_allocated=false;
}
} RenderInfo;
typedef oneapi::tbb::concurrent_queue<int32_t> IndexQueue;
void init(GLFWwindow* window); void init(GLFWwindow* window);
void send_chunk_to_gpu(ChunkMeshData* mesh_data, RenderInfo* render_info);
void render(); void render();
void resize_framebuffer(int width, int height); void resize_framebuffer(int width, int height);
void framebuffer_size_callback(GLFWwindow *window, int width, int height); void framebuffer_size_callback(GLFWwindow *window, int width, int height);
@ -20,8 +50,8 @@ namespace renderer{
void saveScreenshot(bool forceFullHD=false); void saveScreenshot(bool forceFullHD=false);
Shader* getRenderShader(); Shader* getRenderShader();
RenderSet& getChunksToRender(); ChunkMeshDataQueue& getMeshDataQueue();
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*>& getMeshDataQueue(); IndexQueue& getDeleteIndexQueue();
}; };

View File

@ -0,0 +1,21 @@
#ifndef WORLD_UPDATE_MSG_H
#define WORLD_UPDATE_MSG_H
#include <glm/glm.hpp>
#include <oneapi/tbb/concurrent_queue.h>
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<WorldUpdateMsg> WorldUpdateMsgQueue;
#endif

View File

@ -1,10 +1,10 @@
cmake_minimum_required(VERSION 3.2) cmake_minimum_required(VERSION 3.2)
project(OpenGLTest) 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) debugwindow.cpp renderer.cpp spacefilling.cpp stb_image.cpp utils.cpp OpenSimplexNoise.cpp)
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,30 +15,20 @@ 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);
} }
int32_t calculateIndex(glm::vec3 pos){
return calculateIndex(static_cast<int16_t>(pos.x), static_cast<int16_t>(pos.y),
static_cast<int16_t>(pos.z));
}
int32_t calculateIndex(int16_t x, int16_t y, int16_t z){
return x | (y << 10) | (z << 20);
}
Chunk::Chunk(glm::vec3 pos) Chunk::Chunk(glm::vec3 pos)
{ {
this->position = pos; this->position = pos;
this->setState(CHUNK_STATE_EMPTY, true); this->setState(CHUNK_STATE_EMPTY, true);
this->setBlocks(0, CHUNK_MAX_INDEX, Block::AIR); this->setBlocks(0, CHUNK_MAX_INDEX, Block::AIR);
} this->index = calculateIndex(pos);
Chunk ::~Chunk()
{
}
void Chunk::createBuffers(){
glGenVertexArrays(1, &(this->VAO));
glGenBuffers(1, &(this->VBO));
glGenBuffers(1, &(this->extentsBuffer));
glGenBuffers(1, &(this->texinfoBuffer));
}
void Chunk::deleteBuffers(){
glDeleteBuffers(1, &(this->VBO));
glDeleteBuffers(1, &(this->extentsBuffer));
glDeleteBuffers(1, &(this->texinfoBuffer));
glDeleteVertexArrays(1, &(this->VAO));
} }
Block Chunk::getBlock(int x, int y, int z) Block Chunk::getBlock(int x, int y, int z)
@ -59,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(uint8_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,11 +1,15 @@
#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/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include "block.hpp" #include "block.hpp"
@ -18,13 +22,21 @@
namespace chunkmanager namespace chunkmanager
{ {
void blockpick(WorldUpdateMsg& msg); // There's no need of passing by value again (check
// controls.cpp)
void generate(); void generate();
void mesh(); void mesh();
/* 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<int, 3>, chunks_volume> chunks_indices; std::array<std::array<int16_t, 3>, chunks_volume> chunks_indices;
/* World Update messaging data structure */
WorldUpdateMsgQueue WorldUpdateQueue;
/* Multithreading */ /* Multithreading */
std::atomic_bool should_run; std::atomic_bool should_run;
@ -34,54 +46,27 @@ namespace chunkmanager
// 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}; int block_to_place{2};
WorldUpdateMsgQueue& getWorldUpdateQueue(){ return WorldUpdateQueue; }
// Init chunkmanager. Chunk indices and start threads // Init chunkmanager. Chunk indices and start threads
int chunks_volume_real;
void init(){ void init(){
int index{0}; int index{0};
int rr{RENDER_DISTANCE * RENDER_DISTANCE}; constexpr int rr{RENDER_DISTANCE * RENDER_DISTANCE};
int xp{0}, x{0};
bool b = true; bool b = true;
// Iterate over all chunks, in concentric spheres starting fron the player and going outwards. Alternate left and right for(int16_t i = -RENDER_DISTANCE; i < RENDER_DISTANCE; i++)
// Eq. of the sphere (x - a)² + (y - b)² + (z - c)² = r² for(int16_t j = -RENDER_DISTANCE; j < RENDER_DISTANCE; j++)
while (xp <= RENDER_DISTANCE) for(int16_t k = -RENDER_DISTANCE; k < RENDER_DISTANCE; k++){
{
// 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 chunks_indices[index][0]=i;
// possible results) chunks_indices[index][1]=j;
int y1 = static_cast<int>(sqrt((rr) - x*x)); chunks_indices[index][2]=k;
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;
index++; 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++)
chunkmesher::getMeshDataQueue().push(new chunkmesher::MeshData());
should_run = true; should_run = true;
update_thread = std::thread(update); update_thread = std::thread(update);
@ -95,8 +80,12 @@ namespace chunkmanager
void generate(){ void generate(){
while(should_run){ while(should_run){
ChunkPQEntry entry; ChunkPQEntry entry;
while(chunks_to_generate_queue.try_pop(entry)) generateChunk(entry.first); if(chunks_to_generate_queue.try_pop(entry)){
generateChunk(entry.first);
entry.first->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, false);
}
} }
//chunks_to_generate_queue.clear();
} }
// Method for chunk meshing thread(s) // Method for chunk meshing thread(s)
@ -105,113 +94,181 @@ namespace chunkmanager
ChunkPQEntry entry; ChunkPQEntry entry;
if(chunks_to_mesh_queue.try_pop(entry)){ if(chunks_to_mesh_queue.try_pop(entry)){
Chunk::Chunk* chunk = entry.first; Chunk::Chunk* chunk = entry.first;
if(chunk->getState(Chunk::CHUNK_STATE_GENERATED)){ chunkmesher::mesh(chunk);
chunkmesher::mesh(chunk); chunk->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, false);
renderer::getChunksToRender().insert(chunk);
}
} }
} }
//chunks_to_mesh_queue.clear();
} }
oneapi::tbb::concurrent_queue<Chunk::Chunk*> chunks_todelete;
int nUnloaded{0};
void update(){ void update(){
while(should_run) { while(should_run) {
int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE); // Setup variables for the whole loop
int chunkY=static_cast<int>(theCamera.getAtomicPosY() / CHUNK_SIZE); // Atomic is needed by parallel_for
int chunkZ=static_cast<int>(theCamera.getAtomicPosZ() / CHUNK_SIZE); 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 chunkY=static_cast<int>(theCamera.getAtomicPosY() / CHUNK_SIZE);
std::atomic_int chunkZ=static_cast<int>(theCamera.getAtomicPosZ() / CHUNK_SIZE);
debug::window::set_parameter("px", theCamera.getAtomicPosX()); /* Process update messages before anything happens */
debug::window::set_parameter("py", theCamera.getAtomicPosY()); WorldUpdateMsg msg;
debug::window::set_parameter("pz", theCamera.getAtomicPosZ()); while(WorldUpdateQueue.try_pop(msg)){
debug::window::set_parameter("cx", chunkX); switch(msg.msg_type){
debug::window::set_parameter("cy", chunkY); case WorldUpdateMsgType::BLOCKPICK_BREAK:
debug::window::set_parameter("cz", chunkZ); case WorldUpdateMsgType::BLOCKPICK_PLACE:
debug::window::set_parameter("lx", theCamera.getFront().x); blockpick(msg);
debug::window::set_parameter("ly", theCamera.getFront().y); break;
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);
if(x > 1023 || y > 1023 || z > 1023) continue;
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))));
if(! (a->second->getState(Chunk::CHUNK_STATE_GENERATED))) {
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)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(x - 1 < 0|| (chunks.find(a1, calculateIndex(x-1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y+1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(y - 1 < 0|| (chunks.find(a1, calculateIndex(x, y-1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z + 1 > 1023 || (chunks.find(a1, calculateIndex(x, y, z+1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(z - 1 < 0|| (chunks.find(a1, 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));
} }
a.release();
} }
debug::window::set_parameter("update_chunks_total", (int) (chunks.size()));
debug::window::set_parameter("update_chunks_bucket", (int) (chunks.max_size()));
Chunk::Chunk* n; // Eventually delete old chunks
nUnloaded = 0; int i;
while(chunks_todelete.try_pop(n)){ ChunkTable::accessor a;
int x = static_cast<uint16_t>(n->getPosition().x); while(chunks_todelete.try_pop(i)){
int y = static_cast<uint16_t>(n->getPosition().y); const int index = i;
int z = static_cast<uint16_t>(n->getPosition().z); if(chunks.find(a, index)){
if(x > 1023 || y > 1023 || z > 1023) continue; Chunk::Chunk* c = a->second;
const uint32_t index = calculateIndex(x, y, z); if(chunks.erase(a)){
nUnloaded++;
chunks.erase(index); renderer::getDeleteIndexQueue().push(index);
//delete n; delete c;
nUnloaded++; } else std::cout << "failed to delete " << index << std::endl;
} else std::cout << "no such element found to delete\n";
} }
// Eventually create new chunks near the player
for(int i = 0; i < chunks_volume; i++) {
const int16_t x = chunks_indices[i][0] + chunkX;
const int16_t y = chunks_indices[i][1] + chunkY;
const int16_t z = chunks_indices[i][2] + chunkZ;
if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue;
nExplored++;
const int32_t index = Chunk::calculateIndex(x, y, z);
ChunkTable::accessor a;
if(!chunks.find(a, index)) chunks.emplace(a, std::make_pair(index, new
Chunk::Chunk(glm::vec3(x,y,z))));
}
// Now update all the chunks
oneapi::tbb::parallel_for(chunks.range(), [&](ChunkTable::range_type &r){
for(ChunkTable::iterator a = r.begin(); a != r.end(); a++){
Chunk::Chunk* c = a->second;
int x = c->getPosition().x;
int y = c->getPosition().y;
int z = c->getPosition().z;
int distx = x - chunkX;
int disty = y - chunkY;
int distz = z - chunkZ;
int gen{0}, mesh{0}, unload{0};
if(
distx >= -RENDER_DISTANCE && distx < RENDER_DISTANCE &&
disty >= -RENDER_DISTANCE && disty < RENDER_DISTANCE &&
distz >= -RENDER_DISTANCE && distz < RENDER_DISTANCE
){
// If within distance
// Reset out-of-view flags
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, false);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
// If not yet generated
if(!c->getState(Chunk::CHUNK_STATE_GENERATED)){
if(c->isFree()){
// Generate
c->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, true);
chunks_to_generate_queue.push(std::make_pair(c, GENERATION_PRIORITY_NORMAL));
}
}else{
gen++;
// If generated but not yet meshed
// TODO: not getting meshed
if(!c->getState(Chunk::CHUNK_STATE_MESHED)){
ChunkTable::accessor a1;
if(c->isFree() &&
(distx+1 >= RENDER_DISTANCE || x + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x+1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(distx-1 < -RENDER_DISTANCE || x - 1 < 0 || (chunks.find(a1, Chunk::calculateIndex(x-1, y, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(disty+1 >= RENDER_DISTANCE || y + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y+1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(disty-1 < -RENDER_DISTANCE || y - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y-1, z)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(distz+1 >= RENDER_DISTANCE || z + 1 > 1023 || (chunks.find(a1, Chunk::calculateIndex(x, y, z+1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED))) &&
(distz-1 < -RENDER_DISTANCE || z - 1 < 0|| (chunks.find(a1, Chunk::calculateIndex(x, y, z-1)) &&
a1->second->getState(Chunk::CHUNK_STATE_GENERATED)))
)
{
// Mesh
c->setState(Chunk::CHUNK_STATE_IN_MESHING_QUEUE, true);
chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_NORMAL));
}
}else{
mesh++;
// If generated & meshed, render
}
}
}else{
// If not within distance
if(c->getState(Chunk::CHUNK_STATE_OUTOFVISION)){
// If enough time has passed, set to be deleted
if(c->isFree() && glfwGetTime() - c->unload_timer >= UNLOAD_TIMEOUT){
c->setState(Chunk::CHUNK_STATE_IN_DELETING_QUEUE, true);
chunks_todelete.push(c->getIndex());
unload++;
}
}else{
// Mark as out of view, and start waiting time
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
c->unload_timer = glfwGetTime();
}
}
nGenerated += gen;
nMeshed += mesh;
nMarkUnload += unload;
}
});
std::cout << "time: " << glfwGetTime() << "\ntotal: " << chunks.size() << "\ngenerated: " << nGenerated << "\nmeshed: "
<< nMeshed << "\nunloaded from prev loop: " << nUnloaded << "\nnew marked for unload: " << nMarkUnload << std::endl;
} }
} }
// uint32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1023). There's actually two spare bits std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; }
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; }
void stop() { void stop() {
should_run=false; should_run=false;
std::cout << "waiting for secondary threads to finish\n";
update_thread.join(); update_thread.join();
std::cout << "update thread terminated\n";
gen_thread.join(); gen_thread.join();
std::cout << "generation thread terminated\n";
mesh_thread.join(); mesh_thread.join();
} std::cout << "meshing thread terminated\n";
void destroy(){
/*for(const auto& n : chunks){
delete n.second;
}*/
} }
void blockpick(bool place){ void destroy(){
}
void blockpick(WorldUpdateMsg& msg){
// 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 pos = glm::vec3(theCamera.getAtomicPosX(), theCamera.getAtomicPosY(), glm::vec3 pos = msg.cameraPos;
theCamera.getAtomicPosZ());
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 = theCamera.getPos() + t * theCamera.getFront(); 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;
@ -225,7 +282,7 @@ namespace chunkmanager
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::accessor a; 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; Chunk::Chunk* c = a->second;
if(!c->getState(Chunk::CHUNK_STATE_GENERATED) || c->getState(Chunk::CHUNK_STATE_EMPTY)) continue; if(!c->getState(Chunk::CHUNK_STATE_GENERATED) || c->getState(Chunk::CHUNK_STATE_EMPTY)) continue;
@ -236,7 +293,7 @@ namespace chunkmanager
if(b != Block::AIR){ if(b != Block::AIR){
// if placing a new block // 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 // Go half a block backwards on the ray, to check the block where the ray was
// coming from // coming from
// Doing this and not using normal adds the unexpected (and unwanted) ability to // Doing this and not using normal adds the unexpected (and unwanted) ability to
@ -254,7 +311,7 @@ namespace chunkmanager
// 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(px1 < 0 || py1 < 0 || pz1 < 0 || px1 >= 1024 || py1 >= 1024 || pz1 >= 1024) return; if(px1 < 0 || py1 < 0 || pz1 < 0 || px1 >= 1024 || py1 >= 1024 || pz1 >= 1024) return;
ChunkTable::accessor a1; 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; Chunk::Chunk* c1 = a1->second;
// place the new block (only stone for now) // place the new block (only stone for now)
c1->setBlock((Block)block_to_place, bx1, by1, bz1); c1->setBlock((Block)block_to_place, bx1, by1, bz1);
@ -263,8 +320,9 @@ namespace chunkmanager
chunks_to_mesh_queue.push(std::make_pair(c1, MESHING_PRIORITY_PLAYER_EDIT)); 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)); 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", (int)msg.msg_type);
debug::window::set_parameter("block_last_action_block_type", (int)(Block::STONE)); 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_x", px1*CHUNK_SIZE + bx1);
debug::window::set_parameter("block_last_action_y", px1*CHUNK_SIZE + by1); debug::window::set_parameter("block_last_action_y", px1*CHUNK_SIZE + by1);
debug::window::set_parameter("block_last_action_z", px1*CHUNK_SIZE + bz1); debug::window::set_parameter("block_last_action_z", px1*CHUNK_SIZE + bz1);
@ -276,20 +334,20 @@ namespace chunkmanager
// When necessary, also mesh nearby chunks // When necessary, also mesh nearby chunks
ChunkTable::accessor a1, a2, b1, b2, c1, c2; 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); chunks_to_mesh_queue.push(std::make_pair(a1->second, MESHING_PRIORITY_PLAYER_EDIT));
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); chunks_to_mesh_queue.push(std::make_pair(a2->second, MESHING_PRIORITY_PLAYER_EDIT));
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); 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, 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); 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, 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); 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, 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); chunks_to_mesh_queue.push(std::make_pair(c2->second, MESHING_PRIORITY_PLAYER_EDIT));
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_block_type", (int) (Block::AIR));
debug::window::set_parameter("block_last_action_x", px*CHUNK_SIZE + bx); 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_y", py*CHUNK_SIZE + by);
@ -312,7 +370,7 @@ namespace chunkmanager
//std::cout << "Block at " << x << ", " << y << ", " << z << " is in chunk " << cx << "," << cy << "," << cz << "\n"; //std::cout << "Block at " << x << ", " << y << ", " << z << " is in chunk " << cx << "," << cy << "," << cz << "\n";
ChunkTable::accessor a; 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 { else {
int bx = x % CHUNK_SIZE; int bx = x % CHUNK_SIZE;
int by = y % CHUNK_SIZE; int by = y % CHUNK_SIZE;

View File

@ -11,15 +11,21 @@
#include "spacefilling.hpp" #include "spacefilling.hpp"
#include "utils.hpp" #include "utils.hpp"
#define CHUNKMESHER_BORDERS 0
namespace chunkmesher{ namespace chunkmesher{
oneapi::tbb::concurrent_queue<MeshData*> MeshDataQueue; ChunkMeshDataQueue MeshDataQueue;
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
oneapi::tbb::concurrent_queue<MeshData*>& getMeshDataQueue(){ return MeshDataQueue; } void init(){
for(int i = 0; i < 10; i++)
MeshDataQueue.push(new ChunkMeshData{});
}
void mesh(Chunk::Chunk* chunk) void mesh(Chunk::Chunk* chunk)
{ {
MeshData* mesh_data; ChunkMeshData* mesh_data;
if(!MeshDataQueue.try_pop(mesh_data)) return; if(!MeshDataQueue.try_pop(mesh_data)) return;
/* /*
@ -38,28 +44,13 @@ void mesh(Chunk::Chunk* chunk)
*/ */
// Cleanup previous data // Cleanup previous data
mesh_data->numVertices = 0; mesh_data->clear();
mesh_data->chunk = chunk; mesh_data->message_type = ChunkMeshDataType::MESH_UPDATE;
mesh_data->vertices.clear(); mesh_data->index = chunk->getIndex();
mesh_data->extents.clear(); mesh_data->position = chunk->getPosition();
mesh_data->texinfo.clear();
// Abort if chunk is empty std::unique_ptr<Block[]> blocks;
if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)){
chunk->setState(Chunk::CHUNK_STATE_MESHED, true);
renderer::getMeshDataQueue().push(mesh_data);
return;
}
// convert tree to array since it is easier to work with it
int length{0}; int length{0};
std::unique_ptr<Block[]> blocks = chunk->getBlocksArray(&length);
if(length == 0) {
chunk->setState(Chunk::CHUNK_STATE_MESHED, true);
renderer::getMeshDataQueue().push(mesh_data);
return;
}
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};
@ -67,6 +58,14 @@ void mesh(Chunk::Chunk* chunk)
int dv[]{0, 0, 0}; int dv[]{0, 0, 0};
std::array<Block, CHUNK_SIZE * CHUNK_SIZE> mask; std::array<Block, CHUNK_SIZE * CHUNK_SIZE> mask;
// Abort if chunk is empty
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);
if(length == 0) goto end;
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
@ -98,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;
@ -112,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;
@ -128,9 +129,15 @@ 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 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;
#else
mask[n++] = b1 == b2 ? Block::NULLBLK
: backFace ? b1 == Block::AIR ? b2 : Block::NULLBLK
: b2 == Block::AIR ? b1 : Block::NULLBLK;
#endif
} }
} }
@ -184,14 +191,15 @@ 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->numVertices++; mesh_data->num_vertices++;
} }
for (l = 0; l < h; ++l) for (l = 0; l < h; ++l)
@ -220,49 +228,8 @@ void mesh(Chunk::Chunk* chunk)
} }
} }
end:
chunk->setState(Chunk::CHUNK_STATE_MESHED, true); chunk->setState(Chunk::CHUNK_STATE_MESHED, true);
renderer::getMeshDataQueue().push(mesh_data); renderer::getMeshDataQueue().push(mesh_data);
} }
void sendtogpu(MeshData* mesh_data)
{
if (mesh_data->numVertices > 0)
{
if(mesh_data->chunk->VAO == 0) mesh_data->chunk->createBuffers();
// bind the Vertex Array Object first, then bind and set vertex buffer(s), and then configure vertex attributes(s).
glBindVertexArray(mesh_data->chunk->VAO);
// position attribute
glBindBuffer(GL_ARRAY_BUFFER, mesh_data->chunk->VBO);
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);
glEnableVertexAttribArray(0);
// normal attribute
glBindBuffer(GL_ARRAY_BUFFER, mesh_data->chunk->extentsBuffer);
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));
glEnableVertexAttribArray(1);
// texcoords attribute
glBindBuffer(GL_ARRAY_BUFFER, mesh_data->chunk->texinfoBuffer);
glBufferData(GL_ARRAY_BUFFER, mesh_data->texinfo.size() * sizeof(GLfloat), &(mesh_data->texinfo[0]), GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)0);
glBindVertexArray(0);
// save the number of indices of the mesh, it is needed later for drawing
mesh_data->chunk->numVertices = (GLuint)(mesh_data->numVertices);
// once data has been sent to the GPU, it can be cleared from system RAM
mesh_data->vertices.clear();
mesh_data->extents.clear();
mesh_data->texinfo.clear();
}
// mark the chunk mesh has loaded on GPU
mesh_data->chunk->setState(Chunk::CHUNK_STATE_MESH_LOADED, true);
}
}; };

63
src/controls.cpp Normal file
View File

@ -0,0 +1,63 @@
#include "controls.hpp"
#include "camera.hpp"
#include "chunkmanager.hpp"
#include "globals.hpp"
#include "renderer.hpp"
namespace controls{
WorldUpdateMsgQueue MsgQueue;
float lastBlockPick=0.0;
bool blockpick = false;
bool cursor = false;
void init(){
}
void update(GLFWwindow* window){
// Reset blockping timeout have passed
float current_time = glfwGetTime();
/* 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){
// 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) {
cursor = !cursor;
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,13 +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", glGetString(GL_VENDOR), glGetString(GL_RENDERER));
//ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr); //ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr);
} }
@ -95,18 +99,15 @@ namespace debug{
} }
if(ImGui::CollapsingHeader("Mesh")){ if(ImGui::CollapsingHeader("Mesh")){
ImGui::Text("Total chunks updated: %d", ImGui::Text("Total chunk meshed: %d",
std::any_cast<int>(parameters.at("render_chunks_total"))); std::any_cast<int>(parameters.at("render_chunks_total")));
ImGui::Text("Of which renderable (not empty): %d",
std::any_cast<int>(parameters.at("render_chunks_renderable")));
ImGui::Text("Chunks rendered: %d", ImGui::Text("Chunks rendered: %d",
std::any_cast<int>(parameters.at("render_chunks_rendered"))); std::any_cast<int>(parameters.at("render_chunks_rendered")));
ImGui::Text("Frustum culled: %d", ImGui::Text("Frustum culled: %d",
std::any_cast<int>(parameters.at("render_chunks_culled"))); std::any_cast<int>(parameters.at("render_chunks_culled")));
ImGui::Text("Chunks out of view: %d", ImGui::Text("Total vertices in the scene: %d",
std::any_cast<int>(parameters.at("render_chunks_oof")));
if(parameters.find("render_chunks_deleted") != parameters.end())
ImGui::Text("Chunks deleted: %d",
std::any_cast<int>(parameters.at("render_chunks_deleted")));
ImGui::Text("Vertices in the scene: %d",
std::any_cast<int>(parameters.at("render_chunks_vertices"))); std::any_cast<int>(parameters.at("render_chunks_vertices")));
ImGui::Checkbox("Wireframe", ImGui::Checkbox("Wireframe",
std::any_cast<bool*>(parameters.at("wireframe_return"))); std::any_cast<bool*>(parameters.at("wireframe_return")));
@ -115,13 +116,21 @@ 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("Chunks freed from memory: %d", ImGui::Text("Buckets in hash map: %d",
std::any_cast<int>(parameters.at("update_chunks_delete")));*/ std::any_cast<int>(parameters.at("update_chunks_buckets")));
ImGui::Text("Bucket size: %d", ImGui::Text("Chunks generated: %d",
std::any_cast<int>(parameters.at("update_chunks_bucket"))); std::any_cast<int>(parameters.at("update_chunks_generated")));
ImGui::Text("Chunks meshed: %d",
std::any_cast<int>(parameters.at("update_chunks_meshed")));
ImGui::Text("Chunks actually freed from memory: %d",
std::any_cast<int>(parameters.at("update_chunks_freed")));
ImGui::Text("Chunks explored: %d",
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::cout << e.what();
}catch(const std::out_of_range& e){
std::cout << e.what();
} }
ImGui::End(); ImGui::End();

View File

@ -1,15 +1,16 @@
#include <glad/glad.h> #include <glad/glad.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include "main.hpp"
#include <iostream> #include <iostream>
#include <thread> #include <thread>
#define GLOBALS_DEFINER #define GLOBALS_DEFINER
#include "globals.hpp" #include "globals.hpp"
#undef GLOBALS_DEFINER #undef GLOBALS_DEFINER
#include "chunkmanager.hpp" #include "chunkmanager.hpp"
#include "main.hpp" #include "controls.hpp"
#include "debugwindow.hpp" #include "debugwindow.hpp"
#include "renderer.hpp" #include "renderer.hpp"
#include "spacefilling.hpp" #include "spacefilling.hpp"
@ -20,10 +21,6 @@ float lastFrame = 0.0f; // Time of last frame
float lastFPSFrame = 0.0f; float lastFPSFrame = 0.0f;
int frames = 0; int frames = 0;
float lastBlockPick=0.0;
bool blockpick = false;
bool cursor = false;
int main() int main()
{ {
@ -61,8 +58,6 @@ 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
std::cout << "Using GPU: " << glGetString(GL_VENDOR) << " " << glGetString(GL_RENDERER) << "\n";
wireframe = false; wireframe = false;
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);
@ -70,7 +65,9 @@ int main()
} }
SpaceFilling::initLUT(); SpaceFilling::initLUT();
controls::init();
chunkmanager::init(); chunkmanager::init();
chunkmesher::init();
debug::window::init(window); debug::window::init(window);
renderer::init(window); renderer::init(window);
@ -94,14 +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);
// Only handle window closing here
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
// Input processing // Input processing
processInput(window); controls::update(window);
// Camera // Camera
theCamera.update(window, deltaTime); theCamera.update(window, deltaTime);
// Reset blockping timeout if 200ms have passed
if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false;
// Render pass // Render pass
renderer::render(); renderer::render();
@ -115,9 +113,9 @@ int main()
chunkmanager::stop(); chunkmanager::stop();
// Cleanup allocated memory // Cleanup allocated memory
chunkmanager::destroy();
renderer::destroy();
debug::window::destroy(); debug::window::destroy();
renderer::destroy();
chunkmanager::destroy();
glfwTerminate(); glfwTerminate();
return 0; return 0;
@ -134,33 +132,3 @@ void mouse_callback(GLFWwindow *window, double xpos, double ypos)
{ {
theCamera.mouseCallback(window, xpos, 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

@ -1,7 +1,9 @@
#include "renderer.hpp" #include "renderer.hpp"
#include <oneapi/tbb/concurrent_vector.h> #include <glm/ext.hpp>
#include <oneapi/tbb/concurrent_queue.h> #include <glm/gtx/string_cast.hpp>
#include <oneapi/tbb/concurrent_hash_map.h>
#include "chunkmanager.hpp" #include "chunkmanager.hpp"
#include "chunkmesher.hpp" #include "chunkmesher.hpp"
@ -12,23 +14,25 @@
#include "stb_image_write.h" #include "stb_image_write.h"
namespace renderer{ namespace renderer{
RenderSet chunks_torender; typedef oneapi::tbb::concurrent_hash_map<int32_t, RenderInfo*> RenderTable;
oneapi::tbb::concurrent_vector<Chunk::Chunk*> render_todelete;
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*> MeshDataQueue; RenderTable ChunksToRender;
ChunkMeshDataQueue MeshDataQueue;
IndexQueue MeshDataToDelete;
Shader* theShader, *quadShader; Shader* theShader, *quadShader;
GLuint chunkTexture; GLuint chunkTexture;
Shader* getRenderShader() { return theShader; }
RenderSet& getChunksToRender(){ return chunks_torender; }
oneapi::tbb::concurrent_queue<chunkmesher::MeshData*>& getMeshDataQueue(){ return MeshDataQueue; }
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
@ -138,26 +142,56 @@ namespace renderer{
theShader->use(); theShader->use();
theShader->setVec3("viewPos", cameraPos); theShader->setVec3("viewPos", cameraPos);
chunkmesher::MeshData* m; ChunkMeshData* m;
while(MeshDataQueue.try_pop(m)){ while(MeshDataQueue.try_pop(m)){
chunkmesher::sendtogpu(m); RenderTable::accessor a;
RenderInfo* render_info;
if(ChunksToRender.find(a, m->index)){
render_info = a->second;
render_info->position = m->position;
render_info->num_vertices = m->num_vertices;
std::cout << "index collision on " << render_info->index << std::endl;
}else{
render_info = new RenderInfo();
render_info->index = m->index;
render_info->position = m->position;
render_info->num_vertices = m->num_vertices;
ChunksToRender.emplace(a, std::make_pair(render_info->index, render_info));
}
send_chunk_to_gpu(m, render_info);
chunkmesher::getMeshDataQueue().push(m); chunkmesher::getMeshDataQueue().push(m);
} }
for(auto& c : chunks_torender){ // TODO: implement removal of chunks from rendering
float dist = glm::distance(c->getPosition(), cameraChunkPos); int32_t queue_index;
if(dist <= static_cast<float>(RENDER_DISTANCE)){ while(MeshDataToDelete.try_pop(queue_index)){
if(!c->getState(Chunk::CHUNK_STATE_MESH_LOADED)) continue; RenderTable::accessor a;
if(ChunksToRender.find(a, queue_index)){
RenderInfo* render_info = a->second;
render_info->deallocateBuffers();
delete render_info;
ChunksToRender.erase(a);
}
}
// Render the chunks
// 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++){
RenderInfo* render_info = i->second;
if(render_info->num_vertices > 0)
{
total++;
// Increase total vertex count // Increase total vertex count
vertices += c->numVertices; vertices += render_info->num_vertices;
// reset out-of-vision and unload flags
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, false);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
// Perform frustum culling and eventually render // Perform frustum culling and eventually render
glm::vec3 chunk = c->getPosition(); glm::vec3 chunk = render_info->position;
glm::vec4 chunkW = glm::vec4(chunk.x*static_cast<float>(CHUNK_SIZE), chunk.y*static_cast<float>(CHUNK_SIZE), chunk.z*static_cast<float>(CHUNK_SIZE),1.0); glm::vec4 chunkW = glm::vec4(chunk.x*static_cast<float>(CHUNK_SIZE), chunk.y*static_cast<float>(CHUNK_SIZE), chunk.z*static_cast<float>(CHUNK_SIZE),1.0);
glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk); glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk);
@ -179,57 +213,25 @@ namespace renderer{
if (!out) if (!out)
{ {
if(c->numVertices > 0) theShader->setMat4("model", model);
{ theShader->setMat4("view", theCamera.getView());
theShader->setMat4("model", model); theShader->setMat4("projection", theCamera.getProjection());
theShader->setMat4("view", theCamera.getView());
theShader->setMat4("projection", theCamera.getProjection());
glBindVertexArray(c->VAO); glBindVertexArray(render_info->VAO);
glDrawArrays(GL_POINTS, 0, c->numVertices); glDrawArrays(GL_POINTS, 0, render_info->num_vertices);
glBindVertexArray(0); glBindVertexArray(0);
toGpu++; toGpu++;
}
} }
}else{
// When the chunk is outside render distance
if(c->getState(Chunk::CHUNK_STATE_OUTOFVISION)){
oof++;
if(glfwGetTime() - c->unload_timer > UNLOAD_TIMEOUT){
// If chunk was already out and enough time has passed
// Mark the chunk to be unloaded
// And mark is to be removed from the render set
render_todelete.push_back(c);
}
} else{
// Mark has out of vision and annotate when it started
c->setState(Chunk::CHUNK_STATE_OUTOFVISION, true);
c->setState(Chunk::CHUNK_STATE_UNLOADED, false);
c->unload_timer = glfwGetTime();
}
} }
} }
total = chunks_torender.size(); debug::window::set_parameter("render_chunks_total", (int)(ChunksToRender.size()));
debug::window::set_parameter("render_chunks_total", total);
debug::window::set_parameter("render_chunks_rendered", toGpu); debug::window::set_parameter("render_chunks_rendered", toGpu);
debug::window::set_parameter("render_chunks_renderable", total);
debug::window::set_parameter("render_chunks_culled", total-toGpu); debug::window::set_parameter("render_chunks_culled", total-toGpu);
debug::window::set_parameter("render_chunks_oof", oof);
debug::window::set_parameter("render_chunks_deleted", (int) (render_todelete.size()));
debug::window::set_parameter("render_chunks_vertices", vertices); debug::window::set_parameter("render_chunks_vertices", vertices);
for(auto& c : render_todelete){
// we can get away with unsafe erase as access to the container is only done by this
// thread
c->deleteBuffers();
chunks_torender.unsafe_erase(c);
chunkmanager::getDeleteVector().push(c);
}
render_todelete.clear();
/* DISPLAY TEXTURE ON A QUAD THAT FILLS THE SCREEN */ /* DISPLAY TEXTURE ON A QUAD THAT FILLS THE SCREEN */
// Now to render the quad, with the texture on top // Now to render the quad, with the texture on top
// Switch to the default frame buffer // Switch to the default frame buffer
@ -251,6 +253,39 @@ namespace renderer{
debug::window::render(); debug::window::render();
} }
void send_chunk_to_gpu(ChunkMeshData* mesh_data, RenderInfo* render_info)
{
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).
glBindVertexArray(render_info->VAO);
// TODO: change GL_STATIC_DRAW to the one that means "few redraws and further in between"
// position attribute
glBindBuffer(GL_ARRAY_BUFFER, render_info->VBO);
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);
glEnableVertexAttribArray(0);
// normal attribute
glBindBuffer(GL_ARRAY_BUFFER, render_info->extentsBuffer);
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));
glEnableVertexAttribArray(1);
// texcoords attribute
glBindBuffer(GL_ARRAY_BUFFER, render_info->texinfoBuffer);
glBufferData(GL_ARRAY_BUFFER, mesh_data->texinfo.size() * sizeof(GLfloat), &(mesh_data->texinfo[0]), GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void *)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();
}