/
Xakka
/
CPP4
Обзор
Документация
Войти
/
Xakka
/
CPP4
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/gtk.cpp
597 строк
20 KB
moshedur
master: release 1.0
05 янв 2025, 00:19
05 янв 2025, 00:19
c27b78a
Код
Авторство
О чём код?
#include "gtk.h" #ifndef CPP4_3DVIEWER_V2_0_1_SRC_TESTS int main(int argc, char **argv) { s21::View app; return app.run(argc, argv); } #endif /** * Constructor for the s21::View class. * * @param None * * @return None * * @throws None */ s21::View::View() : scale_value(1), move_coord(0), rotate_coord(180) { create("org.gtkmm.example"); signal_activate().connect(sigc::mem_fun(*this, &s21::View::on_app_activate)); } /** * Initializes the application when it is activated. * * @throws std::runtime_error if there is an error loading the UI file. */ void s21::View::on_app_activate() { auto refBuilder = Gtk::Builder::create(); try { refBuilder->add_from_file("MainWindow.ui"); } catch (const Glib::FileError &ex) { throw(std::runtime_error(ex.what())); } catch (const Glib::MarkupError &ex) { throw(std::runtime_error(ex.what())); } catch (const Gtk::BuilderError &ex) { throw(std::runtime_error(ex.what())); } std::ifstream istrm("settings.bin", std::ios::binary); if (istrm.is_open()) { GdkRGBA tmp; istrm.read((char *)&tmp, sizeof(GdkRGBA)); bg_color = Gdk::RGBA(&tmp); istrm.read((char *)&tmp, sizeof(GdkRGBA)); ln_color = Gdk::RGBA(&tmp); istrm.read((char *)&tmp, sizeof(GdkRGBA)); point_color = Gdk::RGBA(&tmp); } else { bg_color = Gdk::RGBA(0, 0, 0, 0); ln_color = Gdk::RGBA(0, 0, 0, 0); point_color = Gdk::RGBA(0, 0, 0, 0); } auto pDialog = refBuilder->get_widget<Gtk::Window>("MainWindow"); if (!pDialog) { throw(std::runtime_error("Could not get the dialog")); } gl_area = refBuilder->get_widget<Gtk::GLArea>("GLArea"); gl_area->signal_realize().connect( sigc::mem_fun(*this, &s21::View::on_realize)); gl_area->signal_render().connect(sigc::mem_fun(*this, &s21::View::on_render), false); auto LoadButton = refBuilder->get_widget<Gtk::Button>("LoadButton"); LoadButton->signal_clicked().connect( sigc::mem_fun(*this, &s21::View::on_clicked_load_button)); auto bg_dialog = Gtk::ColorDialog::create(); bg_color_button = refBuilder->get_widget<Gtk::ColorDialogButton>("bg_ColorButton"); bg_color_button->set_dialog(bg_dialog); bg_color_button->set_rgba(bg_color); bg_color_button->property_rgba().signal_changed().connect( sigc::mem_fun(*this, &s21::View::bg_notify_rgba)); auto ln_dialog = Gtk::ColorDialog::create(); ln_color_button = refBuilder->get_widget<Gtk::ColorDialogButton>("ln_ColorButton"); ln_color_button->set_rgba(ln_color); ln_color_button->set_dialog(ln_dialog); ln_color_button->property_rgba().signal_changed().connect( sigc::mem_fun(*this, &s21::View::ln_notify_rgba)); auto point_dialog = Gtk::ColorDialog::create(); point_color_button = refBuilder->get_widget<Gtk::ColorDialogButton>("point_ColorButton"); point_color_button->set_rgba(point_color); point_color_button->set_dialog(point_dialog); point_color_button->property_rgba().signal_changed().connect( sigc::mem_fun(*this, &s21::View::point_notify_rgba)); name_label = refBuilder->get_widget<Gtk::Label>("name_label"); vertices_label = refBuilder->get_widget<Gtk::Label>("CountVLabel"); edges_label = refBuilder->get_widget<Gtk::Label>("CountELabel"); filename_label = refBuilder->get_widget<Gtk::Frame>("gl_frame"); add_window(*pDialog); add_signals(refBuilder); pDialog->set_visible(true); } /** * Handles the event when the load button is clicked. * * Creates a file dialog with a filter for Wavefront OBJ files and opens it. * When the dialog is finished, it calls the on_file_dialog_finish function. * * @return None * * @throws None */ void s21::View::on_clicked_load_button() { auto file_dialog = Gtk::FileDialog::create(); auto filters = Gio::ListStore<Gtk::FileFilter>::create(); auto filter_obj = Gtk::FileFilter::create(); filter_obj->set_name("Wavefront OBJ"); filter_obj->add_pattern("*.obj"); filters->append(filter_obj); file_dialog->set_filters(filters); file_dialog->open(sigc::bind( sigc::mem_fun(*this, &s21::View::on_file_dialog_finish), file_dialog)); } /** * Handles the file dialog finish event. * * This function is called when the file dialog is closed. It retrieves the * selected file, opens it using the ObjReader, and updates the model and UI * accordingly. * * @param result The result of the file dialog operation. * @param dialog The file dialog that triggered this event. * * @return None * * @throws Gtk::DialogError If no file was selected. * @throws Glib::Error If an unexpected error occurs. */ void s21::View::on_file_dialog_finish( const Glib::RefPtr<Gio::AsyncResult> &result, const Glib::RefPtr<Gtk::FileDialog> &dialog) { try { auto file = dialog->open_finish(result); std::string filename = file->get_path(); s21::ObjReader *obj_reader = s21::ObjReader::getInstance(); obj_reader->openFile(filename); model = &obj_reader->model; name_label->set_label(obj_reader->model.name); vertices_label->set_label(obj_reader->model.count_vertices); filename_label->set_label(obj_reader->model.filename); edges_label->set_label(obj_reader->model.count_edges); gl_area->attach_buffers(); glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * obj_reader->model.vertices.size() * 3, obj_reader->model.vertices.data(), GL_STREAM_DRAW); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * obj_reader->model.indices.size(), obj_reader->model.indices.data(), GL_STREAM_DRAW); update_glarea(); } catch (const Gtk::DialogError &err) { std::cout << "No file selected. " << err.what() << std::endl; } catch (const Glib::Error &err) { std::cout << "Unexpected exception. " << err.what() << std::endl; } } /** * Initializes the OpenGL context and sets up the necessary resources for * rendering. * * This function is called when the widget is realized, and it performs the * following tasks: * - Initializes the GLEW library * - Creates and sets up the vertex buffer object (VBO) and element buffer * object (EBO) * - Specifies the vertex attribute layout * - Enables the vertex attribute array * * @return None * * @throws GLEW error if the GLEW library fails to initialize */ void s21::View::on_realize() { gl_area->make_current(); if (!gl_area->has_error()) { GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW ERROR: %s\n", glewGetErrorString(err)); } fprintf(stdout, "Status: Using GLEW %s\n", glewGetString(GLEW_VERSION)); s21::Shader vertexShader(GL_VERTEX_SHADER, "vertex_shader.glsl"); s21::Shader fragmentShader(GL_FRAGMENT_SHADER, "fragment_shader.glsl"); s21::ShaderProgram shaderProgram(vertexShader, fragmentShader); shaderProgram.use(); GLuint VAO; glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); GLuint VBO; glGenBuffers(1, &VBO); glBindBuffer(GL_ARRAY_BUFFER, VBO); GLuint EBO; glGenBuffers(1, &EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glVertexAttribPointer(0, 3, GL_FLOAT, GL_TRUE, 3 * sizeof(GLfloat), (GLvoid *)0); glEnableVertexAttribArray(0); } else { g_print("REALIZE ERROR\n"); } transform = glm::mat4(1.0f); } /** * Handles the rendering of the OpenGL context. * * This function is responsible for clearing the color buffer, setting up the * shader program, and drawing the model using the specified line width and * point size. * * @param context A reference to the Gdk::GLContext object. * * @return True if the rendering was successful, false otherwise. */ bool s21::View::on_render( [[maybe_unused]] const Glib::RefPtr<Gdk::GLContext> &context) { float r = 0, g = 0, b = 0, a = 0; if (bg_color.get_red() || bg_color.get_green() || bg_color.get_blue() || bg_color.get_alpha()) { r = bg_color.get_red(); g = bg_color.get_green(); b = bg_color.get_blue(); a = bg_color.get_alpha(); } glClearColor(r, g, b, a); glClear(GL_COLOR_BUFFER_BIT); GLint shaderProgram; glGetIntegerv(GL_CURRENT_PROGRAM, &shaderProgram); GLint vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor"); r = 0, g = 0, b = 0, a = 1; if (ln_color.get_red() || ln_color.get_green() || ln_color.get_blue() || ln_color.get_alpha()) { r = ln_color.get_red(); g = ln_color.get_green(); b = ln_color.get_blue(); a = ln_color.get_alpha(); } glUniform4f(vertexColorLocation, r, g, b, a); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "transform"), 1, GL_FALSE, (const GLfloat *)glm::value_ptr(transform)); glLineWidth(line_width_value); glDrawElements(GL_LINES, model->indices.size(), GL_UNSIGNED_INT, 0); glUniform1f(glGetUniformLocation(shaderProgram, "point_size"), point_size_value); glUniform4f(vertexColorLocation, point_color.get_red(), point_color.get_green(), point_color.get_blue(), point_color.get_alpha()); glDrawElements(GL_POINTS, model->indices.size(), GL_UNSIGNED_INT, 0); update_glarea(); return true; } /** * Updates the horizontal expansion of the GL area. * * @return None * * @throws None */ void s21::View::update_glarea() { gl_area->set_hexpand(false); gl_area->set_hexpand(true); } /** * Updates the background color based on the selected RGBA value. * * This function retrieves the RGBA value from the background color button, * updates the bg_color variable, and saves the new color settings to a binary * file. Finally, it calls the update_glarea function to refresh the display. * * @return None */ void s21::View::bg_notify_rgba() { bg_color = bg_color_button->get_rgba(); std::ofstream ostrm("settings.bin", std::ios::binary); if (ostrm.is_open()) { ostrm.write((char *)bg_color.gobj(), sizeof(GdkRGBA)); ostrm.write((char *)ln_color.gobj(), sizeof(GdkRGBA)); ostrm.write((char *)point_color.gobj(), sizeof(GdkRGBA)); } update_glarea(); } /** * Updates the line color based on the selected RGBA value. * * This function retrieves the RGBA value from the line color button, * updates the ln_color variable, and saves the new color settings to a binary * file. Finally, it calls the update_glarea function to refresh the display. * * @return None */ void s21::View::ln_notify_rgba() { ln_color = ln_color_button->get_rgba(); std::ofstream ostrm("settings.bin", std::ios::binary); if (ostrm.is_open()) { ostrm.write((char *)bg_color.gobj(), sizeof(GdkRGBA)); ostrm.write((char *)ln_color.gobj(), sizeof(GdkRGBA)); ostrm.write((char *)point_color.gobj(), sizeof(GdkRGBA)); } update_glarea(); } /** * Updates the point color based on the selected RGBA value. * * This function retrieves the RGBA value from the point color button, * updates the point_color variable, and saves the new color settings to a * binary file. Finally, it calls the update_glarea function to refresh the * display. * * @return None */ void s21::View::point_notify_rgba() { point_color = point_color_button->get_rgba(); std::ofstream ostrm("settings.bin", std::ios::binary); if (ostrm.is_open()) { ostrm.write((char *)bg_color.gobj(), sizeof(GdkRGBA)); ostrm.write((char *)ln_color.gobj(), sizeof(GdkRGBA)); ostrm.write((char *)point_color.gobj(), sizeof(GdkRGBA)); } update_glarea(); } /** * Handles changes to the scale value. * * This function updates the move, rotation, scale, point size, or line width * values based on the input parameter x and the new scale value. It then calls * the trans function to apply the transformations and the update_glarea * function to refresh the display. * * @param x An integer value that determines which transformation to apply. * @param scale A pointer to the Gtk::Scale object that triggered the change. * * @return None * * @throws None */ void s21::View::on_Scale_changed(int x, Gtk::Scale *scale) { int y = x / 3; if (y == 0) { move_coord[x % 3] = 1 - (scale->get_value() / 50.0); } else if (y == 1) { rotate_coord[x % 3] = scale->get_value(); } else if (y == 2) { scale_value = (scale->get_value() / 50.0) * (scale->get_value() / 50.0); } else if (y == 3) { point_size_value = scale->get_value(); } else if (y == 4) { line_width_value = scale->get_value(); } trans(); update_glarea(); } /** * Applies transformations to the model view matrix. * * This function translates, rotates, and scales the model view matrix based on * the current move, rotation, and scale values. * * @return None * * @throws None */ void s21::View::trans() { transform = glm::mat4(1.0f); transform = glm::translate(transform, move_coord); transform = glm::rotate(transform, glm::radians(rotate_coord[0]), glm::vec3(1.0, 0.0, 0.0)); transform = glm::rotate(transform, glm::radians(rotate_coord[1]), glm::vec3(0.0, 1.0, 0.0)); transform = glm::rotate(transform, glm::radians(rotate_coord[2]), glm::vec3(0.0, 0.0, 1.0)); transform = glm::scale(transform, glm::vec3(scale_value)); } /** * Connects signals to the widgets in the GTK builder. * * This function takes a GTK builder reference and connects the value changed * signals of the scale widgets to the on_Scale_changed function. * * @param refBuilder A reference to the GTK builder. * * @return None * * @throws None */ void s21::View::add_signals(Glib::RefPtr<Gtk::Builder> refBuilder) { { auto x_scale = refBuilder->get_widget<Gtk::Scale>("X_MoveScale"); auto x_adj = x_scale->get_adjustment(); x_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 0, x_scale)); auto y_scale = refBuilder->get_widget<Gtk::Scale>("Y_MoveScale"); auto y_adj = y_scale->get_adjustment(); y_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 1, y_scale)); auto z_scale = refBuilder->get_widget<Gtk::Scale>("Z_MoveScale"); auto z_adj = z_scale->get_adjustment(); z_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 2, z_scale)); auto x_rotate = refBuilder->get_widget<Gtk::Scale>("X_RotateScale"); auto x_r_adj = x_rotate->get_adjustment(); x_r_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 3, x_rotate)); auto y_rotate = refBuilder->get_widget<Gtk::Scale>("Y_RotateScale"); auto y_r_adj = y_rotate->get_adjustment(); y_r_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 4, y_rotate)); auto z_rotate = refBuilder->get_widget<Gtk::Scale>("Z_RotateScale"); auto z_r_adj = z_rotate->get_adjustment(); z_r_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 5, z_rotate)); auto scale_scale = refBuilder->get_widget<Gtk::Scale>("ScaleScale"); auto scale_adj = scale_scale->get_adjustment(); scale_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 6, scale_scale)); auto point_size = refBuilder->get_widget<Gtk::Scale>("pointSizeScale"); auto point_size_adj = point_size->get_adjustment(); point_size_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 9, point_size)); auto line_width = refBuilder->get_widget<Gtk::Scale>("lineWidthScale"); auto line_width_adj = line_width->get_adjustment(); line_width_adj->signal_value_changed().connect(sigc::bind( sigc::mem_fun(*this, &s21::View::on_Scale_changed), 12, line_width)); } } /** * Initializes a shader object with the specified type and file path. * * @param shaderType The type of shader to create (e.g. GL_VERTEX_SHADER, * GL_FRAGMENT_SHADER). * @param path The file path to the shader source code. * * @return None * * @throws Compilation error if the shader fails to compile. */ s21::Shader::Shader(GLenum shaderType, const GLchar *path) { std::ifstream shaderFile; std::stringstream shaderStream; shaderFile.open(path); shaderStream << shaderFile.rdbuf(); shaderFile.close(); std::string tmp_shaderCode; tmp_shaderCode = shaderStream.str(); id = glCreateShader(shaderType); shaderCode = tmp_shaderCode.c_str(); glShaderSource(id, 1, &shaderCode, NULL); glCompileShader(id); glGetShaderiv(id, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(id, 512, NULL, infoLog); g_print("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog); } } s21::Shader::~Shader() { glDeleteShader(id); } /** * Constructs a shader program using the provided vertex and fragment shaders. * * @param vertexShader The vertex shader to use in the program. * @param fragmentShader The fragment shader to use in the program. * * @return None. * * @throws None. */ s21::ShaderProgram::ShaderProgram(s21::Shader vertexShader, s21::Shader fragmentShader) { id = glCreateProgram(); glAttachShader(id, vertexShader.id); glAttachShader(id, fragmentShader.id); glLinkProgram(id); glGetProgramiv(id, GL_LINK_STATUS, &success); if (!success) { glGetProgramInfoLog(id, 512, NULL, infoLog); g_print("ERROR::shaderProgram::COMPILATION_FAILED\n%s\n", infoLog); } } void s21::ShaderProgram::use() { glUseProgram(id); } /** * Opens an OBJ file and populates the model data. * * @param filename The path to the OBJ file to open. * * @return None. * * @throws None. */ void s21::ObjReader::openFile(const std::string &filename) { tinyobj::ObjReaderConfig reader_config; reader_config.triangulate = false; if (!ParseFromFile(filename, reader_config)) { if (!Error().empty()) { std::cerr << Error(); } exit(1); throw(std::runtime_error("ObjReader error")); } model.filename = filename; model.name = GetShapes().data()->name; model.count_vertices = std::to_string(GetAttrib().vertices.size() / 3); model.vertices.clear(); model.vertices = GetAttrib().vertices; normalizeIndices(); getEdges(); } /** * Normalizes the indices of the model by iterating through each shape and face, * and adjusting the vertex indices to ensure proper rendering. * * @return None * * @throws None */ void s21::ObjReader::normalizeIndices() { model.indices.clear(); for (auto shape : GetShapes()) { int dop = 0; for (auto num : shape.mesh.num_face_vertices) { int first = 0; for (int i = 0; i < num; i++) { int vertex_index = shape.mesh.indices[dop++].vertex_index; model.indices.emplace_back(vertex_index); model.indices.emplace_back(vertex_index); if (i == 0) { first = vertex_index; model.indices.pop_back(); } } model.indices.emplace_back(first); } } } /** * Returns the singleton instance of the ObjReader class. * * @return The singleton instance of the ObjReader class. */ s21::ObjReader *s21::ObjReader::getInstance() { { if (!instance) { instance = new ObjReader(); } return instance; } } /** * Extracts the edges from the model's indices and updates the model's edge * count. * * @return None * * @throws None */ void s21::ObjReader::getEdges() { int curr = -1; std::set<std::string> tmp_set; for (auto index : model.indices) { if (curr == -1) { curr = index; } else { tmp_set.emplace(std::to_string(index < curr ? index : curr) + " " + std::to_string(index > curr ? index : curr)); curr = -1; } } model.count_edges = std::to_string(tmp_set.size()); tmp_set.clear(); } /** * Destructor for the s21::ObjReader class. * * @param None * * @return None * * @throws None */ s21::ObjReader::~ObjReader() { delete instance; }