void Octree::constructOctree(int depth) { //1. bounding box generation Vec3f leftDown(MAX, MAX, MAX); Vec3f rightUp(MIN, MIN, MIN); Vec3f mid; for(int i=0;i<NbNode;i++) { for(int j=0;j<3;j++) { if(leftDown[j]>(*Node)[i][j]) leftDown[j]=(*Node)[i][j]; if(rightUp[j]<(*Node)[i][j]) rightUp[j]=(*Node)[i][j]; } } mid=(rightUp+leftDown)/2.0; //2. Root node Root=new OctreeNode; Root->setBoundingBox(leftDown, rightUp); Root->Depth=0; //3. Descendant OctreeNode** descendant=new OctreeNode*[8]; for(int i=0;i<8;i++) { descendant[i]=new OctreeNode; descendant[i]->Depth=Root->Depth+1; Root->Descendant[i]=descendant[i]; } //4. Descendant generation std::vector<int> nodeIdx[8]; for(int i=0;i<NbNode;i++) { if((*Node)[i][0]>mid[0]) { if((*Node)[i][1]>mid[1]) { if((*Node)[i][2]>mid[2]) { //RightTopFront nodeIdx[0].push_back(i); } else { //RightTopBehind nodeIdx[1].push_back(i); } } else { if((*Node)[i][2]>mid[2]) { //RightBottomFront nodeIdx[2].push_back(i); } else { //RightBottomBehind nodeIdx[3].push_back(i); } } } else { if((*Node)[i][1]>mid[1]) { if((*Node)[i][2]>mid[2]) { //LeftTopFront nodeIdx[4].push_back(i); } else { //LeftTopBehind nodeIdx[5].push_back(i); } } else { if((*Node)[i][2]>mid[2]) { //LeftBottomFront nodeIdx[6].push_back(i); } else { //LeftBottomBehind nodeIdx[7].push_back(i); } } } } //RightTopFront Vec3f p1=mid; Vec3f p2=rightUp; genOctree(descendant[0], p1, p2, nodeIdx[0], depth); //RightTopBehind p1=Vec3f(mid[0], mid[1], leftDown[2]); p2=Vec3f(rightUp[0], rightUp[1], mid[2]); genOctree(descendant[1], p1, p2, nodeIdx[1], depth); //RightBottomFront p1=Vec3f(mid[0], leftDown[1], mid[2]); p2=Vec3f(rightUp[0], mid[1], rightUp[2]) ; genOctree(descendant[2], p1, p2, nodeIdx[2], depth); //RightBottomBehind p1=Vec3f(mid[0],leftDown[1],leftDown[2]); p2=Vec3f(rightUp[0], mid[1], mid[2]); genOctree(descendant[3], p1, p2, nodeIdx[3], depth); //LeftTopFront p1=Vec3d(leftDown[0], mid[1], mid[2]); p2=Vec3d(mid[0], rightUp[1], rightUp[2]); genOctree(descendant[4], p1, p2, nodeIdx[4], depth); //LeftTopBehind p1=Vec3f(leftDown[0], mid[1], leftDown[2]); p2=Vec3f(mid[0], rightUp[1], mid[2]); genOctree(descendant[5], p1, p2, nodeIdx[5], depth); //LeftBottomFront p1=Vec3f(leftDown[0], leftDown[1], mid[2]); p2=Vec3f(mid[0], mid[1], rightUp[2]); genOctree(descendant[6], p1, p2, nodeIdx[6], depth); //LeftBottomBehind p1=leftDown; p2=(leftDown+rightUp)/2.0; genOctree(descendant[7], p1, p2, nodeIdx[7], depth); delete [] descendant; }
void VolumeRender::generate() { double start_time, end_time, dt; start_time = glfwGetTime(); // Load tree int32_t tree_size = 0; tree_data = 0; // Load from file if (_scene == 0) { tree_size = rendering::read_full_file_binary(_treeFilename, &tree_data); if (tree_size == 0) { fprintf(stderr, "Invalid tree file: %s\n", _treeFilename); exit(1); } } else { char filename_buffer[1024]; sprintf(filename_buffer, "scene%d.tree.%d", _scene, _depth); FILE* file = fopen(filename_buffer, "rb"); if (file != nullptr) { // Load tree printf("Loading file %s\n", filename_buffer); fclose(file); tree_size = rendering::read_full_file_binary(filename_buffer, &tree_data); if (tree_size == 0) { fprintf(stderr, "Invalid tree file: %s\n", filename_buffer); exit(1); } } else { // Generate printf("Generating scene for the first time, please wait..\n"); // Generate from sphere if (_scene == 1) { tree_size = rendering::genOctreeSphere((int32_t**)&tree_data, _depth, glm::vec3(0.5f, 0.5f, 0.5f), 0.4f); } // Generate from miku mesh if (_scene == 2) { if (_mesh.load("miku.md2")) { // Rotate mesh to right rotation (md2s are messed up like that..) glm::mat4 rotation = glm::rotate(180.0f, glm::vec3(0, 0, 1.0f)); rotation = glm::rotate(rotation, 90.0f, glm::vec3(0, 1.0f, 0)); _mesh.transform(rotation); tree_size = genOctreeMesh((int32_t**)&tree_data, _depth, &_mesh); } } // r = 1.5 sphere with embedded r = 1 sphere if (_scene == 3) { if (_mesh.load("miku.md2")) { // Rotate mesh to right rotation (md2s are messed up like that..) glm::mat4 rotation = glm::rotate(180.0f, glm::vec3(0, 0, 1.0f)); rotation = glm::rotate(rotation, 90.0f, glm::vec3(0, 1.0f, 0)); _mesh.transform(rotation); // Get min, max and func for octree generation glm::vec3 overall_min = *_mesh.getMin(); glm::vec3 overall_max = *_mesh.getMax(); overall_max += (overall_max - overall_min); // Make cube around bounds glm::vec3 extents = (overall_max - overall_min) * 0.5f; glm::vec3 centre = overall_min + extents; float greatest_extent = std::max(std::max(extents.x, extents.y), extents.z); extents.x = greatest_extent; extents.y = greatest_extent; extents.z = greatest_extent; overall_min = centre - extents; overall_max = centre + extents; // Sphere position glm::vec3 sphere_pos = centre; sphere_pos.x += extents.x * 0.5f; float sphere_radius = 10.0f; // Test function auto test_func = [&] (glm::vec3 min, glm::vec3 max, raw_attachment_uncompressed& shading_attributes) { glm::vec3 half_size = 0.5f * (max - min); glm::vec3 centre = min + half_size; float half_size_one_axis = half_size.x; //bool sphere_intersect = cubeSphereSurfaceIntersection(centre, half_size_one_axis, sphere_pos, sphere_radius); bool sphere_intersect = boxSphereIntersection(min, max, sphere_pos, sphere_radius); if (sphere_intersect) { // This is not normalised to save generation time // it is normalised later in the GPU anyway after being unpacked shading_attributes.normal = sphere_pos - centre; shading_attributes.colour = glm::vec4(1.0f, 1.0f, 1.0f, 0.5f); shading_attributes.reflectivity = 1.0f; shading_attributes.refractive_index = 1.5f; float distFromCentre = glm::length(shading_attributes.normal); if (distFromCentre < 0.5f * sphere_radius) shading_attributes.refractive_index = 1.0f; return true; } else { bool mesh_intersect = meshAABBIntersect(&_mesh, min, max, shading_attributes); if (mesh_intersect) { shading_attributes.colour.a = 1.0f; shading_attributes.reflectivity = 0.0f; return true; } } return false; }; point_test_func func(test_func); // Generate octree tree_size = genOctree((int32_t**)&tree_data, _depth, func, overall_min, overall_max); } } // r = 1.5 sphere with hollow sphere (should be the same as above) if (_scene == 4) { if (_mesh.load("miku.md2")) { // Rotate mesh to right rotation (md2s are messed up like that..) glm::mat4 rotation = glm::rotate(180.0f, glm::vec3(0, 0, 1.0f)); rotation = glm::rotate(rotation, 90.0f, glm::vec3(0, 1.0f, 0)); _mesh.transform(rotation); // Get min, max and func for octree generation glm::vec3 overall_min = *_mesh.getMin(); glm::vec3 overall_max = *_mesh.getMax(); overall_max += (overall_max - overall_min); // Make cube around bounds glm::vec3 extents = (overall_max - overall_min) * 0.5f; glm::vec3 centre = overall_min + extents; float greatest_extent = std::max(std::max(extents.x, extents.y), extents.z); extents.x = greatest_extent; extents.y = greatest_extent; extents.z = greatest_extent; overall_min = centre - extents; overall_max = centre + extents; // Sphere position glm::vec3 sphere_pos = centre; sphere_pos.x += extents.x * 0.5f; float sphere_radius = 10.0f; // Test function auto test_func = [&] (glm::vec3 min, glm::vec3 max, raw_attachment_uncompressed& shading_attributes) { glm::vec3 half_size = 0.5f * (max - min); glm::vec3 centre = min + half_size; float half_size_one_axis = half_size.x; //bool sphere_intersect = cubeSphereSurfaceIntersection(centre, half_size_one_axis, sphere_pos, sphere_radius); bool sphere_intersect = boxSphereIntersection(min, max, sphere_pos, sphere_radius); if (sphere_intersect) { // This is not normalised to save generation time // it is normalised later in the GPU anyway after being unpacked shading_attributes.normal = sphere_pos - centre; shading_attributes.colour = glm::vec4(1.0f, 1.0f, 1.0f, 0.5f); shading_attributes.reflectivity = 1.0f; shading_attributes.refractive_index = 1.5f; float distFromCentre = glm::length(shading_attributes.normal); if (distFromCentre < 0.5f * sphere_radius) return false; return true; } else { bool mesh_intersect = meshAABBIntersect(&_mesh, min, max, shading_attributes); if (mesh_intersect) { shading_attributes.colour.a = 1.0f; shading_attributes.reflectivity = 0.0f; return true; } } return false; }; point_test_func func(test_func); // Generate octree tree_size = genOctree((int32_t**)&tree_data, _depth, func, overall_min, overall_max); } } // Generate from mesh if (_scene == 5) { Mesh teapot("teapot.obj", true); Mesh checkerboard("checkerboard.obj", true); // Lift teapot off checkerboard a bit so they don't intersect glm::mat4 translation = glm::translate(glm::vec3(0.0f, 0.1f, 0.0f)); glm::mat4 rotation = glm::rotate(180.0f, glm::vec3(0, 0, 1.0f)); checkerboard.transform(translation * rotation); teapot.transform(rotation); // Get min, max and func for octree generation glm::vec3 overall_min = *teapot.getMin(); glm::vec3 overall_max = *teapot.getMax(); overall_max += (overall_max - overall_min); // Make cube around bounds glm::vec3 extents = (overall_max - overall_min) * 0.5f; glm::vec3 centre = overall_min + extents; float greatest_extent = std::max(std::max(extents.x, extents.y), extents.z); extents.x = greatest_extent; extents.y = greatest_extent; extents.z = greatest_extent; overall_min = centre - extents; overall_max = centre + extents; // Test function auto test_func = [&] (glm::vec3 min, glm::vec3 max, raw_attachment_uncompressed& shading_attributes) { if (meshAABBIntersect(&checkerboard, min, max, shading_attributes)) { shading_attributes.colour.a = 1.0f; shading_attributes.reflectivity = 0.0f; return true; } if (meshAABBIntersect(&teapot, min, max, shading_attributes)) { shading_attributes.colour.a = 0.3f; shading_attributes.reflectivity = 0.5f; return true; } return false; }; point_test_func func(test_func); // Generate octree tree_size = genOctree((int32_t**)&tree_data, _depth, func, overall_min, overall_max); } // transparent and reflective boxes on checkerboard if (_scene == 6) { Mesh checkerboard("checkerboard.obj", true); // The cube should have flat normals instead of smooth Mesh box1("cube.obj", true, aiProcess_GenNormals); Mesh box2("cube.obj", true, aiProcess_GenNormals); // Rotate checkerboard glm::mat4 rotation = glm::rotate(180.0f, glm::vec3(0, 0, 1.0f)); checkerboard.transform(rotation); // Scale and position box glm::mat4 translation = glm::translate(glm::vec3(0, 1.0f, 0)); glm::mat4 scale = glm::scale(glm::vec3(0.25f)); glm::mat4 box_transform = scale * translation; box1.transform(scale); box2.transform(scale); // Get min, max and func for octree generation glm::vec3 overall_min = *checkerboard.getMin(); glm::vec3 overall_max = *checkerboard.getMax(); overall_max += (overall_max - overall_min); // Make cube around bounds glm::vec3 extents = (overall_max - overall_min) * 0.5f; glm::vec3 centre = overall_min + extents; float greatest_extent = std::max(std::max(extents.x, extents.y), extents.z); extents.x = greatest_extent; extents.y = greatest_extent; extents.z = greatest_extent; overall_min = centre - extents; overall_max = centre + extents; // Test function auto test_func = [&] (glm::vec3 min, glm::vec3 max, raw_attachment_uncompressed& shading_attributes) { if (meshAABBIntersect(&checkerboard, min, max, shading_attributes)) { shading_attributes.colour.a = 1.0f; shading_attributes.reflectivity = 0.0f; return true; } if (meshAABBIntersect(&box1, min, max, shading_attributes)) { shading_attributes.colour.a = 1.0f; shading_attributes.reflectivity = 1.0f; return true; } return false; }; point_test_func func(test_func); // Generate octree tree_size = genOctree((int32_t**)&tree_data, _depth, func, overall_min, overall_max); } // Reflective inverted box with teapot inside if (_scene == 7) { // The cube should have flat normals instead of smooth Mesh teapot("teapot.obj", true); Mesh box1("inverted_cube.obj", true, aiProcess_GenNormals); teapot.transform(glm::scale(glm::vec3(0.2f)) * glm::rotate(180.0f, glm::vec3(0.0f, 0.0f, 1.0f))); // Get min, max and func for octree generation glm::vec3 overall_min = *box1.getMin(); glm::vec3 overall_max = *box1.getMax(); overall_max += (overall_max - overall_min); // Make cube around bounds glm::vec3 extents = (overall_max - overall_min) * 0.5f; glm::vec3 centre = overall_min + extents; float greatest_extent = std::max(std::max(extents.x, extents.y), extents.z); extents.x = greatest_extent; extents.y = greatest_extent; extents.z = greatest_extent; overall_min = centre - extents; overall_max = centre + extents; // Test function auto test_func = [&] (glm::vec3 min, glm::vec3 max, raw_attachment_uncompressed& shading_attributes) { glm::vec3 aabb_extents = (max - min) * 0.5f; glm::vec3 aabb_centre = aabb_centre + min; if (meshAABBIntersect(&box1, min, max, shading_attributes)) { shading_attributes.colour.a = 1.0f; shading_attributes.reflectivity = 1.0f; return true; } if (meshAABBIntersect(&teapot, min, max, shading_attributes)) { shading_attributes.colour = glm::vec4(1.0f, 0.0f, 0.0f, 1.0f); shading_attributes.reflectivity = 0.0f; return true; } return false; }; point_test_func func(test_func); // Generate octree tree_size = genOctree((int32_t**)&tree_data, _depth, func, overall_min, overall_max); } // Write custom generation to file if (_saveTrees) { printf("Writing %s...\n", filename_buffer); file = fopen(filename_buffer, "wb"); if (file == nullptr) { fprintf(stderr, "Failed to open file for writing: %s\n", filename_buffer); } else { fwrite(tree_data, tree_size, sizeof(char), file); fclose(file); } } } } // Calculate time to load/generate end_time = glfwGetTime(); dt = end_time - start_time; printf("Time to load/generate tree: %f\n", dt); printf("%.2fMB\n", tree_size / (1024.0f * 1024.0f)); // Reset timer start_time = glfwGetTime(); // Upload sphere to GPU gpuErrchk(cudaMalloc((void**)&_gpuTree, tree_size)); gpuErrchk(cudaMemcpy(_gpuTree, tree_data, tree_size, cudaMemcpyHostToDevice)); // Calculate time to upload end_time = glfwGetTime(); dt = end_time - start_time; printf("Time to upload tree: %f\n", dt); printf("%.2fMB\n", tree_size / (1024.0f * 1024.0f)); // Free CPU memory //free(tree_data); }
int Octree::genOctree(OctreeNode* root, Vec3f& leftDown, Vec3f& rightUp, std::vector<int>& nodeIdx, int maxDepth) { //1. Set bounding box root->setBoundingBox(leftDown, rightUp); //2. Stop if there is only one node in the box if(Mode==OCTREE_NORMAL) { if(nodeIdx.size()==0) { root->End=true; return 0; } if(root->Depth==maxDepth) { root->End=true; return 0; } } if(Mode==OCTREE_UNIFORM) { if(root->Depth==maxDepth) { root->End=true; return 0; } } if(Mode==OCTREE_MULTILEVEL) { if(root->Depth==maxDepth) { if(nodeIdx.size()==0) { root->End=true; return 0; } } if(root->Depth==(maxDepth+1)) { root->End=true; return 0; } } //3. Descendant OctreeNode** descendant=new OctreeNode*[8]; for(int i=0;i<8;i++) { descendant[i]=new OctreeNode; descendant[i]->Depth=root->Depth+1; root->Descendant[i]=descendant[i]; if(nodeIdx.size()>0) descendant[i]->NodeIdx=1; } //4. Descendant generation Vec3f mid=(leftDown+rightUp)/2.0; std::vector<int> _nodeIdx[8]; for(int i=0;i<nodeIdx.size();i++) { if((*Node)[nodeIdx[i]][0]>mid[0]) { if((*Node)[nodeIdx[i]][1]>mid[1]) { if((*Node)[nodeIdx[i]][2]>mid[2]) { //RightTopFront _nodeIdx[0].push_back(nodeIdx[i]); } else { //RightTopBehind _nodeIdx[1].push_back(nodeIdx[i]); } } else { if((*Node)[nodeIdx[i]][2]>mid[2]) { //RightBottomFront _nodeIdx[2].push_back(nodeIdx[i]); } else { //RightBottomBehind _nodeIdx[3].push_back(nodeIdx[i]); } } } else { if((*Node)[nodeIdx[i]][1]>mid[1]) { if((*Node)[nodeIdx[i]][2]>mid[2]) { //LeftTopFront _nodeIdx[4].push_back(nodeIdx[i]); } else { //LeftTopBehind _nodeIdx[5].push_back(nodeIdx[i]); } } else { if((*Node)[nodeIdx[i]][2]>mid[2]) { //LeftBottomFront _nodeIdx[6].push_back(nodeIdx[i]); } else { //LeftBottomBehind _nodeIdx[7].push_back(nodeIdx[i]); } } } } Vec3f p1; Vec3f p2; //RightTopFront p1=mid; p2=rightUp; genOctree(descendant[0], p1, p2, _nodeIdx[0], maxDepth); //RightTopBehind p1=Vec3f(mid[0], mid[1], leftDown[2]); p2=Vec3f(rightUp[0], rightUp[1], mid[2]); genOctree(descendant[1], p1, p2, _nodeIdx[1], maxDepth); //RightBottomFront p1=Vec3f(mid[0], leftDown[1], mid[2]); p2=Vec3f(rightUp[0], mid[1], rightUp[2]) ; genOctree(descendant[2], p1, p2, _nodeIdx[2], maxDepth); //RightBottomBehind p1=Vec3f(mid[0],leftDown[1],leftDown[2]); p2=Vec3f(rightUp[0], mid[1], mid[2]); genOctree(descendant[3], p1, p2, _nodeIdx[3], maxDepth); //LeftTopFront p1=Vec3d(leftDown[0], mid[1], mid[2]); p2=Vec3d(mid[0], rightUp[1], rightUp[2]); genOctree(descendant[4], p1, p2, _nodeIdx[4], maxDepth); //LeftTopBehind p1=Vec3f(leftDown[0], mid[1], leftDown[2]); p2=Vec3f(mid[0], rightUp[1], mid[2]); genOctree(descendant[5], p1, p2, _nodeIdx[5], maxDepth); //LeftBottomFront p1=Vec3f(leftDown[0], leftDown[1], mid[2]); p2=Vec3f(mid[0], mid[1], rightUp[2]); genOctree(descendant[6], p1, p2, _nodeIdx[6], maxDepth); //LeftBottomBehind p1=leftDown; p2=mid; genOctree(descendant[7], p1, p2, _nodeIdx[7], maxDepth); delete [] descendant; return 0; }