#include "Application.h" #include "Mesh.h" #include "Shader.h" #include "gl.h" #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; 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 cursor_callback(GLFWwindow* window, double x, double y) { Application::instance().CursorCallback(window, x, y); } 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), meteoridCount(1000), meteroidMinRadius( 0.005), meteroidMaxRadius(0.2), meteroidSizeExponent(2048), densityFrequency( 2), densityOctave(2), densityScale(1.0f), resolution(0.1), smoothMesh( true), viewDistance(3.0f), fpsMode(false), meshUploadRequest( false), textureUploadRequest(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("assets/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); glfwSetCursorPosCallback(window, cursor_callback); glfwSetScrollCallback(window, scroll_callback); glfwSetKeyCallback(window, key_callback); glfwSetCharCallback(window, char_callback); glfwMakeContextCurrent(window); gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); glfwSwapInterval(1); glfwGetCursorPos(window, &lastMouseX, &lastMouseY); 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(glGenTextures(1, &tAlbedoId)) GLCK(glGenTextures(1, &tNormalId)) GLCK(glGenTextures(1, &tRoughnessId)) GLCK(glGenTextures(1, &tMetalicId)) loadShader(); glMesh.create(); glMesh.update(programId); generateAsteroid(); skybox.Load("background"); } void Application::shutdown() { ImGui_ImplGlfwGL3_Shutdown(); 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::CursorCallback(GLFWwindow* window, double x, double y) { } 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() { if (meshProgress.begin("Random Impacts", 1)) { if (density.getImpacts().size() == 0) density.addRandomImpacts(1000, densityIsolevel, 0.01, 0.005, 0.2, 256); //density.saveCrossSection("density.png", 512); } polygonizer.polygonize(resolution, meshProgress); if (smoothMesh && meshProgress.begin("Smooth", 1)) { polygonizer.mesh.smooth(); // { // std::ofstream out("stage2.obj"); // saveAttrib(out, "v ", polygonizer.vertices); // saveFaces(out, polygonizer.indices, 1); // } } if (meshProgress.begin("Generate Edges", 1)) { polygonizer.mesh.generateEdges(); } if (meshProgress.begin("Generate Adjacent", 1)) { polygonizer.mesh.generateAdjacent(); } if (meshProgress.begin("Generate Face Normals", 1)) { polygonizer.mesh.generateFaceNormals(); } if (meshProgress.begin("Generate Vertex Normals", 1)) { polygonizer.mesh.generateNormals(); } if (meshProgress.begin("Generate Patched", 1)) { mesh = polygonizer.mesh.createPatched(0.8); } if (meshProgress.begin("Save Patched", 1)) { mesh.save("patched.obj"); } if (meshProgress.begin("Map Textures", 1)) { texturemapper.map(mesh.positions, mesh.normals, mesh.indices); } if (meshProgress.begin("Move To Mean", 1)) { mesh.moveToMean(); } if (meshProgress.begin("Save", 4)) { mesh.save("final.obj"); } meshProgress.finish(); if (!meshProgress.isCanceled()) { meshUploadRequest = true; textureUploadRequest = true; } } 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(tAlbedoLoc = glGetAttribLocation(programId, "tAlbedo")) GLCK(tNormalLoc = glGetAttribLocation(programId, "tNormal")) GLCK(tRoughnessLoc = glGetAttribLocation(programId, "tRoughness")) GLCK(tMetalicLoc = glGetAttribLocation(programId, "tMetalic")) } void Application::uploadTexture() { printf("upload texture\n"); glBindTexture(GL_TEXTURE_2D, tAlbedoId); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texturemapper.getTextureSize(), texturemapper.getTextureSize(), 0, GL_RGBA, 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() { glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); glMesh.render(); } void Application::updateMatrices() { double mouseX, mouseY; glfwGetCursorPos(window, &mouseX, &mouseY); if (fpsMode) { glm::mat4 fpsTrafoInv = glm::inverse(fpsTrafo); glm::vec4 movement(0.0); 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); } if (glfwGetMouseButton(window, 0) == GLFW_PRESS) { glm::vec3 up = fpsTrafoInv * glm::vec4(0.0, 1.0, 0.0, 0.0); glm::vec3 side = fpsTrafoInv * glm::vec4(1.0, 0.0, 0.0, 0.0); glm::vec3 front = fpsTrafoInv * glm::vec4(0.0, 0.0, 1.0, 0.0); fpsTrafo = glm::rotate(fpsTrafo, (float) (mouseX - lastMouseX) * 0.01f, up); fpsTrafo = glm::rotate(fpsTrafo, (float) (mouseY - lastMouseY) * 0.01f, side); } //fpsTrafo = glm::rotate(fpsTrafo, mouseX - lastMouseX, up); glm::vec3 absolute = fpsTrafoInv * movement; fpsTrafo = glm::translate(fpsTrafo, absolute); view = fpsTrafo; } else { if (!ImGui::IsMouseHoveringAnyWindow() && glfwGetMouseButton(window, 0) == GLFW_PRESS) { glm::mat4 modelTrafoInv = glm::inverse(modelTrafo); modelTrafo = glm::rotate(modelTrafo, (float) (mouseX - lastMouseX) * 0.01f, vec3(modelTrafoInv * glm::vec4(0.0, 1.0, 0.0, 0.0))); modelTrafo = glm::rotate(modelTrafo, (float) (mouseY - lastMouseY) * 0.01f, vec3(modelTrafoInv * glm::vec4(1.0, 0.0, 0.0, 0.0))); } //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) ); } lastMouseX = mouseX; lastMouseY = mouseY; perspective = glm::perspective(glm::radians(45.0f), (float) width / (float) height, 0.1f, 100.0f); mvp = perspective * view * modelTrafo; } void Application::prepareShader() { 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(modelTrafo))) } 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", mesh.positions.size(), mesh.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); ImGui::Checkbox("Smooth", &smoothMesh); // 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 (meshProgress.isRunning()) { if (ImGui::Button("Cancel")) meshProgress.cancel(); ImGui::Text("Task: %s", meshProgress.getTask().c_str()); ImGui::ProgressBar(meshProgress.getProgress()); } else { if (meshProgress.isFinished() && meshThread.joinable()) meshThread.join(); if (ImGui::Button("Generate")) meshThread = std::thread(&Application::generateAsteroid, this); } ImGui::End(); ImGui::Begin("Rendering"); if (ImGui::Button("Reload Shader")) { loadShader(); glMesh.update(programId); } 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); if (meshUploadRequest) { glMesh.upload(mesh); meshUploadRequest = false; } if (textureUploadRequest) { uploadTexture(); textureUploadRequest = false; } updateMatrices(); prepareShader(); renderMesh(); skybox.Render(mvp); ImGui::Render(); glfwSwapBuffers(window); } }