Compare commits
38 Commits
fix-multit
...
main
Author | SHA1 | Date |
---|---|---|
EmaMaker | fa3d36ec58 | |
EmaMaker | ebef608000 | |
EmaMaker | 0b294bb35b | |
EmaMaker | f3d8ffed54 | |
EmaMaker | 0560fd43c6 | |
EmaMaker | 62212a22ab | |
EmaMaker | 357f67aac1 | |
EmaMaker | 1b4cef8958 | |
EmaMaker | e4c6eb50f3 | |
EmaMaker | 9b5939d256 | |
EmaMaker | 8401a3dca7 | |
EmaMaker | 255460892d | |
EmaMaker | afdd622ec2 | |
EmaMaker | 93bc0e7066 | |
EmaMaker | 8544620899 | |
EmaMaker | a4f1e5fc1f | |
EmaMaker | 78b65894b7 | |
EmaMaker | 355da726f6 | |
EmaMaker | ba95db4678 | |
EmaMaker | 7c82a71dd0 | |
EmaMaker | 7786d4f04d | |
EmaMaker | 1822911845 | |
EmaMaker | d0ddf2256f | |
EmaMaker | 88abf21502 | |
EmaMaker | f4947d5f70 | |
EmaMaker | 4e7fadd2b9 | |
EmaMaker | d1b151f92f | |
EmaMaker | 44edf3e53a | |
EmaMaker | 3adb061057 | |
EmaMaker | 1a50d1fb84 | |
EmaMaker | 60bbc85682 | |
EmaMaker | 490f207e39 | |
EmaMaker | c6d00c4200 | |
EmaMaker | 2a57796ed2 | |
EmaMaker | ca043bac68 | |
EmaMaker | f6f4057109 | |
EmaMaker | 353ef37186 | |
EmaMaker | 880c634be0 |
|
@ -3,8 +3,8 @@ cmake_minimum_required(VERSION 3.2)
|
|||
project(cmake-project-template)
|
||||
|
||||
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 -g")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O3")
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g")
|
||||
|
||||
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
|
||||
|
||||
|
|
|
@ -8,9 +8,6 @@
|
|||
|
||||
#include <atomic>
|
||||
|
||||
#include "chunk.hpp"
|
||||
#include "debugwindow.hpp"
|
||||
|
||||
class Camera
|
||||
{
|
||||
|
||||
|
@ -55,16 +52,6 @@ public:
|
|||
cameraFront = glm::normalize(direction);
|
||||
|
||||
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)
|
||||
|
@ -129,7 +116,7 @@ public:
|
|||
|
||||
|
||||
private:
|
||||
glm::vec3 cameraPos = glm::vec3(512.0, 256.0f, 512.0f);
|
||||
glm::vec3 cameraPos = glm::vec3(512.0, 80.0f, 512.0f);
|
||||
glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||
glm::vec3 cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
glm::vec3 direction = glm::vec3(0.0f);
|
||||
|
|
|
@ -20,38 +20,47 @@
|
|||
#define CHUNK_VOLUME (CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE)
|
||||
#define CHUNK_MAX_INDEX (CHUNK_VOLUME - 1)
|
||||
|
||||
// int32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1023). There's actually two spare bits
|
||||
typedef int32_t chunk_index_t;
|
||||
typedef int16_t chunk_intcoord_t;
|
||||
typedef uint16_t chunk_state_t;
|
||||
|
||||
namespace Chunk
|
||||
{
|
||||
|
||||
constexpr uint16_t CHUNK_STATE_GENERATED = 1;
|
||||
constexpr uint16_t CHUNK_STATE_MESHED = 2;
|
||||
constexpr uint16_t CHUNK_STATE_OUTOFVISION = 4;
|
||||
constexpr uint16_t CHUNK_STATE_UNLOADED = 8;
|
||||
constexpr uint16_t CHUNK_STATE_EMPTY = 16;
|
||||
constexpr uint16_t CHUNK_STATE_IN_GENERATION_QUEUE = 32;
|
||||
constexpr uint16_t CHUNK_STATE_IN_MESHING_QUEUE = 64;
|
||||
constexpr uint16_t CHUNK_STATE_IN_DELETING_QUEUE = 128;
|
||||
chunk_index_t calculateIndex(chunk_intcoord_t i, chunk_intcoord_t j, chunk_intcoord_t k);
|
||||
chunk_index_t calculateIndex(glm::vec3 pos);
|
||||
|
||||
constexpr chunk_state_t CHUNK_STATE_GENERATED = 1;
|
||||
constexpr chunk_state_t CHUNK_STATE_MESHED = 2;
|
||||
constexpr chunk_state_t CHUNK_STATE_MESH_LOADED = 4;
|
||||
constexpr chunk_state_t CHUNK_STATE_LOADED = 8;
|
||||
constexpr chunk_state_t CHUNK_STATE_OUTOFVISION = 16;
|
||||
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);
|
||||
int32_t calculateIndex(int16_t x, int16_t y, int16_t z);
|
||||
int32_t calculateIndex(glm::vec3 pos);
|
||||
|
||||
class Chunk
|
||||
{
|
||||
|
||||
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();
|
||||
|
||||
public:
|
||||
void createBuffers();
|
||||
void deleteBuffers();
|
||||
|
||||
glm::vec3 getPosition() { return this->position; }
|
||||
void setState(uint16_t nstate, bool value);
|
||||
uint16_t getTotalState() { return this->state; }
|
||||
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 setState(chunk_state_t nstate, bool value);
|
||||
bool getState(chunk_state_t n) { return (this->state & n) == n; }
|
||||
bool isFree(){ return !(
|
||||
this->getState(CHUNK_STATE_IN_GENERATION_QUEUE) ||
|
||||
this->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 setBlocks(int start, int end, Block b);
|
||||
|
@ -60,17 +69,15 @@ namespace Chunk
|
|||
std::unique_ptr<Block[]> getBlocksArray(int* len) { return (this->blocks.toArray(len)); }
|
||||
|
||||
public:
|
||||
GLuint VAO{0}, VBO{0}, extentsBuffer{0}, texinfoBuffer{0}, numVertices{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; }
|
||||
chunk_index_t getIndex(){ return this->index; }
|
||||
|
||||
private:
|
||||
glm::vec3 position{};
|
||||
IntervalMap<Block> blocks{};
|
||||
|
||||
int32_t index;
|
||||
std::atomic_uint16_t state{0};
|
||||
std::atomic<chunk_state_t> state{0};
|
||||
chunk_index_t index;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
#include <oneapi/tbb/concurrent_hash_map.h>
|
||||
#include <oneapi/tbb/concurrent_queue.h>
|
||||
#include <oneapi/tbb/concurrent_priority_queue.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <thread>
|
||||
|
||||
#include "chunk.hpp"
|
||||
|
@ -13,7 +11,7 @@
|
|||
#include "worldupdatemessage.h"
|
||||
|
||||
// Seconds to be passed outside of render distance for a chunk to be destroyed
|
||||
#define UNLOAD_TIMEOUT 5
|
||||
#define UNLOAD_TIMEOUT 10
|
||||
|
||||
#define MESHING_PRIORITY_NORMAL 0
|
||||
#define MESHING_PRIORITY_PLAYER_EDIT 10
|
||||
|
@ -21,10 +19,7 @@
|
|||
|
||||
namespace chunkmanager
|
||||
{
|
||||
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 oneapi::tbb::concurrent_hash_map<chunk_index_t, Chunk::Chunk*> ChunkTable;
|
||||
typedef std::pair<Chunk::Chunk*, uint8_t> ChunkPQEntry;
|
||||
// The comparing function to use
|
||||
struct compare_f {
|
||||
|
@ -39,7 +34,7 @@ namespace chunkmanager
|
|||
void stop();
|
||||
void destroy();
|
||||
WorldUpdateMsgQueue& getWorldUpdateQueue();
|
||||
std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices();
|
||||
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume>& getChunksIndices();
|
||||
Block getBlockAtPos(int x, int y, int z);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
#ifndef CHUNK_MESH_DATA_H
|
||||
#define CHUNK_MESH_DATA_H
|
||||
|
||||
#include <oneapi/tbb/concurrent_queue.h>
|
||||
#include "chunk.hpp"
|
||||
|
||||
enum class ChunkMeshDataType{
|
||||
MESH_UPDATE
|
||||
};
|
||||
|
||||
typedef struct ChunkMeshData{
|
||||
int32_t index;
|
||||
chunk_index_t index;
|
||||
glm::vec3 position;
|
||||
int num_vertices = 0;
|
||||
|
||||
|
@ -30,3 +33,4 @@ typedef oneapi::tbb::concurrent_queue<ChunkMeshData*> ChunkMeshDataQueue;
|
|||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
#include "shader.hpp"
|
||||
|
||||
namespace chunkmesher{
|
||||
struct MeshData{
|
||||
Chunk::Chunk* chunk;
|
||||
GLuint numVertices{0};
|
||||
|
||||
std::vector<GLfloat> vertices;
|
||||
std::vector<GLfloat> extents;
|
||||
std::vector<GLfloat> texinfo;
|
||||
};
|
||||
|
||||
ChunkMeshDataQueue& getMeshDataQueue();
|
||||
void init();
|
||||
void mesh(Chunk::Chunk* chunk);
|
||||
|
@ -22,3 +31,4 @@ namespace chunkmesher{
|
|||
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4,15 +4,11 @@
|
|||
#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
|
||||
|
|
|
@ -10,11 +10,11 @@
|
|||
#define extr extern
|
||||
#endif
|
||||
|
||||
#define RENDER_DISTANCE 8
|
||||
#define RENDER_DISTANCE 16
|
||||
|
||||
extr Camera theCamera;
|
||||
//constexpr int chunks_volume = static_cast<int>(1.333333333333*M_PI*(RENDER_DISTANCE*RENDER_DISTANCE*RENDER_DISTANCE));
|
||||
constexpr int chunks_volume = RENDER_DISTANCE*RENDER_DISTANCE*RENDER_DISTANCE*8;
|
||||
// the cube spans in both directions, to each axis has to be multiplied by 2. 2^3=8
|
||||
constexpr int chunks_volume = 8*(RENDER_DISTANCE*RENDER_DISTANCE*RENDER_DISTANCE);
|
||||
extr bool wireframe;
|
||||
|
||||
extr float sines[360];
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *, int, int);
|
||||
void mouse_callback(GLFWwindow *window, double xpos, double ypos);
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef RENDERER_H
|
||||
#define RENDERER_H
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <oneapi/tbb/concurrent_unordered_set.h>
|
||||
#include <oneapi/tbb/concurrent_queue.h>
|
||||
|
||||
#include "chunk.hpp"
|
||||
#include "chunkmesher.hpp"
|
||||
|
@ -10,7 +11,7 @@
|
|||
|
||||
namespace renderer{
|
||||
typedef struct RenderInfo {
|
||||
int32_t index;
|
||||
chunk_index_t index;
|
||||
int num_vertices;
|
||||
glm::vec3 position;
|
||||
bool buffers_allocated=false;
|
||||
|
@ -53,6 +54,8 @@ namespace renderer{
|
|||
ChunkMeshDataQueue& getMeshDataQueue();
|
||||
IndexQueue& getDeleteIndexQueue();
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <oneapi/tbb/concurrent_queue.h>
|
||||
|
||||
#include "block.hpp"
|
||||
|
||||
enum class WorldUpdateMsgType{
|
||||
BLOCKPICK_PLACE,
|
||||
BLOCKPICK_BREAK
|
||||
|
@ -14,6 +16,7 @@ typedef struct WorldUpdateMsg{
|
|||
glm::vec3 cameraPos;
|
||||
glm::vec3 cameraFront;
|
||||
float time;
|
||||
Block block;
|
||||
} WorldUpdateMsg;
|
||||
|
||||
typedef oneapi::tbb::concurrent_queue<WorldUpdateMsg> WorldUpdateMsgQueue;
|
||||
|
|
143
intervalmap.hpp
143
intervalmap.hpp
|
@ -1,143 +0,0 @@
|
|||
#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
|
|
@ -26,29 +26,30 @@ void main(){
|
|||
EmitVertex();
|
||||
|
||||
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);
|
||||
FragPos = vec3(gl_Position);
|
||||
gl_Position = projection * view * gl_Position;
|
||||
EmitVertex();
|
||||
|
||||
TexCoord = vec3(gs_in[0].Extents.y, 0.0, gs_in[0].BlockType);
|
||||
TexCoord = vec3(0.0, gs_in[0].Extents.y, gs_in[0].BlockType);
|
||||
gl_Position = gl_in[0].gl_Position + vec4(0.0, gs_in[0].Extents.y, 0.0, 0.0);
|
||||
FragPos = vec3(gl_Position);
|
||||
gl_Position = projection * view * gl_Position;
|
||||
EmitVertex();
|
||||
|
||||
TexCoord = vec3(gs_in[0].Extents.y, gs_in[0].Extents.z, gs_in[0].BlockType);
|
||||
TexCoord = vec3(gs_in[0].Extents.z, gs_in[0].Extents.y, gs_in[0].BlockType);
|
||||
}
|
||||
else if(gs_in[0].Extents.y == 0){
|
||||
TexCoord = vec3(0.0, gs_in[0].Extents.z, gs_in[0].BlockType);
|
||||
gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, gs_in[0].Extents.z, 0.0);
|
||||
TexCoord = vec3(gs_in[0].Extents.x, 0.0, gs_in[0].BlockType);
|
||||
gl_Position = gl_in[0].gl_Position + vec4(gs_in[0].Extents.x, 0.0, 0.0, 0.0);
|
||||
FragPos = vec3(gl_Position);
|
||||
gl_Position = projection * view * gl_Position;
|
||||
EmitVertex();
|
||||
|
||||
TexCoord = vec3(gs_in[0].Extents.x, 0.0, gs_in[0].BlockType);
|
||||
gl_Position = gl_in[0].gl_Position + vec4(gs_in[0].Extents.x, 0.0, 0.0, 0.0);
|
||||
TexCoord = vec3(0.0, gs_in[0].Extents.z, gs_in[0].BlockType);
|
||||
gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.0, gs_in[0].Extents.z, 0.0);
|
||||
FragPos = vec3(gl_Position);
|
||||
gl_Position = projection * view * gl_Position;
|
||||
EmitVertex();
|
||||
|
|
|
@ -7,4 +7,4 @@ set(SOURCE_FILES main.cpp controls.cpp chunk.cpp chunkmanager.cpp chunkmesher.cp
|
|||
add_executable(OpenGLTest ${SOURCE_FILES})
|
||||
|
||||
target_link_libraries(OpenGLTest glfw tbb glad glm imgui)
|
||||
install(TARGETS OpenGLTest DESTINATION)
|
||||
install(TARGETS OpenGLTest DESTINATION ${DIVISIBLE_INSTALL_BIN_DIR})
|
||||
|
|
|
@ -15,12 +15,13 @@ namespace Chunk
|
|||
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));
|
||||
chunk_index_t calculateIndex(glm::vec3 pos){
|
||||
return calculateIndex(static_cast<chunk_intcoord_t>(pos.x), static_cast<chunk_intcoord_t>(pos.y),
|
||||
static_cast<chunk_intcoord_t>(pos.z));
|
||||
}
|
||||
int32_t calculateIndex(int16_t x, int16_t y, int16_t z){
|
||||
return x | (y << 10) | (z << 20);
|
||||
|
||||
chunk_index_t calculateIndex(chunk_intcoord_t i, chunk_intcoord_t j, chunk_intcoord_t k){
|
||||
return i | (j << 10) | (k << 20);
|
||||
}
|
||||
|
||||
Chunk::Chunk(glm::vec3 pos)
|
||||
|
@ -31,6 +32,10 @@ namespace Chunk
|
|||
this->index = calculateIndex(pos);
|
||||
}
|
||||
|
||||
Chunk ::~Chunk()
|
||||
{
|
||||
}
|
||||
|
||||
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 ||
|
||||
|
@ -49,7 +54,7 @@ namespace Chunk
|
|||
this->blocks.insert(start < 0 ? 0 : start, end >= CHUNK_VOLUME ? CHUNK_VOLUME : end, b);
|
||||
}
|
||||
|
||||
void Chunk::setState(uint16_t nstate, bool value)
|
||||
void Chunk::setState(chunk_state_t nstate, bool value)
|
||||
{
|
||||
if (value)
|
||||
this->state.fetch_or(nstate);
|
||||
|
|
|
@ -63,9 +63,9 @@ std::array<TreeCellInfo, TREE_LUT_SIZE*TREE_LUT_SIZE> treeLUT;
|
|||
|
||||
void generateNoise(Chunk::Chunk *chunk)
|
||||
{
|
||||
int cx = static_cast<int>(chunk->getPosition().x) * CHUNK_SIZE;
|
||||
int cy = static_cast<int>(chunk->getPosition().y) * CHUNK_SIZE;
|
||||
int cz = static_cast<int>(chunk->getPosition().z) * CHUNK_SIZE;
|
||||
int cx = chunk->getPosition().x * CHUNK_SIZE;
|
||||
int cy = chunk->getPosition().y * CHUNK_SIZE;
|
||||
int cz = chunk->getPosition().z * CHUNK_SIZE;
|
||||
|
||||
// Precalculate LUTs
|
||||
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
#include "chunkmanager.hpp"
|
||||
|
||||
#include <oneapi/tbb.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <math.h>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <oneapi/tbb/parallel_for.h>
|
||||
|
||||
#include "block.hpp"
|
||||
#include "chunk.hpp"
|
||||
#include "chunkgenerator.hpp"
|
||||
|
@ -19,6 +18,7 @@
|
|||
#include "debugwindow.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace chunkmanager
|
||||
{
|
||||
|
@ -26,14 +26,13 @@ namespace chunkmanager
|
|||
// controls.cpp)
|
||||
void generate();
|
||||
void mesh();
|
||||
void send_to_chunk_meshing_thread(Chunk::Chunk* c, int priority);
|
||||
|
||||
/* Chunk holding data structures */
|
||||
// Concurrent hash table of 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
|
||||
std::array<std::array<int16_t, 3>, chunks_volume> chunks_indices;
|
||||
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume> chunks_indices;
|
||||
|
||||
/* World Update messaging data structure */
|
||||
WorldUpdateMsgQueue WorldUpdateQueue;
|
||||
|
@ -41,26 +40,21 @@ namespace chunkmanager
|
|||
/* Multithreading */
|
||||
std::atomic_bool should_run;
|
||||
std::thread gen_thread, mesh_thread, update_thread;
|
||||
|
||||
// Queue of chunks to be generated
|
||||
ChunkPriorityQueue chunks_to_generate_queue;
|
||||
// Queue of chunks to be meshed
|
||||
ChunkPriorityQueue chunks_to_mesh_queue;
|
||||
|
||||
/* Block picking */
|
||||
int block_to_place{2};
|
||||
|
||||
WorldUpdateMsgQueue& getWorldUpdateQueue(){ return WorldUpdateQueue; }
|
||||
|
||||
// Init chunkmanager. Chunk indices and start threads
|
||||
void init(){
|
||||
int index{0};
|
||||
constexpr int rr{RENDER_DISTANCE * RENDER_DISTANCE};
|
||||
|
||||
bool b = true;
|
||||
|
||||
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++){
|
||||
for(chunk_intcoord_t i = -RENDER_DISTANCE; i < RENDER_DISTANCE; i++)
|
||||
for(chunk_intcoord_t j = -RENDER_DISTANCE; j < RENDER_DISTANCE; j++)
|
||||
for(chunk_intcoord_t k = -RENDER_DISTANCE; k < RENDER_DISTANCE; k++){
|
||||
|
||||
chunks_indices[index][0]=i;
|
||||
chunks_indices[index][1]=j;
|
||||
|
@ -72,8 +66,6 @@ namespace chunkmanager
|
|||
update_thread = std::thread(update);
|
||||
gen_thread = std::thread(generate);
|
||||
mesh_thread = std::thread(mesh);
|
||||
|
||||
debug::window::set_parameter("block_type_return", &block_to_place);
|
||||
}
|
||||
|
||||
// Method for world generation thread(s)
|
||||
|
@ -81,11 +73,12 @@ namespace chunkmanager
|
|||
while(should_run){
|
||||
ChunkPQEntry entry;
|
||||
if(chunks_to_generate_queue.try_pop(entry)){
|
||||
generateChunk(entry.first);
|
||||
entry.first->setState(Chunk::CHUNK_STATE_IN_GENERATION_QUEUE, false);
|
||||
Chunk::Chunk* chunk = entry.first;
|
||||
generateChunk(chunk);
|
||||
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)
|
||||
|
@ -98,12 +91,18 @@ namespace chunkmanager
|
|||
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(){
|
||||
while(should_run) {
|
||||
// Setup variables for the whole loop
|
||||
/* Setup variables for the whole loop */
|
||||
// Atomic is needed by parallel_for
|
||||
std::atomic_int nUnloaded{0}, nMarkUnload{0}, nExplored{0}, nMeshed{0}, nGenerated{0};
|
||||
std::atomic_int chunkX=static_cast<int>(theCamera.getAtomicPosX() / CHUNK_SIZE);
|
||||
|
@ -122,37 +121,46 @@ namespace chunkmanager
|
|||
}
|
||||
|
||||
|
||||
// Eventually delete old chunks
|
||||
int i;
|
||||
/* Delete old chunks */
|
||||
// In my head it makes sense to first delete old chunks, then create new ones
|
||||
// I think it's easier for memory allocator to re-use the memory that was freed just
|
||||
// before, but this isn't backed be any evidence and I might be wrong. Anyway this way
|
||||
// works fine so I'm gonna keep it.
|
||||
chunk_index_t i;
|
||||
ChunkTable::accessor a;
|
||||
while(chunks_todelete.try_pop(i)){
|
||||
const int index = i;
|
||||
const chunk_index_t index = i;
|
||||
if(chunks.find(a, index)){
|
||||
Chunk::Chunk* c = a->second;
|
||||
// Use the accessor to erase the element
|
||||
// Using the key doesn't work
|
||||
if(chunks.erase(a)){
|
||||
nUnloaded++;
|
||||
renderer::getDeleteIndexQueue().push(index);
|
||||
delete c;
|
||||
} else std::cout << "failed to delete " << index << std::endl;
|
||||
} else {
|
||||
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";
|
||||
}
|
||||
|
||||
// Eventually create new chunks near the player
|
||||
/* Create new chunks around 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;
|
||||
const chunk_intcoord_t x = chunks_indices[i][0] + chunkX;
|
||||
const chunk_intcoord_t y = chunks_indices[i][1] + chunkY;
|
||||
const chunk_intcoord_t z = chunks_indices[i][2] + chunkZ;
|
||||
|
||||
if(x < 0 || y < 0 || z < 0 || x > 1023 || y > 1023 || z > 1023) continue;
|
||||
nExplored++;
|
||||
|
||||
const int32_t index = Chunk::calculateIndex(x, y, z);
|
||||
const chunk_index_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
|
||||
/* 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;
|
||||
|
@ -164,6 +172,7 @@ namespace chunkmanager
|
|||
int disty = y - chunkY;
|
||||
int distz = z - chunkZ;
|
||||
|
||||
// Local variables avoid continously having to call atomic variables
|
||||
int gen{0}, mesh{0}, unload{0};
|
||||
|
||||
if(
|
||||
|
@ -181,38 +190,44 @@ namespace chunkmanager
|
|||
if(!c->getState(Chunk::CHUNK_STATE_GENERATED)){
|
||||
if(c->isFree()){
|
||||
// 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);
|
||||
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() &&
|
||||
|
||||
// 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)) &&
|
||||
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)) &&
|
||||
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)) &&
|
||||
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)) &&
|
||||
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)) &&
|
||||
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)) &&
|
||||
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));
|
||||
|
||||
// Mark as present in the queue before sending to avoid strange
|
||||
// a chunk being marked as in the queue after it was already
|
||||
// processed
|
||||
send_to_chunk_meshing_thread(c, MESHING_PRIORITY_NORMAL);
|
||||
}
|
||||
}else{
|
||||
mesh++;
|
||||
// If generated & meshed, render
|
||||
}
|
||||
}else mesh++;
|
||||
}
|
||||
|
||||
}else{
|
||||
|
@ -232,43 +247,56 @@ namespace chunkmanager
|
|||
}
|
||||
}
|
||||
|
||||
// Update atomic variables only once at the end
|
||||
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;
|
||||
|
||||
debug::window::set_parameter("update_chunks_total", (int)chunks.size());
|
||||
debug::window::set_parameter("update_chunks_generated", (int) nGenerated);
|
||||
debug::window::set_parameter("update_chunks_meshed", (int) nMeshed);
|
||||
debug::window::set_parameter("update_chunks_freed", (int) nUnloaded);
|
||||
debug::window::set_parameter("update_chunks_explored", (int) nExplored);
|
||||
}
|
||||
}
|
||||
|
||||
std::array<std::array<int16_t, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; }
|
||||
std::array<std::array<chunk_intcoord_t, 3>, chunks_volume>& getChunksIndices(){ return chunks_indices; }
|
||||
|
||||
void stop() {
|
||||
should_run=false;
|
||||
|
||||
std::cout << "waiting for secondary threads to finish\n";
|
||||
std::cout << "Waiting for secondary threads to shut down" << std::endl;
|
||||
update_thread.join();
|
||||
std::cout << "update thread terminated\n";
|
||||
std::cout << "Update thread has terminated" << std::endl;
|
||||
gen_thread.join();
|
||||
std::cout << "generation thread terminated\n";
|
||||
std::cout << "Generation thread has terminated" << std::endl;
|
||||
mesh_thread.join();
|
||||
std::cout << "meshing thread terminated\n";
|
||||
|
||||
std::cout << "Meshing thread has terminated" << std::endl;
|
||||
}
|
||||
|
||||
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
|
||||
glm::vec3 pos = msg.cameraPos;
|
||||
glm::vec3 origin = startposition;
|
||||
glm::vec3 pos = origin;
|
||||
glm::vec3 front = startdir;
|
||||
for(float t = 0.0; t <= 10.0; t += 0.5){
|
||||
// traverse the ray a block at the time
|
||||
pos = msg.cameraPos + t*msg.cameraFront;
|
||||
pos = origin + t*front;
|
||||
|
||||
// get which chunk and block the ray is at
|
||||
int px = ((int)(pos.x))/CHUNK_SIZE;
|
||||
|
@ -278,85 +306,196 @@ namespace chunkmanager
|
|||
int by = pos.y - py*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
|
||||
if(px < 0 || py < 0 || pz < 0 || px >= 1024 || py >= 1024 || pz >= 1024) continue;
|
||||
|
||||
ChunkTable::accessor a;
|
||||
ChunkTable::const_accessor a;
|
||||
if(!chunks.find(a, Chunk::calculateIndex(px, py, pz))) continue;
|
||||
Chunk::Chunk* c = a->second;
|
||||
if(!c->getState(Chunk::CHUNK_STATE_GENERATED) || c->getState(Chunk::CHUNK_STATE_EMPTY)) continue;
|
||||
if(!c->isFree() || !c->getState(Chunk::CHUNK_STATE_GENERATED)){
|
||||
a.release();
|
||||
continue;
|
||||
}
|
||||
|
||||
Block b = c->getBlock(bx, by, bz);
|
||||
|
||||
a.release();
|
||||
|
||||
// if the block is non empty
|
||||
if(b != Block::AIR){
|
||||
if(b != Block::AIR) return pos;
|
||||
|
||||
// if placing a new block
|
||||
if(msg.msg_type == WorldUpdateMsgType::BLOCKPICK_PLACE){
|
||||
// Go half a block backwards on the ray, to check the block where the ray was
|
||||
// coming from
|
||||
// Doing this and not using normal adds the unexpected (and unwanted) ability to
|
||||
// place blocks diagonally, without faces colliding with the block that has
|
||||
// been clicked
|
||||
pos -= theCamera.getFront()*0.5f;
|
||||
old_chunk = c;
|
||||
old_bx = bx;
|
||||
old_by = by;
|
||||
old_bz = bz;
|
||||
old_px = px;
|
||||
old_py = py;
|
||||
old_pz = pz;
|
||||
old_pos = pos;
|
||||
|
||||
int px1 = ((int)(pos.x))/CHUNK_SIZE;
|
||||
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;
|
||||
}
|
||||
return glm::vec3(-1);
|
||||
}
|
||||
|
||||
// exit early if the position is invalid or the chunk does not exist
|
||||
if(px1 < 0 || py1 < 0 || pz1 < 0 || px1 >= 1024 || py1 >= 1024 || pz1 >= 1024) return;
|
||||
ChunkTable::accessor a1;
|
||||
if(!chunks.find(a1, 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);
|
||||
void blockpick(WorldUpdateMsg& msg){
|
||||
//std::cout << glm::to_string(ray_intersect(msg.cameraPos, msg.cameraFront)) << std::endl;
|
||||
glm::vec3 ray_pos = ray_intersect(msg.cameraPos, msg.cameraFront);
|
||||
if(ray_pos == glm::vec3(-1)) return;
|
||||
|
||||
// mark the mesh of the chunk the be updated
|
||||
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));
|
||||
// Chunk in which the blockpick is happening
|
||||
int chunkx = (int)(ray_pos.x) / CHUNK_SIZE;
|
||||
int chunky = (int)(ray_pos.y) / CHUNK_SIZE;
|
||||
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;
|
||||
|
||||
debug::window::set_parameter("block_last_action", (int)msg.msg_type);
|
||||
debug::window::set_parameter("block_last_action_block_type",
|
||||
(int)(block_to_place));
|
||||
debug::window::set_parameter("block_last_action_x", px1*CHUNK_SIZE + bx1);
|
||||
debug::window::set_parameter("block_last_action_y", px1*CHUNK_SIZE + by1);
|
||||
debug::window::set_parameter("block_last_action_z", px1*CHUNK_SIZE + bz1);
|
||||
// The chunk must exist, otherwise ray_intersect would have returned an error
|
||||
// Also, the block must be different from AIR
|
||||
|
||||
ChunkTable::accessor a;
|
||||
if(!chunks.find(a, Chunk::calculateIndex(chunkx, chunky, chunkz))) return;
|
||||
Chunk::Chunk* c = a->second;
|
||||
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{
|
||||
// replace the current block with air to remove it
|
||||
c->setBlock( Block::AIR, bx, by, bz);
|
||||
|
||||
chunks_to_mesh_queue.push(std::make_pair(c, MESHING_PRIORITY_PLAYER_EDIT));
|
||||
|
||||
// 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);
|
||||
|
||||
if(tMaxY < tMaxZ){
|
||||
y += stepY;
|
||||
tMaxY += tDeltaY;
|
||||
}else{
|
||||
z += stepZ;
|
||||
tMaxZ += tDeltaZ;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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){
|
||||
|
@ -382,3 +521,4 @@ namespace chunkmanager
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -11,15 +11,18 @@
|
|||
#include "spacefilling.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#define CHUNKMESHER_BORDERS 0
|
||||
#define CHUNK_MESH_DATA_QUANTITY 100
|
||||
#define CHUNK_MESH_WORLD_LIMIT_BORDERS 0
|
||||
|
||||
namespace chunkmesher{
|
||||
|
||||
ChunkMeshDataQueue MeshDataQueue;
|
||||
|
||||
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
|
||||
|
||||
void init(){
|
||||
for(int i = 0; i < 10; i++)
|
||||
void init()
|
||||
{
|
||||
for(int i = 0; i < CHUNK_MESH_DATA_QUANTITY; i++)
|
||||
MeshDataQueue.push(new ChunkMeshData{});
|
||||
}
|
||||
|
||||
|
@ -49,23 +52,23 @@ void mesh(Chunk::Chunk* chunk)
|
|||
mesh_data->index = chunk->getIndex();
|
||||
mesh_data->position = chunk->getPosition();
|
||||
|
||||
std::unique_ptr<Block[]> blocks;
|
||||
// convert tree to array since it is easier to work with it
|
||||
int length{0};
|
||||
std::unique_ptr<Block[]> blocks;
|
||||
|
||||
int k, l, u, v, w, h, n, j, i;
|
||||
int x[]{0, 0, 0};
|
||||
int q[]{0, 0, 0};
|
||||
int du[]{0, 0, 0};
|
||||
int dv[]{0, 0, 0};
|
||||
|
||||
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;
|
||||
|
||||
|
||||
std::array<Block, CHUNK_SIZE * CHUNK_SIZE> mask;
|
||||
for (bool backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b)
|
||||
{
|
||||
// iterate over 3 dimensions
|
||||
|
@ -97,7 +100,6 @@ void mesh(Chunk::Chunk* chunk)
|
|||
Block b1, b2;
|
||||
if(x[dim] >= 0) b1 = blocks[HILBERT_XYZ_ENCODE[x[0]][x[1]][x[2]]];
|
||||
else{
|
||||
// b1 = Block::NULLBLK;
|
||||
int cx = chunk->getPosition().x*CHUNK_SIZE;
|
||||
int cy = chunk->getPosition().y*CHUNK_SIZE;
|
||||
int cz = chunk->getPosition().z*CHUNK_SIZE;
|
||||
|
@ -112,7 +114,6 @@ void mesh(Chunk::Chunk* chunk)
|
|||
if(x[dim] < CHUNK_SIZE - 1) b2 = blocks[HILBERT_XYZ_ENCODE[x[0] + q[0]][x[1]
|
||||
+ q[1]][x[2] + q[2]]];
|
||||
else{
|
||||
//b2 = Block::NULLBLK;
|
||||
int cx = chunk->getPosition().x*CHUNK_SIZE;
|
||||
int cy = chunk->getPosition().y*CHUNK_SIZE;
|
||||
int cz = chunk->getPosition().z*CHUNK_SIZE;
|
||||
|
@ -129,7 +130,7 @@ void mesh(Chunk::Chunk* chunk)
|
|||
// The else case provides face culling for adjacent solid faces
|
||||
// Checking for NULLBLK avoids creating empty faces if nearby chunk was not
|
||||
// yet generated
|
||||
#if CHUNKMESHER_BORDERS == 1
|
||||
#if CHUNK_MESH_WORLD_LIMIT_BORDERS == 1
|
||||
mask[n++] = b1 == b2 ? Block::NULLBLK
|
||||
: backFace ? b1 == Block::NULLBLK || b1 == Block::AIR ? b2 : Block::NULLBLK
|
||||
: b2 == Block::NULLBLK || b2 == Block::AIR ? b1 : Block::NULLBLK;
|
||||
|
@ -191,12 +192,11 @@ void mesh(Chunk::Chunk* chunk)
|
|||
mesh_data->vertices.push_back(x[1]); //bottomLeft.y
|
||||
mesh_data->vertices.push_back(x[2]); //bottomLeft.z
|
||||
|
||||
// extents
|
||||
// extents, use normals for now
|
||||
mesh_data->extents.push_back(du[0] + dv[0]);
|
||||
mesh_data->extents.push_back(du[1] + dv[1]);
|
||||
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((int)(mask[n]) - 2);
|
||||
mesh_data->num_vertices++;
|
||||
|
|
|
@ -2,20 +2,24 @@
|
|||
|
||||
#include "camera.hpp"
|
||||
#include "chunkmanager.hpp"
|
||||
#include "debugwindow.hpp"
|
||||
#include "globals.hpp"
|
||||
#include "renderer.hpp"
|
||||
|
||||
namespace controls{
|
||||
WorldUpdateMsgQueue MsgQueue;
|
||||
/* Block picking */
|
||||
int block_to_place{2};
|
||||
float lastBlockPick=0.0;
|
||||
bool blockpick = false;
|
||||
|
||||
/* Cursor */
|
||||
bool cursor = false;
|
||||
|
||||
void init(){
|
||||
debug::window::set_parameter("block_type_return", &block_to_place);
|
||||
}
|
||||
|
||||
void update(GLFWwindow* window){
|
||||
// Reset blockping timeout have passed
|
||||
float current_time = glfwGetTime();
|
||||
|
||||
/* BlockPicking */
|
||||
|
@ -45,6 +49,7 @@ namespace controls{
|
|||
msg.time = current_time;
|
||||
msg.msg_type = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS ?
|
||||
WorldUpdateMsgType::BLOCKPICK_PLACE : WorldUpdateMsgType::BLOCKPICK_BREAK;
|
||||
msg.block = (Block)(block_to_place);
|
||||
|
||||
// Send to chunk manager
|
||||
chunkmanager::getWorldUpdateQueue().push(msg);
|
||||
|
@ -58,6 +63,4 @@ namespace controls{
|
|||
glfwSetInputMode(window, GLFW_CURSOR, cursor ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
WorldUpdateMsgQueue& getWorldUpdateQueue(){ return MsgQueue; }
|
||||
};
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "debugwindow.hpp"
|
||||
|
||||
#include <imgui/imgui.h>
|
||||
|
@ -65,14 +62,16 @@ namespace debug{
|
|||
void show_debug_window(){
|
||||
ImGui::Begin("Debug Window");
|
||||
|
||||
//ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
|
||||
ImGui::PushItemWidth(ImGui::GetFontSize() * -12);
|
||||
|
||||
try{
|
||||
if (ImGui::CollapsingHeader("Frametimes")){
|
||||
ImGui::Text("FPS: %d", std::any_cast<int>(parameters.at("fps")));
|
||||
ImGui::Text("Frametime (ms): %f",
|
||||
std::any_cast<float>(parameters.at("frametime"))*1000);
|
||||
ImGui::Text("GPU: %s %s", glGetString(GL_VENDOR), glGetString(GL_RENDERER));
|
||||
ImGui::Text("GPU: (%s) %s",
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -83,6 +82,11 @@ namespace debug{
|
|||
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")) );
|
||||
|
||||
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()){
|
||||
ImGui::Text("Last Block action: %s",
|
||||
std::any_cast<bool>(parameters.at("block_last_action")) ? "place" : "destroy");
|
||||
|
@ -91,11 +95,6 @@ namespace debug{
|
|||
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")) );
|
||||
}
|
||||
|
||||
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")){
|
||||
|
@ -116,8 +115,6 @@ namespace debug{
|
|||
if(ImGui::CollapsingHeader("Chunks")){
|
||||
ImGui::Text("Total chunks present: %d",
|
||||
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",
|
||||
std::any_cast<int>(parameters.at("update_chunks_generated")));
|
||||
ImGui::Text("Chunks meshed: %d",
|
||||
|
@ -128,9 +125,9 @@ namespace debug{
|
|||
std::any_cast<int>(parameters.at("update_chunks_explored")));
|
||||
}
|
||||
}catch(const std::bad_any_cast& e){
|
||||
std::cout << e.what();
|
||||
std::cout << e.what() << std::endl;
|
||||
}catch(const std::out_of_range& e){
|
||||
std::cout << e.what();
|
||||
std::cout << e.what() << std::endl;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
|
25
src/main.cpp
25
src/main.cpp
|
@ -1,6 +1,3 @@
|
|||
#include <glad/glad.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "main.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
@ -13,8 +10,8 @@
|
|||
#include "controls.hpp"
|
||||
#include "debugwindow.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "spacefilling.hpp"
|
||||
#include "shader.hpp"
|
||||
#include "spacefilling.hpp"
|
||||
|
||||
float deltaTime = 0.0f; // Time between current frame and last frame
|
||||
float lastFrame = 0.0f; // Time of last frame
|
||||
|
@ -58,7 +55,9 @@ int main()
|
|||
//glEnable(GL_FRAMEBUFFER_SRGB); //gamma correction done in fragment shader
|
||||
//glEnable(GL_CULL_FACE); //GL_BACK GL_CCW by default
|
||||
|
||||
wireframe = false;
|
||||
debug::window::set_parameter("gpu_vendor", glGetString(GL_VENDOR));
|
||||
debug::window::set_parameter("gpu_renderer", glGetString(GL_RENDERER));
|
||||
|
||||
for(int i = 0; i < 360; i++){
|
||||
sines[i] = sin(3.14 / 180 * i);
|
||||
cosines[i] = cos(3.14 / 180 * i);
|
||||
|
@ -91,15 +90,26 @@ int main()
|
|||
glClearColor(0.431f, 0.694f, 1.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Only handle window closing here
|
||||
// Input handling
|
||||
// Only close event is handles by main
|
||||
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
|
||||
glfwSetWindowShouldClose(window, true);
|
||||
// the rest of input processing is handled by controls.cpp
|
||||
|
||||
// Input processing
|
||||
controls::update(window);
|
||||
|
||||
// Camera
|
||||
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
|
||||
renderer::render();
|
||||
|
@ -113,9 +123,8 @@ int main()
|
|||
chunkmanager::stop();
|
||||
|
||||
// Cleanup allocated memory
|
||||
debug::window::destroy();
|
||||
renderer::destroy();
|
||||
chunkmanager::destroy();
|
||||
renderer::destroy();
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
#include <glm/ext.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
|
||||
#include <oneapi/tbb/concurrent_hash_map.h>
|
||||
|
||||
#include "chunkmanager.hpp"
|
||||
|
@ -14,7 +13,7 @@
|
|||
#include "stb_image_write.h"
|
||||
|
||||
namespace renderer{
|
||||
typedef oneapi::tbb::concurrent_hash_map<int32_t, RenderInfo*> RenderTable;
|
||||
typedef oneapi::tbb::concurrent_hash_map<chunk_index_t, RenderInfo*> RenderTable;
|
||||
|
||||
RenderTable ChunksToRender;
|
||||
ChunkMeshDataQueue MeshDataQueue;
|
||||
|
@ -23,16 +22,16 @@ namespace renderer{
|
|||
Shader* theShader, *quadShader;
|
||||
GLuint chunkTexture;
|
||||
|
||||
Shader* getRenderShader() { return theShader; }
|
||||
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
|
||||
IndexQueue& getDeleteIndexQueue(){ return MeshDataToDelete; }
|
||||
|
||||
GLuint renderTexFrameBuffer, renderTex, renderTexDepthBuffer, quadVAO, quadVBO;
|
||||
int screenWidth, screenHeight;
|
||||
|
||||
int crosshair_type{0};
|
||||
bool wireframe{false};
|
||||
|
||||
Shader* getRenderShader() { return theShader; }
|
||||
ChunkMeshDataQueue& getMeshDataQueue(){ return MeshDataQueue; }
|
||||
IndexQueue& getDeleteIndexQueue(){ return MeshDataToDelete; }
|
||||
|
||||
void init(GLFWwindow* window){
|
||||
// Setup rendering
|
||||
// We will render the image to a texture, then display the texture on a quad that fills the
|
||||
|
@ -142,6 +141,7 @@ namespace renderer{
|
|||
theShader->use();
|
||||
theShader->setVec3("viewPos", cameraPos);
|
||||
|
||||
/* Process incoming mesh data */
|
||||
ChunkMeshData* m;
|
||||
while(MeshDataQueue.try_pop(m)){
|
||||
RenderTable::accessor a;
|
||||
|
@ -151,7 +151,11 @@ namespace renderer{
|
|||
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;
|
||||
|
||||
// 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{
|
||||
render_info = new RenderInfo();
|
||||
render_info->index = m->index;
|
||||
|
@ -159,14 +163,16 @@ namespace renderer{
|
|||
render_info->num_vertices = m->num_vertices;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO: implement removal of chunks from rendering
|
||||
int32_t queue_index;
|
||||
/* Process chunks to be removed */
|
||||
chunk_index_t queue_index;
|
||||
while(MeshDataToDelete.try_pop(queue_index)){
|
||||
RenderTable::accessor a;
|
||||
|
||||
|
@ -178,11 +184,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
|
||||
for(RenderTable::iterator i = ChunksToRender.begin(); i != ChunksToRender.end(); i++){
|
||||
RenderInfo* render_info = i->second;
|
||||
|
||||
|
||||
if(render_info->num_vertices > 0)
|
||||
{
|
||||
total++;
|
||||
|
@ -197,7 +203,7 @@ namespace renderer{
|
|||
|
||||
// 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
|
||||
// plane against each corner of the chunk
|
||||
// plane against each corner of the chunk
|
||||
bool out=false;
|
||||
int a{0};
|
||||
for(int p = 0; p < 6; p++){
|
||||
|
@ -255,37 +261,35 @@ namespace renderer{
|
|||
|
||||
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();
|
||||
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);
|
||||
// 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"
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
// 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);
|
||||
}
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow *window, int width, int height){
|
||||
resize_framebuffer(width, height);
|
||||
}
|
||||
|
|
34
test2.cpp
34
test2.cpp
|
@ -1,34 +0,0 @@
|
|||
#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();
|
||||
}
|
Loading…
Reference in New Issue