From b2f549d7d1295fc610224da9ef6bd11c3729e4c4 Mon Sep 17 00:00:00 2001 From: Yorick Geoffre Date: Tue, 11 Apr 2023 09:14:21 +0200 Subject: [PATCH 1/2] added basic GL implementations --- src/gl/CMakeLists.txt | 39 ++++++++++++ src/gl/Camera.hpp | 97 ++++++++++++++++++++++++++++++ src/gl/FlyingWindow.hpp | 105 ++++++++++++++++++++++++++++++++ src/gl/Mesh.hpp | 65 ++++++++++++++++++++ src/gl/Model.hpp | 65 ++++++++++++++++++++ src/gl/Shader.hpp | 105 ++++++++++++++++++++++++++++++++ src/gl/TextRenderer.hpp | 128 ++++++++++++++++++++++++++++++++++++++++ src/gl/install_deps.sh | 45 ++++++++++++++ 8 files changed, 649 insertions(+) create mode 100644 src/gl/CMakeLists.txt create mode 100644 src/gl/Camera.hpp create mode 100644 src/gl/FlyingWindow.hpp create mode 100644 src/gl/Mesh.hpp create mode 100644 src/gl/Model.hpp create mode 100644 src/gl/Shader.hpp create mode 100644 src/gl/TextRenderer.hpp create mode 100644 src/gl/install_deps.sh diff --git a/src/gl/CMakeLists.txt b/src/gl/CMakeLists.txt new file mode 100644 index 0000000..597e83e --- /dev/null +++ b/src/gl/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.10) + +set(flying_window_src + ./Model.hpp + ./Mesh.hpp + ./Shader.hpp + ./Camera.hpp + ./FlyingWindow.hpp + ./TextRenderer.hpp +) + +MESSAGE(STATUS "Building gl") + +set(FreeType_DIR "/path/to/freetype/install/dir") + +find_package(OpenCV REQUIRED) +find_package(OpenGL REQUIRED) +find_package(GLUT REQUIRED) +find_package(FreeType REQUIRED) + +include_directories( + ${OPENGL_INCLUDE_DIRS} + ${GLUT_INCLUDE_DIRS} + ${FREETYPE_INCLUDE_DIRS} +) + +MESSAGE(STATUS "OpenCV_INCLUDE_DIRS: ${OpenCV_INCLUDE_DIRS}") +MESSAGE(STATUS "OpenGL_INCLUDE_DIRS: ${OPENGL_INCLUDE_DIRS}") +MESSAGE(STATUS "GLUT_INCLUDE_DIRS: ${GLUT_INCLUDE_DIRS}") +MESSAGE(STATUS "FreeType_INCLUDE_DIRS: ${FREETYPE_INCLUDE_DIRS}") + +add_library(ARHSGL ${flying_window_src}) + +target_link_libraries(ARHSGL ${OPENGL_LIBRARIES}) +target_link_libraries(ARHSGL ${GLUT_INCLUDE_DIRS}) +target_link_libraries(ARHSGL ${OpenCV_LIBS}) +target_link_libraries(ARHSGL ${FREETYPE_LIBRARIES}) + +MESSAGE(STATUS "Done building gl") \ No newline at end of file diff --git a/src/gl/Camera.hpp b/src/gl/Camera.hpp new file mode 100644 index 0000000..7ea1d24 --- /dev/null +++ b/src/gl/Camera.hpp @@ -0,0 +1,97 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include + +enum Camera_Movement { + FORWARD, + BACKWARD, + LEFT, + RIGHT +}; + +const float YAW = -90.0f; +const float PITCH = 0.0f; +const float SPEED = 2.5f; +const float SENSITIVITY = 0.1f; +const float ZOOM = 45.0f; + +class Camera { +public: + glm::vec3 Position; + glm::vec3 Front; + glm::vec3 Up; + glm::vec3 Right; + glm::vec3 WorldUp; + + float Yaw; + float Pitch; + + float MovementSpeed; + float MouseSensitivity; + float Zoom; + + Camera(glm::vec3 position = glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f), float yaw = YAW, float pitch = PITCH) + : Front(glm::vec3(0.0f, 0.0f, -1.0f)), MovementSpeed(SPEED), MouseSensitivity(SENSITIVITY), Zoom(ZOOM) { + Position = position; + WorldUp = up; + Yaw = yaw; + Pitch = pitch; + updateCameraVectors(); + } + + glm::mat4 GetViewMatrix() { + return glm::lookAt(Position, Position + Front, Up); + } + + void ProcessKeyboard(Camera_Movement direction, float deltaTime) { + float velocity = MovementSpeed * deltaTime; + if (direction == FORWARD) + Position += Front * velocity; + if (direction == BACKWARD) + Position -= Front * velocity; + if (direction == LEFT) + Position -= Right * velocity; + if (direction == RIGHT) + Position += Right * velocity; + } + + void ProcessMouseMovement(float xOffset, float yOffset, bool constrainPitch = true) { + xOffset *= MouseSensitivity; + yOffset *= MouseSensitivity; + + Yaw += xOffset; + Pitch += yOffset; + + if (constrainPitch) { + if (Pitch > 89.0f) + Pitch = 89.0f; + if (Pitch < -89.0f) + Pitch = -89.0f; + } + + updateCameraVectors(); + } + + void ProcessMouseScroll(float yOffset) { + Zoom -= yOffset; + if (Zoom < 1.0f) + Zoom = 1.0f; + if (Zoom > 45.0f) + Zoom = 45.0f; + } + +private: + void updateCameraVectors() { + glm::vec3 front; + front.x = cos(glm::radians(Yaw)) * cos(glm::radians(Pitch)); + front.y = sin(glm::radians(Pitch)); + front.z = sin(glm::radians(Yaw)) * cos(glm::radians(Pitch)); + Front = glm::normalize(front); + Right = glm::normalize(glm::cross(Front, WorldUp)); + Up = glm::normalize(glm::cross(Right, Front)); + } +}; + +#endif diff --git a/src/gl/FlyingWindow.hpp b/src/gl/FlyingWindow.hpp new file mode 100644 index 0000000..3dad047 --- /dev/null +++ b/src/gl/FlyingWindow.hpp @@ -0,0 +1,105 @@ +#ifndef FLYING_WINDOW_H +#define FLYING_WINDOW_H + +#include +#include +#include "Camera.hpp" +#include "Model.hpp" +#include "TextRenderer.hpp" + +class FlyingWindow { +public: + FlyingWindow(const Camera& camera, const Model& parentModel, const glm::vec3& offset, const std::string& text, const cv::Mat& image) + : camera(camera), parentModel(parentModel), offset(offset), text(text), image(image) { + init(); + } + + void render() { + // Render quad with texture + shader.use(); + shader.setMat4("projection", camera.GetProjectionMatrix()); + shader.setMat4("view", camera.GetViewMatrix()); + glm::mat4 model = glm::translate(parentModel.modelMatrix, offset); + shader.setMat4("model", model); + glBindVertexArray(VAO); + glBindTexture(GL_TEXTURE_2D, texture); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + glBindVertexArray(0); + + // Render text + textRenderer.renderText(text, model); + } + +private: + Camera camera; + Model parentModel; + glm::vec3 offset; + std::string text; + cv::Mat image; + Shader shader; + GLuint VAO, VBO; + GLuint texture; + TextRenderer textRenderer; // Your custom TextRenderer class + + void init() { + initShader(); + initQuad(); + initTexture(); + initTextRenderer(); + } + + void initShader() { + // Load your vertex and fragment shaders + shader = Shader("path/to/vertex_shader.glsl", "path/to/fragment_shader.glsl"); + } + + void initQuad() { + float quadVertices[] = { + // positions // texture coordinates + -0.5f, -0.5f, 0.0f, 0.0f, + 0.5f, -0.5f, 1.0f, 0.0f, + -0.5f, 0.5f, 0.0f, 1.0f, + 0.5f, 0.5f, 1.0f, 1.0f, + }; + + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float))); + + glBindVertexArray(0); + } + + void initTexture() { + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + if (image.empty()) { + std::cout << "Failed to load image for FlyingWindow texture" << std::endl; + } else { + cv::cvtColor(image, image, cv::COLOR_BGR2RGBA); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.cols, image.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, image.data); + glGenerateMipmap(GL_TEXTURE_2D); + } + glBindTexture(GL_TEXTURE_2D, 0); + } + + void initTextRenderer() { + // Initialize your custom TextRenderer class + textRenderer = TextRenderer(); // Add necessary arguments for your TextRenderer constructor + } +}; + +#endif diff --git a/src/gl/Mesh.hpp b/src/gl/Mesh.hpp new file mode 100644 index 0000000..a057a47 --- /dev/null +++ b/src/gl/Mesh.hpp @@ -0,0 +1,65 @@ +#ifndef MESH_H +#define MESH_H + +#include +#include +#include +#include + +#include "Shader.hpp" + +struct Vertex { + glm::vec3 Position; + glm::vec3 Normal; + glm::vec2 TexCoords; +}; + +class Mesh { +public: + std::vector vertices; + std::vector indices; + + Mesh(const std::vector& vertices, const std::vector& indices) + : vertices(vertices), indices(indices) { + setupMesh(); + } + + void Draw(Shader& shader) { + glBindVertexArray(VAO); + glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0); + glBindVertexArray(0); + } + +private: + unsigned int VAO, VBO, EBO; + + void setupMesh() { + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glGenBuffers(1, &EBO); + + glBindVertexArray(VAO); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW); + + // Vertex positions + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0); + + // Vertex normals + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, Normal)); + + // Vertex texture coordinates + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, TexCoords)); + + glBindVertexArray(0); + } +}; + +#endif \ No newline at end of file diff --git a/src/gl/Model.hpp b/src/gl/Model.hpp new file mode 100644 index 0000000..bcde485 --- /dev/null +++ b/src/gl/Model.hpp @@ -0,0 +1,65 @@ +#ifndef MODEL_H +#define MODEL_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Shader.hpp" +#include "Mesh.hpp" + +class Model { +public: + Model(const std::string& path) { + loadModel(path); + } + + void Draw(Shader& shader) { + for (unsigned int i = 0; i < meshes.size(); i++) { + meshes[i].Draw(shader); + } + } + +private: + std::vector meshes; + std::string directory; + + void loadModel(const std::string& path) { + Assimp::Importer importer; + const aiScene* scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices); + + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { + std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; + return; + } + + directory = path.substr(0, path.find_last_of('/')); + processNode(scene->mRootNode, scene); + } + + void processNode(aiNode* node, const aiScene* scene) { + for (unsigned int i = 0; i < node->mNumMeshes; i++) { + aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; + meshes.push_back(processMesh(mesh, scene)); + } + + for (unsigned int i = 0; i < node->mNumChildren; i++) { + processNode(node->mChildren[i], scene); + } + } + + Mesh processMesh(aiMesh* mesh, const aiScene* scene) { + // Process the mesh data (vertices, normals, texture coordinates, and indices) + // and create a Mesh object using that data + // (This part has been omitted for brevity. Refer to the Mesh class implementation.) + + return Mesh(/* Pass the processed mesh data */); + } +}; + +#endif diff --git a/src/gl/Shader.hpp b/src/gl/Shader.hpp new file mode 100644 index 0000000..5647a7b --- /dev/null +++ b/src/gl/Shader.hpp @@ -0,0 +1,105 @@ +#ifndef SHADER_H +#define SHADER_H + +#include +#include +#include +#include +#include +#include +#include + +class Shader { +public: + unsigned int ID; + + Shader(const std::string& vertexPath, const std::string& fragmentPath) { + std::string vertexCode, fragmentCode; + std::ifstream vertexFile, fragmentFile; + + vertexFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + fragmentFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + + try { + vertexFile.open(vertexPath); + fragmentFile.open(fragmentPath); + std::stringstream vertexStream, fragmentStream; + + vertexStream << vertexFile.rdbuf(); + fragmentStream << fragmentFile.rdbuf(); + + vertexFile.close(); + fragmentFile.close(); + + vertexCode = vertexStream.str(); + fragmentCode = fragmentStream.str(); + } catch (std::ifstream::failure& e) { + std::cout << "ERROR::SHADER::FILE_NOT_SUCCESSFULLY_READ" << std::endl; + } + + const char* vertexShaderCode = vertexCode.c_str(); + const char* fragmentShaderCode = fragmentCode.c_str(); + + unsigned int vertexShader, fragmentShader; + int success; + char infoLog[512]; + + vertexShader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertexShader, 1, &vertexShaderCode, nullptr); + glCompileShader(vertexShader); + glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(vertexShader, 512, nullptr, infoLog); + std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl; + } + + fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragmentShader, 1, &fragmentShaderCode, nullptr); + glCompileShader(fragmentShader); + glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(fragmentShader, 512, nullptr, infoLog); + std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl; + } + + ID = glCreateProgram(); + glAttachShader(ID, vertexShader); + glAttachShader(ID, fragmentShader); + glLinkProgram(ID); + glGetProgramiv(ID, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(ID, 512, nullptr, infoLog); + std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl; + } + + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + } + + void use() { + glUseProgram(ID); + } + + // Utility functions for setting shader uniforms + void setBool(const std::string& name, bool value) const { + glUniform1i(glGetUniformLocation(ID, name.c_str()), (int)value); + } + + void setInt(const std::string& name, int value) const { + glUniform1i(glGetUniformLocation(ID, name.c_str()), value); + } + + void setFloat(const std::string& name, float value) const { + glUniform1f(glGetUniformLocation(ID, name.c_str()), value); + } + + void setVec3(const std::string& name, const glm::vec3& value) const { + glUniform3fv(glGetUniformLocation(ID, name.c_str()), 1, glm::value_ptr(value)); + } + + void setMat4(const std::string& name, const glm::mat4& value) const { + glUniformMatrix4fv(glGetUniformLocation(ID, name.c_str()), 1, GL_FALSE, glm::value_ptr(value)); + } +}; + +#endif \ No newline at end of file diff --git a/src/gl/TextRenderer.hpp b/src/gl/TextRenderer.hpp new file mode 100644 index 0000000..80141b4 --- /dev/null +++ b/src/gl/TextRenderer.hpp @@ -0,0 +1,128 @@ +#ifndef TEXT_RENDERER_H +#define TEXT_RENDERER_H + +#include +#include +#include +#include FT_FREETYPE_H +#include +#include +#include "Shader.hpp" + +struct Character { + GLuint TextureID; + glm::ivec2 Size; + glm::ivec2 Bearing; + GLuint Advance; +}; + +class TextRenderer { +public: + std::map Characters; + Shader TextShader; + + TextRenderer(GLuint width, GLuint height) { + // Load and configure shader + TextShader = Shader("path/to/text_vertex_shader.glsl", "path/to/text_fragment_shader.glsl"); + TextShader.use(); + TextShader.setMat4("projection", glm::ortho(0.0f, static_cast(width), 0.0f, static_cast(height))); + TextShader.setInt("text", 0); + + // Configure FreeType + FT_Library ft; + if (FT_Init_FreeType(&ft)) { + std::cout << "ERROR::FREETYPE: Could not init FreeType Library" << std::endl; + } + + FT_Face face; + if (FT_New_Face(ft, "path/to/font.ttf", 0, &face)) { + std::cout << "ERROR::FREETYPE: Failed to load font" << std::endl; + } + + FT_Set_Pixel_Sizes(face, 0, 48); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + // Load characters + for (unsigned char c = 0; c < 128; c++) { + if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { + std::cout << "ERROR::FREETYPE: Failed to load Glyph" << std::endl; + continue; + } + + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED, GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + Character character = { + texture, + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + static_cast(face->glyph->advance.x) + }; + + Characters.insert(std::pair(c, character)); + } + + glBindTexture(GL_TEXTURE_2D, 0); + FT_Done_Face(face); + FT_Done_FreeType(ft); + + // Configure VAO/VBO for texture quads + glGenVertexArrays(1, &VAO); + glGenBuffers(1, &VBO); + glBindVertexArray(VAO); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, nullptr, GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0); + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + } + + void renderText(const std::string &text, const glm::mat4 &model) { + TextShader.use(); + TextShader.setMat4("model", model); + glActiveTexture(GL_TEXTURE0); + glBindVertexArray(VAO); + + for (const char &c : text) { + Character ch = Characters[c]; + + GLfloat xpos = ch.Bearing.x; + GLfloat ypos = -ch.Bearing.y; + GLfloat w = ch.Size.x; + GLfloat h = ch.Size.y; + + GLfloat vertices[6][4] = { + { xpos, ypos + h, 0.0, 0.0 }, + { xpos, ypos, 0.0, 1.0 }, + { xpos + w, ypos, 1.0, 1.0 }, + + { xpos, ypos + h, 0.0, 0.0 }, + { xpos + w, ypos, 1.0, 1.0 }, + { xpos + w, ypos + h, 1.0, 0.0 } + }; + + glBindTexture(GL_TEXTURE_2D, ch.TextureID); + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + } + + glBindVertexArray(0); + glBindTexture(GL_TEXTURE_2D, 0); + } + +private: + GLuint VAO, VBO; +}; + +#endif diff --git a/src/gl/install_deps.sh b/src/gl/install_deps.sh new file mode 100644 index 0000000..e584bc6 --- /dev/null +++ b/src/gl/install_deps.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Update package lists +sudo apt-get update + +# Install necessary packages +sudo apt-get install -y \ + build-essential \ + cmake \ + pkg-config \ + libopencv-dev \ + libgl1-mesa-dev \ + libglu1-mesa-dev \ + freeglut3-dev \ + libfreetype6-dev + +# Download and install GLFW +git clone https://github.com/glfw/glfw.git +cd glfw +mkdir build && cd build +cmake .. && make +sudo make install +cd ../.. +rm -rf glfw + +# Download and install GLAD +git clone https://github.com/Dav1dde/glad.git +cd glad +mkdir build && cd build +cmake .. && make +sudo make install +cd ../.. +rm -rf glad + +# Download and install GLEW +wget https://github.com/nigels-com/glew/releases/download/glew-2.2.0/glew-2.2.0.tgz +tar -xvf glew-2.2.0.tgz +cd glew-2.2.0/build +cmake ./cmake && make +sudo make install +cd ../.. +rm -rf glew-2.2.0 +rm glew-2.2.0.tgz + +echo "All dependencies have been installed." \ No newline at end of file From 724c612b2863e817a15f00ba038f7480320890fd Mon Sep 17 00:00:00 2001 From: Yorick Geoffre Date: Tue, 11 Apr 2023 09:23:50 +0200 Subject: [PATCH 2/2] overwritten master --- src/gl/install_deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gl/install_deps.sh b/src/gl/install_deps.sh index e584bc6..ea07249 100644 --- a/src/gl/install_deps.sh +++ b/src/gl/install_deps.sh @@ -2,7 +2,7 @@ # Update package lists sudo apt-get update - + # Install necessary packages sudo apt-get install -y \ build-essential \