From 707396f38401c7d72057c578a43b7f9fbe74b339 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 21 Dec 2018 06:32:37 -0500 Subject: [PATCH] Add level editor --- CMakeLists.txt | 28 ++++++----- include/ecs.h | 36 +++++++------- include/renderer.h | 3 ++ src/renderer.cpp | 19 ++++--- src/smaapass.cpp | 25 +++++----- tools/CMakeLists.txt | 1 + tools/leveleditor/CMakeLists.txt | 15 ++++++ tools/leveleditor/include/context.h | 7 +++ tools/leveleditor/include/mainwindow.h | 10 ++++ tools/leveleditor/include/renderwindow.h | 29 +++++++++++ tools/leveleditor/src/main.cpp | 63 ++++++++++++++++++++++++ tools/leveleditor/src/mainwindow.cpp | 20 ++++++++ tools/leveleditor/src/renderwindow.cpp | 55 +++++++++++++++++++++ 13 files changed, 263 insertions(+), 48 deletions(-) create mode 100644 tools/CMakeLists.txt create mode 100644 tools/leveleditor/CMakeLists.txt create mode 100644 tools/leveleditor/include/context.h create mode 100644 tools/leveleditor/include/mainwindow.h create mode 100644 tools/leveleditor/include/renderwindow.h create mode 100644 tools/leveleditor/src/main.cpp create mode 100644 tools/leveleditor/src/mainwindow.cpp create mode 100644 tools/leveleditor/src/renderwindow.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 803b21d..61290b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,8 +44,7 @@ IMPORTED_LOCATION ${ASSIMP_IMPORTED_PATH}) INTERFACE_INCLUDE_DIRECTORIES ${ASSIMP_INCLUDE_DIRS}) endif() -set(GRAPH_SRC - src/main.cpp +set(ENGINE_SRC src/renderer.cpp src/worldpass.cpp src/postpass.cpp @@ -61,18 +60,17 @@ set(GRAPH_SRC src/smaapass.cpp) if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(GRAPH_SRC - ${GRAPH_SRC} + set(ENGINE_SRC + ${ENGINE_SRC} src/imguipass.cpp) endif() -add_executable(Graph - ${GRAPH_SRC}) - -target_compile_options(Graph +add_library(Engine + ${ENGINE_SRC}) +target_compile_options(Engine PUBLIC -fno-exceptions) -target_link_libraries(Graph +target_link_libraries(Engine PUBLIC SDL2::SDL2 Vulkan::Vulkan @@ -82,17 +80,23 @@ target_link_libraries(Graph SMAA::SMAA) if(CMAKE_BUILD_TYPE STREQUAL "Debug") - target_link_libraries(Graph + target_link_libraries(Engine PUBLIC imgui::imgui) endif() -target_include_directories(Graph +target_include_directories(Engine PUBLIC include PRIVATE ${GLM_INCLUDE_DIRS}) +add_executable(Graph + src/main.cpp) +target_link_libraries(Graph + PRIVATE + Engine) + add_shaders(Graph shaders/mesh.vert shaders/mesh.frag @@ -121,3 +125,5 @@ add_data(Graph data/test.material data/empty.world data/player.obj) + +add_subdirectory(tools) diff --git a/include/ecs.h b/include/ecs.h index fb92a93..582815d 100644 --- a/include/ecs.h +++ b/include/ecs.h @@ -30,7 +30,7 @@ enum class LightType { struct LightComponent { LightType type = LightType::Point; glm::vec3 color = glm::vec3(1); - + glm::mat4 matrix = glm::mat4(1.0f); }; @@ -45,28 +45,28 @@ using EntityID = uint64_t; struct World; -namespace ECS { +namespace ECS { // components inline std::map infos; inline std::map transforms; inline std::map meshes; inline std::map lights; inline std::map cameras; - + inline std::map worlds; inline EntityID lastID = 1; - + static inline EntityID createEntity(World* world) { EntityID newID = lastID++; worlds[newID] = world; - + return newID; } - + static inline void destroyEntity(const EntityID id) { - + } - + template static inline T* addComponent(const EntityID id) { T* t = new T(); @@ -81,10 +81,10 @@ namespace ECS { } else if constexpr(std::is_same::value) { cameras[id] = t; } - + return t; } - + template static inline T* getComponent(const EntityID id) { if constexpr(std::is_same::value) { @@ -99,36 +99,36 @@ namespace ECS { return cameras[id]; } } - + template - static inline auto getWorldComponents(World* world) { + static inline auto getWorldComponents(World* world) { std::vector entities; - for (auto it = worlds.begin(); it != worlds.end(); ++it) { + for (auto it = worlds.begin(); it != worlds.end(); ++it) { if(it->second == world) { entities.push_back(it->first); } } - + std::vector> components; for(auto ent : entities) { T* t = getComponent(ent); if(t != nullptr) components.push_back(std::tuple(ent, t)); } - + return components; } - + static inline std::vector getWorldEntities(World* world) { std::vector entities; for (auto it = worlds.begin(); it != worlds.end(); ++it) { if(it->second == world) entities.push_back(it->first); } - + return entities; } - + template static inline void removeComponent(const EntityID id) { delete transforms[id]; diff --git a/include/renderer.h b/include/renderer.h index 49633c3..56bbc3c 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -85,6 +85,9 @@ struct RenderTarget { struct GraphicsConfig { int shadowResolution, dofDownscale; bool vsync = true, filterPCF; +#ifdef DEBUG + bool enableImgui = true; +#endif }; struct World; diff --git a/src/renderer.cpp b/src/renderer.cpp index 777885e..4d2235f 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -33,7 +33,8 @@ Renderer::Renderer(GraphicsConfig config) : config_(config) { postPass_ = new PostPass(*this); dofPass_ = new DoFPass(*this); #ifdef DEBUG - imguiPass_ = new ImGuiPass(*this); + if(config.enableImgui) + imguiPass_ = new ImGuiPass(*this); #endif skyPass_ = new SkyPass(*this); smaaPass_ = new SMAAPass(*this); @@ -45,7 +46,8 @@ Renderer::~Renderer() { delete smaaPass_; delete skyPass_; #ifdef DEBUG - delete imguiPass_; + if(config_.enableImgui) + delete imguiPass_; #endif delete dofPass_; delete postPass_; @@ -168,7 +170,8 @@ void Renderer::render(World& world, RenderTarget* target) { postPass_->render(commandBuffer, target); #ifdef DEBUG - imguiPass_->render(commandBuffer, target); + if(config_.enableImgui) + imguiPass_->render(commandBuffer, target); #endif vkCmdEndRenderPass(commandBuffer); @@ -729,6 +732,8 @@ void Renderer::destroyRenderTarget(RenderTarget* target) { delete[] target->commandBuffers; + vkFreeDescriptorSets(device_, descriptorPool_, target->numImages, target->blendDescriptorSets); + vkFreeDescriptorSets(device_, descriptorPool_, target->numImages, target->edgeDescriptorSets); vkFreeDescriptorSets(device_, descriptorPool_, target->numImages, target->dofSets); vkFreeDescriptorSets(device_, descriptorPool_, target->numImages, target->postSets); @@ -799,6 +804,8 @@ void Renderer::destroyRenderTarget(RenderTarget* target) { delete[] target->swapchainImageViews; delete[] target->swapchainImages; + delete[] target->blendDescriptorSets; + delete[] target->edgeDescriptorSets; delete[] target->dofSets; delete[] target->postSets; @@ -1512,8 +1519,8 @@ void Renderer::createPresentationRenderPass() { void Renderer::createDescriptorPool() { const std::array poolSizes = { - VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 25}, - VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 25} + VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 50}, + VkDescriptorPoolSize{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 50} }; VkDescriptorPoolCreateInfo poolInfo = {}; @@ -1521,7 +1528,7 @@ void Renderer::createDescriptorPool() { poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; poolInfo.poolSizeCount = poolSizes.size(); poolInfo.pPoolSizes = poolSizes.data(); - poolInfo.maxSets = 25; + poolInfo.maxSets = 50; vkCreateDescriptorPool(device_, &poolInfo, nullptr, &descriptorPool_); } diff --git a/src/smaapass.cpp b/src/smaapass.cpp index 0845852..8b76cfc 100644 --- a/src/smaapass.cpp +++ b/src/smaapass.cpp @@ -62,8 +62,7 @@ void SMAAPass::render(VkCommandBuffer commandBuffer, RenderTarget* target) { renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = edgeRenderPass; renderPassInfo.framebuffer = target->edgeFramebuffers[target->currentResource]; - renderPassInfo.renderArea.extent.width = 1280; - renderPassInfo.renderArea.extent.height = 720; + renderPassInfo.renderArea.extent = target->extent; renderPassInfo.clearValueCount = 1; renderPassInfo.pClearValues = &clearValue; @@ -410,21 +409,10 @@ void SMAAPass::createPipelines() { inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; inputAssembly.primitiveRestartEnable = VK_FALSE; - VkViewport viewport = {}; - viewport.width = 1280; - viewport.height = 720; - viewport.maxDepth = 1.0f; - - VkRect2D scissor = {}; - scissor.extent.width = 1280; - scissor.extent.height = 720; - VkPipelineViewportStateCreateInfo viewportState = {}; viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewportState.viewportCount = 1; - viewportState.pViewports = &viewport; viewportState.scissorCount = 1; - viewportState.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer = {}; rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; @@ -451,6 +439,16 @@ void SMAAPass::createPipelines() { colorBlending.attachmentCount = 1; colorBlending.pAttachments = &colorBlendAttachment; + const std::array dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicState = {}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = dynamicStates.size(); + dynamicState.pDynamicStates = dynamicStates.data(); + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutInfo.setLayoutCount = 1; @@ -468,6 +466,7 @@ void SMAAPass::createPipelines() { pipelineInfo.pRasterizationState = &rasterizer; pipelineInfo.pMultisampleState = &multisampling; pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; pipelineInfo.layout = edgePipelineLayout; pipelineInfo.renderPass = edgeRenderPass; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..d82a657 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(leveleditor) diff --git a/tools/leveleditor/CMakeLists.txt b/tools/leveleditor/CMakeLists.txt new file mode 100644 index 0000000..9aaa62c --- /dev/null +++ b/tools/leveleditor/CMakeLists.txt @@ -0,0 +1,15 @@ +find_package(Qt5Widgets CONFIG REQUIRED) + +set(INCLUDE_FILES + include/mainwindow.h + include/renderwindow.h) + +qt5_wrap_cpp(EDITOR_SRC ${INCLUDE_FILES}) + +add_executable(LevelEditor + src/main.cpp + src/mainwindow.cpp + src/renderwindow.cpp + ${EDITOR_SRC}) +target_include_directories(LevelEditor PRIVATE include) +target_link_libraries(LevelEditor Qt5::Widgets Engine) diff --git a/tools/leveleditor/include/context.h b/tools/leveleditor/include/context.h new file mode 100644 index 0000000..bcf5a29 --- /dev/null +++ b/tools/leveleditor/include/context.h @@ -0,0 +1,7 @@ +#pragma once + +class Renderer; + +struct Context { + Renderer* renderer; +}; diff --git a/tools/leveleditor/include/mainwindow.h b/tools/leveleditor/include/mainwindow.h new file mode 100644 index 0000000..668e698 --- /dev/null +++ b/tools/leveleditor/include/mainwindow.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +struct Context; + +class MainWindow : public QMainWindow { +public: + MainWindow(Context& context); +}; diff --git a/tools/leveleditor/include/renderwindow.h b/tools/leveleditor/include/renderwindow.h new file mode 100644 index 0000000..fd0e268 --- /dev/null +++ b/tools/leveleditor/include/renderwindow.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#include "context.h" + +struct RenderTarget; + +class RendererWindow : public QWindow { + Q_OBJECT +public: + RendererWindow(Context& context); + ~RendererWindow(); + +protected: + void exposeEvent(QExposeEvent* event) override; + bool event(QEvent* event) override; + +private: + void init(); + void render(); + + VkSurfaceKHR surface = nullptr; + RenderTarget* target = nullptr; + + bool initialized = false; + Context& context; +}; diff --git a/tools/leveleditor/src/main.cpp b/tools/leveleditor/src/main.cpp new file mode 100644 index 0000000..9699570 --- /dev/null +++ b/tools/leveleditor/src/main.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include "mainwindow.h" +#include "platform.h" +#include "context.h" +#include "renderer.h" +#include "worldmanager.h" +#include "assetmanager.h" +#include "ecs.h" + +static std::vector extensionList; + +std::vector platform::getRequiredExtensions() { + QVulkanInstance instance; + instance.create(); + + for(const auto& extension : instance.extensions()) + extensionList.push_back(extension.data()); + + instance.destroy(); + + std::vector extensions; + for(const auto& extension : extensionList) + extensions.push_back(extension.c_str()); + + return extensions; +} + +uint32_t platform::getTime() { + return 0; +} + +int main(int argc, char* argv[]) { + QApplication app(argc, argv); + + GraphicsConfig config; + config.shadowResolution = 256; + config.dofDownscale = 2; + config.filterPCF = true; + config.enableImgui = false; + + Context context; + context.renderer = new Renderer(config); + + assetManager.setRenderer(context.renderer); + + worldManager.loadWorld("test.world"); + worldManager.switchWorld("test.world"); + + EntityID entity = ECS::createEntity(worldManager.getCurrentWorld()); + + auto cameraTransform = ECS::addComponent(entity); + cameraTransform->position = glm::vec3(5, 5, 5); + + auto cameraComponent = ECS::addComponent(entity); + cameraComponent->fov = 70.0f; + + MainWindow window(context); + window.show(); + + return app.exec(); +} diff --git a/tools/leveleditor/src/mainwindow.cpp b/tools/leveleditor/src/mainwindow.cpp new file mode 100644 index 0000000..d236026 --- /dev/null +++ b/tools/leveleditor/src/mainwindow.cpp @@ -0,0 +1,20 @@ +#include "mainwindow.h" + +#include + +#include "renderwindow.h" +#include "renderer.h" + +MainWindow::MainWindow(Context& context) { + setWindowTitle("Level Editor"); + + QVulkanInstance* instance = new QVulkanInstance(); + instance->setVkInstance(context.renderer->getInstance()); + instance->create(); + + RendererWindow* window = new RendererWindow(context); + window->setVulkanInstance(instance); + + QWidget* wrapper = QWidget::createWindowContainer(window); + setCentralWidget(wrapper); +} diff --git a/tools/leveleditor/src/renderwindow.cpp b/tools/leveleditor/src/renderwindow.cpp new file mode 100644 index 0000000..34a21ba --- /dev/null +++ b/tools/leveleditor/src/renderwindow.cpp @@ -0,0 +1,55 @@ +#include "renderwindow.h" + +#include +#include + +#include "renderer.h" +#include "worldmanager.h" + +RendererWindow::RendererWindow(Context& context) : QWindow(), context(context) { + setSurfaceType(SurfaceType::VulkanSurface); +} + +RendererWindow::~RendererWindow() { + +} + +void RendererWindow::exposeEvent(QExposeEvent*) { + if (isExposed() && !initialized) { + init(); + render(); + + initialized = true; + } +} + +bool RendererWindow::event(QEvent* event) { + switch(event->type()) { + case QEvent::UpdateRequest: + render(); + break; + default: + break; + } + + return QWindow::event(event); +} + +void RendererWindow::init() { + surface = QVulkanInstance::surfaceForWindow(this); + + target = context.renderer->createSurfaceRenderTarget(surface); +} + +void RendererWindow::render() { + context.renderer->render(*worldManager.getCurrentWorld(), target); + + const auto width = size().width(); + const auto height = size().height(); + + if(target->extent.width != width || target->extent.height != height) + target = context.renderer->createSurfaceRenderTarget(surface, target); + + vulkanInstance()->presentQueued(this); + requestUpdate(); +}