#include #include #include #include #include #include "include/glad/glad.h" #include "include/glfw/glfw3.h" #include #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 #include #include "visualization.hpp" #include #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 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 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 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; }