From 4febc51784b76363baf16621dc55e2c837e7768d Mon Sep 17 00:00:00 2001 From: Yggdrasil75 Date: Wed, 28 Jan 2026 14:34:58 -0500 Subject: [PATCH] it works --- tests/g3etest.cpp | 634 +++++++++++++++++++++++++++++++++------ util/grid/camera.hpp | 80 +++-- util/grid/grid3eigen.hpp | 192 ++++++++++++ 3 files changed, 790 insertions(+), 116 deletions(-) diff --git a/tests/g3etest.cpp b/tests/g3etest.cpp index 942f644..a1c79aa 100644 --- a/tests/g3etest.cpp +++ b/tests/g3etest.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -20,112 +21,547 @@ #include #include "../stb/stb_image.h" -int main() { - // Define octree boundaries (world space) - using PointType = Eigen::Matrix; - PointType minBound(-2.0f, -2.0f, -2.0f); - PointType maxBound(2.0f, 2.0f, 2.0f); - - // Create octree - Octree octree(minBound, maxBound, 16, 8); // max 16 points per node, max depth 8 - - // Create green sphere (center at origin, radius 1.0) - float radius = 1.0f; - int pointCount = 0; - Eigen::Vector3f greenColor(0.0f, 1.0f, 0.0f); // green - - // Add points on sphere surface (simplified representation) - for (int i = 0; i < 10000; ++i) { - // Spherical coordinates - float u = static_cast(rand()) / RAND_MAX; - float v = static_cast(rand()) / RAND_MAX; - - // Convert to spherical coordinates - float theta = 2.0f * M_PI * u; // azimuth - float phi = acos(2.0f * v - 1.0f); // polar - - // Convert to Cartesian coordinates - float x = radius * sin(phi) * cos(theta); - float y = radius * sin(phi) * sin(theta); - float z = radius * cos(phi); - - PointType pos(x, y, z); - - // Set point data with larger size for visibility - // Note: The third parameter is size, which should be radius squared for intersection test - octree.set(i, pos, true, greenColor, 1.0, true); - pointCount++; +struct defaults { + int outWidth = 512; + int outHeight = 512; + int gridSizecube = 512; + PNoise2 noise = PNoise2(42); +}; + +struct spheredefaults { + float centerX = 0.0f; + float centerY = 0.0f; + float centerZ = 0.0f; + float radius = 128.0f; + float color[3] = {0.0f, 1.0f, 0.0f}; + bool light = false; + float emittance = 0.0f; + float reflection = 0.0f; + float refraction = 0.0f; + bool fillInside = true; + float voxelSize = 1.0f; +}; + +struct ceilingdefaults { + float minX = 0.0f; + float maxX = 512.0f; + float minZ = 0.0f; + float maxZ = 512.0f; + float yLevel = 450.0f; // Near the top + float spacing = 10.0f; // Distance between light points + float color[3] = {1.0f, 1.0f, 1.0f}; // White light + float emittance = 5.0f; // Brightness + float voxelSize = 2.0f; + bool enabled = true; +}; + +std::mutex PreviewMutex; +GLuint textu = 0; +bool textureInitialized = false; +bool updatePreview = false; +bool previewRequested = false; +using PointType = Eigen::Matrix; + +void createSphere(const defaults& config, const spheredefaults& sconfig, Octree& grid) { + if (!grid.empty()) grid.clear(); + int minX = std::max(0, (int)(sconfig.centerX - sconfig.radius - 1)); + int maxX = std::min(config.gridSizecube, (int)(sconfig.centerX + sconfig.radius + 1)); + int minY = std::max(0, (int)(sconfig.centerY - sconfig.radius - 1)); + int maxY = std::min(config.gridSizecube, (int)(sconfig.centerY + sconfig.radius + 1)); + int minZ = std::max(0, (int)(sconfig.centerZ - sconfig.radius - 1)); + int maxZ = std::min(config.gridSizecube, (int)(sconfig.centerZ + sconfig.radius + 1)); + + float radSq = sconfig.radius * sconfig.radius; + float innerRadSq = 0.0f; + if (!sconfig.fillInside) { + float innerR = std::max(0.0f, sconfig.radius - 2.0f); + innerRadSq = innerR * innerR; } - std::cout << "Added " << pointCount << " points to the green sphere." << std::endl; - - // Render parameters - int width = 2048; - int height = 2048; - - // Set up random number generator for camera positions - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_real_distribution angleDist(0.0f, 2.0f * M_PI); - std::uniform_real_distribution elevationDist(0.1f, M_PI - 0.1f); // Avoid poles - std::uniform_real_distribution radiusDist(2.0f, 4.0f); // Distance from sphere - - // Generate and save 15 random views - const int numViews = 15; - - for (int viewIndex = 0; viewIndex < numViews; ++viewIndex) { - std::cout << "\nRendering view " << (viewIndex + 1) << " of " << numViews << "..." << std::endl; - - // Generate random spherical coordinates for camera position - float azimuth = angleDist(gen); - float elevation = elevationDist(gen); - float camRadius = radiusDist(gen); - - // Convert to Cartesian coordinates for camera position - float camX = camRadius * sin(elevation) * cos(azimuth); - float camY = camRadius * sin(elevation) * sin(azimuth); - float camZ = camRadius * cos(elevation); - - // Camera looks at the origin (center of sphere) - Vector3f cameraPos(camX, camY, camZ); - Vector3f lookAt(0.0f, 0.0f, 0.0f); - - // Calculate camera direction (from position to lookAt) - Vector3f cameraDir = (lookAt - cameraPos).normalized(); - - // Calculate up vector (avoid gimbal lock) - Vector3f worldUp(0.0f, 1.0f, 0.0f); - Vector3f right = cameraDir.cross(worldUp).normalized(); - Vector3f cameraUp = right.cross(cameraDir).normalized(); - - // Create camera - Camera cam(cameraPos, cameraDir, cameraUp, 80); - - // Render frame - frame renderedFrame = octree.renderFrame(cam, height, width, frame::colormap::RGB); - - std::cout << "Frame rendered. Dimensions: " - << renderedFrame.getWidth() << "x" - << renderedFrame.getHeight() << std::endl; - - // Save as BMP - std::string filename = "output/green_sphere_view_" + std::to_string(viewIndex + 1) + ".bmp"; - std::cout << "Saving to " << filename << "..." << std::endl; - - if (BMPWriter::saveBMP(filename, renderedFrame)) { - std::cout << "Successfully saved view to " << filename << std::endl; + Eigen::Vector3f colorVec(sconfig.color[0], sconfig.color[1], sconfig.color[2]); + for (int x = minX; x < maxX; ++x) { + for (int y = minY; y < maxY; ++y) { + for (int z = minZ; z < maxZ; ++z) { + float dx = x - sconfig.centerX; + float dy = y - sconfig.centerY; + float dz = z - sconfig.centerZ; + float distSq = dx*dx + dy*dy + dz*dz; + + bool solid = distSq <= radSq; + + if (solid) { + if (!(sconfig.fillInside) && distSq < innerRadSq) { + continue; + } + + PointType pos((float)x, (float)y, (float)z); + grid.set(1,pos, true, colorVec, sconfig.voxelSize, true, sconfig.light, sconfig.emittance, sconfig.refraction, sconfig.reflection); + } + } + } + } +} + +void addCeilingLight(const defaults& config, const ceilingdefaults& ceilingconf, Octree& grid) { + if (!ceilingconf.enabled) return; + + Eigen::Vector3f colorVec(ceilingconf.color[0], ceilingconf.color[1], ceilingconf.color[2]); + + // Iterate over X and Z within bounds, stepping by 'spacing' + for (float x = ceilingconf.minX; x <= ceilingconf.maxX; x += ceilingconf.spacing) { + for (float z = ceilingconf.minZ; z <= ceilingconf.maxZ; z += ceilingconf.spacing) { - // Print camera position for reference - std::cout << "Camera position: (" << camX << ", " << camY << ", " << camZ << ")" << std::endl; - } else { - std::cerr << "Failed to save BMP file: " << filename << std::endl; + PointType pos(x, ceilingconf.yLevel, z); + + grid.set(2, pos, true, colorVec, ceilingconf.voxelSize, true, true, ceilingconf.emittance, 0.0f, 0.0f); + } + } + grid.printStats(); +} + +void livePreview(Octree& grid, defaults& config, const Camera& cam) { + std::lock_guard lock(PreviewMutex); + updatePreview = true; + frame currentPreviewFrame = grid.renderFrame(cam, config.outWidth, config.outHeight, frame::colormap::RGB); + + glGenTextures(1, &textu); + glBindTexture(GL_TEXTURE_2D, textu); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + glBindTexture(GL_TEXTURE_2D, textu); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, currentPreviewFrame.getWidth(), currentPreviewFrame.getHeight(), + 0, GL_RGB, GL_UNSIGNED_BYTE, currentPreviewFrame.getData().data()); + //BMPWriter::saveBMP("output/frameoutput.bmp", currentPreviewFrame); + updatePreview = false; + textureInitialized = true; +} + +void resetView(Camera& cam, float gridSize) { + cam.origin = Vector3f(gridSize * 1.5f, gridSize * 1.5f, gridSize * 1.5f); + Vector3f center(gridSize / 2.0f, gridSize / 2.0f, gridSize / 2.0f); + cam.lookAt(center); +} + +static void glfw_error_callback(int error, const char* description) +{ + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} + +int main() { + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) { + std::cerr << "gui stuff is dumb in c++." << std::endl; + glfwTerminate(); + return 1; + } + // COPIED VERBATIM FROM IMGUI. + #if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 (WebGL 1.0) + const char* glsl_version = "#version 100"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + #elif defined(IMGUI_IMPL_OPENGL_ES3) + // GL ES 3.0 + GLSL 300 es (WebGL 2.0) + const char* glsl_version = "#version 300 es"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + #elif defined(__APPLE__) + // GL 3.2 + GLSL 150 + const char* glsl_version = "#version 150"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac + #else + // GL 3.0 + GLSL 130 + const char* glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + //glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + //glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only + #endif + + bool application_not_closed = true; + GLFWwindow* window = glfwCreateWindow((int)(1280), (int)(800), "voxelgrid live renderer", nullptr, nullptr); + if (window == nullptr) { + glfwTerminate(); + return 1; + } + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; + ImGui::StyleColorsDark(); + ImGuiStyle& style = ImGui::GetStyle(); + ImGui_ImplGlfw_InitForOpenGL(window, true); + + #ifdef __EMSCRIPTEN__ + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); + #endif + ImGui_ImplOpenGL3_Init(glsl_version); + + bool show_demo_window = true; + bool show_another_window = false; + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + defaults config; + PointType minBound(-config.gridSizecube, -config.gridSizecube, -config.gridSizecube); + PointType maxBound(config.gridSizecube, config.gridSizecube, config.gridSizecube); + Octree grid(minBound, maxBound, 16, 16); + bool gridInitialized = false; + float ghalf = config.gridSizecube / 2.f; + Camera cam(PointType(ghalf, ghalf, ghalf), PointType(0,0,1), PointType(0,1,0), 80); + spheredefaults sphereConf; + ceilingdefaults ceilingConf; + + bool autoRotate = false; + bool autoRotateView = false; + float rotationSpeedX = 0.1f; + float rotationSpeedY = 0.07f; + float rotationSpeedZ = 0.05f; + float autoRotationTime = 0.0f; + PointType initialViewDir(0, 0, 1); + float rotationRadius = 255.0f; + float yawSpeed = 0.5f; + float pitchSpeed = 0.3f; + float rollSpeed = 0.2f; + float autoRotationAngle = 0.0f; + PointType initialUpDir(0, 1, 0); + float camX = 0.0f; + float camY = 0.0f; + float camZ = 0.0f; + float camvX = 0.f; + float camvY = 0.f; + float camvZ = 0.f; + float camspeed = 50; + + // Keyboard state tracking + std::map keyStates; + bool mouseCaptured = false; + double lastMouseX = 0, lastMouseY = 0; + float deltaTime = 0.016f; + + while (!glfwWindowShouldClose(window)) { + double currentTime = glfwGetTime(); + static double lastFrameTime = currentTime; + deltaTime = currentTime - lastFrameTime; + lastFrameTime = currentTime; + + glfwPollEvents(); + + for (int i = GLFW_KEY_SPACE; i <= GLFW_KEY_LAST; i++) { + keyStates[i] = (glfwGetKey(window, i) == GLFW_PRESS); } - // Small delay to ensure unique random seeds - std::this_thread::sleep_for(std::chrono::milliseconds(10)); + // Camera movement - WASD + QE + ZX + float actualMoveSpeed = deltaTime; + float actualRotateSpeed = deltaTime; + + if (keyStates[GLFW_KEY_W]) { + cam.moveForward(actualMoveSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_S]) { + cam.moveBackward(actualMoveSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_A]) { + cam.moveLeft(actualMoveSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_D]) { + cam.moveRight(actualMoveSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_Z]) { + cam.moveUp(actualMoveSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_X]) { + cam.moveDown(actualMoveSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_Q]) { + cam.rotateYaw(actualRotateSpeed); + previewRequested = true; + } + if (keyStates[GLFW_KEY_R]) { + cam.rotateYaw(-actualRotateSpeed); + previewRequested = true; + } + + // Update camera position and view direction variables for UI + camX = cam.origin[0]; + camY = cam.origin[1]; + camZ = cam.origin[2]; + + camvX = cam.direction[0]; + camvY = cam.direction[1]; + camvZ = cam.direction[2]; + camspeed = cam.movementSpeed; + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + { + ImGui::Begin("Controls"); + + ImGui::Text("Sphere Parameters"); + float pos[3] = { sphereConf.centerX, sphereConf.centerY, sphereConf.centerZ }; + if (ImGui::DragFloat3("Center (X,Y,Z)", pos, 1.0f, 0.0f, (float)config.gridSizecube)) { + sphereConf.centerX = pos[0]; + sphereConf.centerY = pos[1]; + sphereConf.centerZ = pos[2]; + } + + ImGui::DragFloat("Radius", &sphereConf.radius, 0.5f, 1.0f, 250.0f); + ImGui::ColorEdit3("Color", sphereConf.color); + ImGui::DragFloat("Voxel Size", &sphereConf.voxelSize, 0.1f, 0.1f, 5.0f); + + ImGui::Separator(); + ImGui::Checkbox("Is Light", &sphereConf.light); + if(sphereConf.light) { + ImGui::DragFloat("Emittance", &sphereConf.emittance, 0.1f, 0.0f, 100.0f); + } + ImGui::SliderFloat("Reflection", &sphereConf.reflection, 0.0f, 1.0f); + ImGui::SliderFloat("Refraction", &sphereConf.refraction, 0.0f, 1.0f); + ImGui::Checkbox("Fill Inside", &sphereConf.fillInside); + + if (ImGui::CollapsingHeader("Ceiling Light Parameters", ImGuiTreeNodeFlags_DefaultOpen)) { + ImGui::Checkbox("Enable Ceiling Light", &ceilingConf.enabled); + ImGui::DragFloat("Height (Y)", &ceilingConf.yLevel, 1.0f, 0.0f, (float)config.gridSizecube); + ImGui::DragFloat("Spacing", &ceilingConf.spacing, 0.5f, 1.0f, 100.0f); + ImGui::DragFloat("Light Emittance", &ceilingConf.emittance, 0.1f, 0.0f, 100.0f); + ImGui::ColorEdit3("Light Color", ceilingConf.color); + ImGui::DragFloat("Light Voxel Size", &ceilingConf.voxelSize, 0.1f, 0.1f, 10.0f); + } + + ImGui::Separator(); + + if (ImGui::Button("Create Sphere & Render")) { + createSphere(config, sphereConf, grid); + grid.printStats(); + addCeilingLight(config, ceilingConf, grid); + gridInitialized = true; + + resetView(cam, config.gridSizecube); + + livePreview(grid, config, cam); + ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight)); + + } + + ImGui::End(); + } + + { + ImGui::Begin("Preview"); + + if (gridInitialized && textureInitialized) { + ImGui::Image((void*)(intptr_t)textu, ImVec2(config.outWidth, config.outHeight)); + } else if (gridInitialized) { + ImGui::Text("Preview not generated yet"); + } else { + ImGui::Text("No grid generated"); + } + + ImGui::End(); + } + + { + ImGui::Begin("controls"); + + ImGui::Separator(); + ImGui::Text("Camera Controls:"); + + float maxSliderValueX = config.gridSizecube; + float maxSliderValueY = config.gridSizecube; + float maxSliderValueZ = config.gridSizecube; + float maxSliderValueRotation = 360.0f; + + ImGui::Text("Position (0 to grid size²):"); + if (ImGui::SliderFloat("Camera X", &camX, 0.0f, maxSliderValueX)) { + cam.origin[0] = camX; + } + if (ImGui::SliderFloat("Camera Y", &camY, 0.0f, maxSliderValueY)) { + cam.origin[1] = camY; + } + if (ImGui::SliderFloat("Camera Z", &camZ, 0.0f, maxSliderValueZ)) { + cam.origin[2] = camZ; + } + + ImGui::Separator(); + ImGui::Text("View Direction:"); + if (ImGui::SliderFloat("Camera View X", &camvX, -1.0f, 1.0f)) { + cam.direction[0] = camvX; + } + if (ImGui::SliderFloat("Camera View Y", &camvY, -1.0f, 1.0f)) { + cam.direction[1] = camvY; + } + if (ImGui::SliderFloat("Camera View Z", &camvZ, -1.0f, 1.0f)) { + cam.direction[2] = camvZ; + } + + if (ImGui::SliderFloat("Camera Speed", &camspeed, 1, 100)) { + cam.movementSpeed = camspeed; + } + + ImGui::Separator(); + ImGui::Text("Current Camera Position:"); + ImGui::Text("X: %.2f, Y: %.2f, Z: %.2f", + cam.origin[0], + cam.origin[1], + cam.origin[2]); + + ImGui::Text("Auto-Rotation:"); + + // Toggle button for auto-rotation + if (ImGui::Button(autoRotate ? "Stop Auto-Rotation" : "Start Auto-Rotation")) { + autoRotate = !autoRotate; + if (autoRotate) { + autoRotationTime = 0.0f; + initialViewDir = PointType(camvX, camvY, camvZ); + } + } + + if (ImGui::Button(autoRotateView ? "Stop Looking Around" : "Start Looking Around")) { + autoRotateView = !autoRotateView; + if (autoRotateView) { + autoRotationAngle = 0.0f; + initialViewDir = PointType(camvX, camvY, camvZ); + } + } + + if (autoRotate) { + ImGui::SameLine(); + ImGui::Text("(Running)"); + + // Calculate new view direction using frame-based timing + float angleX = autoRotationTime * rotationSpeedX; + float angleY = autoRotationTime * rotationSpeedY; + float angleZ = autoRotationTime * rotationSpeedZ; + + camvX = sinf(angleX) * cosf(angleY); + camvY = sinf(angleY) * sinf(angleZ); + camvZ = cosf(angleX) * cosf(angleZ); + + // Normalize + float length = sqrtf(camvX * camvX + camvY * camvY + camvZ * camvZ); + if (length > 0.001f) { + camvX /= length; + camvY /= length; + camvZ /= length; + } + + // Update camera position + camX = config.gridSizecube / 2.0f + rotationRadius * camvX; + camY = config.gridSizecube / 2.0f + rotationRadius * camvY; + camZ = config.gridSizecube / 2.0f + rotationRadius * camvZ; + + // Update camera + cam.origin = PointType(camX, camY, camZ); + cam.direction = PointType(camvX, camvY, camvZ); + + // Sliders to control rotation speeds + ImGui::SliderFloat("X Speed", &rotationSpeedX, 0.01f, 1.0f); + ImGui::SliderFloat("Y Speed", &rotationSpeedY, 0.01f, 1.0f); + ImGui::SliderFloat("Z Speed", &rotationSpeedZ, 0.01f, 1.0f); + + // Slider for orbit radius + ImGui::SliderFloat("Orbit Radius", &rotationRadius, 10.0f, 200.0f); + } + + if (autoRotateView) { + ImGui::SameLine(); + ImGui::TextColored(ImVec4(0, 1, 0, 1), " ACTIVE"); + + // Calculate rotation angles using frame-based timing + float yaw = autoRotationAngle * yawSpeed * (3.14159f / 180.0f); + float pitch = sinf(autoRotationAngle * 0.7f) * pitchSpeed * (3.14159f / 180.0f); + + // Apply rotations + PointType forward = initialViewDir; + + // Yaw rotation (around Y axis) + float cosYaw = cosf(yaw); + float sinYaw = sinf(yaw); + PointType tempForward; + tempForward[0] = forward[0] * cosYaw + forward[2] * sinYaw; + tempForward[1] = forward[1]; + tempForward[2] = -forward[0] * sinYaw + forward[2] * cosYaw; + forward = tempForward; + + // Pitch rotation (around X axis) + float cosPitch = cosf(pitch); + float sinPitch = sinf(pitch); + tempForward[0] = forward[0]; + tempForward[1] = forward[1] * cosPitch - forward[2] * sinPitch; + tempForward[2] = forward[1] * sinPitch + forward[2] * cosPitch; + forward = tempForward; + + // Normalize + float length = sqrtf(forward[0] * forward[0] + forward[1] * forward[1] + forward[2] * forward[2]); + if (length > 0.001f) { + forward[0] /= length; + forward[1] /= length; + forward[2] /= length; + } + + // Update view direction + camvX = forward[0]; + camvY = forward[1]; + camvZ = forward[2]; + + // Update camera + cam.direction = PointType(camvX, camvY, camvZ); + + // Show current view direction + ImGui::Text("Current View: (%.3f, %.3f, %.3f)", camvX, camvY, camvZ); + + // Sliders to control rotation speeds + ImGui::SliderFloat("Yaw Speed", &yawSpeed, 0.1f, 5.0f, "%.2f deg/sec"); + ImGui::SliderFloat("Pitch Speed", &pitchSpeed, 0.0f, 2.0f, "%.2f deg/sec"); + } + + ImGui::End(); + } + + if (gridInitialized) livePreview(grid, config, cam); + + ImGui::Render(); + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(window); } - std::cout << "\nAll " << numViews << " views have been saved to the output directory." << std::endl; + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + glfwDestroyWindow(window); + if (textu != 0) { + glDeleteTextures(1, &textu); + textu = 0; + } + glfwTerminate(); + + FunctionTimer::printStats(FunctionTimer::Mode::ENHANCED); return 0; } \ No newline at end of file diff --git a/util/grid/camera.hpp b/util/grid/camera.hpp index fa1ab3f..dbb239b 100644 --- a/util/grid/camera.hpp +++ b/util/grid/camera.hpp @@ -13,24 +13,23 @@ struct Camera { Vector3f direction; Vector3f up; float fov; + float movementSpeed; + float rotationSpeed; - Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80) - : origin(pos), direction(viewdir), up(up.normalized()), fov(fov) {} + Camera(const Vector3f& pos, const Vector3f& viewdir, const Vector3f& up, float fov = 80, + float moveSpeed = 1.0f, float rotSpeed = 0.5f) + : origin(pos), direction(viewdir.normalized()), up(up.normalized()), fov(fov), movementSpeed(moveSpeed), rotationSpeed(rotSpeed) {} void rotateYaw(float angle) { - float cosA = cos(angle); - float sinA = sin(angle); - - Vector3f right = direction.cross(up).normalized(); - - // Rotate around up vector (yaw) + angle *= rotationSpeed; Matrix3f rotation; rotation = Eigen::AngleAxisf(angle, up); direction = rotation * direction; + direction.normalize(); } void rotatePitch(float angle) { - // Clamp pitch to avoid gimbal lock + angle *= rotationSpeed; Vector3f right = direction.cross(up).normalized(); // Rotate around right vector (pitch) @@ -43,6 +42,30 @@ struct Camera { up = right.cross(direction).normalized(); } + void moveForward(float distance) { + origin += forward() * distance * movementSpeed; + } + + void moveBackward(float distance) { + origin -= forward() * distance * movementSpeed; + } + + void moveRight(float distance) { + origin += right() * distance * movementSpeed; + } + + void moveLeft(float distance) { + origin -= right() * distance * movementSpeed; + } + + void moveUp(float distance) { + origin += up * distance * movementSpeed; + } + + void moveDown(float distance) { + origin -= up * distance * movementSpeed; + } + Vector3f forward() const { return direction.normalized(); } @@ -55,17 +78,32 @@ struct Camera { return fov * (M_PI / 180.0f); } - // Additional useful methods - void moveForward(float distance) { - origin += forward() * distance; + // Look at a specific point + void lookAt(const Vector3f& target) { + direction = (target - origin).normalized(); + + // Recalculate up vector + Vector3f worldUp(0, 1, 0); + if (direction.cross(worldUp).norm() < 0.001f) { + worldUp = Vector3f(0, 0, 1); + } + + Vector3f right = direction.cross(worldUp).normalized(); + up = right.cross(direction).normalized(); } - void moveRight(float distance) { - origin += right() * distance; + // Set position directly + void setPosition(const Vector3f& pos) { + origin = pos; } - void moveUp(float distance) { - origin += up * distance; + // Set view direction directly + void setDirection(const Vector3f& dir) { + direction = dir.normalized(); + // Recalculate up + Vector3f worldUp(0, 1, 0); + Vector3f right = direction.cross(worldUp).normalized(); + up = right.cross(direction).normalized(); } // Get view matrix (lookAt matrix) @@ -88,7 +126,7 @@ struct Camera { } // Get projection matrix (perspective) - Eigen::Matrix4f getProjectionMatrix(float aspectRatio, float nearPlane = 0.1f, float farPlane = 100.0f) const { + Eigen::Matrix4f getProjectionMatrix(float aspectRatio, float nearPlane = 0.1f, float farPlane = 1000.0f) const { float fovrad = fovRad(); float tanHalfFov = tan(fovrad / 2.0f); @@ -102,6 +140,14 @@ struct Camera { return projection; } + + void mouseLook(float deltaX, float deltaY) { + float yaw = -deltaX * 0.001f; + float pitch = -deltaY * 0.001f; + + rotateYaw(yaw); + rotatePitch(pitch); + } }; #endif \ No newline at end of file diff --git a/util/grid/grid3eigen.hpp b/util/grid/grid3eigen.hpp index adfef99..9d776d3 100644 --- a/util/grid/grid3eigen.hpp +++ b/util/grid/grid3eigen.hpp @@ -14,6 +14,7 @@ #include #include #include +#include #ifdef SSE #include @@ -44,6 +45,10 @@ public: float reflection = 0.0f) : data(data), position(pos), active(active), visible(visible), color(color), size(size), light(light), emittance(emittance), refraction(refraction), reflection(reflection) {} + + // Default constructor for serialization + NodeData() : active(false), visible(false), size(0.0f), light(false), + emittance(0.0f), refraction(0.0f), reflection(0.0f) {} }; struct OctreeNode { @@ -139,6 +144,116 @@ private: return false; } + template + void writeVal(std::ofstream& out, const V& val) const { + out.write(reinterpret_cast(&val), sizeof(V)); + } + + template + void readVal(std::ifstream& in, V& val) { + in.read(reinterpret_cast(&val), sizeof(V)); + } + + void writeVec3(std::ofstream& out, const Eigen::Vector3f& vec) const { + writeVal(out, vec.x()); + writeVal(out, vec.y()); + writeVal(out, vec.z()); + } + + void readVec3(std::ifstream& in, Eigen::Vector3f& vec) { + float x, y, z; + readVal(in, x); readVal(in, y); readVal(in, z); + vec = Eigen::Vector3f(x, y, z); + } + + void serializeNode(std::ofstream& out, const OctreeNode* node) const { + writeVal(out, node->isLeaf); + + if (node->isLeaf) { + size_t pointCount = node->points.size(); + writeVal(out, pointCount); + for (const auto& pt : node->points) { + // Write raw data T (Must be POD) + writeVal(out, pt->data); + // Write properties + writeVec3(out, pt->position); + writeVal(out, pt->active); + writeVal(out, pt->visible); + writeVal(out, pt->size); + writeVec3(out, pt->color); + writeVal(out, pt->light); + writeVal(out, pt->emittance); + writeVal(out, pt->refraction); + writeVal(out, pt->reflection); + } + } else { + // Write bitmask of active children + uint8_t childMask = 0; + for (int i = 0; i < 8; ++i) { + if (node->children[i] != nullptr) { + childMask |= (1 << i); + } + } + writeVal(out, childMask); + + // Recursively write only existing children + for (int i = 0; i < 8; ++i) { + if (node->children[i]) { + serializeNode(out, node->children[i].get()); + } + } + } + } + + void deserializeNode(std::ifstream& in, OctreeNode* node) { + bool isLeaf; + readVal(in, isLeaf); + node->isLeaf = isLeaf; + + if (isLeaf) { + size_t pointCount; + readVal(in, pointCount); + node->points.reserve(pointCount); + + for (size_t i = 0; i < pointCount; ++i) { + auto pt = std::make_shared(); + readVal(in, pt->data); + readVec3(in, pt->position); + readVal(in, pt->active); + readVal(in, pt->visible); + readVal(in, pt->size); + readVec3(in, pt->color); + readVal(in, pt->light); + readVal(in, pt->emittance); + readVal(in, pt->refraction); + readVal(in, pt->reflection); + node->points.push_back(pt); + } + } else { + uint8_t childMask; + readVal(in, childMask); + + PointType center = node->center; + + for (int i = 0; i < 8; ++i) { + if ((childMask >> i) & 1) { + // Reconstruct bounds for child + PointType childMin, childMax; + for (int d = 0; d < Dim; ++d) { + bool high = (i >> d) & 1; + childMin[d] = high ? center[d] : node->bounds.first[d]; + childMax[d] = high ? node->bounds.second[d] : center[d]; + } + + node->children[i] = std::make_unique(childMin, childMax); + deserializeNode(in, node->children[i].get()); + } else { + node->children[i] = nullptr; + } + } + } + } + void bitonic_sort_8(std::array, 8>& arr) const noexcept { #ifdef SSE alignas(32) float values[8]; @@ -280,6 +395,8 @@ public: root_(std::make_unique(minBound, maxBound)), maxPointsPerNode(maxPointsPerNode), maxDepth(maxDepth), size(0) {} + Octree() : root_(nullptr), maxPointsPerNode(16), maxDepth(16), size(0) {} + bool set(const T& data, const PointType& pos, bool visible, Eigen::Vector3f color, float size, bool active, bool light = false, float emittance = 0.0f, float refraction = 0.0f, float reflection = 0.0f) { auto pointData = std::make_shared(data, pos, visible, color, size, active, @@ -291,6 +408,53 @@ public: return false; } + bool save(const std::string& filename) const { + if (!root_) return false; + std::ofstream out(filename, std::ios::binary); + if (!out) return false; + + uint32_t magic = 0x79676733; + writeVal(out, magic); + writeVal(out, maxDepth); + writeVal(out, maxPointsPerNode); + writeVal(out, size); + + writeVec3(out, root_->bounds.first); + writeVec3(out, root_->bounds.second); + + serializeNode(out, root_.get()); + + out.close(); + return true; + } + + // Load Octree from binary file + bool load(const std::string& filename) { + std::ifstream in(filename, std::ios::binary); + if (!in) return false; + + uint32_t magic; + readVal(in, magic); + if (magic != 0x0C78E3) { + std::cerr << "Invalid Octree file format" << std::endl; + return false; + } + + readVal(in, maxDepth); + readVal(in, maxPointsPerNode); + readVal(in, size); + + PointType minBound, maxBound; + readVec3(in, minBound); + readVec3(in, maxBound); + + root_ = std::make_unique(minBound, maxBound); + deserializeNode(in, root_.get()); + + in.close(); + return true; + } + std::vector> voxelTraverse(const PointType& origin, const PointType& direction, float maxDist, bool stopAtFirstHit) { std::vector> hits; @@ -580,6 +744,34 @@ public: } bool empty() const { return size == 0; } + + void clear() { + if (!root_) return; + + std::function clearNode = [&](OctreeNode* node) { + if (!node) return; + + node->points.clear(); + node->points.shrink_to_fit(); + + for (int i = 0; i < 8; ++i) { + if (node->children[i]) { + clearNode(node->children[i].get()); + node->children[i].reset(nullptr); + } + } + + node->isLeaf = true; + }; + + clearNode(root_.get()); + + PointType minBound = root_->bounds.first; + PointType maxBound = root_->bounds.second; + root_ = std::make_unique(minBound, maxBound); + + size = 0; + } }; #endif