#include #include #include #include #include #include #include #include #include "shader.h" #include #include #include #include const glm::vec3 initialCameraPos = glm::vec3(20, 20, 30); const glm::vec3 initialCameraTarget = glm::vec3(0, 10, 0); const glm::vec3 initialCameraUp = glm::vec3(0, 1, 0); const float initialGravity = 9.81f; const float initialAirResistance = 0.1f; const float initialSpeed = 5.0f; const float initialAngle = 45.0f; const int windowWidth = 1200; const int windowHeight = 800; bool isLaunched = false; const int ballsAmount = 10; struct Ball { float x, y, z; float vx, vy, vz; float r, g, b; }; float gravity = initialGravity; float airResistance = initialAirResistance; float speed = initialSpeed; float angle = initialAngle * M_PI / 180.0f; float ballsAngle[ballsAmount] = {}; glm::vec3 cameraPos = initialCameraPos; glm::vec3 cameraTarget = initialCameraTarget; glm::vec3 cameraUp = initialCameraUp; GLFWwindow* window; std::vector balls(ballsAmount); // Wektor przechowujący ballsAmount kul GLuint shaderProgram; GLuint VAO, VBO; void initializeBalls() { srand(static_cast(time(0))); for (int i = 0; i < ballsAmount; ++i) { float localAngle = angle + static_cast(rand() % 60); // float localSpeed = speed + static_cast(rand() % 5); ballsAngle[i] = localAngle; balls[i] = { .x = 0.0f, .y = 2.0f * i, .z = 0.0f, .vx = static_cast(localSpeed * cos(localAngle * M_PI / 180.0f)), .vy = static_cast(localSpeed * sin(localAngle * M_PI / 180.0f)), .vz = 0.0f, .r = static_cast(rand()) / RAND_MAX, .g = static_cast(rand()) / RAND_MAX, .b = static_cast(rand()) / RAND_MAX }; } isLaunched = false; } 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 imGuiRenderCameraControlsFrame(){ ImGui::Begin("Camera Control"); ImGui::SliderFloat3("Camera Position", glm::value_ptr(cameraPos), -100.0f, 100.0f); ImGui::SliderFloat3("Camera Target", glm::value_ptr(cameraTarget), -100.0f, 100.0f); ImGui::SliderFloat3("Camera Up", glm::value_ptr(cameraUp), -1.0f, 1.0f); if(ImGui::Button("Reset Camera")){ cameraPos = initialCameraPos; cameraTarget = initialCameraTarget; cameraUp = initialCameraUp; } ImGui::End(); } void imGuiRenderBallsControlsFrame(){ ImGui::Begin("Balls Control"); ImGui::SliderFloat("Gravity", &gravity, 0.0f, 20.0f); ImGui::SliderFloat("Air Resistance", &airResistance, 0.0f, 1.0f); ImGui::SliderFloat("Speed", &speed, 0.0f, 20.0f); ImGui::SliderFloat("Angle", &angle, 0.0f, 90.0f); if(ImGui::Button("Reset ball modifiers")){ gravity = initialGravity; airResistance = initialAirResistance; speed = initialSpeed; angle = initialAngle * M_PI / 180.0f; } ImGui::End(); } void imGuiBallsThrowingStateControlsFrame(){ ImGui::Begin("Balls Throwing State"); if (ImGui::Button("Reset Balls")) { initializeBalls(); } if (ImGui::Button("Launch Balls")) { isLaunched = true; } ImGui::End(); } void imGuiBallsMetricFrame(){ ImGui::Begin("Balls Metric"); for(int i = 0; i < ballsAmount; i++){ ImGui::Text("Ball %d Position: (%.2f, %.2f, %.2f)", i + 1, balls[i].x, balls[i].y, balls[i].z); ImGui::Text("Ball %d Angle: %.2f", i + 1, ballsAngle[i]); } ImGui::End(); } void renderImGui(){ ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); ImGui::NewFrame(); imGuiRenderBallsControlsFrame(); imGuiRenderCameraControlsFrame(); imGuiBallsThrowingStateControlsFrame(); imGuiBallsMetricFrame(); ImGui::Render(); ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); } void cleanUpImGui(){ ImGui_ImplOpenGL3_Shutdown(); ImGui_ImplGlfw_Shutdown(); ImGui::DestroyContext(); } void compileShaders(){ Shader shader("circle_vs.glsl", "circle_fs.glsl"); shaderProgram = shader.programID(); } // Funkcja do integracji Rungego-Kutty void rungeKuttaStep(Ball &ball, float dt) { float k1vx = -airResistance * ball.vx; float k1vy = -gravity - airResistance * ball.vy; float k2vx = -airResistance * (ball.vx + 0.5f * dt * k1vx); float k2vy = -gravity - airResistance * (ball.vy + 0.5f * dt * k1vy); float k3vx = -airResistance * (ball.vx + 0.5f * dt * k2vx); float k3vy = -gravity - airResistance * (ball.vy + 0.5f * dt * k2vy); float k4vx = -airResistance * (ball.vx + dt * k3vx); float k4vy = -gravity - airResistance * (ball.vy + dt * k3vy); ball.vx += (dt / 6.0f) * (k1vx + 2.0f * k2vx + 2.0f * k3vx + k4vx); ball.vy += (dt / 6.0f) * (k1vy + 2.0f * k2vy + 2.0f * k3vy + k4vy); ball.x += ball.vx * dt; ball.y += ball.vy * dt; } void setupSphereBuffer() { float radius = 0.5f; int slices = 20; int stacks = 20; std::vector vertices; for (int i = 0; i <= stacks; ++i) { float V = i / (float) stacks; float phi = V * M_PI; for (int j = 0; j <= slices; ++j) { float U = j / (float) slices; float theta = U * (M_PI * 2); float x = cosf(theta) * sinf(phi); float y = cosf(phi); float z = sinf(theta) * sinf(phi); vertices.push_back(x * radius); vertices.push_back(y * radius); vertices.push_back(z * radius); } } glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.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 drawSphere() { glBindVertexArray(VAO); glDrawArrays(GL_TRIANGLE_STRIP, 0, (20 + 1) * (20 + 1)); glBindVertexArray(0); } void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(shaderProgram); // Ustawienie widoku kamery glm::mat4 view = glm::lookAt(cameraPos, cameraTarget, cameraUp); glm::mat4 projection = glm::perspective(glm::radians(45.0f), (float)windowWidth / (float)windowHeight, 0.1f, 100.0f); // Renderowanie każdej z ballsAmount kul for (int i = 0; i < ballsAmount; ++i) { Ball &ball = balls[i]; // Uzyskanie dostępu do danej kuli glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(ball.x, ball.y, ball.z)); // Ustawienie macierzy modelu, widoku i projekcji w shaderze GLint modelLoc = glGetUniformLocation(shaderProgram, "model"); GLint viewLoc = glGetUniformLocation(shaderProgram, "view"); GLint projLoc = glGetUniformLocation(shaderProgram, "projection"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model)); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view)); glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection)); // Ustawienie koloru kuli w shaderze GLint colorLoc = glGetUniformLocation(shaderProgram, "color"); glUniform3f(colorLoc, ball.r, ball.g, ball.b); drawSphere(); // Rysowanie kuli } renderImGui(); glfwSwapBuffers(glfwGetCurrentContext()); // Przełączanie buforów } void update() { float dt = 0.01f; if (isLaunched) { for (auto &ball : balls) { rungeKuttaStep(ball, dt); } } } void setupOpenGL() { compileShaders(); setupSphereBuffer(); 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, "Animacja 10 kul - Rzut ukośny", nullptr, nullptr); if (!window) { std::cerr << "Failed to create GLFW window" << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glfwSetKeyCallback(window, keyCallback); // Rejestracja callbacku klawiatury glewExperimental = GL_TRUE; if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW" << std::endl; return -1; } initImGui(); initializeBalls(); setupOpenGL(); while (!glfwWindowShouldClose(window)) { display(); update(); glfwPollEvents(); } cleanUpImGui(); glDeleteVertexArrays(1, &VAO); glDeleteBuffers(1, &VBO); glDeleteProgram(shaderProgram); glfwDestroyWindow(window); glfwTerminate(); return 0; }