#include #include #include #include #include #include #include #include #include #include #define GLM_FORCE_RADIANS #include #include #include #include #include #include "common.hh" #include "shaders.hh" #include "texture.hh" #include "VBOManager.hh" #include "objectParser.hh" #include "Object.hh" #include "font.hh" #include "Overlay.hh" #include "TextWidget.hh" #include "LinearLayout.hh" #include "GUILoader.hh" #include "FontProvider.hh" #include "ImageProvider.hh" using namespace gl; void glAfterCallback(glbinding::FunctionCall const& fc) { glbinding::setCallbackMask(glbinding::CallbackMask::None); GLenum error = glGetError(); glbinding::setCallbackMask(glbinding::CallbackMask::After); if (error != GL_NO_ERROR) throw GLException(error); } int main(int argc, char *argv[]) { SDL_Window *window; int retcode = 0; glbinding::Binding::initialize(); if (SDL_Init(SDL_INIT_VIDEO) < 0) { std::printf("Could not init SDL: %s\n", SDL_GetError()); return 1; } if (IMG_Init(IMG_INIT_PNG | IMG_INIT_JPG) == 0) { std::printf("Could not init SDL_image: %s\n", SDL_GetError()); SDL_Quit(); return 1; } if(TTF_Init() == -1) { printf("Could not init SDL_ttf: %s\n", TTF_GetError()); IMG_Quit(); SDL_Quit(); return 1; } SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); window = SDL_CreateWindow("SDL2 Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1680, 1050, /*SDL_WINDOW_FULLSCREEN_DESKTOP |*/ SDL_WINDOW_OPENGL); if (!window) { std::printf("Could not create window: %s\n", SDL_GetError()); TTF_Quit(); IMG_Quit(); SDL_Quit(); return 1; } SDL_GLContext glcontext = SDL_GL_CreateContext(window); if (!glcontext) { std::printf("Could not create GL context: %s\n", SDL_GetError()); SDL_DestroyWindow(window); TTF_Quit(); IMG_Quit(); SDL_Quit(); return 1; } // Clear GL errors while(glGetError() != GL_NO_ERROR); try { glbinding::setCallbackMask(glbinding::CallbackMask::After); glbinding::setAfterCallback(glAfterCallback); { int depth, stencil, aa, major, minor; SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth); SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil); SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &aa); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &major); SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &minor); std::printf("Depth: %d, Stencil: %d, AA: %d, GL %d.%d\n", depth, stencil, aa, major, minor); } int width, height; SDL_GetWindowSize(window, &width, &height); glm::mat4 proj = glm::perspectiveFov(75.0f, static_cast(width), static_cast(height), 0.1f, 100.0f); glm::mat4 view = glm::lookAt(glm::vec3(2.0f, 2.0f, 10), glm::vec3(0.0f, 0.0f, 0), glm::vec3(0, 1, 0)); //Font font{"DejaVuSans.ttf", 12}; // sf::Font font; // if (!font.loadFromFile("DejaVuSans.ttf")) { // std::printf("Error loading font\n"); // return 1; // } // sf::Text fpsText{"0", font, 12}; // fpsText.setPosition({200, 200}); // window.setFramerateLimit(60); // sf::Clock clock; // sf::Time last = clock.getElapsedTime(); auto last = SDL_GetTicks(); Texture2D cubeTex("textures/Wood_Box_Texture.jpg"); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, static_cast(GL_LINEAR_MIPMAP_LINEAR)); if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic")) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 4.0f); else std::printf("Warning: extension GL_EXT_texture_filter_anisotropic not supported\n"); Texture2D redTex("textures/red.png"); Texture2D whiteTex("textures/white.png"); const unsigned lights = 2; const unsigned shadowMapSize = 512; glm::mat4 shadowProj = glm::perspectiveFov(90.0f, static_cast(shadowMapSize), static_cast(shadowMapSize), 1.0f, 128.0f); //glm::mat4 shadowProj = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, 1.0f, 128.0f); std::vector shadowMaps; for(unsigned i = 0;i < lights;++i) { shadowMaps.emplace_back(shadowMapSize); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, static_cast(GL_LESS)); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, static_cast(GL_COMPARE_REF_TO_TEXTURE)); } const glm::vec3 lightPos[lights] = {glm::vec3(2.0, -2.0, 10.0), glm::vec3(2.0, 2.0, 10.0)}; // const glm::vec3 lightColor[lights] = {glm::vec3(1.0, 0.9, 0.8), // glm::vec3(0.0, 1.0, 0.0)}; // const float lightIntensity[lights] = {75.0f, 25.0f}; glm::mat4 ovlProj = glm::ortho(0.0f, static_cast(width), static_cast(height), 0.0f); Framebuffer shadowFB; VertexShader vs{fileToString("shaders/textured.vs")}; FragmentShader fs{fileToString("shaders/textured.fs")}; Program prog{vs, fs}; VertexShader shadowVs{fileToString("shaders/shadow.vs")}; FragmentShader shadowFs{fileToString("shaders/shadow.fs")}; Program shadowProg{shadowVs, shadowFs}; VertexShader ovlVs{fileToString("shaders/overlay.vs")}; FragmentShader ovlFs{fileToString("shaders/overlay.fs")}; Program ovlProg{ovlVs, ovlFs}; prog.use(); glUniformMatrix4fv(prog.getUniformLocation("projection_matrix"), 1, GL_FALSE, glm::value_ptr(proj)); glUniformMatrix4fv(prog.getUniformLocation("view_matrix"), 1, GL_FALSE, glm::value_ptr(view)); glUniform1i(prog.getUniformLocation("texBase"), 0); const GLint shadowMapTUs[] = {1, 2}; glUniform1iv(prog.getUniformLocation("texShadowMaps"), 2, shadowMapTUs); shadowProg.use(); glUniformMatrix4fv(shadowProg.getUniformLocation("projection_matrix"), 1, GL_FALSE, glm::value_ptr(shadowProj)); ovlProg.use(); glUniformMatrix4fv(ovlProg.getUniformLocation("projection_matrix"), 1, GL_FALSE, glm::value_ptr(ovlProj)); glUniform1i(ovlProg.getUniformLocation("texBase"), 0); Object box("objects/woodbox.obj"); Object pyramid("objects/pyramid.obj"); Object plane("objects/plane.obj"); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); bool close = false; TTF_Font * font = FontProvider::getInstance().getFont("DejaVuSans.ttf", 12); unsigned fpsTime = 0, fpsCount = 0; TextWidget fpsText{font, "0 FPS", WRAP_CONTENT, WRAP_CONTENT, "fpsText"}; fpsText.setBackgroundColor(SDL_Color{0, 255, 0, 128}); fpsText.setPadding(8, 8, 8, 8); auto laber = make_unique(font, "Dies ist ein längerer\nText, der umgebrochen werden sollte. Bla laber schwafl, laber bullshit bingo", 204, WRAP_CONTENT, "laber"); laber->setBackgroundColor(SDL_Color{255, 0, 0, 128}); SDLSurfaceUPtr buttonImg(IMG_Load("textures/button_100x30.png")); if (!buttonImg) throw SDLException(); auto button = make_unique(font, "Do stuff!", 100, 30, "button"); button->setPadding(6, 6, 6, 6); button->setBackground(buttonImg.get()); button->setForegroundColor(SDL_Color{0, 0, 0, 255}); button->setAlignment(ALIGN_CENTER, ALIGN_CENTER); auto button2 = make_unique(font, "Cancel", 100, 30, "button2"); button2->setPadding(6, 6, 6, 6); button2->setBackground(buttonImg.get()); button2->setForegroundColor(SDL_Color{0, 0, 0, 255}); button2->setAlignment(ALIGN_CENTER, ALIGN_CENTER); auto layout2 = make_unique(DIR_HORIZONTAL, MATCH_PARENT, WRAP_CONTENT, "layout2"); layout2->setBackgroundColor(SDL_Color{0, 255, 0, 128}); layout2->setChildPadding(4); layout2->addChild(std::move(button2)); layout2->addChild(std::move(button)); auto layout = make_unique(DIR_VERTICAL, WRAP_CONTENT, WRAP_CONTENT, "layout"); layout->setBackgroundColor(SDL_Color{0, 255, 255, 128}); layout->addChild(std::move(laber)); layout->addChild(std::move(layout2)); layout->setChildPadding(4); layout->setPadding(4, 4, 4, 4); layout->setGLRenderPos(200, 200); auto gui = loadGUIFromFile("layouts/test.xml"); if (!gui) throw Exception{"GUI load failed"}; View& guiRoot = dynamic_cast(*gui); gui->setGLRenderPos(200, 500); TextWidget& xmlButton = dynamic_cast(guiRoot.getPath("layout2/button")); int countdown = 5; xmlButton.setText(std::to_string(countdown)); while (!close) { SDL_Event event; while (SDL_PollEvent(&event)) { switch(event.type) { case SDL_KEYDOWN: close = true; break; case SDL_WINDOWEVENT: if (event.window.event == SDL_WINDOWEVENT_CLOSE) close = true; break; } } // Draw shadow maps GLint origVP[4]; glGetIntegerv(GL_VIEWPORT, origVP); glViewport(0, 0, shadowMapSize, shadowMapSize); shadowProg.use(); shadowFB.bind(); glPolygonOffset(1.1f, 4.0f); glEnable(GL_POLYGON_OFFSET_FILL); glEnable(GL_POLYGON_OFFSET_LINE); glEnable(GL_POLYGON_OFFSET_POINT); for (unsigned light = 0;light < lights;++light) { for (unsigned i = 0;i < 6;++i) { GLenum const face[6] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}; const glm::vec3 dir[6] = {glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, -1.0f)}; const glm::vec3 up[6] = {glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)}; shadowMaps[light].bind(); glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, face[i], shadowMaps[light].getID(), 0); glClear(GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glm::mat4 view = glm::lookAt(lightPos[light], lightPos[light]+dir[i], up[i]); glUniformMatrix4fv(prog.getUniformLocation("view_matrix"), 1, GL_FALSE, glm::value_ptr(view)); glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) * glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f)); glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(model)); box.draw(); model = glm::translate(glm::vec3(2.0f, -2.5f, 0.0f)) * glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f)); glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(model)); plane.draw(); model = glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f)); glUniformMatrix4fv(shadowProg.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(model)); pyramid.draw(); } } GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, UnusedMask()); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); glViewport(origVP[0], origVP[1], origVP[2], origVP[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glDisable(GL_POLYGON_OFFSET_FILL); glDisable(GL_POLYGON_OFFSET_LINE); glDisable(GL_POLYGON_OFFSET_POINT); glActiveTexture(GL_TEXTURE2); shadowMaps[1].bind(); glActiveTexture(GL_TEXTURE1); shadowMaps[0].bind(); glActiveTexture(GL_TEXTURE0); glm::mat4 model = glm::translate(glm::vec3(0.5f, 0.5f, -0.5f)) * glm::rotate(SDL_GetTicks()*0.001f, glm::vec3(1.0f, 0.0f, 0.0f)) * glm::translate(glm::vec3(-0.5f, -0.5f, 0.5f)); cubeTex.bind(); prog.use(); glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(model)); // Shadow maps must be rendered before real drawing can begin glClientWaitSync(fence, SyncObjectMask(), 0xffffffffu); glDeleteSync(fence); box.draw(); whiteTex.bind(); model = glm::translate(glm::vec3(2.0f, -2.5f, 0.0f)) * glm::rotate(0.35f, glm::vec3(0.0f, 1.0f, 0.0f)); glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(model)); plane.draw(); redTex.bind(); model = glm::translate(glm::vec3(-2.0f, 0.0f, 0.0f)); glUniformMatrix4fv(prog.getUniformLocation("model_matrix"), 1, GL_FALSE, glm::value_ptr(model)); pyramid.draw(); auto now = SDL_GetTicks(); auto elapsed = now - last; last = now; if (fpsTime+elapsed > 1000) { fpsText.setText(std::to_string(fpsCount + 1) + " FPS"); fpsCount = 0; fpsTime = 0; if(countdown > 0) { --countdown; if (countdown > 0) xmlButton.setText(std::to_string(countdown)); else xmlButton.setText("Do stuff!"); } } else { ++fpsCount; fpsTime += elapsed; } glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); ovlProg.use(); fpsText.renderToGL(); layout->renderToGL(); gui->renderToGL(); glDisable(GL_BLEND); SDL_GL_SwapWindow(window); } }catch(Exception &ex) { std::printf("%s\n", ex.toString().c_str()); retcode = 1; } SDL_GL_DeleteContext(glcontext); SDL_DestroyWindow(window); FontProvider::getInstance().cleanup(); ImageProvider::getInstance().cleanup(); // Clean up TTF_Quit(); IMG_Quit(); SDL_Quit(); return retcode; }