2024-06-28 17:01:29 +02:00
|
|
|
#include <GL/glew.h>
|
|
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <cmath>
|
|
|
|
#include <vector>
|
|
|
|
#include <iostream>
|
|
|
|
#include <glm/glm.hpp>
|
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#include "shader.h"
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <imgui.h>
|
|
|
|
#include <imgui_impl_glfw.h>
|
|
|
|
#include <imgui_impl_opengl3.h>
|
|
|
|
|
|
|
|
const int windowWidth = 1200;
|
|
|
|
const int windowHeight = 800;
|
|
|
|
|
|
|
|
const int ballsAmount = 2;
|
|
|
|
|
|
|
|
const float initTimeStep = 0.001f;
|
|
|
|
const float initCameraScale = 1.0f;
|
2024-06-28 17:24:34 +02:00
|
|
|
const float initSphereScale = 0.05f;
|
2024-06-28 17:01:29 +02:00
|
|
|
|
|
|
|
bool shouldUpdate = true;
|
|
|
|
bool shouldAlwaysUpdate = true;
|
|
|
|
float t = 0.0f;
|
|
|
|
|
|
|
|
float timeStep = initTimeStep;
|
|
|
|
float cameraScale = initCameraScale;
|
2024-06-28 17:24:34 +02:00
|
|
|
float sphereScale = initSphereScale;
|
2024-06-28 17:01:29 +02:00
|
|
|
int currentTrackedBall = -1;
|
|
|
|
|
|
|
|
struct Ball {
|
|
|
|
glm::vec2 position;
|
|
|
|
glm::vec3 color;
|
|
|
|
};
|
|
|
|
|
|
|
|
GLFWwindow* window;
|
|
|
|
std::vector<Ball> 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);
|
2024-06-28 17:24:34 +02:00
|
|
|
ImGui::SliderFloat("Sphere scale", &sphereScale, 0.01f, 0.1f, "%.3f", 1.0f);
|
2024-06-28 17:01:29 +02:00
|
|
|
|
|
|
|
|
|
|
|
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[1].position = glm::vec2(1.0f, 0.0f);
|
|
|
|
balls[1].color = glm::vec3(0.0f, 0.0f, 1.0f); // Niebieski
|
|
|
|
}
|
|
|
|
|
|
|
|
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<float> 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<float> 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
|
|
|
|
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) {
|
2024-06-28 17:17:20 +02:00
|
|
|
glm::mat4 model = glm::translate(glm::mat4(1.0f), glm::vec3(ball.position.x, ball.position.y, 0.0f));
|
2024-06-28 17:24:34 +02:00
|
|
|
model = glm::scale(model, glm::vec3(sphereScale, sphereScale, 1.0f)); // Skala dla małych kul
|
2024-06-28 17:01:29 +02:00
|
|
|
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;
|
|
|
|
}
|