#include "Application.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using glm::vec3; using glm::uvec3; using std::map; using std::vector; void calculateNormals(const vector &vtx, vector &tri, vector &normals) { normals.clear(); normals.resize(vtx.size(), vec3(0)); for (size_t i = 0; i < tri.size(); i++) { int a = tri[i].x, b = tri[i].y, c = tri[i].z; vec3 faceNormal = normalize( glm::cross(vtx[b] - vtx[a], vtx[c] - vtx[a])); normals[a] += faceNormal; normals[b] += faceNormal; normals[c] += faceNormal; } for (size_t i = 0; i < normals.size(); i++) normals[i] = glm::normalize(normals[i]); } void moveToMean(vector &vtx) { vec3 mean(0); for (size_t i = 0; i < vtx.size(); i++) { mean += vtx[i]; } mean *= vec3(1.f / vtx.size()); for (size_t i = 0; i < vtx.size(); i++) { vtx[i] -= mean; } } void smooth(vector &vtx, const vector &tri) { vector cogs(vtx.size(), vec3(0.f)); vector valence(vtx.size(), 0); for (size_t iTri = 0; iTri < tri.size(); iTri++) { const uvec3 &idx = tri[iTri]; for (size_t iE = 0; iE < 3; iE++) { valence[idx[iE]] += 2; cogs[idx[iE]] += vtx[idx[(iE + 1) % 3]]; cogs[idx[iE]] += vtx[idx[(iE + 2) % 3]]; } } /* for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it) { cog[0] = cog[1] = cog[2] = valence = 0.0; for (vv_it = mesh.vv_iter(*v_it); vv_it.is_valid(); ++vv_it) { cog += mesh.point(*vv_it); ++valence; } cogs.push_back(cog / valence); } for (v_it = mesh.vertices_begin(), cog_it = cogs.begin(); v_it != v_end; ++v_it, ++cog_it) if (!mesh.is_boundary(*v_it)) mesh.set_point(*v_it, *cog_it); */ for (size_t i = 0; i < vtx.size(); i++) { // vtx[i] = vtx[i] * vec3(0.8) // + cogs[i] * vec3(0.2f / valence[i]); vtx[i] = cogs[i] * vec3(1.f / valence[i]); } } void saveAttrib(std::ostream &out, const char *prefix, vector &elements) { for (size_t i = 0; i < elements.size(); i++) { out << prefix << elements[i].x << " " << elements[i].y << " " << elements[i].z << "\n"; } } void saveAttrib(std::ostream &out, const char *prefix, vector &elements) { for (size_t i = 0; i < elements.size(); i++) { out << prefix << elements[i].x << " " << elements[i].y << "\n"; } } void saveFaces(std::ostream &out, const vector &tris, size_t attribs) { for (size_t i = 0; i < tris.size(); i++) { out << "f"; for (size_t j = 0; j < 3; j++) { int v = tris[i][j] + 1; out << " " << v; if (attribs > 1) out << "/" << v; if (attribs > 2) out << "/" << v; out << " "; } out << "\n"; } } void calculateEdges(const vector &vtx, vector &tri, vector &edges) { edges.clear(); map, uint32_t> edgeMap; for (size_t iTri = 0; iTri < tri.size(); iTri++) { uvec3 idx = tri[iTri]; for (size_t k = 0; k < 3; k++) { int a = idx[k]; int b = idx[(k + 1) % 3]; glm::uvec4 edge1(std::min(a, b), std::max(a, b), iTri, 0); auto it1 = edgeMap.find(std::make_pair(edge1.x, edge1.y)); if (it1 != edgeMap.end()) { edges[it1->second].z = iTri; } else { edges.push_back(edge1); } } } } void computeTangentBasis(vector & vertices, vector & uvs, vector & normals, vector & tangents, vector & bitangents) { for (size_t i = 0; i < vertices.size(); i += 3) { // Shortcuts for vertices vec3 & v0 = vertices[i + 0]; vec3 & v1 = vertices[i + 1]; vec3 & v2 = vertices[i + 2]; // Shortcuts for UVs glm::vec2 & uv0 = uvs[i + 0]; glm::vec2 & uv1 = uvs[i + 1]; glm::vec2 & uv2 = uvs[i + 2]; // Edges of the triangle : postion delta vec3 deltaPos1 = v1 - v0; vec3 deltaPos2 = v2 - v0; // UV delta glm::vec2 deltaUV1 = uv1 - uv0; glm::vec2 deltaUV2 = uv2 - uv0; float r = 1.0f / (deltaUV1.x * deltaUV2.y - deltaUV1.y * deltaUV2.x); vec3 tangent = (deltaPos1 * deltaUV2.y - deltaPos2 * deltaUV1.y) * r; vec3 bitangent = (deltaPos2 * deltaUV1.x - deltaPos1 * deltaUV2.x) * r; // Set the same tangent for all three vertices of the triangle. // They will be merged later, in vboindexer.cpp tangents.push_back(tangent); tangents.push_back(tangent); tangents.push_back(tangent); // Same thing for binormals bitangents.push_back(bitangent); bitangents.push_back(bitangent); bitangents.push_back(bitangent); } } static bool glck(const char* call) { int err = glGetError(); if (err == 0) { return true; } else { switch (err) { case GL_INVALID_VALUE: printf("GL_INVALID_VALUE: %s\n", call); break; case GL_INVALID_ENUM: printf("GL_INVALID_ENUM: %s\n", call); break; case GL_INVALID_OPERATION: printf("GL_INVALID_OPERATION: %s\n", call); break; default: printf("UNKNOWN Error: %s\n", call); } return false; } } #define GLCK(a) {a; assert(glck(#a));} static void error_callback(int error, const char* description) { fprintf(stderr, "Error: %s\n", description); } static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { Application::instance().KeyCallback(window, key, scancode, action, mods); } static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) { Application::instance().MouseButtonCallback(window, button, action, mods); } static void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { Application::instance().ScrollCallback(window, xoffset, yoffset); } static void char_callback(GLFWwindow* window, unsigned int c) { Application::instance().CharCallback(window, c); } Application::Application() : window(0), density(), densityIsolevel(0.2), polygonizer(density, densityIsolevel), textureSize(512), texturemapper(density, textureSize), programId(0), vPositionLoc(-1), vTexCoordLoc(-1), vNormalLoc( -1), meteoridCount(1000), meteroidMinRadius(0.005), meteroidMaxRadius( 0.2), meteroidSizeExponent(2048), densityFrequency(2), densityOctave( 2), densityScale(1.0f), resolution(0.1), viewDistance(3.0f), fpsMode( false) { init(); } Application::~Application() { shutdown(); } Application &Application::instance() { static Application app; return app; } void Application::init() { glfwSetErrorCallback(error_callback); if (!glfwInit()) exit(EXIT_FAILURE); // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); window = glfwCreateWindow(1200, 800, "Procedural Asteroid Generator", NULL, NULL); if (!window) { glfwTerminate(); exit(EXIT_FAILURE); } GLFWimage icon; int n; icon.pixels = stbi_load("icon.png", &icon.width, &icon.height, &n, 4); if (icon.pixels) { glfwSetWindowIcon(window, 1, &icon); stbi_image_free(icon.pixels); } else std::cout << "Failed to load icon.png" << std::endl; glfwSetMouseButtonCallback(window, mouse_button_callback); glfwSetScrollCallback(window, scroll_callback); glfwSetKeyCallback(window, key_callback); glfwSetCharCallback(window, char_callback); glfwMakeContextCurrent(window); gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); glfwSwapInterval(1); std::cout << "OpenGL version: " << glGetString(GL_VERSION) << std::endl; std::cout << "GLSL version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl; std::cout << "Vendor: " << glGetString(GL_VENDOR) << std::endl; std::cout << "Renderer: " << glGetString(GL_RENDERER) << std::endl; ImGui_ImplGlfwGL3_Init(window, false); GLCK(glGenVertexArrays(1, &vertexArrayId)) GLCK(glGenBuffers(1, &vPositionId)) GLCK(glGenBuffers(1, &vTexCoordId)) GLCK(glGenBuffers(1, &vNormalId)) GLCK(glGenBuffers(1, &indexBufferId)) GLCK(glGenTextures(1, &tAlbedoId)) GLCK(glGenTextures(1, &tNormalId)) GLCK(glGenTextures(1, &tRoughnessId)) GLCK(glGenTextures(1, &tMetalicId)) loadShader(); updateVertexArrayObject(); generateAsteroid(); uploadMesh(); } void Application::shutdown() { ImGui_ImplGlfwGL3_Shutdown(); GLCK(glDeleteBuffers(1, &vPositionId)) GLCK(glDeleteBuffers(1, &vTexCoordId)) GLCK(glDeleteBuffers(1, &vNormalId)) GLCK(glDeleteBuffers(1, &indexBufferId)) GLCK(glDeleteVertexArrays(1, &vertexArrayId)) glfwDestroyWindow(window); glfwTerminate(); } void Application::KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) { if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) glfwSetWindowShouldClose(window, GLFW_TRUE); if (ImGui::IsMouseHoveringAnyWindow()) ImGui_ImplGlfwGL3_KeyCallback(window, key, scancode, action, mods); else { if (key == GLFW_KEY_TAB && action == GLFW_RELEASE) { toogleFpsMode(); } } } void Application::MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { ImGui_ImplGlfwGL3_MouseButtonCallback(window, button, action, mods); } void Application::ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) { if (ImGui::IsMouseHoveringAnyWindow()) ImGui_ImplGlfwGL3_ScrollCallback(window, xoffset, yoffset); else viewDistance -= yoffset * viewDistance * 0.1; } void Application::CharCallback(GLFWwindow* window, unsigned int c) { ImGui_ImplGlfwGL3_CharCallback(window, c); } void Application::toogleFpsMode() { if (fpsMode) { fpsMode = false; } else { fpsMode = true; fpsTrafo = glm::lookAt(vec3(0, 0, viewDistance), vec3(0, 0, 0), vec3(0, 1, 0)); } } void Application::generateAsteroid() { std::srand(123); if (density.getImpacts().size() == 0) density.addRandomImpacts(1000, 0.2, 0.01, 0.005, 0.2, 4096); //density.saveCrossSection("density.png", 512); polygonizer.polygonize(resolution); { std::ofstream out("stage1.obj"); saveAttrib(out, "v ", polygonizer.vertices); saveFaces(out, polygonizer.indices, 1); } smooth(polygonizer.vertices, polygonizer.indices); { std::ofstream out("stage2.obj"); saveAttrib(out, "v ", polygonizer.vertices); saveFaces(out, polygonizer.indices, 1); } calculateNormals(polygonizer.vertices, polygonizer.indices, normals); { std::ofstream out("stage3.obj"); saveAttrib(out, "v ", polygonizer.vertices); saveAttrib(out, "vn ", normals); saveFaces(out, polygonizer.indices, 2); } vector edges; std::cout << "Vertices: " << polygonizer.vertices.size() << std::endl; std::cout << "Triangles: " << polygonizer.indices.size() << std::endl; texturemapper.map(polygonizer.vertices, normals, polygonizer.indices); moveToMean(texturemapper.vertices); { std::ofstream out("stage4.obj"); saveAttrib(out, "v ", texturemapper.vertices); saveAttrib(out, "vn ", texturemapper.normals); saveAttrib(out, "vt ", texturemapper.texcoords); saveFaces(out, texturemapper.indices, 3); } } bool Application::bindVertexAttrib(GLuint id, GLint loc, GLuint size) { if (loc < 0) return false; GLCK(glBindBuffer(GL_ARRAY_BUFFER, id)) GLCK(glEnableVertexAttribArray(loc)) GLCK(glVertexAttribPointer(loc, // attribute size,// size GL_FLOAT,// type GL_FALSE,// normalized? 0,// stride (void* ) 0// array buffer offset )) return true; } void Application::updateVertexArrayObject() { GLCK(glBindVertexArray(vertexArrayId)) if (vPositionLoc >= 0) GLCK(glDisableVertexAttribArray(vPositionLoc)) if (vNormalLoc >= 0) GLCK(glDisableVertexAttribArray(vNormalLoc)) if (vTexCoordLoc >= 0) GLCK(glDisableVertexAttribArray(vTexCoordLoc)) bindVertexAttrib(vPositionId, vPositionLoc, 3); bindVertexAttrib(vNormalId, vNormalLoc, 3); bindVertexAttrib(vTexCoordId, vTexCoordLoc, 2); GLCK(glBindVertexArray(0)) } GLuint Application::loadProgram(const std::string &name) { // Create the shaders GLuint VertexShaderID = glCreateShader(GL_VERTEX_SHADER); GLuint FragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); // Read the Vertex Shader code from the file std::string VertexShaderCode; std::string vertex_file_path = "shader/" + name + ".vertex.glsl"; std::ifstream VertexShaderStream(vertex_file_path, std::ios::in); if (VertexShaderStream.is_open()) { std::string Line = ""; while (getline(VertexShaderStream, Line)) VertexShaderCode += "\n" + Line; VertexShaderStream.close(); } else { std::cout << "Failed to open " << vertex_file_path << std::endl; return 0; } // Read the Fragment Shader code from the file std::string FragmentShaderCode; std::string fragment_file_path = "shader/" + name + ".fragment.glsl"; std::ifstream FragmentShaderStream(fragment_file_path, std::ios::in); if (FragmentShaderStream.is_open()) { std::string Line = ""; while (getline(FragmentShaderStream, Line)) FragmentShaderCode += "\n" + Line; FragmentShaderStream.close(); } else { std::cout << "Failed to open " << vertex_file_path << std::endl; return 0; } GLint Result = GL_FALSE; int InfoLogLength; // Compile Vertex Shader std::cout << "Compiling vertex shader : " << name << std::endl; char const * VertexSourcePointer = VertexShaderCode.c_str(); glShaderSource(VertexShaderID, 1, &VertexSourcePointer, NULL); glCompileShader(VertexShaderID); // Check Vertex Shader glGetShaderiv(VertexShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(VertexShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0) { vector VertexShaderErrorMessage(InfoLogLength + 1); glGetShaderInfoLog(VertexShaderID, InfoLogLength, NULL, &VertexShaderErrorMessage[0]); printf("%s\n", &VertexShaderErrorMessage[0]); } // Compile Fragment Shader std::cout << "Compiling fragment shader : " << name << std::endl; char const * FragmentSourcePointer = FragmentShaderCode.c_str(); glShaderSource(FragmentShaderID, 1, &FragmentSourcePointer, NULL); glCompileShader(FragmentShaderID); // Check Fragment Shader glGetShaderiv(FragmentShaderID, GL_COMPILE_STATUS, &Result); glGetShaderiv(FragmentShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0) { vector FragmentShaderErrorMessage(InfoLogLength + 1); glGetShaderInfoLog(FragmentShaderID, InfoLogLength, NULL, &FragmentShaderErrorMessage[0]); printf("%s\n", &FragmentShaderErrorMessage[0]); } // Link the program std::cout << "Linking program : " << name << std::endl; GLuint ProgramID = glCreateProgram(); glAttachShader(ProgramID, VertexShaderID); glAttachShader(ProgramID, FragmentShaderID); glLinkProgram(ProgramID); // Check the program glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); if (InfoLogLength > 0) { vector ProgramErrorMessage(InfoLogLength + 1); glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); printf("%s\n", &ProgramErrorMessage[0]); } glDetachShader(ProgramID, VertexShaderID); glDetachShader(ProgramID, FragmentShaderID); glDeleteShader(VertexShaderID); glDeleteShader(FragmentShaderID); return ProgramID; } void Application::loadShader() { if (programId >= 0) GLCK(glDeleteProgram(programId)) programId = loadProgram("standard"); GLCK(MVPloc = glGetUniformLocation(programId, "MVP")) GLCK(Mloc = glGetUniformLocation(programId, "M")) GLCK(Vloc = glGetUniformLocation(programId, "V")) GLCK( LightPosition_worldspaceloc = glGetUniformLocation(programId, "LightPosition_worldspace")) GLCK(vPositionLoc = glGetAttribLocation(programId, "vPosition")) GLCK(vTexCoordLoc = glGetAttribLocation(programId, "vTexCoord")) GLCK(vNormalLoc = glGetAttribLocation(programId, "vNormal")) GLCK(tAlbedoLoc = glGetAttribLocation(programId, "tAlbedo")) GLCK(tNormalLoc = glGetAttribLocation(programId, "tNormal")) GLCK(tRoughnessLoc = glGetAttribLocation(programId, "tRoughness")) GLCK(tMetalicLoc = glGetAttribLocation(programId, "tMetalic")) } void Application::uploadMesh() { GLCK(glBindVertexArray(vertexArrayId)) GLCK(glBindBuffer(GL_ARRAY_BUFFER, vPositionId)) GLCK( glBufferData(GL_ARRAY_BUFFER, texturemapper.vertices.size() * sizeof(vec3), texturemapper.vertices.data(), GL_STATIC_DRAW)) GLCK(glBindBuffer(GL_ARRAY_BUFFER, vTexCoordId)) GLCK( glBufferData(GL_ARRAY_BUFFER, texturemapper.texcoords.size() * sizeof(glm::vec2), texturemapper.texcoords.data(), GL_STATIC_DRAW)) GLCK(glBindBuffer(GL_ARRAY_BUFFER, vNormalId)) GLCK( glBufferData(GL_ARRAY_BUFFER, texturemapper.normals.size() * sizeof(vec3), texturemapper.normals.data(), GL_STATIC_DRAW)) GLCK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBufferId)) GLCK( glBufferData(GL_ELEMENT_ARRAY_BUFFER, texturemapper.indices.size() * sizeof(uvec3), texturemapper.indices.data(), GL_STATIC_DRAW)) GLCK(glBindVertexArray(0)) glBindTexture(GL_TEXTURE_2D, tAlbedoId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturemapper.getTextureSize(), texturemapper.getTextureSize(), 0, GL_BGR, GL_UNSIGNED_BYTE, texturemapper.albedo.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } void Application::renderMesh() { GLCK(glBindVertexArray(vertexArrayId)) GLCK( glDrawElements(GL_TRIANGLES, texturemapper.indices.size() * 3, GL_UNSIGNED_INT, (void* ) 0)) GLCK(glBindVertexArray(0)) } void Application::prepareShader() { glm::mat4 model(1.0f); glm::mat4 view; if (fpsMode) { glm::vec4 movement(0.0); if (fpsMode) { if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) { movement += glm::vec4(0.0, 0.0, 0.1, 0.0); } else if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) { movement -= glm::vec4(0.0, 0.0, 0.1, 0.0); } } glm::vec3 absolute = glm::transpose(fpsTrafo) * movement; fpsTrafo = glm::translate(fpsTrafo, absolute); view = fpsTrafo; } else { model = glm::rotate(model, (float) glfwGetTime(), vec3(0.f, 1.f, 0.f)); view = glm::lookAt(vec3(0, 0, viewDistance), // Camera is at (4,3,3), in World Space vec3(0, 0, 0), // and looks at the origin vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down) ); } glm::mat4 perspective = glm::perspective(glm::radians(45.0f), (float) width / (float) height, 0.1f, 100.0f); glm::mat4 mvp = perspective * view * model; GLCK(glUseProgram(programId)) if (MVPloc >= 0) { GLCK(glUniformMatrix4fv(MVPloc, 1, GL_FALSE, glm::value_ptr(mvp))) } if (Mloc >= 0) { GLCK(glUniformMatrix4fv(Mloc, 1, GL_FALSE, glm::value_ptr(model))) } if (Vloc >= 0) { GLCK(glUniformMatrix4fv(Vloc, 1, GL_FALSE, glm::value_ptr(view))) } if (LightPosition_worldspaceloc >= 0) { vec3 LightPosition_worldspace(10.0f, 10.0f, 10.0f); GLCK( glUniform3fv(LightPosition_worldspaceloc, 1, glm::value_ptr(LightPosition_worldspace))) } int idx = 0; if (tAlbedoLoc >= 0) { glUniform1i(tAlbedoLoc, idx); glActiveTexture(GL_TEXTURE0 + idx); glBindTexture(GL_TEXTURE_2D, tAlbedoId); idx++; } if (tNormalLoc >= 0) { glUniform1i(tNormalLoc, idx); glActiveTexture(GL_TEXTURE0 + idx); glBindTexture(GL_TEXTURE_2D, tNormalId); idx++; } if (tRoughnessLoc >= 0) { glUniform1i(tRoughnessLoc, idx); glActiveTexture(GL_TEXTURE0 + idx); glBindTexture(GL_TEXTURE_2D, tRoughnessId); idx++; } if (tMetalicLoc >= 0) { glUniform1i(tMetalicLoc, idx); glActiveTexture(GL_TEXTURE0 + idx); glBindTexture(GL_TEXTURE_2D, tMetalicId); idx++; } } void Application::gui() { ImGui::Begin("Mesh Generation"); ImGui::Text("Vertices: %lu, Triangles: %lu", polygonizer.vertices.size(), polygonizer.indices.size()); //ImGui::InputFloat("Resolution", &resolution); ImGui::SliderFloat("Resolution", &resolution, 0.1, 0.001, "%.3f", 0.1); if (ImGui::SliderFloat("Frequency", &densityFrequency, 1, 10, "%.0f", 1)) density.setFrequency(densityFrequency); if (ImGui::SliderFloat("Octave", &densityOctave, 1, 10, "%.0f", 1)) density.setOctave(densityOctave); // if (ImGui::InputFloat("Frequency", &densityFrequency)) // if (ImGui::InputFloat("Octave", &densityOctave)) // density.setOctave(densityOctave); static const char *textureSizesStrings[] = { "512", "1024", "2048", "4096" }; static const int textureSizes[] = { 512, 1024, 2048, 4096 }; static int currentTextureSize = 0; if (ImGui::Combo("Texture Size", ¤tTextureSize, textureSizesStrings, 4, -1)) { texturemapper.setTextureSize(textureSizes[currentTextureSize]); } if (ImGui::Button("Generate")) { generateAsteroid(); uploadMesh(); } ImGui::End(); ImGui::Begin("Rendering"); if (ImGui::Button("Reload Shader")) { loadShader(); updateVertexArrayObject(); } if (ImGui::Button("Wireframe")) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); if (ImGui::Button("Fill")) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); ImGui::End(); ImGui::Begin("Textures"); ImGui::Image((ImTextureID) (intptr_t) tAlbedoId, ImVec2(ImGui::GetWindowContentRegionMax().x, ImGui::GetWindowContentRegionMax().x)); ImGui::End(); } void Application::run() { while (!glfwWindowShouldClose(window)) { glfwPollEvents(); glfwGetFramebufferSize(window, &width, &height); ImGui_ImplGlfwGL3_NewFrame(); gui(); glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); prepareShader(); renderMesh(); ImGui::Render(); glfwSwapBuffers(window); } }