#include "Application.h" #include "Mesh.h" #include "Shader.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; 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 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), 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), 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("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(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(); } 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::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)) { smooth(polygonizer.vertices, polygonizer.indices); // { // std::ofstream out("stage2.obj"); // saveAttrib(out, "v ", polygonizer.vertices); // saveFaces(out, polygonizer.indices, 1); // } } uvec4v_t edges; if (meshProgress.begin("Find Edges", 1)) { findEdges(polygonizer.vertices, polygonizer.indices, edges); } uvec3v_t adjacents; if (meshProgress.begin("Find Adjacent", 1)) { findAdjacent(edges, polygonizer.indices.size(), adjacents); } vec3v_t faceNormals; calculateFaceNormals(polygonizer.vertices, polygonizer.indices, faceNormals); vec3v_t positions; uvec3v_t indices; uintv_t patches; createPatches(polygonizer.vertices, polygonizer.indices, faceNormals, adjacents, positions, indices, patches, 0.9); { std::ofstream out("stage1.obj"); saveAttrib(out, "v ", positions); saveFaces(out, indices, 1); } if (meshProgress.begin("Calculate Vertex Normals", 1)) { calculateVertexNormals(polygonizer.vertices, polygonizer.indices, normals); // { // std::ofstream out("stage3.obj"); // saveAttrib(out, "v ", polygonizer.vertices); // saveAttrib(out, "vn ", normals); // saveFaces(out, polygonizer.indices, 2); // } } std::cout << "Vertices: " << polygonizer.vertices.size() << std::endl; std::cout << "Triangles: " << polygonizer.indices.size() << std::endl; if (meshProgress.begin("Map Textures", 1)) { texturemapper.map(polygonizer.vertices, normals, polygonizer.indices); } if (meshProgress.begin("Move To Mean", 1)) { 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); // } } if (meshProgress.begin("Save", 4)) { std::ofstream out("asteroid.obj"); saveAttrib(out, "v ", texturemapper.vertices); meshProgress.advance(); saveAttrib(out, "vn ", texturemapper.normals); meshProgress.advance(); saveAttrib(out, "vt ", texturemapper.texcoords); meshProgress.advance(); saveFaces(out, texturemapper.indices, 3); meshProgress.advance(); } meshProgress.finish(); if (!meshProgress.isCanceled()) { meshUploadRequest = true; textureUploadRequest = true; } } 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)) } 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() { printf("upload mesh\n"); 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)) } 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() { GLCK(glBindVertexArray(vertexArrayId)) GLCK( glDrawElements(GL_TRIANGLES, texturemapper.indices.size() * 3, GL_UNSIGNED_INT, (void* ) 0)) GLCK(glBindVertexArray(0)) } void Application::prepareShader() { glm::mat4 view; 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; glm::mat4 perspective = glm::perspective(glm::radians(45.0f), (float) width / (float) height, 0.1f, 100.0f); glm::mat4 mvp = perspective * view * modelTrafo; 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", 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); 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(); 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); if (meshUploadRequest) { uploadMesh(); meshUploadRequest = false; } if (textureUploadRequest) { uploadTexture(); textureUploadRequest = false; } prepareShader(); renderMesh(); ImGui::Render(); glfwSwapBuffers(window); } }