398 lines
12 KiB
C++
398 lines
12 KiB
C++
#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 = 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<glm::vec3> bezierVertices;
|
|
|
|
struct Ball {
|
|
glm::vec3 position;
|
|
glm::vec3 color;
|
|
};
|
|
|
|
GLFWwindow* window;
|
|
std::vector<Ball> balls(ballsAmount);
|
|
GLuint shaderProgram;
|
|
GLuint bezierVAO, bezierVBO;
|
|
GLuint sphereVAO, sphereVBO, sphereEBO;
|
|
|
|
std::vector<glm::vec3> 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<float> vertices;
|
|
std::vector<unsigned int> 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<float>(i) / static_cast<float>(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;
|
|
}
|