diff --git a/apps/mapeditor/CMakeLists.txt b/apps/mapeditor/CMakeLists.txt index 5bb4c13..6cd997e 100644 --- a/apps/mapeditor/CMakeLists.txt +++ b/apps/mapeditor/CMakeLists.txt @@ -7,11 +7,13 @@ target_sources(novus-mapeditor include/mainwindow.h include/maplistwidget.h include/mapview.h + include/objectpass.h src/main.cpp src/mainwindow.cpp src/maplistwidget.cpp - src/mapview.cpp) + src/mapview.cpp + src/objectpass.cpp) target_include_directories(novus-mapeditor PUBLIC include) diff --git a/apps/mapeditor/include/objectpass.h b/apps/mapeditor/include/objectpass.h new file mode 100644 index 0000000..4980760 --- /dev/null +++ b/apps/mapeditor/include/objectpass.h @@ -0,0 +1,26 @@ +#pragma once + +#include "pass.h" + +#include +#include + +class RenderManager; +class Device; + +class ObjectPass : public RendererPass +{ +public: + ObjectPass(RenderManager *renderer); + + void render(VkCommandBuffer commandBuffer, Camera &camera) override; + +private: + void createPipeline(); + + VkPipeline pipeline_ = nullptr; + VkPipelineLayout pipelineLayout_ = nullptr; + + RenderManager *m_renderer; + Device &m_device; +}; diff --git a/apps/mapeditor/src/mapview.cpp b/apps/mapeditor/src/mapview.cpp index 59b9b74..4764620 100644 --- a/apps/mapeditor/src/mapview.cpp +++ b/apps/mapeditor/src/mapview.cpp @@ -7,6 +7,7 @@ #include #include "filecache.h" +#include "objectpass.h" MapView::MapView(GameData *data, FileCache &cache, QWidget *parent) : QWidget(parent) @@ -15,6 +16,9 @@ MapView::MapView(GameData *data, FileCache &cache, QWidget *parent) { mdlPart = new MDLPart(data, cache); mdlPart->enableFreemode(); + connect(mdlPart, &MDLPart::initializeRender, this, [this] { + mdlPart->manager()->addPass(new ObjectPass(mdlPart->manager())); + }); auto layout = new QVBoxLayout(); layout->setContentsMargins(0, 0, 0, 0); diff --git a/apps/mapeditor/src/objectpass.cpp b/apps/mapeditor/src/objectpass.cpp new file mode 100644 index 0000000..6fbfafe --- /dev/null +++ b/apps/mapeditor/src/objectpass.cpp @@ -0,0 +1,162 @@ +#include "objectpass.h" +#include "device.h" +#include "rendermanager.h" +#include "simplerenderer.h" +#include "swapchain.h" + +#include + +ObjectPass::ObjectPass(RenderManager *renderer) + : m_renderer(renderer) + , m_device(m_renderer->device()) +{ + createPipeline(); +} + +void ObjectPass::render(VkCommandBuffer commandBuffer, Camera &camera) +{ + if (auto renderer = dynamic_cast(m_renderer->renderer())) { + } else { + qWarning() << "Can't render object pass in non-simple renderer for now!!"; + } + + /*VkClearValue clearValue = {}; + clearValue.color = {0.0f, 0.0f, 0.0f, 0.0f}; + + VkRenderPassBeginInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; + renderPassInfo.renderPass = m_renderPass; + renderPassInfo.framebuffer = target->sobelFramebuffers[target->currentResource]; + renderPassInfo.renderArea.extent = target->extent; + renderPassInfo.clearValueCount = 1; + renderPassInfo.pClearValues = &clearValue; + + vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); + + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_); + + if(extraInfo != nullptr) { + for (auto mesh: collection.meshes) { + bool shouldRender = false; + for (int i = 0; i < extraInfo->numSelectedEntities; i++) { + if (extraInfo->selectedEntities[i] == mesh.entity) + shouldRender = true; + } + + if (shouldRender) { + glm::mat4 mvp; + mvp = glm::perspective(glm::radians(collection.camera.camera->fov), + (float) target->extent.width / target->extent.height, + collection.camera.camera->near, collection.camera.camera->far); + mvp *= glm::lookAt(collection.camera.transform->position, collection.camera.camera->target, + glm::vec3(0, -1, 0)); + mvp = glm::translate(mvp, mesh.transform->position); + + vkCmdPushConstants(commandBuffer, pipelineLayout_, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), + &mvp); + + const VkDeviceSize offsets[] = {0}; + vkCmdBindVertexBuffers(commandBuffer, 0, 1, &mesh.mesh->mesh->vertexBuffer, offsets); + vkCmdBindIndexBuffer(commandBuffer, mesh.mesh->mesh->indexBuffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(commandBuffer, mesh.mesh->mesh->indices.size(), 1, 0, 0, 0); + } + } + } + + vkCmdEndRenderPass(commandBuffer);*/ +} + +void ObjectPass::createPipeline() +{ + VkPipelineShaderStageCreateInfo vertexShaderStageInfo = {}; + vertexShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertexShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertexShaderStageInfo.module = m_device.loadShaderFromDisk(":/shaders/debug.vert.spv"); + vertexShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragmentShaderStageInfo = {}; + fragmentShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragmentShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + fragmentShaderStageInfo.module = m_device.loadShaderFromDisk(":/shaders/debug.vert.spv"); + fragmentShaderStageInfo.pName = "main"; + + const std::array shaderStages = {vertexShaderStageInfo, fragmentShaderStageInfo}; + + VkVertexInputBindingDescription vertexBindingDescription = {}; + vertexBindingDescription.stride = sizeof(Vertex); + + VkVertexInputAttributeDescription positionAttributeDescription = {}; + positionAttributeDescription.format = VK_FORMAT_R32G32B32_SFLOAT; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertexInputInfo.vertexBindingDescriptionCount = 1; + vertexInputInfo.pVertexBindingDescriptions = &vertexBindingDescription; + vertexInputInfo.vertexAttributeDescriptionCount = 1; + vertexInputInfo.pVertexAttributeDescriptions = &positionAttributeDescription; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.cullMode = VK_CULL_MODE_NONE; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + 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(); + + VkPushConstantRange pushConstant = {}; + pushConstant.size = sizeof(glm::mat4); + pushConstant.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.pushConstantRangeCount = 1; + pipelineLayoutInfo.pPushConstantRanges = &pushConstant; + + vkCreatePipelineLayout(m_device.device, &pipelineLayoutInfo, nullptr, &pipelineLayout_); + + auto renderer = dynamic_cast(m_renderer->renderer()); + + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = shaderStages.size(); + pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = pipelineLayout_; + pipelineInfo.renderPass = renderer->renderPass(); + + vkCreateGraphicsPipelines(m_device.device, nullptr, 1, &pipelineInfo, nullptr, &pipeline_); +} diff --git a/parts/mdl/mdlpart.cpp b/parts/mdl/mdlpart.cpp index 3d9c269..12a81ab 100644 --- a/parts/mdl/mdlpart.cpp +++ b/parts/mdl/mdlpart.cpp @@ -423,4 +423,9 @@ int MDLPart::numModels() const return models.size(); } +RenderManager *MDLPart::manager() const +{ + return renderer; +} + #include "moc_mdlpart.cpp" diff --git a/parts/mdl/mdlpart.h b/parts/mdl/mdlpart.h index ca6d51f..315956b 100644 --- a/parts/mdl/mdlpart.h +++ b/parts/mdl/mdlpart.h @@ -49,6 +49,8 @@ public: int numModels() const; + RenderManager *manager() const; + physis_PBD pbd{}; bool enableRacialDeform = true; @@ -56,6 +58,8 @@ public: Q_SIGNALS: void modelChanged(); void skeletonChanged(); + // Called when a Vulkan context is available, and you can safely access RenderManager + void initializeRender(); public Q_SLOTS: /// Clears all stored MDLs. diff --git a/parts/mdl/vulkanwindow.cpp b/parts/mdl/vulkanwindow.cpp index d2c1f18..9b1e773 100644 --- a/parts/mdl/vulkanwindow.cpp +++ b/parts/mdl/vulkanwindow.cpp @@ -25,6 +25,7 @@ void VulkanWindow::exposeEvent(QExposeEvent *) auto surface = m_instance->surfaceForWindow(this); if (!m_renderer->initSwapchain(surface, width() * screen()->devicePixelRatio(), height() * screen()->devicePixelRatio())) { + Q_EMIT part->initializeRender(); m_initialized = false; } else { render(); diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt index f8bc97c..268b1e3 100644 --- a/renderer/CMakeLists.txt +++ b/renderer/CMakeLists.txt @@ -22,6 +22,7 @@ target_sources(renderer include/simplerenderer.h include/swapchain.h include/texture.h + include/pass.h src/device.cpp src/gamerenderer.cpp @@ -42,7 +43,9 @@ qt_add_resources(renderer shaders/mesh.vert.spv shaders/skinned.vert.spv shaders/blit.vert.spv - shaders/blit.frag.spv) + shaders/blit.frag.spv + shaders/debug.vert.spv + shaders/debug.frag.spv) target_include_directories(renderer PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_link_libraries(renderer PUBLIC diff --git a/renderer/include/baserenderer.h b/renderer/include/baserenderer.h index 180b34e..73741fa 100644 --- a/renderer/include/baserenderer.h +++ b/renderer/include/baserenderer.h @@ -18,6 +18,8 @@ struct DrawObject; struct Camera; struct Texture; struct Scene; +class Pass; +class Device; /// Base class for all rendering implementations class BaseRenderer @@ -33,4 +35,6 @@ public: /// The final composite texture that is drawn into with render() virtual Texture &getCompositeTexture() = 0; + + virtual Device &device() = 0; }; \ No newline at end of file diff --git a/renderer/include/gamerenderer.h b/renderer/include/gamerenderer.h index fa2e442..c9f64d8 100644 --- a/renderer/include/gamerenderer.h +++ b/renderer/include/gamerenderer.h @@ -35,6 +35,8 @@ public: Texture &getCompositeTexture() override; + Device &device() override; + private: struct RequestedBinding { VkDescriptorType type; diff --git a/renderer/include/pass.h b/renderer/include/pass.h new file mode 100644 index 0000000..01d5d28 --- /dev/null +++ b/renderer/include/pass.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class Camera; + +class RendererPass +{ +public: + virtual void render(VkCommandBuffer commandBuffer, Camera &camera) = 0; +}; \ No newline at end of file diff --git a/renderer/include/rendermanager.h b/renderer/include/rendermanager.h index 909c1b8..5811f32 100644 --- a/renderer/include/rendermanager.h +++ b/renderer/include/rendermanager.h @@ -20,6 +20,7 @@ class ImGuiPass; struct ImGuiContext; class BaseRenderer; +class RendererPass; /// Render 3D scenes made up of FFXIV game objects class RenderManager @@ -49,6 +50,10 @@ public: VkSampler defaultSampler(); + void addPass(RendererPass *pass); + + BaseRenderer *renderer(); + private: void updateCamera(Camera &camera); void initBlitPipeline(); @@ -68,4 +73,5 @@ private: Device *m_device = nullptr; BaseRenderer *m_renderer = nullptr; GameData *m_data = nullptr; + std::vector m_passes; }; \ No newline at end of file diff --git a/renderer/include/simplerenderer.h b/renderer/include/simplerenderer.h index e9e348d..c2b1d11 100644 --- a/renderer/include/simplerenderer.h +++ b/renderer/include/simplerenderer.h @@ -34,6 +34,11 @@ public: Texture &getCompositeTexture() override; + Device &device() override; + + VkFramebuffer framebuffer(); + VkRenderPass renderPass(); + private: void initRenderPass(); void initPipeline(); diff --git a/renderer/shaders/blit.frag.spv b/renderer/shaders/blit.frag.spv index 7cd79c4..600535c 100644 Binary files a/renderer/shaders/blit.frag.spv and b/renderer/shaders/blit.frag.spv differ diff --git a/renderer/shaders/blit.vert.spv b/renderer/shaders/blit.vert.spv index cd8fd23..d76c840 100644 Binary files a/renderer/shaders/blit.vert.spv and b/renderer/shaders/blit.vert.spv differ diff --git a/renderer/shaders/compile_shaders.sh b/renderer/shaders/compile_shaders.sh index 28c7a0d..5ea6c76 100755 --- a/renderer/shaders/compile_shaders.sh +++ b/renderer/shaders/compile_shaders.sh @@ -9,4 +9,6 @@ glslc imgui.vert -o imgui.vert.spv && glslc imgui.frag -o imgui.frag.spv && glslc dummy.frag -o dummy.frag.spv && glslc blit.vert -o blit.vert.spv && -glslc blit.frag -o blit.frag.spv \ No newline at end of file +glslc blit.frag -o blit.frag.spv && +glslc debug.vert -o debug.vert.spv && +glslc debug.frag -o debug.frag.spv \ No newline at end of file diff --git a/renderer/shaders/debug.frag b/renderer/shaders/debug.frag new file mode 100644 index 0000000..f7da319 --- /dev/null +++ b/renderer/shaders/debug.frag @@ -0,0 +1,10 @@ +// SPDX-FileCopyrightText: 2025 Joshua Goins +// SPDX-License-Identifier: CC0-1.0 + +#version 450 + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(1, 0, 0, 1); +} diff --git a/renderer/shaders/debug.frag.spv b/renderer/shaders/debug.frag.spv new file mode 100644 index 0000000..aa92776 Binary files /dev/null and b/renderer/shaders/debug.frag.spv differ diff --git a/renderer/shaders/debug.vert b/renderer/shaders/debug.vert new file mode 100644 index 0000000..2b0f6b6 --- /dev/null +++ b/renderer/shaders/debug.vert @@ -0,0 +1,16 @@ +// SPDX-FileCopyrightText: 2025 Joshua Goins +// SPDX-License-Identifier: CC0-1.0 + +#version 450 + +layout(location = 0) in vec3 inPosition; + +layout(std430, push_constant) uniform PushConstant { + mat4 vp, model; +}; + +void main() { + vec4 bPos = model * vec4(inPosition, 1.0); + + gl_Position = vp * bPos; +} diff --git a/renderer/shaders/debug.vert.spv b/renderer/shaders/debug.vert.spv new file mode 100644 index 0000000..845de50 Binary files /dev/null and b/renderer/shaders/debug.vert.spv differ diff --git a/renderer/shaders/imgui.frag.spv b/renderer/shaders/imgui.frag.spv index 77c6e5f..7087c7b 100644 Binary files a/renderer/shaders/imgui.frag.spv and b/renderer/shaders/imgui.frag.spv differ diff --git a/renderer/shaders/imgui.vert.spv b/renderer/shaders/imgui.vert.spv index 0f0ee37..eca9579 100644 Binary files a/renderer/shaders/imgui.vert.spv and b/renderer/shaders/imgui.vert.spv differ diff --git a/renderer/shaders/mesh.frag.spv b/renderer/shaders/mesh.frag.spv index b2791e2..e95abc4 100644 Binary files a/renderer/shaders/mesh.frag.spv and b/renderer/shaders/mesh.frag.spv differ diff --git a/renderer/shaders/mesh.vert.spv b/renderer/shaders/mesh.vert.spv index 1b15571..61d99a1 100644 Binary files a/renderer/shaders/mesh.vert.spv and b/renderer/shaders/mesh.vert.spv differ diff --git a/renderer/shaders/skinned.vert.spv b/renderer/shaders/skinned.vert.spv index 3f02e37..a44b53c 100644 Binary files a/renderer/shaders/skinned.vert.spv and b/renderer/shaders/skinned.vert.spv differ diff --git a/renderer/src/gamerenderer.cpp b/renderer/src/gamerenderer.cpp index f1c71b3..2f4ba55 100644 --- a/renderer/src/gamerenderer.cpp +++ b/renderer/src/gamerenderer.cpp @@ -1498,3 +1498,8 @@ void GameRenderer::bindDescriptorSets(VkCommandBuffer commandBuffer, vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipelineLayout, i, 1, &pipeline.cachedDescriptors[i], 0, nullptr); } } + +Device &GameRenderer::device() +{ + return m_device; +} diff --git a/renderer/src/rendermanager.cpp b/renderer/src/rendermanager.cpp index a59e6af..4de5982 100644 --- a/renderer/src/rendermanager.cpp +++ b/renderer/src/rendermanager.cpp @@ -15,6 +15,7 @@ #include "gamerenderer.h" #include "imgui.h" #include "imguipass.h" +#include "pass.h" #include "simplerenderer.h" #include "swapchain.h" @@ -423,6 +424,13 @@ void RenderManager::render(const std::vector &models) m_renderer->render(commandBuffer, camera, scene, models); + // render extra passes + for (const auto &pass : m_passes) { + pass->render(commandBuffer, camera); + } + + vkCmdEndRenderPass(commandBuffer); + VkRenderPassBeginInfo renderPassInfo = {}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = m_renderPass; @@ -708,3 +716,13 @@ void RenderManager::initBlitPipeline() vkUpdateDescriptorSets(m_device->device, 1, &multiDescriptorWrite2, 0, nullptr); } + +void RenderManager::addPass(RendererPass *pass) +{ + m_passes.push_back(pass); +} + +BaseRenderer *RenderManager::renderer() +{ + return m_renderer; +} diff --git a/renderer/src/simplerenderer.cpp b/renderer/src/simplerenderer.cpp index fabe0ad..9c76f01 100644 --- a/renderer/src/simplerenderer.cpp +++ b/renderer/src/simplerenderer.cpp @@ -161,8 +161,6 @@ void SimpleRenderer::render(VkCommandBuffer commandBuffer, Camera &camera, Scene vkCmdDrawIndexed(commandBuffer, part.numIndices, 1, 0, 0, 0); } } - - vkCmdEndRenderPass(commandBuffer); } void SimpleRenderer::initRenderPass() @@ -569,3 +567,18 @@ Texture &SimpleRenderer::getCompositeTexture() { return m_compositeTexture; } + +Device &SimpleRenderer::device() +{ + return m_device; +} + +VkFramebuffer SimpleRenderer::framebuffer() +{ + return m_framebuffer; +} + +VkRenderPass SimpleRenderer::renderPass() +{ + return m_renderPass; +}