#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 = 1; const float initTimeStep = 0.001f; const float initCameraScale = 1.0f; const float initSphereScale = 0.1f; const float initRadius = 5.0f; const float initAzimuth = glm::radians(90.0f); const float initElevation = glm::radians(90.0f); const glm::vec3 initTarget = glm::vec3(0.0f, 0.0f, 0.0f); bool shouldUpdate = true; bool shouldAlwaysUpdate = true; float t = 0.0f; glm::vec3 currentVelocity; glm::vec3 currentAcceleration; float timeStep = initTimeStep; float sphereScale = initSphereScale; int currentTrackedBall = -1; float radius = initRadius; float azimuth = initAzimuth; float altitude = initElevation; glm::vec3 target = initTarget; // Punkt, na który kamera patrzy std::vector bezierVertices; struct Ball { glm::vec3 position; glm::vec3 color; }; GLFWwindow* window; std::vector balls(ballsAmount); GLuint shaderProgram; GLuint bezierVAO, bezierVBO; GLuint sphereVAO, sphereVBO, sphereEBO; std::vector controlPoints = { glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec3(-0.5f, 4.0f, 0.0f), glm::vec3(0.5f, -6.0f, 0.0f), glm::vec3(1.0f, 2.0f, 0.0f) }; float easingFn(float t) { // return 1 - pow(1 - t, 3); return t < 0.5 ? (1 - pow(1 - 2*t, 3)) / 2 : (pow(2*t - 1, 3) + 1) / 2; } glm::vec3 cubicBezier(const glm::vec3& P0, const glm::vec3& P1, const glm::vec3& P2, const glm::vec3& P3, float t) { return (1-t)*(1-t)*(1-t) * P0 + 3*(1-t)*(1-t) * t * P1 + 3*(1-t) * t*t * P2 + t*t*t * P3; } glm::vec3 bezierFirstDerivative(const glm::vec3& P0, const glm::vec3& P1, const glm::vec3& P2, const glm::vec3& P3, float t) { return 3 * (1-t)*(1-t) * (P1 - P0) + 6 * (1-t) * t * (P2 - P1) + 3 * t*t * (P3 - P2); } glm::vec3 bezierSecondDerivative(const glm::vec3& P0, const glm::vec3& P1, const glm::vec3& P2, const glm::vec3& P3, float t) { return 6 * (1-t) * (P2 - 2.0f*P1 + P0) + 6 * t * (P3 - 2.0f*P2 + P1); } glm::vec3 sphericalToCartesian() { float trackedBallX = currentTrackedBall == -1 ? 1.0f : balls[currentTrackedBall].position.x; float trackedBallY = currentTrackedBall == -1 ? 1.0f : balls[currentTrackedBall].position.y; float x = radius * sin(altitude) * cos(azimuth) * trackedBallX; float y = radius * cos(altitude) * trackedBallY; float z = radius * sin(altitude) * sin(azimuth); return glm::vec3(x, y, z); } 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 imGuiSimulationMetricsFrame(){ ImGui::Begin("Simulation Metrics"); ImGui::SliderFloat("Time step", &timeStep, 0.001f, 0.01f, "%.3f", 1.0f); ImGui::Text("Current time: %.3f, current eased time: %.3f", t, easingFn(t)); ImGui::Text("Velocity: (%.2f, %.2f, %.2f)", currentVelocity.x, currentVelocity.y, currentVelocity.z); ImGui::Text("Acceleration: (%.2f, %.2f, %.2f)", currentAcceleration.x, currentAcceleration.y, currentAcceleration.z); ImGui::End(); } void imGuiCameraControlsFrame(){ ImGui::Begin("Track Ball"); if(ImGui::Button("Track ball")){ currentTrackedBall = 0; } if(ImGui::Button("Stop tracking")){ currentTrackedBall = -1; target = initTarget; } ImGui::SliderFloat("Camera radius", &radius, 1.0f, 10.0f, "%.1f", 1.0f); ImGui::SliderAngle("Camera longitude", &altitude); ImGui::SliderAngle("Camera latitude", &azimuth); if(ImGui::Button("Reset")){ radius = initRadius; azimuth = initAzimuth; altitude = initElevation; } ImGui::End(); } void renderImGui(){ ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); imGuiCameraControlsFrame(); imGuiSimulationMetricsFrame(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } void cleanUpImGui(){ ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } void initializeBalls() { balls[0].position = glm::vec3(1.0f, 0.0f, 0.0f); balls[0].color = glm::vec3(1.0f, 0.0f, 0.0f); // Czerwony } void compileShaders() { Shader shader("circle_vs.glsl", "circle_fs.glsl"); shaderProgram = shader.programID(); } void setupSphereBuffers() { const int latitudeBands = 30; const int longitudeBands = 30; const float radius = 1.0f; std::vector vertices; std::vector indices; for (int latNumber = 0; latNumber <= latitudeBands; ++latNumber) { float theta = latNumber * M_PI / latitudeBands; float sinTheta = sin(theta); float cosTheta = cos(theta); for (int longNumber = 0; longNumber <= longitudeBands; ++longNumber) { float phi = longNumber * 2 * M_PI / longitudeBands; float sinPhi = sin(phi); float cosPhi = cos(phi); float x = cosPhi * sinTheta; float y = cosTheta; float z = sinPhi * sinTheta; vertices.push_back(radius * x); vertices.push_back(radius * y); vertices.push_back(radius * z); } } for (int latNumber = 0; latNumber < latitudeBands; ++latNumber) { for (int longNumber = 0; longNumber < longitudeBands; ++longNumber) { int first = (latNumber * (longitudeBands + 1)) + longNumber; int second = first + longitudeBands + 1; indices.push_back(first); indices.push_back(second); indices.push_back(first + 1); indices.push_back(second); indices.push_back(second + 1); indices.push_back(first + 1); } } glGenVertexArrays(1, &sphereVAO); glGenBuffers(1, &sphereVBO); glGenBuffers(1, &sphereEBO); glBindVertexArray(sphereVAO); glBindBuffer(GL_ARRAY_BUFFER, sphereVBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereEBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void setupBezierCurveBuffers(){ const int numSegments = 100; for (int i = 0; i <= numSegments; ++i) { float t = static_cast(i) / static_cast(numSegments); glm::vec3 point = cubicBezier(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], t); bezierVertices.push_back(point); } glGenVertexArrays(1, &bezierVAO); glGenBuffers(1, &bezierVBO); glBindVertexArray(bezierVAO); glBindBuffer(GL_ARRAY_BUFFER, bezierVBO); glBufferData(GL_ARRAY_BUFFER, bezierVertices.size() * sizeof(glm::vec3), bezierVertices.data(), GL_STATIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); } void drawBezierCurve() { glBindVertexArray(bezierVAO); glDrawArrays(GL_LINE_STRIP, 0, bezierVertices.size()); glBindVertexArray(0); } void drawSphere() { glBindVertexArray(sphereVAO); glDrawElements(GL_TRIANGLES, 6 * 30 * 30, GL_UNSIGNED_INT, 0); // 6 * latitudeBands * longitudeBands glBindVertexArray(0); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(shaderProgram); glm::vec3 cameraPos = sphericalToCartesian(); if(currentTrackedBall != -1){ target = balls[currentTrackedBall].position; } // Ustawienie kamery glm::mat4 view = glm::lookAt( cameraPos, // Pozycja kamery target, // Punkt, na który kamera patrzy glm::vec3(0.0f, 1.0f, 0.0f) // Wektor wskazujący górę ); glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeight, 0.1f, 100.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 krzywej Béziera glm::mat4 model = glm::mat4(1.0f); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glUniform3f(colorLoc, 1.0f, 1.0f, 1.0f); drawBezierCurve(); // Rysowanie kuli model = glm::translate(glm::mat4(1.0f), balls[0].position); model = glm::scale(model, glm::vec3(sphereScale, sphereScale, sphereScale)); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glUniform3f(colorLoc, balls[0].color.r, balls[0].color.g, balls[0].color.b); drawSphere(); renderImGui(); glfwSwapBuffers(window); } void update() { if(!shouldUpdate) return; t += timeStep; if (t > 1.0f) { if (shouldAlwaysUpdate) { t = 0.0f; } else { shouldUpdate = false; } } float easedT = easingFn(t); balls[0].position = cubicBezier(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], easedT); currentVelocity = bezierFirstDerivative(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], easedT); currentAcceleration = bezierSecondDerivative(controlPoints[0], controlPoints[1], controlPoints[2], controlPoints[3], easedT); } void setupOpenGL() { compileShaders(); setupSphereBuffers(); setupBezierCurveBuffers(); glEnable(GL_DEPTH_TEST); 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, &bezierVAO); glDeleteBuffers(1, &bezierVBO); glDeleteProgram(shaderProgram); glDeleteVertexArrays(1, &sphereVAO); glDeleteBuffers(1, &sphereVBO); glDeleteBuffers(1, &sphereEBO); cleanUpImGui(); glfwDestroyWindow(window); glfwTerminate(); return 0; }