Files
Visualizer/main.cpp

453 lines
15 KiB
C++

#include <cstddef>
#include <cstdint>
#include <handleapi.h>
#include <thread>
#include <windows.h>
#include "include/glad/glad.h"
#include "include/glfw/glfw3.h"
#include <iostream>
#include "include/glm/gtc/quaternion.hpp"
#include "include/imgui/imgui.h"
#include "include/imgui/imgui_impl_glfw.h"
#include "include/imgui/imgui_impl_opengl3.h"
#include "serialcomm.hpp"
#include <string>
#include <mutex>
#include "visualization.hpp"
#include <vector>
#include "include/glm/glm.hpp"
#include "include/glm/gtc/matrix_transform.hpp"
#include "include/glm/gtc/type_ptr.hpp"
#define STB_IMAGE_IMPLEMENTATION
#include "include/stb/stb_image.h"
#include "include/glm/gtc/type_ptr.hpp"
// global variables for the communication between threads (gui, serial)
std::mutex g_mutex;
Quaternion g_currQuat = {1.0f, 0.0f, 0.0f, 0.0f};
std::vector<Quaternion> g_quadHistory;
bool g_running = false;
HANDLE g_serialHandle = INVALID_HANDLE_VALUE;
// shader
const char *vertexShaderSource = "#version 330 core\n"
"layout (location = 0) in vec3 aPos;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"uniform mat4 transformMatrix;\n"
"uniform mat4 model;\n"
"uniform mat4 view;\n"
"uniform mat4 projection;\n"
"out vec2 TexCoord;\n"
"void main()\n"
"{\n"
" gl_Position = projection * view * model *vec4(aPos, 1.0);\n"
" TexCoord = aTexCoord;\n"
"}\0";
const char *fragmentShaderSource = "#version 330 core\n"
"out vec4 FragColor;\n"
"in vec2 TexCoord;\n"
"uniform sampler2D ourTexture;\n"
"void main()\n"
"{\n"
" FragColor = texture(ourTexture, TexCoord);\n"
"}\0";
// handle the serial communication wioth the stm32 in a separate thread
void serialParserThread() {
// define communication variables
const char* portName = "COM3";
DWORD baudRate = CBR_115200;
char buffer[64];
DWORD bytesRead;
std::string incompleteDataBuffer;
// establish communication to stm32
g_serialHandle = initSerialPort(portName, baudRate);
if (g_serialHandle == INVALID_HANDLE_VALUE){
g_running = false;
std::cout << "Serial connection FAILED" << std::endl;
return;
} else {std::cout << "Serial connection succesfully established" << std::endl;}
// serial reading loop
while (g_running) {
if (readSerialData(g_serialHandle, buffer, sizeof(buffer), bytesRead)) {
if (bytesRead > 0) {
// add newly arrived data to buffer
buffer[bytesRead] = '\0';
incompleteDataBuffer += buffer;
// process any complete lines in the buffer
size_t pos;
while ((pos = incompleteDataBuffer.find('\n')) != std::string::npos) {
// create substring of completed line and erase it from incompleteDataBuffer
std::string completeLine = incompleteDataBuffer.substr(0, pos);
incompleteDataBuffer.erase(0, pos + 1);
// now parse the quaternions from line
Quaternion quat;
if (parseQuaternion(completeLine, quat)) {
// lock mutex while updating it
std::lock_guard<std::mutex> lock(g_mutex);
g_currQuat = quat;
g_quadHistory.push_back(quat);
if (g_quadHistory.size() > 200) {
g_quadHistory.erase(g_quadHistory.begin());
}
std::cout << "w: " << g_currQuat.w << "x: " << g_currQuat.x << "y: " << g_currQuat.y << "z: " << g_currQuat.z << std::endl;
}
}
}
}
}
// close and exit the thread
CloseHandle(g_serialHandle);
std::cout << "Serial communication thread exiting" << std::endl;
}
int main() {
// Locap copy of quaternion
Quaternion lQuad;
// initialize main window
GLFWwindow* window = initWindow();
if (!window) {
std::cerr << "Window initialization failed in mainthread" << std::endl;
return 1;
} else {std::cout << "Window initialization succesful in mainthread" << std::endl;}
// compile the shaders
int success;
char errorLog[512];
unsigned int vertexShader;
unsigned int fragmentShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader);
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
if(!success) {
glGetShaderInfoLog(vertexShader, 512, NULL, errorLog);
std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << errorLog << std::endl;
}
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
glCompileShader(fragmentShader);
glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
if(!success) {
glGetShaderInfoLog(fragmentShader, 512, NULL, errorLog);
std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << errorLog << std::endl;
}
// link the shaders
unsigned int shaderProgramm = glCreateProgram();
glAttachShader(shaderProgramm, vertexShader);
glAttachShader(shaderProgramm, fragmentShader);
glLinkProgram(shaderProgramm);
glGetProgramiv(shaderProgramm, GL_LINK_STATUS, &success);
if(!success) {
glGetShaderInfoLog(shaderProgramm, 512, NULL, errorLog);
std::cout << "ERROR::LINKING::SHADER::COMPILATION_FAILED\n" << errorLog << std::endl;
}
glUseProgram(shaderProgramm);
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
// texture stuff
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// texture wrapping and filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// now load and create the texture
int imWidth, imHeigth, imNrChannels;
stbi_set_flip_vertically_on_load(true);
unsigned char *data = stbi_load("kekusmaximus.jpg", &imWidth, &imHeigth, &imNrChannels, 0);
if (data) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, imWidth, imHeigth, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
} else {
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
// configure depth settings for 3D
glEnable(GL_DEPTH_TEST);
// only screen coordinates between -1 and 1 are visible, (0, 0, 0) being display centerpoint
float vertexData[] = {
// Front face
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // 0
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // 1
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // 2
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // 3
// Back face
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // 4
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // 5
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // 6
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // 7
// Left face
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // 8
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f, // 9
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, // 10
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, // 11
// Right face
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, // 12
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // 13
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // 14
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, // 15
// Bottom face
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, // 16
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, // 17
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, // 18
-0.5f, -0.5f, 0.5f, 0.0f, 1.0f, // 19
// Top face
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, // 20
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, // 21
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, // 22
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f // 23
};
unsigned int vertexIndices[] = {
// Front face
0, 1, 2, 2, 3, 0,
// Back face
4, 5, 6, 6, 7, 4,
// Left face
8, 9, 10, 10, 11, 8,
// Right face
12, 13, 14, 14, 15, 12,
// Bottom face
16, 17, 18, 18, 19, 16,
// Top face
20, 21, 22, 22, 23, 20
};
// create vertex/element buffer object VBO to store data on GPU
unsigned int VBO, VAO, EBO;
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);
glGenBuffers(1, &EBO);
glBindVertexArray(VAO);
// bind vertex to VBO buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
// bind indices to EBO buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertexIndices), vertexIndices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5*sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// add texture coordinates
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NavEnableSetMousePos;
// setup platform/renderer backends
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init();
// Start serial parsing thread
// pass the address of the code in memory so that thread can start its instance of the function
//std::thread parserThread(serialParserThread);
std::thread* parserThread = nullptr;
// set rendering state of window
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
// glm quad to test purposes
// main rendering loop for the ui
while (!glfwWindowShouldClose(window)) {
// process new events from window evevnt queue
glfwPollEvents();
// clear the color buffer == clear the screen / / GL_DEPTH_BUFFER_BIT would be for 3D rendering
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// activate and bind texture before draw
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUseProgram(shaderProgramm);
// use the texture actually
glUniform1i(glGetUniformLocation(shaderProgramm, "outTexture"), 0);
// create the transformations - TRS order (translate, rotate, scale) for proper transformation
// create identity matrix
glm::mat4 transformMatrix = glm::mat4(1.0f);
transformMatrix = glm::translate(transformMatrix, glm::vec3(0.3f, 0.1f, 0.0f));
transformMatrix = glm::rotate(transformMatrix, (float)glfwGetTime(), glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::mat4(1.0f);
glm::mat4 projection = glm::mat4(1.0f);
// apply MVP
//model = glm::rotate(model, (float)glfwGetTime() * glm::radians(50.0f), glm::vec3(0.5f, 1.0f, 0.0f));
// this needs to be deleted and redone
glm::quat kekus_q(g_currQuat.w, g_currQuat.x, g_currQuat.y, g_currQuat.z);
model = glm::mat4_cast(kekus_q);
view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f));
projection = glm::perspective(glm::radians(45.0f), (float)1280 / (float)720, 0.1f, 100.f);
// get uniform matrix location and set matrix
// unsigned int transformLoc = glGetUniformLocation(shaderProgramm, "transformMatrix");
// glad_glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(transformMatrix));
unsigned int modelLoc = glGetUniformLocation(shaderProgramm, "model");
unsigned int viewLoc = glGetUniformLocation(shaderProgramm, "view");
unsigned int projLoc = glGetUniformLocation(shaderProgramm, "projection");
//shaderProgramm.setMat4("projection", 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));
// OpenGL 3D Render
glBindVertexArray(VAO);
//glDrawArrays(GL_TRIANGLES, 0, 3);
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);
//glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// start imgui frame
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
// accessing g_currQuat through the mutex protection
// limited scope block just for the mutex operations
{
std::lock_guard<std::mutex> lock(g_mutex);
lQuad = g_currQuat;
static int frame_count = 0;
if (frame_count++ % 60 == 0) {
std::cout << "Rendering frame with quaternion: "
<< "w: " << g_currQuat.w << " x: " << g_currQuat.x
<< " y: " << g_currQuat.y << " z: " << g_currQuat.z << std::endl;
}
}
ImGui::Text("Hello Daddy, I'm here to serve");
ImGui::Text("w: %.4f x: %.4f y: %.4f z: %.4f", lQuad.w, lQuad.x, lQuad.y, lQuad.z);
ImGui::Text("Connection to MCU: ");
ImGui::BeginDisabled(parserThread != nullptr);
if (ImGui::Button("Connect")) {
g_running = true;
parserThread = new std::thread(serialParserThread);
}
ImGui::EndDisabled();
ImGui::SameLine();
ImGui::BeginDisabled(parserThread == nullptr);
if (ImGui::Button("Disconnect")) {
g_running = false;
parserThread -> join();
delete parserThread;
parserThread = nullptr;
}
ImGui::EndDisabled();
// create scrolling section for last 200 measurements
ImGui::BeginChild("History", ImVec2(0, 300), true, ImGuiWindowFlags_HorizontalScrollbar);
// loop through quadHistory vector and display each element as line
for (uint32_t i = 0; i < g_quadHistory.size(); i++) {
const Quaternion& q = g_quadHistory[i];
ImGui::Text("w: %.4f x: %.4f y: %.4f z: %.4f", q.w, q.x, q.y, q.z);
}
// enable autoscroll to newest entry
if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) {
ImGui::SetScrollHereY(1.0f);
}
ImGui::EndChild();
// render the frame
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
// swap the buffers to display what was rendered
glfwSwapBuffers(window);
glfwPollEvents();
std::this_thread::sleep_for(std::chrono::milliseconds(16));
}
// set the parsing thread to exit and wait for it
// g_running = false;
//if (parserThread.joinable()) {
// parserThread.join();
//}
// clean up ImGui
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
// clean up OpenGL
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
glDeleteProgram(shaderProgramm);
// clean up the window
glfwDestroyWindow(window);
glfwTerminate();
return 0;
}