diff --git a/include/camera.hpp b/include/camera.hpp index ed788a0..198536b 100644 --- a/include/camera.hpp +++ b/include/camera.hpp @@ -8,8 +8,6 @@ #include -#include "chunk.hpp" - class Camera { @@ -17,13 +15,15 @@ public: Camera() { view = glm::mat4(1.0f); - // note that we're translating the scene in the reverse direction of where we want to move + + // 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, 200.0f); } void update(GLFWwindow *window, float deltaTime) { - const float cameraSpeed = 10.0f * deltaTime; // adjust accordingly + const float cameraSpeed = 25.0f * deltaTime; // adjust accordingly + if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) this->cameraPos += cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) @@ -72,34 +72,16 @@ public: pitch = -89.0f; } - glm::vec3 getPos() - { - return cameraPos; - } - - glm::vec3 getFront() - { - return cameraFront; - } - - glm::vec3 getUp() - { - return cameraUp; - } - - glm::mat4 getView() - { - return view; - } - - glm::mat4 getProjection() - { - return projection; - } + glm::vec3 getPos() { return cameraPos; } + glm::vec3 getFront() { return cameraFront; } + glm::vec3 getUp() { return cameraUp; } + glm::mat4 getView() { return view; } + glm::mat4 getProjection() { return projection; } // Plane extraction as per Gribb&Hartmann // 6 planes, each with 4 components (a,b,c,d) - void getFrustumPlanes(glm::vec4 planes[6], bool normalize){ + void getFrustumPlanes(glm::vec4 planes[6], bool normalize) + { glm::mat4 mat = transpose(projection*view); // This just compressed the code below @@ -122,9 +104,8 @@ public: } - private: - glm::vec3 cameraPos = glm::vec3(static_cast(CHUNK_SIZE)*24, 40.0f, static_cast(CHUNK_SIZE)*24); + glm::vec3 cameraPos = glm::vec3(0.0, 80.0f, 0.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); diff --git a/include/chunk.hpp b/include/chunk.hpp index 0eb7184..2fdc6e7 100644 --- a/include/chunk.hpp +++ b/include/chunk.hpp @@ -15,7 +15,7 @@ #include "intervalmap.hpp" #include "shader.hpp" -#define CHUNK_SIZE 8 +#define CHUNK_SIZE 16 #define CHUNK_VOLUME (CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE) #define CHUNK_MAX_INDEX (CHUNK_VOLUME - 1) diff --git a/include/chunkmanager.hpp b/include/chunkmanager.hpp index ed575ce..c3d3cb9 100644 --- a/include/chunkmanager.hpp +++ b/include/chunkmanager.hpp @@ -13,17 +13,15 @@ namespace chunkmanager void stopGenThread(); void stopMeshThread(); - void update(float deltaTime); - - void updateChunk(uint32_t, uint16_t, uint16_t, uint16_t); - void destroy(); - - void blockpick(bool place); - uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k); - void mesh(); void generate(); + void blockpick(bool place); + uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k); + + void destroy(); + void update(float deltaTime); + void updateChunk(uint32_t, uint16_t, uint16_t, uint16_t); } #endif diff --git a/include/chunkmesher.hpp b/include/chunkmesher.hpp index f6ca427..3f985e9 100644 --- a/include/chunkmesher.hpp +++ b/include/chunkmesher.hpp @@ -4,20 +4,19 @@ #include #include +#include #include #include "chunk.hpp" #include "globals.hpp" #include "shader.hpp" -#include namespace chunkmesher{ -void mesh(Chunk::Chunk* chunk); -void sendtogpu(Chunk::Chunk* chunk); -void draw(Chunk::Chunk* chunk, glm::mat4 model); - -void quad(Chunk::Chunk* chunk, glm::vec3 bottomLeft, glm::vec3 topLeft, glm::vec3 topRight, glm::vec3 bottomRight, Block block, bool backFace); + void mesh(Chunk::Chunk* chunk); + void sendtogpu(Chunk::Chunk* chunk); + void draw(Chunk::Chunk* chunk, glm::mat4 model); + void quad(Chunk::Chunk* chunk, glm::vec3 bottomLeft, glm::vec3 topLeft, glm::vec3 topRight, glm::vec3 bottomRight, Block block, bool backFace); } diff --git a/include/globals.hpp b/include/globals.hpp index a703ef4..d9dca83 100644 --- a/include/globals.hpp +++ b/include/globals.hpp @@ -10,7 +10,7 @@ #define extr extern #endif -#define RENDER_DISTANCE 8 +#define RENDER_DISTANCE 16 extr Camera theCamera; extr Shader* theShader; @@ -20,4 +20,4 @@ extr uint32_t MORTON_XYZ_DECODE[CHUNK_VOLUME][3]; extr uint32_t HILBERT_XYZ_ENCODE[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE]; extr uint32_t HILBERT_XYZ_DECODE[CHUNK_VOLUME][3]; -#endif \ No newline at end of file +#endif diff --git a/include/intervalmap.hpp b/include/intervalmap.hpp index 8b11bf5..f5d1f9d 100644 --- a/include/intervalmap.hpp +++ b/include/intervalmap.hpp @@ -20,10 +20,7 @@ public: if (start >= end) return; - // higherEntry -> upper_bound - // lowerEntry -> find()-- // c++ has no builtin function to find the greater key LESS THAN the supplied key. The solution I've found is to get an iterator to the key with find() and traverse back with std::prev - const auto &tmp = treemap.lower_bound(end); const auto &end_prev_entry = tmp != treemap.begin() && tmp != treemap.end() ? std::prev(tmp) : tmp; // first element before end const auto &end_next_entry = tmp; // first element after end @@ -40,7 +37,11 @@ public: { if(end_next_entry->first != end) treemap[end] = end_prev_entry->second; + + // A little optimization: delete next key if it is of the same value of the end key + if(end_next_entry->second == treemap[end]) treemap.erase(end_next_entry); } + // insert the start key. Replaces whatever value is already there. Do not place if the element before is of the same value treemap[start] = value; treemap.erase(treemap.upper_bound(start), treemap.lower_bound(end)); @@ -70,23 +71,18 @@ public: { if (treemap.empty()) { - // std::cout << "List is empty" << std::endl; *length = 0; return nullptr; } const auto &end = std::prev(treemap.end()); - *length = end->first; if(*length == 0) return nullptr; - // std::cout << "Length: " << *length << "\n"; std::unique_ptr arr(new V[*length]); - auto start = treemap.begin(); for (auto i = std::next(treemap.begin()); i != treemap.end(); i++) { - // std::cout << "creating list from " << start->first << " to " << i->first << " of type " << (int)(start->second) << "\n"; for (int k = start->first; k < i->first; k++) arr[k] = start->second; start = i; diff --git a/include/main.hpp b/include/main.hpp index 9d9f4b1..38ff189 100644 --- a/include/main.hpp +++ b/include/main.hpp @@ -5,5 +5,4 @@ void framebuffer_size_callback(GLFWwindow *, int, int); void mouse_callback(GLFWwindow *window, double xpos, double ypos); void processInput(GLFWwindow *); - -#endif \ No newline at end of file +#endif diff --git a/include/spacefilling.hpp b/include/spacefilling.hpp index 6cfed9f..f6a8878 100644 --- a/include/spacefilling.hpp +++ b/include/spacefilling.hpp @@ -7,18 +7,11 @@ namespace SpaceFilling { uint32_t MortonToHilbert3D(const uint32_t morton, const uint32_t bits); - uint32_t HilbertToMorton3D(const uint32_t hilbert, const uint32_t bits); - uint32_t Morton_3D_Encode_5bit(uint32_t index1, uint32_t index2, uint32_t index3); - - void Morton_3D_Decode_5bit(const uint32_t morton, - uint32_t &index1, uint32_t &index2, uint32_t &index3); - + void Morton_3D_Decode_5bit(const uint32_t morton, uint32_t &index1, uint32_t &index2, uint32_t &index3); uint32_t Morton_3D_Encode_10bit(uint32_t index1, uint32_t index2, uint32_t index3); - - void Morton_3D_Decode_10bit(const uint32_t morton, - uint32_t &index1, uint32_t &index2, uint32_t &index3); + void Morton_3D_Decode_10bit(const uint32_t morton, uint32_t &index1, uint32_t &index2, uint32_t &index3); void initLUT(); }; diff --git a/include/utils.hpp b/include/utils.hpp index 21b1699..5dfb568 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -6,10 +6,8 @@ namespace utils { bool withinDistance(int startx, int starty, int startz, int x, int y, int z, int dist); - // https://stackoverflow.com/questions/20266201/3d-array-1d-flat-indexing - // flatten 3d coords to 1d array cords int coord3DTo1D(int x, int y, int z, int maxX, int maxY, int maxZ); std::array coord1DTo3D(int idx, int maxX, int maxY, int mazZ); } -#endif \ No newline at end of file +#endif diff --git a/src/chunk.cpp b/src/chunk.cpp index 5a35682..c96f7ef 100644 --- a/src/chunk.cpp +++ b/src/chunk.cpp @@ -19,7 +19,7 @@ namespace Chunk { this->position = pos; this->setState(CHUNK_STATE_EMPTY, true); - // std::cout << "CHUNK" << std::endl; + glGenVertexArrays(1, &(this->VAO)); glGenBuffers(1, &(this->colorBuffer)); glGenBuffers(1, &(this->VBO)); diff --git a/src/chunkgenerator.cpp b/src/chunkgenerator.cpp index 120076e..d1d4bb2 100644 --- a/src/chunkgenerator.cpp +++ b/src/chunkgenerator.cpp @@ -33,6 +33,7 @@ OpenSimplexNoise::Noise noiseGen2(12345); std::array grassNoiseLUT; std::array dirtNoiseLUT; + void generateNoise(Chunk::Chunk *chunk) { for (int i = 0; i < grassNoiseLUT.size(); i++) @@ -88,6 +89,5 @@ void generatePyramid(Chunk::Chunk *chunk) for (int i = 0; i < CHUNK_SIZE; i++) for (int j = 0; j < CHUNK_SIZE; j++) for (int k = 0; k < CHUNK_SIZE; k++) - // blocks[utils::coord3DTo1D(i, j, k, CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE)] = j == 0 ? Block::STONE : Block::AIR; chunk->setBlock(i >= j && i < CHUNK_SIZE - j && k >= j && k < CHUNK_SIZE - j ? (j & 1) == 0 ? Block::GRASS : Block::STONE : Block::AIR, i, j, k); } diff --git a/src/chunkmanager.cpp b/src/chunkmanager.cpp index 00503f0..816291e 100644 --- a/src/chunkmanager.cpp +++ b/src/chunkmanager.cpp @@ -20,16 +20,25 @@ std::unordered_map chunks; namespace chunkmanager { + // thread management std::mutex mutex_queue_generate; - std::set to_generate; - std::set to_generate_shared; - std::mutex mutex_queue_mesh; + std::set to_generate; std::set to_mesh; - std::set to_mesh_shared; - - std::atomic_bool mesh_should_run; std::atomic_bool generate_should_run; + std::atomic_bool mesh_should_run; + + // update variables + uint8_t f = 0; + int rr{RENDER_DISTANCE * RENDER_DISTANCE}; + glm::vec4 frustumPlanes[6]; + glm::vec3 cameraPos; + int chunkX, chunkY, chunkZ; + int total{0}, toGpu{0}; + + // disposal + std::unordered_map to_delete; + std::set to_delete_delete; void mesh() { @@ -75,6 +84,7 @@ namespace chunkmanager std::thread mesh_thread(mesh); return mesh_thread; } + std::thread initGenThread() { generate_should_run = true; @@ -90,31 +100,25 @@ namespace chunkmanager mesh_should_run = false; } - int total{0}, toGpu{0}; - int rr{RENDER_DISTANCE * RENDER_DISTANCE}; - uint8_t f = 0; - glm::vec4 frustumPlanes[6]; - std::unordered_map to_delete; - std::set to_delete_delete; - - glm::vec3 cameraPos = theCamera.getPos(); - int chunkX, chunkY, chunkZ; void update(float deltaTime) { - int nUnloaded{0}; + // Try to lock resources f = 0; f |= mutex_queue_generate.try_lock(); f |= mutex_queue_mesh.try_lock() << 1; - // Iterate over all chunks, in concentric spheres starting fron the player and going outwards - // Eq. of the sphere (x - a)² + (y - b)² + (z - c)² = r² cameraPos = theCamera.getPos(); theCamera.getFrustumPlanes(frustumPlanes, true); - chunkX=(static_cast(cameraPos.x)) / CHUNK_SIZE; chunkY=(static_cast(cameraPos.y)) / CHUNK_SIZE; chunkZ=(static_cast(cameraPos.z)) / CHUNK_SIZE; + chunkX=static_cast(cameraPos.x) / CHUNK_SIZE; + chunkY=static_cast(cameraPos.y) / CHUNK_SIZE; + chunkZ=static_cast(cameraPos.z) / CHUNK_SIZE; + + // Use time in float to be consistent with glfw + float currentTime = glfwGetTime(); - std::time_t currentTime = std::time(nullptr); // Check for far chunks that need to be cleaned up from memory + int nUnloaded{0}; for(const auto& n : chunks){ Chunk::Chunk* c = n.second; int x{(int)(c->getPosition().x)}; @@ -130,6 +134,8 @@ namespace chunkmanager delete chunks.at(n.first); chunks.erase(n.first); nUnloaded++; + + // Delete afterwards to avoid exception due to invalid iterators to_delete_delete.insert(n.first); } } @@ -137,6 +143,9 @@ namespace chunkmanager to_delete_delete.clear(); if(nUnloaded) std::cout << "Unloaded " << nUnloaded << " chunks\n"; + // Iterate over all chunks, in concentric spheres starting fron the player and going + // outwards. Alternate left and right + // Eq. of the sphere (x - a)² + (y - b)² + (z - c)² = r² // Possible change: coordinates everything at the origin, then translate later? // Step 1. Eq. of a circle. Fix the x coordinate, get the 2 possible y's @@ -144,16 +153,9 @@ namespace chunkmanager bool b = true; while (xp <= RENDER_DISTANCE) { - if (b) - { - x = chunkX + xp; - } - else - { - x = chunkX - xp; - } - // for (int x = chunkX - RENDER_DISTANCE; x < chunkX + RENDER_DISTANCE; x++) - // { + // Alternate between left and right + if (b) x = chunkX + xp; + else x = chunkX - xp; // Possible optimization: use sqrt lookup int y1 = sqrt((rr) - (x - chunkX) * (x - chunkX)) + chunkY; @@ -192,7 +194,6 @@ namespace chunkmanager else k = z; - // uint32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1024). There's actually two spare bits uint32_t in = calculateIndex(i, j, k); chunkmanager::updateChunk(in, i, j, k); } @@ -203,10 +204,7 @@ namespace chunkmanager xp++; b = true; } - else - { - b = false; - } + else b = false; } //std::cout << "Chunks to mesh: " << to_mesh.size() << "\n"; //std::cout << "Chunks to generate: " << to_generate.size() << "\n"; @@ -214,6 +212,8 @@ namespace chunkmanager //total = 0; //toGpu = 0; + // Unlock mutexes if previously locked. Unlocking a mutex not locked by the current thread + // or already locked is undefined behaviour, so checking has to be done if ((f & 1)) mutex_queue_generate.unlock(); if ((f & 2)) @@ -221,8 +221,10 @@ namespace chunkmanager } // Generation and meshing happen in two separate threads from the main one - // Chunk states are used to decide which actions need to be done on the chunk and queues+mutexes to pass the chunks between the threads - // Uploading data to GPU still needs to be done in the main thread, or another OpenGL context needs to be opened, which further complicates stuff + // Chunk states are used to decide which actions need to be done on the chunk and sets+mutexes + // to pass the chunks to be operated on between the threads. + // Uploading data to GPU still needs to be done in the main thread, or another OpenGL context + // needs to be opened, which further complicates stuff. void updateChunk(uint32_t index, uint16_t i, uint16_t j, uint16_t k) { if (chunks.find(index) == chunks.end()) @@ -252,6 +254,7 @@ namespace chunkmanager else { if (!c->getState(Chunk::CHUNK_STATE_MESH_LOADED)) chunkmesher::sendtogpu(c); + // Frustum Culling of chunk total++; @@ -259,18 +262,15 @@ namespace chunkmanager glm::vec4 chunkW = glm::vec4(chunk.x*static_cast(CHUNK_SIZE), chunk.y*static_cast(CHUNK_SIZE), chunk.z*static_cast(CHUNK_SIZE),1.0); glm::mat4 model = glm::translate(glm::mat4(1.0), ((float)CHUNK_SIZE) * chunk); + // 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 bool out=false; - // First test, check if all the corners of the chunk are outside any of the - // planes + int a{0}; for(int p = 0; p < 6; p++){ - - int a{0}; - for(int i = 0; i < 8; i++) { - a+=glm::dot(frustumPlanes[p], glm::vec4(chunkW.x + ((float)(i & 1))*CHUNK_SIZE, chunkW.y - + ((float)((i - & 2) >> 1))*CHUNK_SIZE, chunkW.z + ((float)((i & 4) >> - 2))*CHUNK_SIZE, 1.0)) < 0.0; - } + a = 0; + for(int i = 0; i < 8; i++) a += glm::dot(frustumPlanes[p], glm::vec4(chunkW.x + ((float)(i & 1))*CHUNK_SIZE, chunkW.y + + ((float)((i & 2) >> 1))*CHUNK_SIZE, chunkW.z + ((float)((i & 4) >> 2))*CHUNK_SIZE, 1.0)) < 0.0; if(a==8){ out=true; @@ -292,7 +292,7 @@ namespace chunkmanager void blockpick(bool place){ // cast a ray from the camera in the direction pointed by the camera itself glm::vec3 pos = cameraPos; - for(float t = 0.0; t <= 25.0; t += 0.5){ + for(float t = 0.0; t <= 10.0; t += 0.5){ // traverse the ray a block at the time pos = theCamera.getPos() + t * theCamera.getFront(); @@ -356,6 +356,7 @@ namespace chunkmanager } } + // uint32_t is fine, since i'm limiting the coordinate to only use up to ten bits (1024). There's actually two spare bits uint32_t calculateIndex(uint16_t i, uint16_t j, uint16_t k){ return i | (j << 10) | (k << 20); } diff --git a/src/chunkmesher.cpp b/src/chunkmesher.cpp index 2b62a96..800bf1e 100755 --- a/src/chunkmesher.cpp +++ b/src/chunkmesher.cpp @@ -27,18 +27,14 @@ void mesh(Chunk::Chunk* chunk) * across different planes everytime I change dimension without having to * write 3 separate 3-nested-for-loops */ - /* - * It's not feasible to just create a new mesh everytime a quad needs placing. - * This ends up creating TONS of meshes and the game will just lag out. - * As I did in the past, it's better to create a single mesh for each chunk, - * containing all the quads. In the future, maybe translucent blocks and liquids - * will need a separate mesh, but still on a per-chunk basis - */ + + // Cleanup previous data chunk->vertices.clear(); chunk->indices.clear(); chunk->colors.clear(); chunk->vIndex = 0; + // Abort if chunk is empty if(chunk->getState(Chunk::CHUNK_STATE_EMPTY)) return; // convert tree to array since it is easier to work with it @@ -95,7 +91,8 @@ void mesh(Chunk::Chunk* chunk) // Additionally checking whether b1 and b2 are AIR or Block::NULLBLK allows face culling, // thus not rendering faces that cannot be seen - // Removing the control for Block::NULLBLK disables chunk borders + // Removing the control for Block::NULLBLK disables chunk borders, which is + // not always wanted and needs further checking // This can be surely refactored in something that isn't such a big one-liner mask[n++] = b1 != Block::NULLBLK && b2 != Block::NULLBLK && b1 == b2 ? Block::NULLBLK : backFace ? b1 == Block::AIR || b1 == Block::NULLBLK ? b2 : Block::NULLBLK @@ -209,12 +206,16 @@ void sendtogpu(Chunk::Chunk* chunk) glBindVertexArray(0); + // save the number of indices of the mesh, it is needed later for drawing chunk->vIndex = (GLuint)(chunk->indices.size()); + // once data has been sent to the GPU, it can be cleared from system RAM chunk->vertices.clear(); chunk->indices.clear(); chunk->colors.clear(); } + + // mark the chunk mesh has loaded on GPU chunk->setState(Chunk::CHUNK_STATE_MESH_LOADED, true); } @@ -274,30 +275,30 @@ void quad(Chunk::Chunk* chunk, glm::vec3 bottomLeft, glm::vec3 topLeft, glm::vec } chunk->vIndex += 4; - // ugly switch case + // ugly switch case for colors GLfloat r, g, b; switch (block) { - case Block::STONE: - r = 0.588f; - g = 0.588f; - b = 0.588f; - break; - case Block::GRASS: - r = 0.05f; - g = 0.725f; - b = 0.0f; - break; - case Block::DIRT: - r = 0.176f; - g = 0.282f; - b = 0.169f; - break; - default: - r = 0.0f; - g = 0.0f; - b = 0.0f; - break; + case Block::STONE: + r = 0.588f; + g = 0.588f; + b = 0.588f; + break; + case Block::GRASS: + r = 0.05f; + g = 0.725f; + b = 0.0f; + break; + case Block::DIRT: + r = 0.176f; + g = 0.282f; + b = 0.169f; + break; + default: + r = 0.0f; + g = 0.0f; + b = 0.0f; + break; } // Fake shadows diff --git a/src/main.cpp b/src/main.cpp index f707be1..49ba8d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,8 +79,6 @@ int main() lastFPSFrame = currentFrame; } - if(glfwGetTime() - lastBlockPick > 0.2) blockpick = false; - glClearColor(0.2f, 0.3f, 0.3f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -89,6 +87,9 @@ int main() // Camera theCamera.update(window, deltaTime); + + // Reset blockping timeout if 200ms have passed + if(glfwGetTime() - lastBlockPick > 0.1) blockpick = false; // ChunkManager chunkmanager::update(deltaTime); @@ -98,14 +99,14 @@ int main() glfwPollEvents(); } + // Stop threads and wait for them to finish chunkmanager::stopGenThread(); chunkmanager::stopMeshThread(); - genThread.join(); meshThread.join(); + // Cleanup allocated memory chunkmanager::destroy(); - delete theShader; @@ -128,16 +129,20 @@ 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; } diff --git a/src/spacefilling.cpp b/src/spacefilling.cpp index 1f0bc4c..f5657b4 100644 --- a/src/spacefilling.cpp +++ b/src/spacefilling.cpp @@ -4,6 +4,7 @@ #include +// http://and-what-happened.blogspot.com/2011/08/fast-2d-and-3d-hilbert-curves-and.html namespace SpaceFilling { uint32_t MortonToHilbert3D(const uint32_t morton, const uint32_t bits) @@ -212,4 +213,4 @@ namespace SpaceFilling } } } -}; \ No newline at end of file +}; diff --git a/src/utils.cpp b/src/utils.cpp index 8f98c2d..c83a29e 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,19 +1,19 @@ #include "utils.hpp" -bool utils::withinDistance(int startx, int starty, int startz, int x, int y, int z, int dist){ +bool utils::withinDistance(int startx, int starty, int startz, int x, int y, int z, int dist) +{ return (x-startx)*(x-startx) + (y - starty)*(y-starty) + (z-startz)*(z-startz) <= dist*dist; } - // https://stackoverflow.com/questions/20266201/3d-array-1d-flat-indexing - //flatten 3d coords to 1d array cords -int utils::coord3DTo1D (int x, int y, int z, int maxX, int maxY, int maxZ){ - return x + maxX * (y + z * maxY); - } +//flatten 3d coords to 1d array cords +// https://stackoverflow.com/questions/20266201/3d-array-1d-flat-indexing +int utils::coord3DTo1D (int x, int y, int z, int maxX, int maxY, int maxZ) { return x + maxX * (y + z * maxY); } -std::array utils::coord1DTo3D(int idx, int maxX, int maxY, int mazZ){ +std::array utils::coord1DTo3D(int idx, int maxX, int maxY, int mazZ) +{ int z = idx / (maxX * maxY); idx -= (z * maxX* maxY); int y = idx / maxX; int x = idx % maxX; return std::array {x, y, z}; - } +}