From b01125ab08e55e1cee84af344ccf8922427719c9 Mon Sep 17 00:00:00 2001 From: Adam Wahab <adamfwahab@hotmail.com> Date: Wed, 25 Sep 2024 11:56:09 +0200 Subject: [PATCH] fixed axis direction, need to add json input, hpmodel input --- opengl/CMakeLists.txt | 4 +- ...l_example.cpp => opengl_visualization.cpp} | 329 +++++++++--------- 2 files changed, 158 insertions(+), 175 deletions(-) rename opengl/{opengl_example.cpp => opengl_visualization.cpp} (78%) diff --git a/opengl/CMakeLists.txt b/opengl/CMakeLists.txt index dfa4947..8abfd1b 100644 --- a/opengl/CMakeLists.txt +++ b/opengl/CMakeLists.txt @@ -39,7 +39,7 @@ target_include_directories(imgui PUBLIC target_link_libraries(imgui PUBLIC glfw) # Add your OpenGL source files -add_library(OpenGLStuff opengl_example.cpp) +add_library(OpenGLStuff opengl_visualization.cpp) # Include directories for OpenGLStuff target_include_directories(OpenGLStuff PUBLIC @@ -52,7 +52,7 @@ target_include_directories(OpenGLStuff PUBLIC target_link_libraries(OpenGLStuff PUBLIC OpenGL::GL glfw glad imgui) # Create an executable for the OpenGL example -add_executable(opengl_example_run opengl_example.cpp) +add_executable(opengl_example_run opengl_visualization.cpp) # Include directories for the executable target_include_directories(opengl_example_run PUBLIC diff --git a/opengl/opengl_example.cpp b/opengl/opengl_visualization.cpp similarity index 78% rename from opengl/opengl_example.cpp rename to opengl/opengl_visualization.cpp index 002ebcd..a2e38cd 100644 --- a/opengl/opengl_example.cpp +++ b/opengl/opengl_visualization.cpp @@ -73,6 +73,9 @@ void key_callback(GLFWwindow* window, int key, int scancode, int action, int mod // Function to update window title based on mode void updateWindowTitle(GLFWwindow* window, bool showUI); +// Helper function to convert world coordinates to screen coordinates +bool WorldToScreen(const glm::vec3& worldPos, const glm::mat4& view, const glm::mat4& projection, ImVec2& screenPos); + // Entry point int main() { @@ -158,7 +161,7 @@ int main() std::vector<float> sphereVertices; std::vector<unsigned int> sphereIndices; float sphereRadius = 0.3f; - unsigned int sectorCount = 36; // Increased for smoother spheres + unsigned int sectorCount = 36; unsigned int stackCount = 18; generateSphere(sphereVertices, sphereIndices, sphereRadius, sectorCount, stackCount); @@ -218,26 +221,21 @@ int main() // ------------------- Adding Axes and Grid Points --------------------- - // Define the extent of your axes const float AXIS_LENGTH = 10.0f; - // Axes vertices (position and color) std::vector<float> axesVertices = { // X-axis (Red) - -AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // Start point - AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // End point + -AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, + AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // Y-axis (Green) 0.0f, -AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, // Z-axis (Blue) + 0.0f, 0.0f, AXIS_LENGTH, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -AXIS_LENGTH, 0.0f, 0.0f, 1.0f, - 0.0f, 0.0f, AXIS_LENGTH, 0.0f, 0.0f, 1.0f, }; - // Generate grid points std::vector<float> gridPoints; - - // Generate grid points along each axis for (int i = -static_cast<int>(AXIS_LENGTH); i <= static_cast<int>(AXIS_LENGTH); ++i) { // X-axis points gridPoints.push_back(static_cast<float>(i)); gridPoints.push_back(0.0f); gridPoints.push_back(0.0f); @@ -252,64 +250,46 @@ int main() gridPoints.push_back(1.0f); gridPoints.push_back(1.0f); gridPoints.push_back(1.0f); } - // Set up VAO and VBO for axes unsigned int axesVAO, axesVBO; glGenVertexArrays(1, &axesVAO); glGenBuffers(1, &axesVBO); glBindVertexArray(axesVAO); - glBindBuffer(GL_ARRAY_BUFFER, axesVBO); glBufferData(GL_ARRAY_BUFFER, axesVertices.size() * sizeof(float), axesVertices.data(), GL_STATIC_DRAW); - - // Position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); - - // Color attribute glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(2); - glBindVertexArray(0); - // Set up VAO and VBO for grid points unsigned int gridVAO, gridVBO; glGenVertexArrays(1, &gridVAO); glGenBuffers(1, &gridVBO); glBindVertexArray(gridVAO); - glBindBuffer(GL_ARRAY_BUFFER, gridVBO); glBufferData(GL_ARRAY_BUFFER, gridPoints.size() * sizeof(float), gridPoints.data(), GL_STATIC_DRAW); - - // Position attribute glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); - - // Color attribute glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(2); - glBindVertexArray(0); // ------------------- Initialize ImGui --------------------- - // Initialize ImGui IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; - // Set ImGui style ImGui::StyleColorsLight(); ImGuiStyle& style = ImGui::GetStyle(); style.WindowRounding = 0.0f; // Remove window rounding style.Colors[ImGuiCol_WindowBg] = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // White background - // Setup Platform/Renderer backends ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 330"); - // Initially set cursor mode based on showUI if (showUI) { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); @@ -319,135 +299,154 @@ int main() glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } - // Render loop while (!glfwWindowShouldClose(window)) { - // Per-frame time logic float currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; - // Start ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); - // Handle cursor visibility and input mode based on showUI if (showUI) { - // Ensure cursor is visible if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_NORMAL) { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - firstMouse = true; // Reset mouse to prevent camera jumps when returning to camera mode + firstMouse = true; } } else { - // Ensure cursor is hidden and captured if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - firstMouse = true; // Reset mouse to prevent sudden camera jumps + firstMouse = true; } - // Process camera movement input processInput(window); } - // ImGui window for adding spheres (fixed left sidebar) if (showUI) { - // Optionally, you can set the next window position and size - // to align with your design. Here, we use a sidebar approach. - ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::SetNextWindowSize(ImVec2(static_cast<float>(SIDEBAR_WIDTH), static_cast<float>(SCR_HEIGHT))); ImGui::Begin("Control Panel", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse); - static float spherePos[3] = {0.0f, 0.0f, 0.0f}; - ImGui::InputFloat3("Position", spherePos); + static int spherePos[3] = {0, 0, 0}; + ImGui::InputInt("X Position", &spherePos[0]); + ImGui::InputInt("Y Position", &spherePos[1]); + ImGui::InputInt("Z Position", &spherePos[2]); + + static bool highlight = false; + ImGui::Checkbox("h", &highlight); if (ImGui::Button("Add Sphere")) { Sphere sphere; - sphere.position = glm::vec3(spherePos[0], spherePos[1], spherePos[2]); - sphere.color = glm::vec3(0.5f, 0.5f, 0.5f); // Default color + // For some reason the z orientation is not correct, so we have to add an "minus" to the z coordinate. + sphere.position = glm::vec3(spherePos[0], spherePos[1], -spherePos[2]); - spheres[std::make_tuple((int)spherePos[0], (int)spherePos[1], (int)spherePos[2])] = sphere; + if (highlight) + sphere.color = glm::vec3(1.0f, 0.0f, 0.0f); + else + sphere.color = glm::vec3(0.5f, 0.5f, 0.5f); + + spheres[std::make_tuple(spherePos[0], spherePos[1], spherePos[2])] = sphere; - // Recalculate lines between spheres updateLines(spheres, lineVertices, lineVBO); } ImGui::End(); } - // Render - glClearColor(0.1f, 0.1f, 0.1f, 1.0f); // Dark gray background + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - // Activate shader glUseProgram(shaderProgram); - // Create transformations glm::mat4 view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); - glm::mat4 projection = glm::perspective(glm::radians(fov), - static_cast<float>(SCR_WIDTH) / static_cast<float>(SCR_HEIGHT), 0.1f, 100.0f); + glm::mat4 projection = glm::perspective(glm::radians(fov), static_cast<float>(SCR_WIDTH) / static_cast<float>(SCR_HEIGHT), 0.1f, 100.0f); - // Retrieve the matrix uniform locations unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model"); unsigned int viewLoc = glGetUniformLocation(shaderProgram, "view"); unsigned int projLoc = glGetUniformLocation(shaderProgram, "projection"); - // Pass the matrices to the shader glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); - // Render axes + // --- ADDED CODE START --- + // Render axis labels + ImDrawList* drawList = ImGui::GetBackgroundDrawList(); // Or ImGui::GetForegroundDrawList() + + ImU32 textColor = IM_COL32(255, 255, 255, 255); // White color + + ImVec2 screenPos; + if (WorldToScreen(glm::vec3(AXIS_LENGTH, 0.0f, 0.0f), view, projection, screenPos)) + { + drawList->AddText(screenPos, textColor, "X+"); + } + if (WorldToScreen(glm::vec3(-AXIS_LENGTH, 0.0f, 0.0f), view, projection, screenPos)) + { + drawList->AddText(screenPos, textColor, "X-"); + } + if (WorldToScreen(glm::vec3(0.0f, AXIS_LENGTH, 0.f), view, projection, screenPos)) + { + drawList->AddText(screenPos, textColor, "Y+"); + } + if (WorldToScreen(glm::vec3(0.0f, -AXIS_LENGTH, 0.0f), view, projection, screenPos)) + { + drawList->AddText(screenPos, textColor, "Y-"); + } + if (WorldToScreen(glm::vec3(0.0f, 0.0f, AXIS_LENGTH), view, projection, screenPos)) + { + drawList->AddText(screenPos, textColor, "Z-"); + } + if (WorldToScreen(glm::vec3(0.0f, 0.0f, -AXIS_LENGTH), view, projection, screenPos)) + { + drawList->AddText(screenPos, textColor, "Z+"); + } + glBindVertexArray(axesVAO); - glDrawArrays(GL_LINES, 0, 6); // 6 vertices for the axes + glDrawArrays(GL_LINES, 0, 6); glBindVertexArray(0); - // Render grid points glBindVertexArray(gridVAO); - glPointSize(5.0f); // Adjust point size as needed - glDrawArrays(GL_POINTS, 0, static_cast<unsigned int>(gridPoints.size() / 6)); // Each point has 6 floats (position + color) + glPointSize(5.0f); + glDrawArrays(GL_POINTS, 0, static_cast<unsigned int>(gridPoints.size() / 6)); glBindVertexArray(0); - // Render spheres glBindVertexArray(sphereVAO); for (const auto& [key, sphere] : spheres) { glm::mat4 model = glm::mat4(1.0f); model = glm::translate(model, sphere.position); - // Pass model matrix glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); - // Draw sphere + // Set sphere color + glVertexAttrib3f(2, sphere.color.r, sphere.color.g, sphere.color.b); + glDrawElements(GL_TRIANGLES, static_cast<unsigned int>(sphereIndices.size()), GL_UNSIGNED_INT, 0); } glBindVertexArray(0); - // Render lines + glLineWidth(3.0f); glBindVertexArray(lineVAO); - glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f))); // Identity model matrix + glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f))); glDrawArrays(GL_LINES, 0, static_cast<unsigned int>(lineVertices.size() / 6)); glBindVertexArray(0); + glLineWidth(1.0f); - // Update window title based on mode updateWindowTitle(window, showUI); - // Render ImGui ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - // Swap buffers and poll IO events glfwSwapBuffers(window); glfwPollEvents(); } - // De-allocate resources glDeleteVertexArrays(1, &sphereVAO); glDeleteBuffers(1, &sphereVBO); glDeleteBuffers(1, &sphereEBO); @@ -463,22 +462,19 @@ int main() glDeleteProgram(shaderProgram); - // Cleanup ImGui ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); - // Terminate GLFW glfwTerminate(); return 0; } -// Sphere generation function void generateSphere(std::vector<float>& vertices, std::vector<unsigned int>& indices, float radius, unsigned int sectorCount, unsigned int stackCount) { - float x, y, z, xy; // vertex position - float nx, ny, nz, lengthInv = 1.0f / radius; // normal + float x, y, z, xy; + float nx, ny, nz, lengthInv = 1.0f / radius; float sectorStep = 2 * glm::pi<float>() / sectorCount; float stackStep = glm::pi<float>() / stackCount; @@ -486,28 +482,23 @@ void generateSphere(std::vector<float>& vertices, std::vector<unsigned int>& ind for(unsigned int i = 0; i <= stackCount; ++i) { - stackAngle = glm::pi<float>() / 2 - i * stackStep; // from pi/2 to -pi/2 - xy = radius * cosf(stackAngle); // r * cos(u) - z = radius * sinf(stackAngle); // r * sin(u) + stackAngle = glm::pi<float>() / 2 - i * stackStep; + xy = radius * cosf(stackAngle); + z = radius * sinf(stackAngle); - // add (sectorCount+1) vertices per stack for(unsigned int j = 0; j <= sectorCount; ++j) { - sectorAngle = j * sectorStep; // from 0 to 2pi + sectorAngle = j * sectorStep; - // vertex position (x, y, z) - x = xy * cosf(sectorAngle); // r * cos(u) * cos(v) - y = xy * sinf(sectorAngle); // r * cos(u) * sin(v) + x = xy * cosf(sectorAngle); + y = xy * sinf(sectorAngle); - // normalized vertex normal (nx, ny, nz) nx = x * lengthInv; ny = y * lengthInv; nz = z * lengthInv; - // vertex color (default white) float r = 1.0f, g = 1.0f, b = 1.0f; - // Add to vertices vertices.push_back(x); vertices.push_back(y); vertices.push_back(z); @@ -522,16 +513,14 @@ void generateSphere(std::vector<float>& vertices, std::vector<unsigned int>& ind } } - // indices unsigned int k1, k2; for(unsigned int i = 0; i < stackCount; ++i) { - k1 = i * (sectorCount + 1); // beginning of current stack - k2 = k1 + sectorCount + 1; // beginning of next stack + k1 = i * (sectorCount + 1); + k2 = k1 + sectorCount + 1; for(unsigned int j = 0; j < sectorCount; ++j, ++k1, ++k2) { - // 2 triangles per sector except for first and last stacks if(i != 0) { indices.push_back(k1); @@ -549,39 +538,81 @@ void generateSphere(std::vector<float>& vertices, std::vector<unsigned int>& ind } } -// Process all input +void updateLines(const std::map<std::tuple<int, int, int>, Sphere>& spheres, + std::vector<float>& lineVertices, unsigned int lineVBO) +{ + lineVertices.clear(); + + for (const auto& [key, sphere] : spheres) + { + int x = std::get<0>(key); + int y = std::get<1>(key); + int z = std::get<2>(key); + + for (int dx = -1; dx <= 1; ++dx) + { + for (int dy = -1; dy <= 1; ++dy) + { + for (int dz = -1; dz <= 1; ++dz) + { + if ((abs(dx) + abs(dy) + abs(dz)) != 1) + continue; + + int nx = x + dx; + int ny = y + dy; + int nz = z + dz; + + auto neighborKey = std::make_tuple(nx, ny, nz); + if (spheres.find(neighborKey) != spheres.end()) + { + if (key < neighborKey) + { + glm::vec3 neighborPos = spheres.at(neighborKey).position; + + lineVertices.insert(lineVertices.end(), { + sphere.position.x, sphere.position.y, sphere.position.z, 0.5f, 0.5f, 0.5f, + neighborPos.x, neighborPos.y, neighborPos.z, 0.5f, 0.5f, 0.5f + }); + } + } + } + } + } + } + + glBindBuffer(GL_ARRAY_BUFFER, lineVBO); + glBufferData(GL_ARRAY_BUFFER, lineVertices.size() * sizeof(float), &lineVertices[0], GL_DYNAMIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + void processInput(GLFWwindow *window) { - float cameraSpeed = 10.0f * deltaTime; // Adjusted camera speed + float cameraSpeed = 10.0f * deltaTime; if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) - cameraPos += cameraSpeed * cameraFront; // Move forward + cameraPos += cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) - cameraPos -= cameraSpeed * cameraFront; // Move backward + cameraPos -= cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) - cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; // Move left + cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) - cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; // Move right + cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; if (glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS) - cameraPos += cameraSpeed * cameraUp; // Move up + cameraPos += cameraSpeed * cameraUp; if (glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) - cameraPos -= cameraSpeed * cameraUp; // Move down + cameraPos -= cameraSpeed * cameraUp; - // Exit application if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) glfwSetWindowShouldClose(window, true); } -// Update framebuffer_size_callback void framebuffer_size_callback(GLFWwindow* window, int width, int height) { SCR_WIDTH = width; SCR_HEIGHT = height; - // Adjust the viewport to cover the entire window glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); } -// GLFW: whenever the mouse moves, this callback is called void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) { if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) @@ -598,7 +629,7 @@ void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) } float xoffset = xpos - lastX; - float yoffset = lastY - ypos; // Reversed since y-coordinates go from bottom to top + float yoffset = lastY - ypos; lastX = xpos; lastY = ypos; @@ -610,13 +641,11 @@ void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) yaw += xoffset; pitch += yoffset; - // Constrain the pitch to avoid screen flipping if(pitch > 89.0f) pitch = 89.0f; if(pitch < -89.0f) pitch = -89.0f; - // Update camera front vector glm::vec3 front; front.x = cos(glm::radians(yaw)) * cos(glm::radians(pitch)); front.y = sin(glm::radians(pitch)); @@ -625,27 +654,23 @@ void mouse_callback(GLFWwindow* window, double xposIn, double yposIn) cameraFront = glm::normalize(front); } -// GLFW: whenever the mouse scroll wheel scrolls, this callback is called void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { - fov -= static_cast<float>(yoffset); // yoffset is positive or negative depending on scroll direction + fov -= static_cast<float>(yoffset); if(fov < 1.0f) fov = 1.0f; if(fov > 90.0f) fov = 90.0f; } -// Shader compilation and linking unsigned int createShaderProgram(const char* vertexShaderSource, const char* fragmentShaderSource) { - // Vertex Shader unsigned int vertexShader; vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, NULL); glCompileShader(vertexShader); - // Check for compile-time errors int success; char infoLog[512]; @@ -658,14 +683,12 @@ unsigned int createShaderProgram(const char* vertexShaderSource, const char* fra return 0; } - // Fragment Shader unsigned int fragmentShader; fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL); glCompileShader(fragmentShader); - // Check for compile-time errors glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); if(!success) { @@ -675,7 +698,6 @@ unsigned int createShaderProgram(const char* vertexShaderSource, const char* fra return 0; } - // Link shaders unsigned int shaderProgram; shaderProgram = glCreateProgram(); @@ -683,7 +705,6 @@ unsigned int createShaderProgram(const char* vertexShaderSource, const char* fra glAttachShader(shaderProgram, fragmentShader); glLinkProgram(shaderProgram); - // Check for linking errors glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if(!success) { @@ -693,91 +714,53 @@ unsigned int createShaderProgram(const char* vertexShaderSource, const char* fra return 0; } - // Delete shaders as they're linked into our program now and no longer necessary glDeleteShader(vertexShader); glDeleteShader(fragmentShader); return shaderProgram; } -// Function to update lines between spheres -void updateLines(const std::map<std::tuple<int, int, int>, Sphere>& spheres, - std::vector<float>& lineVertices, unsigned int lineVBO) -{ - lineVertices.clear(); - - for (const auto& [key, sphere] : spheres) - { - int x = std::get<0>(key); - int y = std::get<1>(key); - int z = std::get<2>(key); - - // Check adjacent positions - for (int dx = -1; dx <= 1; ++dx) - { - for (int dy = -1; dy <= 1; ++dy) - { - for (int dz = -1; dz <= 1; ++dz) - { - // Skip the same sphere or diagonals (non-axis-aligned neighbors) - if (abs(dx) + abs(dy) + abs(dz) != 1) - continue; - - int nx = x + dx; - int ny = y + dy; - int nz = z + dz; - - auto neighborKey = std::make_tuple(nx, ny, nz); - if (spheres.find(neighborKey) != spheres.end()) - { - // Ensure each line is added only once - if (key < neighborKey) - { - glm::vec3 neighborPos = spheres.at(neighborKey).position; - - // Line from sphere to neighbor - lineVertices.insert(lineVertices.end(), { - sphere.position.x, sphere.position.y, sphere.position.z, 0.5f, 0.5f, 0.5f, - neighborPos.x, neighborPos.y, neighborPos.z, 0.5f, 0.5f, 0.5f - }); - } - } - } - } - } - } - - // Update the line VBO - glBindBuffer(GL_ARRAY_BUFFER, lineVBO); - glBufferData(GL_ARRAY_BUFFER, lineVertices.size() * sizeof(float), &lineVertices[0], GL_DYNAMIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, 0); -} - -// Key callback for toggling UI void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { - // Toggle UI visibility with TAB key if (key == GLFW_KEY_TAB && action == GLFW_PRESS) { showUI = !showUI; if (showUI) { - // Show cursor for UI interaction glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } else { - // Hide and capture cursor for camera control glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); - firstMouse = true; // Reset mouse to prevent sudden camera jumps + firstMouse = true; } } } -// Function to update window title based on mode void updateWindowTitle(GLFWwindow* window, bool showUI) { std::string title = "3D Spheres with Connections - "; title += showUI ? "UI Interaction Mode" : "Camera Control Mode"; glfwSetWindowTitle(window, title.c_str()); -} \ No newline at end of file +} + +bool WorldToScreen(const glm::vec3& worldPos, const glm::mat4& view, const glm::mat4& projection, ImVec2& screenPos) +{ + glm::vec4 clipSpacePos = projection * view * glm::vec4(worldPos, 1.0f); + + if (clipSpacePos.w <= 0.0f) + return false; // Behind the camera + + glm::vec3 ndc = glm::vec3(clipSpacePos) / clipSpacePos.w; + + if (ndc.x < -1.0f || ndc.x > 1.0f || ndc.y < -1.0f || ndc.y > 1.0f) + return false; // Outside of screen + + // Map to window coordinates + float windowX = (ndc.x + 1.0f) * SCR_WIDTH / 2.0f; + float windowY = (1.0f - ndc.y) * SCR_HEIGHT / 2.0f; // Invert Y for window coordinates + + screenPos = ImVec2(windowX, windowY); + + return true; +} -- GitLab