#include #include #include #include #include #include #include #include #include "shader.h" #include #include #include #include const int windowWidth = 1200; const int windowHeight = 800; const int ballsAmount = 2; const float initTimeStep = 0.001f; const float initCameraScale = 1.0f; bool shouldUpdate = true; bool shouldAlwaysUpdate = true; float t = 0.0f; float timeStep = initTimeStep; float cameraScale = initCameraScale; int currentTrackedBall = -1; struct Ball { glm::vec2 position; glm::vec3 color; float offset; // Dodajemy przesunięcie }; GLFWwindow* window; std::vector balls(ballsAmount); GLuint shaderProgram; GLuint circleVAO, circleVBO, arcVAO, arcVBO; void initImGui(){ IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; ImGui::StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 330 core"); } void imGuiTrackBallFrame(){ ImGui::Begin("Track Ball"); ImGui::SliderFloat("Camera scale", &cameraScale, 0.1f, 2.0f, "%.3f", 1.0f); if(ImGui::Button("Track red ball")){ currentTrackedBall = 0; } if(ImGui::Button("Track blue ball")){ currentTrackedBall = 1; } if(ImGui::Button("Untrack ball")){ currentTrackedBall = -1; } ImGui::End(); } void imGuiSimulationStateControlsFrame(){ ImGui::Begin("Simulation State Controls"); ImGui::Checkbox("Always update", &shouldAlwaysUpdate); if(ImGui::Button("Start")){ shouldUpdate = true; } if(ImGui::Button("Stop")){ shouldUpdate = false; } if(ImGui::Button("Reset")){ t = 0.0f; shouldUpdate = false; } ImGui::End(); } void imGuiBallsMetricFrame(){ ImGui::Begin("Balls Metric"); float angle1 = abs(atan2(balls[0].position.y, balls[0].position.x) * (180.0 / M_PI)); float angle2 = abs(atan2(balls[1].position.y, balls[1].position.x) * (180.0 / M_PI)); if(angle1 > 60.0f){ angle1 = 60.0f; } if(angle2 > 150.0f){ angle2 = 150.0f; } ImGui::SliderFloat("Time step", &timeStep, 0.001f, 0.1f, "%.3f", 1.0f); ImGui::Text("Red ball (60deg): current angle: %.2f", angle1); ImGui::Text("Blue ball (150deg): current angle: %.2f", angle2); ImGui::End(); } void renderImGui(){ ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); imGuiSimulationStateControlsFrame(); imGuiBallsMetricFrame(); imGuiTrackBallFrame(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } void cleanUpImGui(){ ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } void initializeBalls() { balls[0].position = glm::vec2(1.0f, 0.0f); balls[0].color = glm::vec3(1.0f, 0.0f, 0.0f); // Czerwony balls[0].offset = 0.0f; // Przesunięcie dla pierwszego łuku balls[1].position = glm::vec2(1.0f, 0.0f); balls[1].color = glm::vec3(0.0f, 0.0f, 1.0f); // Niebieski balls[1].offset = 0.0f; // Przesunięcie dla drugiego łuku } void compileShaders() { Shader shader("circle_vs.glsl", "circle_fs.glsl"); shaderProgram = shader.programID(); } glm::vec2 customSlerp(const glm::vec2& start, const glm::vec2& end, float t) { float dot = glm::dot(start, end); dot = glm::clamp(dot, -1.0f, 1.0f); float theta = acos(dot) * t; glm::vec2 relativeVec = glm::normalize(end - start * dot); return start * cos(theta) + relativeVec * sin(theta); } void setupCircleBuffers() { const int numSegments = 360; std::vector vertices; float radius = 1.0f; for (int i = 0; i <= numSegments; ++i) { float angle = i * 2.0f * M_PI / numSegments; vertices.push_back(cos(angle) * radius); vertices.push_back(sin(angle) * radius); } glGenVertexArrays(1, &circleVAO); glGenBuffers(1, &circleVBO); glBindVertexArray(circleVAO); glBindBuffer(GL_ARRAY_BUFFER, circleVBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void setupArcBuffers() { const int numSegments = 100; std::vector arcVertices; for (int i = 0; i <= numSegments; ++i) { float angle = glm::radians(210.0f) * i / numSegments; arcVertices.push_back(cos(angle)); arcVertices.push_back(sin(angle)); } glGenVertexArrays(1, &arcVAO); glGenBuffers(1, &arcVBO); glBindVertexArray(arcVAO); glBindBuffer(GL_ARRAY_BUFFER, arcVBO); glBufferData(GL_ARRAY_BUFFER, arcVertices.size() * sizeof(float), arcVertices.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void drawCircle() { glBindVertexArray(circleVAO); glDrawArrays(GL_LINE_LOOP, 0, 361); glBindVertexArray(0); } void drawSmallCircle() { glBindVertexArray(circleVAO); glDrawArrays(GL_TRIANGLE_FAN, 0, 361); glBindVertexArray(0); } void drawArcs() { glBindVertexArray(arcVAO); // Przesunięcie łuku 60 stopni glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); model = glm::rotate(model, glm::radians(-60.0f), glm::vec3(0.0f, 0.0f, 1.0f)); unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glDrawArrays(GL_LINE_STRIP, 0, 101); // Przesunięcie łuku 150 stopni // model = glm::translate(glm::mat4(1.0f), glm::vec3(offset, 0.0f, 0.0f)); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glDrawArrays(GL_LINE_STRIP, 101, 101); glBindVertexArray(0); } void display() { glClear(GL_COLOR_BUFFER_BIT); glUseProgram(shaderProgram); glm::mat4 view = glm::mat4(0.5f); glm::mat4 projection = glm::ortho(-2.0f, 2.0f, -1.5f, 1.5f); if(currentTrackedBall != -1){ projection = glm::scale(projection, glm::vec3(cameraScale, cameraScale, 1.0f)); projection = glm::translate(projection, glm::vec3(-balls[currentTrackedBall].position.x, -balls[currentTrackedBall].position.y, 0.0f)); } unsigned int viewLoc = glGetUniformLocation(shaderProgram, "view"); unsigned int projectionLoc = glGetUniformLocation(shaderProgram, "projection"); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projection)); unsigned int modelLoc = glGetUniformLocation(shaderProgram, "model"); unsigned int colorLoc = glGetUniformLocation(shaderProgram, "color"); // Rysowanie łuków glUniform3f(colorLoc, 1.0f, 1.0f, 1.0f); drawArcs(); // Rysowanie kul for (const auto &ball : balls) { glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(ball.position.x + ball.offset, ball.position.y, 0.0f)); model = glm::scale(model, glm::vec3(0.05f, 0.05f, 1.0f)); // Skala dla małych kul glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glUniform3f(colorLoc, ball.color.r, ball.color.g, ball.color.b); drawSmallCircle(); // Rysujemy małe koło reprezentujące kulę } renderImGui(); glfwSwapBuffers(glfwGetCurrentContext()); } void update() { if(!shouldUpdate) { balls[0].position = customSlerp(glm::vec2(1.0f, 0.0f), glm::vec2(0.5f, -sqrt(3) / 2.0f), t); // 60 stopni balls[1].position = customSlerp(glm::vec2(1.0f, 0.0f), glm::vec2(-sqrt(3) / 2.0f, 0.5f), t); // 150 stopni return; }; t += timeStep; if (t > 1.0f){ if(shouldAlwaysUpdate){ t = 0.0f; } else { shouldUpdate = false; } }; balls[0].position = customSlerp(glm::vec2(1.0f, 0.0f), glm::vec2(0.5f, -sqrt(3) / 2.0f), t); // 60 stopni balls[1].position = customSlerp(glm::vec2(1.0f, 0.0f), glm::vec2(-sqrt(3) / 2.0f, 0.5f), t); // 150 stopni } void setupOpenGL() { compileShaders(); setupCircleBuffers(); setupArcBuffers(); glClearColor(0.1f, 0.1f, 0.1f, 1.0f); } void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (action == GLFW_PRESS) { switch (key) { case GLFW_KEY_ESCAPE: glfwSetWindowShouldClose(window, GLFW_TRUE); break; } } } int main() { if (!glfwInit()) { std::cerr << "Failed to initialize GLFW" << std::endl; return -1; } glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); window = glfwCreateWindow(windowWidth, windowHeight, "Visualizacja 2D - SLERP", nullptr, nullptr); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetKeyCallback(window, keyCallback); glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW" << std::endl; return -1; } initializeBalls(); setupOpenGL(); initImGui(); while (!glfwWindowShouldClose(window)) { display(); update(); glfwPollEvents(); } glDeleteVertexArrays(1, &circleVAO); glDeleteBuffers(1, &circleVBO); glDeleteVertexArrays(1, &arcVAO); glDeleteBuffers(1, &arcVBO); glDeleteProgram(shaderProgram); cleanUpImGui(); glfwDestroyWindow(window); glfwTerminate(); return 0; }