diff --git a/include/application.hpp b/include/application.hpp index 8b6cdcb..207087b 100644 --- a/include/application.hpp +++ b/include/application.hpp @@ -2,6 +2,8 @@ #include "resource_manager.hpp" +#include "gfx.hpp" + #include #include #include @@ -23,7 +25,7 @@ namespace engine { class Application { public: - Application(const char* appName, const char* appVersion); + Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings); ~Application(); Application(const Application&) = delete; Application& operator=(const Application&) = delete; diff --git a/include/gfx.hpp b/include/gfx.hpp index e863760..9511529 100644 --- a/include/gfx.hpp +++ b/include/gfx.hpp @@ -3,8 +3,32 @@ #include #include +// Enums and structs for the graphics abstraction + namespace engine::gfx { + enum class MSAALevel { + MSAA_OFF, + MSAA_2X, + MSAA_4X, + MSAA_8X, + MSAA_16X, + }; + + struct GraphicsSettings { + + GraphicsSettings() + { + // sane defaults + vsync = false; + msaaLevel = MSAALevel::MSAA_OFF; + } + + bool vsync; + MSAALevel msaaLevel; + + }; + enum class ShaderType { VERTEX, FRAGMENT, diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index b0a2d15..047121f 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -11,7 +11,7 @@ namespace engine { class GFXDevice { public: - GFXDevice(const char* appName, const char* appVersion, SDL_Window* window, bool vsync = false); + GFXDevice(const char* appName, const char* appVersion, SDL_Window* window, gfx::GraphicsSettings settings); GFXDevice(const GFXDevice&) = delete; GFXDevice& operator=(const GFXDevice&) = delete; diff --git a/include/logger.hpp b/include/logger.hpp index 497e1ee..96dcfab 100644 --- a/include/logger.hpp +++ b/include/logger.hpp @@ -27,8 +27,10 @@ namespace engine { std::vector sinks; sinks.emplace_back(std::make_shared(log_path.string(), true)); + sinks.back()->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v"); sinks.emplace_back(std::make_shared()); + sinks.back()->set_pattern("[%H:%M:%S.%e] [%l] %v"); auto logger = std::make_shared(appName, sinks.begin(), sinks.end()); @@ -36,6 +38,7 @@ namespace engine { spdlog::register_logger(logger); spdlog::set_default_logger(logger); + spdlog::flush_every(std::chrono::seconds(60)); INFO("Created log with path: {}", log_path.string()); diff --git a/include/systems/render.hpp b/include/systems/render.hpp index aed217d..8c3e1cc 100644 --- a/include/systems/render.hpp +++ b/include/systems/render.hpp @@ -23,8 +23,8 @@ namespace engine { // only uses transform component, which is required for all entities anyway uint32_t camEntity = 0; float verticalFovDegrees = 70.0f; - float clipNear = 0.1f; - float clipFar = 1000.0f; + float clipNear = 0.5f; + float clipFar = 10000.0f; } m_camera; float m_viewportAspectRatio = 1.0f; diff --git a/res/engine/shaders/textured.vert b/res/engine/shaders/textured.vert index a5b5b2c..8943857 100644 --- a/res/engine/shaders/textured.vert +++ b/res/engine/shaders/textured.vert @@ -26,6 +26,6 @@ void main() { fragNorm = mat3(transpose(inverse(constants.view * constants.model))) * inNorm; fragUV = inUV; - vec3 lightPos = vec3(-10.0, 10.0, 10.0); + vec3 lightPos = vec3(2000.0, 2000.0, -2000.0); fragLightPos = vec3(constants.view * vec4(lightPos, 1.0)); } diff --git a/src/application.cpp b/src/application.cpp index c0c4ad0..55a319f 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -56,10 +56,10 @@ static std::filesystem::path getResourcesPath() namespace engine { - Application::Application(const char* appName, const char* appVersion) + Application::Application(const char* appName, const char* appVersion, gfx::GraphicsSettings graphicsSettings) { m_window = std::make_unique(appName, true, false); - m_gfx = std::make_unique(appName, appVersion, m_window->getHandle()); + m_gfx = std::make_unique(appName, appVersion, m_window->getHandle(), graphicsSettings); m_inputManager = std::make_unique(window()); m_sceneManager = std::make_unique(this); diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 9747c61..9d7d662 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -164,7 +164,7 @@ namespace engine { static VkFilter getTextureFilter(gfx::TextureFilter filter) { - switch(filter) { + switch (filter) { case gfx::TextureFilter::LINEAR: return VK_FILTER_LINEAR; case gfx::TextureFilter::NEAREST: @@ -173,6 +173,29 @@ namespace engine { throw std::runtime_error("Unknown texture filter"); } + static VkSampleCountFlags getSampleCountFlags(gfx::MSAALevel level) + { + switch (level) { + case gfx::MSAALevel::MSAA_OFF: + return VK_SAMPLE_COUNT_1_BIT; + break; + case gfx::MSAALevel::MSAA_2X: + return VK_SAMPLE_COUNT_2_BIT; + break; + case gfx::MSAALevel::MSAA_4X: + return VK_SAMPLE_COUNT_4_BIT; + break; + case gfx::MSAALevel::MSAA_8X: + return VK_SAMPLE_COUNT_8_BIT; + break; + case gfx::MSAALevel::MSAA_16X: + return VK_SAMPLE_COUNT_16_BIT; + break; + default: + throw std::runtime_error("Unknown MSAA level"); + } + } + } // functions @@ -486,13 +509,14 @@ namespace engine { vmaDestroyImage(allocator, db.image, db.allocation); } - static VkSampleCountFlagBits getMaxSampleCount(VkPhysicalDevice physicalDevice) + static VkSampleCountFlagBits getMaxSampleCount(VkPhysicalDevice physicalDevice, gfx::MSAALevel maxLevel) { + VkSampleCountFlags max = vkinternal::getSampleCountFlags(maxLevel); VkPhysicalDeviceProperties physicalDeviceProperties; vkGetPhysicalDeviceProperties(physicalDevice, &physicalDeviceProperties); VkSampleCountFlags counts = physicalDeviceProperties.limits.framebufferColorSampleCounts & physicalDeviceProperties.limits.framebufferDepthSampleCounts; - counts %= VK_SAMPLE_COUNT_4_BIT; // restricts it to 2 or 1 (0b11) + counts %= (max << 1); // restricts sample count to maxLevel if (counts & VK_SAMPLE_COUNT_64_BIT) { return VK_SAMPLE_COUNT_64_BIT; } if (counts & VK_SAMPLE_COUNT_32_BIT) { return VK_SAMPLE_COUNT_32_BIT; } if (counts & VK_SAMPLE_COUNT_16_BIT) { return VK_SAMPLE_COUNT_16_BIT; } @@ -503,7 +527,7 @@ namespace engine { } // This is called not just on initialisation, but also when the window is resized. - static void createSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, VmaAllocator allocator, std::vector queues, SDL_Window* window, VkSurfaceKHR surface, bool vsync, bool useMSAA, Swapchain* swapchain) + static void createSwapchain(VkDevice device, VkPhysicalDevice physicalDevice, VmaAllocator allocator, std::vector queues, SDL_Window* window, VkSurfaceKHR surface, gfx::GraphicsSettings settings, Swapchain* swapchain) { [[maybe_unused]] VkResult res; @@ -574,7 +598,7 @@ namespace engine { } swapchain->presentMode = VK_PRESENT_MODE_FIFO_KHR; // This mode is always available - if (vsync == false) { + if (settings.vsync == false) { for (const auto& presMode : presentModes) { if (presMode == VK_PRESENT_MODE_MAILBOX_KHR) { swapchain->presentMode = presMode; // this mode allows uncapped FPS while also avoiding screen tearing @@ -636,11 +660,13 @@ namespace engine { assert(res == VK_SUCCESS); // Use multisample anti-aliasing - if (useMSAA) - swapchain->msaaSamples = getMaxSampleCount(physicalDevice); + if (settings.msaaLevel != gfx::MSAALevel::MSAA_OFF) + swapchain->msaaSamples = getMaxSampleCount(physicalDevice, settings.msaaLevel); else swapchain->msaaSamples = VK_SAMPLE_COUNT_1_BIT; + INFO("Multisampling mode: {}", swapchain->msaaSamples == VK_SAMPLE_COUNT_1_BIT ? "OFF" : std::to_string(swapchain->msaaSamples) + "x"); + // create depth buffer if old depth buffer is wrong size. // Also do the same for the MSAA buffer. if (swapchain->swapchain == VK_NULL_HANDLE) { @@ -678,10 +704,10 @@ namespace engine { colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - if (useMSAA) { - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else { + if (swapchain->msaaSamples == VK_SAMPLE_COUNT_1_BIT) { colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + } else { + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; } colorAttachmentRef.attachment = 0; @@ -718,10 +744,10 @@ namespace engine { subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &colorAttachmentRef; subpass.pDepthStencilAttachment = &depthAttachmentRef; - if (useMSAA) { - subpass.pResolveAttachments = &colorAttachmentResolveRef; - } else { + if (swapchain->msaaSamples == VK_SAMPLE_COUNT_1_BIT) { subpass.pResolveAttachments = nullptr; + } else { + subpass.pResolveAttachments = &colorAttachmentResolveRef; } VkSubpassDependency dependency{}; @@ -736,10 +762,10 @@ namespace engine { VkRenderPassCreateInfo createInfo{}; createInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; - if (useMSAA) { - createInfo.attachmentCount = 3; - } else { + if (swapchain->msaaSamples == VK_SAMPLE_COUNT_1_BIT) { createInfo.attachmentCount = 2; + } else { + createInfo.attachmentCount = 3; } createInfo.pAttachments = attachments.data(); createInfo.subpassCount = 1; @@ -783,11 +809,11 @@ namespace engine { VkFramebufferCreateInfo framebufferInfo{}; framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebufferInfo.renderPass = swapchain->renderpass; - if (useMSAA) { - framebufferInfo.attachmentCount = 3; - } else { + if (swapchain->msaaSamples == VK_SAMPLE_COUNT_1_BIT) { attachments[0] = swapchain->imageViews[i]; framebufferInfo.attachmentCount = 2; + } else { + framebufferInfo.attachmentCount = 3; } framebufferInfo.pAttachments = attachments.data(); framebufferInfo.width = swapchain->extent.width; @@ -1044,7 +1070,9 @@ namespace engine { VmaAllocator allocator = nullptr; // device settings - bool vsync = false; + gfx::GraphicsSettings graphicsSettings; + + // device properties/limits float maxSamplerAnisotropy; // render loop @@ -1059,7 +1087,7 @@ namespace engine { }; - GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window, bool vsync) + GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window, gfx::GraphicsSettings settings) { pimpl = std::make_unique(); @@ -1067,7 +1095,7 @@ namespace engine { VkResult res; pimpl->window = window; - pimpl->vsync = vsync; + pimpl->graphicsSettings = settings; // initialise vulkan @@ -1458,7 +1486,7 @@ namespace engine { // Now make the swapchain - createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, window, pimpl->surface, pimpl->vsync, false, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, window, pimpl->surface, pimpl->graphicsSettings, &pimpl->swapchain); @@ -1587,7 +1615,7 @@ namespace engine { if (res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); - createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, pimpl->window, pimpl->surface, pimpl->vsync, false, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, pimpl->window, pimpl->surface, pimpl->graphicsSettings, &pimpl->swapchain); return; } else { @@ -1720,7 +1748,7 @@ namespace engine { if (res == VK_SUBOPTIMAL_KHR || res == VK_ERROR_OUT_OF_DATE_KHR) { // recreate swapchain waitIdle(); - createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, pimpl->window, pimpl->surface, pimpl->vsync, false, &pimpl->swapchain); + createSwapchain(pimpl->device, pimpl->physicalDevice, pimpl->allocator, pimpl->queues, pimpl->window, pimpl->surface, pimpl->graphicsSettings, &pimpl->swapchain); } else { assert(res == VK_SUCCESS); diff --git a/src/systems/collisions.cpp b/src/systems/collisions.cpp index 6a59e22..917dc24 100644 --- a/src/systems/collisions.cpp +++ b/src/systems/collisions.cpp @@ -7,6 +7,8 @@ #include "log.hpp" +#include + namespace engine { // static functions diff --git a/test/src/camera_controller.cpp b/test/src/camera_controller.cpp index f74721e..d1f7b62 100644 --- a/test/src/camera_controller.cpp +++ b/test/src/camera_controller.cpp @@ -115,7 +115,7 @@ void CameraControllerSystem::onUpdate(float ts) t->position += hVel * dt; t->position.y += c->dy * dt; - constexpr float MAX_DISTANCE_FROM_ORIGIN = 1000.0f; + constexpr float MAX_DISTANCE_FROM_ORIGIN = 10000.0f; if (glm::length(t->position) > MAX_DISTANCE_FROM_ORIGIN) { t->position = { 0.0f, 5.0f, 0.0f }; diff --git a/test/src/game.cpp b/test/src/game.cpp index 026b063..6bed691 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -40,7 +40,10 @@ static void configureInputs(engine::InputManager* inputManager) void playGame(bool enableFrameLimiter) { - engine::Application app(PROJECT_NAME, PROJECT_VERSION); + engine::gfx::GraphicsSettings graphicsSettings{}; + graphicsSettings.vsync = false; + graphicsSettings.msaaLevel = engine::gfx::MSAALevel::MSAA_16X; + engine::Application app(PROJECT_NAME, PROJECT_VERSION, graphicsSettings); app.setFrameLimiter(enableFrameLimiter); @@ -67,7 +70,8 @@ void playGame(bool enableFrameLimiter) engine::EventSubscriberKind::ENTITY, camera, myScene->getSystem() ); - myScene->getSystem()->setCameraEntity(camera); + auto renderSystem = myScene->getSystem(); + renderSystem->setCameraEntity(camera); } /* shared resources */ @@ -113,21 +117,34 @@ void playGame(bool enableFrameLimiter) enemyCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10.0f, 10.0f, 10.0f } }; // A box enclosing the sphere } + /* sun */ + { + uint32_t sun = myScene->createEntity("sun"); + auto sunRenderable = myScene->addComponent(sun); + sunRenderable->material = std::make_unique(app.getResource("engine.textured")); + sunRenderable->material->m_texture = app.getResource("engine.white"); + sunRenderable->mesh = genSphereMesh(app.gfx(), 500.0f, 32, false, true); + auto sunTransform = myScene->getComponent(sun); + sunTransform->position.x = 2000.0f; + sunTransform->position.y = 2000.0f; + sunTransform->position.z = -2000.0f; + } + /* floor */ { uint32_t floor = myScene->createEntity("floor"); - myScene->getComponent(floor)->position = glm::vec3{-50.0f, -0.1f, -50.0f}; + myScene->getComponent(floor)->position = glm::vec3{-5000.0f, -1.0f, -5000.0f}; auto floorRenderable = myScene->addComponent(floor); floorRenderable->material = std::make_shared(app.getResource("engine.textured")); floorRenderable->material->m_texture = grassTexture; - floorRenderable->mesh = genCuboidMesh(app.gfx(), 100.0f, 0.1f, 100.0f, 128.0f); + floorRenderable->mesh = genCuboidMesh(app.gfx(), 10000.0f, 1.0f, 10000.0f, 5000.0f); auto floorCollider = myScene->addComponent(floor); floorCollider->isStatic = true; - floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 100.0f, 0.1f, 100.0f } }; + floorCollider->aabb = { { 0.0f, 0.0f, 0.0f }, { 10000.0f, 1.0f, 10000.0f } }; } // cubes! - if (0) { + { constexpr int SIZE = 10; const uint32_t cubeParent = myScene->createEntity("cubeParent"); @@ -147,6 +164,11 @@ void playGame(bool enableFrameLimiter) auto transform = myScene->getComponent(cube); auto renderable = myScene->addComponent(cube); + auto collider = myScene->addComponent(cube); + collider->aabb.pos1 = { 0.0f, 0.0f, 0.0f }; + collider->aabb.pos2 = { 10.0f, 10.0f, 10.0f }; + collider->isStatic = true; + collider->isTrigger = false; transform->position = { (float)x, (float)y, (float)z }; renderable->mesh = cubeMesh;