From 316293a110a3867cec9a1881210e4edf544f8e4d Mon Sep 17 00:00:00 2001 From: MitchellHansen Date: Wed, 12 Jul 2017 00:09:19 -0700 Subject: [PATCH] Cleaned up and pulled out some code into Map.cpp in order to start working on the meat of the *Correct* voxel traversal method as explained in the paper. --- include/map/Map.h | 47 ++++--- include/map/Octree.h | 24 ++-- include/util.hpp | 5 +- src/main.cpp | 9 +- src/map/Map.cpp | 293 ++++++++++++++++++++++++++++++++++++------- src/map/Octree.cpp | 23 ++-- 6 files changed, 318 insertions(+), 83 deletions(-) diff --git a/include/map/Map.h b/include/map/Map.h index 00747e0..5775d4f 100644 --- a/include/map/Map.h +++ b/include/map/Map.h @@ -11,36 +11,53 @@ #define _USE_MATH_DEFINES #include -struct XYZHasher { - std::size_t operator()(const sf::Vector3i& k) const { - return ((std::hash()(k.x) - ^ (std::hash()(k.y) << 1)) >> 1) - ^ (std::hash()(k.z) << 1); - } -}; - class Map { public: Map(uint32_t dimensions); + // Sets a voxel in the 3D char dataset void setVoxel(sf::Vector3i position, int val); - bool getVoxelFromOctree(sf::Vector3i position); - bool getVoxel(sf::Vector3i pos); + // Gets a voxel at the 3D position in the octree + char getVoxel(sf::Vector3i pos); + + std::vector> Map::CastRayOctree( + Octree *octree, + sf::Vector3i* map_dim, + sf::Vector2f* cam_dir, + sf::Vector3f* cam_pos + ); + + std::vector> Map::CastRayCharArray( + char *map, + sf::Vector3i* map_dim, + sf::Vector2f* cam_dir, + sf::Vector3f* cam_pos + ); + + // Octree handles all basic octree operations Octree octree; - bool test(); - private: // ======= DEBUG =========== int counter = 0; std::stringstream output_stream; - // ========================= - + + // The 3D char dataset that is generated at runtime. This will be replaced by two different interactions. + // The first a file loading function that loads binary octree data. + // The second being an import tool which will allow Any -> Octree transformation. char* voxel_data; + // ========================= }; - +// Might possibly use this struct for hashing XYZ chunk values into a dict for storage and loading +struct XYZHasher { + std::size_t operator()(const sf::Vector3i& k) const { + return ((std::hash()(k.x) + ^ (std::hash()(k.y) << 1)) >> 1) + ^ (std::hash()(k.z) << 1); + } +}; diff --git a/include/map/Octree.h b/include/map/Octree.h index 815fc57..6eac988 100644 --- a/include/map/Octree.h +++ b/include/map/Octree.h @@ -6,7 +6,7 @@ #define OCT_DIM 32 -struct oct_state { +struct OctState { int parent_stack_position = 0; uint64_t parent_stack[32] = { 0 }; @@ -16,6 +16,10 @@ struct oct_state { uint64_t current_descriptor; + // ====== DEBUG ======= + char found = 1; + + }; @@ -27,11 +31,16 @@ public: Octree(); ~Octree() {}; + // Generate an octree from 3D indexed array of char data void Generate(char* data, sf::Vector3i dimensions); + + // TODO: Load the octree from a serialized or whatever file void Load(std::string octree_file_name); - uint64_t *trunk_buffer; - uint64_t trunk_buffer_position = buffer_size; + // I think the best way to transfer all of the data to the GPU. Each buffer will contain a set of blocks + // except for the trunk buffer. The paper indicates that the cutoff point for the trunk can vary, + // but since I'm going to do seperate buffers, I'm going to set a hard cutoff for the trunk so we + // know when to switch buffers uint64_t *descriptor_buffer; uint64_t descriptor_buffer_position = buffer_size; @@ -46,16 +55,17 @@ public: uint64_t root_index = 0; int page_header_counter = 0x8000; + + // Cheat and underflow to get the position uint64_t current_info_section_position = ((uint64_t)0)-1; + uint64_t stack_pos = 0x8000; uint64_t global_pos = buffer_size - 50; - - uint64_t copy_to_stack(std::vector children, unsigned int voxel_scale); - + // With a position and the head of the stack. Traverse down the voxel hierarchy to find // the IDX and stack position of the highest resolution (maybe set resolution?) oct - bool GetVoxel(sf::Vector3i position); + OctState GetVoxel(sf::Vector3i position); void print_block(int block_pos); diff --git a/include/util.hpp b/include/util.hpp index f62c46a..af2f239 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -8,6 +8,9 @@ #include #include #include +#include +#include +#include const double PI = 3.141592653589793238463; const float PI_F = 3.14159265358979f; @@ -250,8 +253,6 @@ inline bool CheckContiguousValid(const uint64_t c) { return (c & bitmask) == bitmask; } - - inline bool IsLeaf(const uint64_t descriptor) { uint64_t leaf_mask = 0xFF000000; diff --git a/src/main.cpp b/src/main.cpp index 34d2835..e6b2a6c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,8 @@ #include #elif defined _WIN32 +// Good lord, the C++ std overwrote windows.h min() max() definitions +#define NOMINMAX #include // As if hardware is ever going to move away from 1.2 @@ -68,6 +70,9 @@ sf::Texture window_texture; // - Inconsistent lighting constants. GUI manipulation // Ancilary settings buffer and memory controller // - Attachment lookup and aux buffer, contour lookup & masking +// - Traversal algorithm + related stacks and data structures +// - Octree, Map interface with the GPU +// - Octree, Map refactoring int main() { @@ -76,7 +81,9 @@ int main() { Map _map(32); //_map.test(); //std::cin.get(); - return 0; + + + //return 0; // ============================= sf::RenderWindow window(sf::VideoMode(WINDOW_X, WINDOW_Y), "SFML"); diff --git a/src/map/Map.cpp b/src/map/Map.cpp index 651381c..e07a76d 100644 --- a/src/map/Map.cpp +++ b/src/map/Map.cpp @@ -3,6 +3,8 @@ Map::Map(uint32_t dimensions) { + + // ========= TEMP 3D voxel data =========== srand(time(nullptr)); voxel_data = new char[dimensions * dimensions * dimensions]; @@ -17,10 +19,20 @@ Map::Map(uint32_t dimensions) { else voxel_data[i] = 0; } + sf::Vector3i dim3(dimensions, dimensions, dimensions); + + octree.Generate(voxel_data, dim3); + + octree.Validate(voxel_data, dim3); + + sf::Vector2f cam_dir(2, 0.01); + sf::Vector3f cam_pos(10, 10, 10); + std::vector> list1 = CastRayCharArray(voxel_data, &dim3, &cam_dir, &cam_pos); + std::vector> list2 = CastRayOctree(&octree, &dim3, &cam_dir, &cam_pos); + - octree.Generate(voxel_data, sf::Vector3i(dimensions, dimensions, dimensions)); + return; - octree.Validate(voxel_data, sf::Vector3i(dimensions, dimensions, dimensions)); } void Map::setVoxel(sf::Vector3i pos, int val) { @@ -28,76 +40,261 @@ void Map::setVoxel(sf::Vector3i pos, int val) { voxel_data[pos.x + OCT_DIM * (pos.y + OCT_DIM * pos.z)] = val; } -bool Map::getVoxelFromOctree(sf::Vector3i position) -{ - return 0; +char Map::getVoxel(sf::Vector3i pos){ + + return octree.GetVoxel(pos).found; } -bool Map::getVoxel(sf::Vector3i pos){ +std::vector> Map::CastRayCharArray( + char* map, + sf::Vector3i* map_dim, + sf::Vector2f* cam_dir, + sf::Vector3f* cam_pos +) { - if (voxel_data[pos.x + OCT_DIM * (pos.y + OCT_DIM * pos.z)]) { - return true; - } else { - return false; - } -} + std::vector> travel_path; -bool Map::test() { + sf::Vector3f ray_dir(1, 0, 0); - std::cout << "Validating map..." << std::endl; + // Pitch + ray_dir = sf::Vector3f( + ray_dir.z * sin((*cam_dir).x) + ray_dir.x * cos((*cam_dir).x), + ray_dir.y, + ray_dir.z * cos((*cam_dir).x) - ray_dir.x * sin((*cam_dir).x) + ); - for (int x = 0; x < OCT_DIM; x++) { - for (int y = 0; y < OCT_DIM; y++) { - for (int z = 0; z < OCT_DIM; z++) { + // Yaw + ray_dir = sf::Vector3f( + ray_dir.x * cos((*cam_dir).y) - ray_dir.y * sin((*cam_dir).y), + ray_dir.x * sin((*cam_dir).y) + ray_dir.y * cos((*cam_dir).y), + ray_dir.z + ); - sf::Vector3i pos(x, y, z); - bool arr1 = getVoxel(pos); - bool arr2 = getVoxelFromOctree(pos); + // Setup the voxel step based on what direction the ray is pointing + sf::Vector3i voxel_step(1, 1, 1); - if (arr1 != arr2) { - std::cout << "X: " << pos.x << "Y: " << pos.y << "Z: " << pos.z << std::endl; - } + voxel_step.x *= (ray_dir.x > 0) - (ray_dir.x < 0); + voxel_step.y *= (ray_dir.y > 0) - (ray_dir.y < 0); + voxel_step.z *= (ray_dir.z > 0) - (ray_dir.z < 0); - } - } - } + // Setup the voxel coords from the camera origin + sf::Vector3i voxel(*cam_pos); + + // Delta T is the units a ray must travel along an axis in order to + // traverse an integer split + sf::Vector3f delta_t( + fabs(1.0f / ray_dir.x), + fabs(1.0f / ray_dir.y), + fabs(1.0f / ray_dir.z) + ); + + // offset is how far we are into a voxel, enables sub voxel movement + // Intersection T is the collection of the next intersection points + // for all 3 axis XYZ. + sf::Vector3f intersection_t( + delta_t.x * (cam_pos->x - floor(cam_pos->x)) * voxel_step.x, + delta_t.y * (cam_pos->y - floor(cam_pos->y)) * voxel_step.y, + delta_t.z * (cam_pos->z - floor(cam_pos->z)) * voxel_step.z + ); + + // for negative values, wrap around the delta_t + intersection_t.x += delta_t.x * -(std::min(intersection_t.x, 0.0f)); + intersection_t.y += delta_t.y * -(std::min(intersection_t.y, 0.0f)); + intersection_t.z += delta_t.z * -(std::min(intersection_t.z, 0.0f)); + + + int dist = 0; + sf::Vector3i face_mask(0, 0, 0); + int voxel_data = 0; + + // Andrew Woo's raycasting algo + do { - std::cout << "Done" << std::endl; + face_mask.x = intersection_t.x <= std::min(intersection_t.y, intersection_t.z); + face_mask.y = intersection_t.y <= std::min(intersection_t.z, intersection_t.x); + face_mask.z = intersection_t.z <= std::min(intersection_t.x, intersection_t.y); - sf::Clock timer; - - timer.restart(); - for (int x = 0; x < OCT_DIM; x++) { - for (int y = 0; y < OCT_DIM; y++) { - for (int z = 0; z < OCT_DIM; z++) { + intersection_t.x += delta_t.x * fabs(face_mask.x); + intersection_t.y += delta_t.y * fabs(face_mask.y); + intersection_t.z += delta_t.z * fabs(face_mask.z); - sf::Vector3i pos(x, y, z); + voxel.x += voxel_step.x * face_mask.x; + voxel.y += voxel_step.y * face_mask.y; + voxel.z += voxel_step.z * face_mask.z; - bool arr1 = getVoxel(pos); + if ((intersection_t.x) < (intersection_t.y)) { + if ((intersection_t.x) < (intersection_t.z)) { + + voxel.x += voxel_step.x; + intersection_t.x = intersection_t.x + delta_t.x; + } + else { + + voxel.z += voxel_step.z; + intersection_t.z = intersection_t.z + delta_t.z; + } + } + else { + if ((intersection_t.y) < (intersection_t.z)) { + + voxel.y += voxel_step.y; + intersection_t.y = intersection_t.y + delta_t.y; + } + else { + + voxel.z += voxel_step.z; + intersection_t.z = intersection_t.z + delta_t.z; } } - } - std::cout << "Array linear xyz access : "; - std::cout << timer.restart().asMicroseconds() << " microseconds" << std::endl; + if (voxel.x >= map_dim->x || voxel.y >= map_dim->y || voxel.z >= map_dim->z) { + return travel_path; + } + if (voxel.x < 0 || voxel.y < 0 || voxel.z < 0) { + return travel_path; + } + + // If we hit a voxel + voxel_data = map[voxel.x + (*map_dim).x * (voxel.y + (*map_dim).z * (voxel.z))]; + + travel_path.push_back(std::make_tuple(voxel, voxel_data)); + + if (voxel_data != 0) + return travel_path; + + + } while (++dist < 700.0f); + + return travel_path; +} - for (int x = 0; x < OCT_DIM; x++) { - for (int y = 0; y < OCT_DIM; y++) { - for (int z = 0; z < OCT_DIM; z++) { +class Octree; - sf::Vector3i pos(x, y, z); +std::vector> Map::CastRayOctree( + Octree *octree, + sf::Vector3i* map_dim, + sf::Vector2f* cam_dir, + sf::Vector3f* cam_pos +) { - bool arr2 = getVoxelFromOctree(pos); + std::vector> travel_path; + + sf::Vector3f ray_dir(1, 0, 0); + + // Pitch + ray_dir = sf::Vector3f( + ray_dir.z * sin((*cam_dir).x) + ray_dir.x * cos((*cam_dir).x), + ray_dir.y, + ray_dir.z * cos((*cam_dir).x) - ray_dir.x * sin((*cam_dir).x) + ); + + // Yaw + ray_dir = sf::Vector3f( + ray_dir.x * cos((*cam_dir).y) - ray_dir.y * sin((*cam_dir).y), + ray_dir.x * sin((*cam_dir).y) + ray_dir.y * cos((*cam_dir).y), + ray_dir.z + ); + + + // Setup the voxel step based on what direction the ray is pointing + sf::Vector3i voxel_step(1, 1, 1); + + voxel_step.x *= (ray_dir.x > 0) - (ray_dir.x < 0); + voxel_step.y *= (ray_dir.y > 0) - (ray_dir.y < 0); + voxel_step.z *= (ray_dir.z > 0) - (ray_dir.z < 0); + + // Setup the voxel coords from the camera origin + sf::Vector3i voxel(*cam_pos); + + // Delta T is the units a ray must travel along an axis in order to + // traverse an integer split + sf::Vector3f delta_t( + fabs(1.0f / ray_dir.x), + fabs(1.0f / ray_dir.y), + fabs(1.0f / ray_dir.z) + ); + + // offset is how far we are into a voxel, enables sub voxel movement + // Intersection T is the collection of the next intersection points + // for all 3 axis XYZ. + sf::Vector3f intersection_t( + delta_t.x * (cam_pos->y - floor(cam_pos->x)) * voxel_step.x, + delta_t.y * (cam_pos->x - floor(cam_pos->y)) * voxel_step.y, + delta_t.z * (cam_pos->z - floor(cam_pos->z)) * voxel_step.z + ); + + // for negative values, wrap around the delta_t + intersection_t.x += delta_t.x * -(std::min(intersection_t.x, 0.0f)); + intersection_t.y += delta_t.y * -(std::min(intersection_t.y, 0.0f)); + intersection_t.z += delta_t.z * -(std::min(intersection_t.z, 0.0f)); + + + int dist = 0; + sf::Vector3i face_mask(0, 0, 0); + int voxel_data = 0; + + OctState traversal_state = octree->GetVoxel(voxel); + + // Andrew Woo's raycasting algo + do { + + face_mask.x = intersection_t.x <= std::min(intersection_t.y, intersection_t.z); + face_mask.y = intersection_t.y <= std::min(intersection_t.z, intersection_t.x); + face_mask.z = intersection_t.z <= std::min(intersection_t.x, intersection_t.y); + + + intersection_t.x += delta_t.x * fabs(face_mask.x); + intersection_t.y += delta_t.y * fabs(face_mask.y); + intersection_t.z += delta_t.z * fabs(face_mask.z); + + voxel.x += voxel_step.x * face_mask.x; + voxel.y += voxel_step.y * face_mask.y; + voxel.z += voxel_step.z * face_mask.z; + + if ((intersection_t.x) < (intersection_t.y)) { + if ((intersection_t.x) < (intersection_t.z)) { + + voxel.x += voxel_step.x; + intersection_t.x = intersection_t.x + delta_t.x; + } + else { + + voxel.z += voxel_step.z; + intersection_t.z = intersection_t.z + delta_t.z; } } - } + else { + if ((intersection_t.y) < (intersection_t.z)) { + + voxel.y += voxel_step.y; + intersection_t.y = intersection_t.y + delta_t.y; + } + else { - std::cout << "Octree linear xyz access : "; - std::cout << timer.restart().asMicroseconds() << " microseconds" << std::endl; + voxel.z += voxel_step.z; + intersection_t.z = intersection_t.z + delta_t.z; + } + } + + if (voxel.x >= map_dim->x || voxel.y >= map_dim->y || voxel.z >= map_dim->z) { + return travel_path; + } + if (voxel.x < 0 || voxel.y < 0 || voxel.z < 0) { + return travel_path; + } + // If we hit a voxel + //voxel_data = map[voxel.x + (*map_dim).x * (voxel.y + (*map_dim).z * (voxel.z))]; + voxel_data = getVoxel(voxel); + travel_path.push_back(std::make_tuple(voxel, voxel_data)); - return true; -} + if (voxel_data != 0) + return travel_path; + + + } while (++dist < 700.0f); + return travel_path; +} \ No newline at end of file diff --git a/src/map/Octree.cpp b/src/map/Octree.cpp index b3b226e..bb3dab7 100644 --- a/src/map/Octree.cpp +++ b/src/map/Octree.cpp @@ -3,7 +3,6 @@ Octree::Octree() { // initialize the the buffers to 0's - trunk_buffer = new uint64_t[buffer_size](); descriptor_buffer = new uint64_t[buffer_size](); attachment_lookup = new uint32_t[buffer_size](); attachment_buffer = new uint64_t[buffer_size](); @@ -41,10 +40,10 @@ void Octree::Generate(char* data, sf::Vector3i dimensions) { GetVoxel(sf::Vector3i(0, 0, 0)); } -bool Octree::GetVoxel(sf::Vector3i position) { +OctState Octree::GetVoxel(sf::Vector3i position) { // Struct that holds the state necessary to continue the traversal from the found voxel - oct_state state; + OctState state; // push the root node to the parent stack uint64_t current_index = root_index; @@ -115,7 +114,8 @@ bool Octree::GetVoxel(sf::Vector3i position) { if ((head >> 24) & mask_8[mask_index]) { // If it is, then we cannot traverse further as CP's won't have been generated - return true; + state.found = 1; + return state; } // If all went well and we found a valid non-leaf oct then we will traverse further down the hierarchy @@ -134,7 +134,7 @@ bool Octree::GetVoxel(sf::Vector3i position) { // Increment the parent stack position and put the new oct node as the parent state.parent_stack_position++; state.parent_stack[state.parent_stack_position] = head; - + } else { // If the oct was not valid, then no CP's exists any further @@ -144,11 +144,13 @@ bool Octree::GetVoxel(sf::Vector3i position) { // to focus on how to now take care of the end condition. // Currently it adds the last parent on the second to lowest // oct CP. Not sure if thats correct - return false; + state.found = 0; + return state; } } - return true; + state.found = 1; + return state; } void Octree::print_block(int block_pos) { @@ -234,7 +236,8 @@ std::tuple Octree::GenerationRecursion(char* data, sf::Vecto } // We are working bottom up so we need to subtract from the stack position - // the amount of elements we want to use + // the amount of elements we want to use. In the worst case this will be + // a far pointer for ever descriptor (size * 2) int worst_case_insertion_size = descriptor_position_array.size() * 2; @@ -329,7 +332,7 @@ bool Octree::Validate(char* data, sf::Vector3i dimensions){ sf::Vector3i pos(x, y, z); char arr_val = get1DIndexedVoxel(data, dimensions, pos); - char oct_val = GetVoxel(pos); + char oct_val = GetVoxel(pos).found; if (arr_val != oct_val) { std::cout << "X: " << pos.x << " Y: " << pos.y << " Z: " << pos.z << " "; @@ -342,5 +345,5 @@ bool Octree::Validate(char* data, sf::Vector3i dimensions){ std::cout << "Done" << std::endl; - + return true; }