.. _program_listing_file_PrismEngine_src_renderer.cpp: Program Listing for File renderer.cpp ===================================== |exhale_lsh| :ref:`Return to documentation for file ` (``PrismEngine/src/renderer.cpp``) .. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS .. code-block:: cpp #include "renderer.h" #include "meshManager.h" #include "textureManager.h" #include void prism::render::Renderer::linkWindow(prism::scene::WindowResource* window) { this->window = window; } prism::render::Renderer::~Renderer() { } void prism::render::Renderer::init() { pgc.windowResized = &window->windowResized; pgc.windowMinimized = &window->windowMinimized; pgc.context.textures.push_back(PGC::Texture{}); pgc.init(this->settings); } bool prism::render::Renderer::isRenderingActive() { if (!pgc.isWindowReadyForRendering(this->window->getSDLWindow())) { if (pgc.context.wasRenderingActive) { awaitRenderingCompletion(); pgc.context.wasRenderingActive = false; } SDL_PumpEvents(); SDL_Delay(10); return false; } pgc.context.wasRenderingActive = true; return true; } void prism::render::Renderer::beginFrame() { vkWaitForFences(pgc.context.device, 1, &pgc.context.inFlightFences[pgc.context.currentFrame], VK_TRUE, UINT64_MAX); VkResult result = vkAcquireNextImageKHR(pgc.context.device, pgc.context.vkSwapChain, UINT64_MAX, pgc.context.imageAvailableSemaphores[pgc.context.currentFrame], VK_NULL_HANDLE, &pgc.context.imageIndex); if (result == VK_ERROR_OUT_OF_DATE_KHR) { pgc.getSwapChainPtr()->recreate(); return; } else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) { throw std::runtime_error("failed to acquire swap chain image!"); } } void prism::render::Renderer::endFrame() { VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; VkSemaphore waitSemaphores[] = { pgc.context.imageAvailableSemaphores[pgc.context.currentFrame] }; VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; submitInfo.waitSemaphoreCount = 1; submitInfo.pWaitSemaphores = waitSemaphores; submitInfo.pWaitDstStageMask = waitStages; submitInfo.commandBufferCount = 1; submitInfo.pCommandBuffers = &pgc.context.commandBuffers[pgc.context.currentFrame]; VkSemaphore signalSemaphores[] = { pgc.context.renderFinishedSemaphores[pgc.context.currentFrame] }; submitInfo.signalSemaphoreCount = 1; submitInfo.pSignalSemaphores = signalSemaphores; if (vkQueueSubmit(pgc.context.graphicsQueue, 1, &submitInfo, pgc.context.inFlightFences[pgc.context.currentFrame]) != VK_SUCCESS) { throw std::runtime_error("failed to submit draw command buffer!"); } VkPresentInfoKHR presentInfo{}; presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; presentInfo.waitSemaphoreCount = 1; presentInfo.pWaitSemaphores = signalSemaphores; VkSwapchainKHR swapChains[] = { pgc.context.vkSwapChain }; presentInfo.swapchainCount = 1; presentInfo.pSwapchains = swapChains; presentInfo.pImageIndices = &pgc.context.imageIndex; VkResult result = vkQueuePresentKHR(pgc.context.presentQueue, &presentInfo); if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || *pgc.windowResized) { *pgc.windowResized = false; pgc.getSwapChainPtr()->recreate(); } else if (result != VK_SUCCESS) { throw std::runtime_error("failed to present swap chain image!"); } pgc.context.currentFrame = (pgc.context.currentFrame + 1) % pgc.context.MAX_FRAMES_IN_FLIGHT; } void prism::render::Renderer::beginRender() { vkResetFences(pgc.context.device, 1, &pgc.context.inFlightFences[pgc.context.currentFrame]); vkResetCommandBuffer(pgc.context.commandBuffers[pgc.context.currentFrame], /*VkCommandBufferResetFlagBits*/ 0); VkCommandBufferBeginInfo beginInfo{}; beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; beginInfo.flags = 0; // Optional beginInfo.pInheritanceInfo = nullptr; // Optional if (vkBeginCommandBuffer(pgc.context.commandBuffers[pgc.context.currentFrame], &beginInfo) != VK_SUCCESS) { throw std::runtime_error("failed to begin recording command buffer!"); } VkRenderPassBeginInfo renderPassInfo{}; renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassInfo.renderPass = pgc.context.renderPass; renderPassInfo.framebuffer = pgc.context.swapChainFramebuffers[pgc.context.imageIndex]; renderPassInfo.renderArea.offset = { 0, 0 }; renderPassInfo.renderArea.extent = pgc.context.swapChainExtent; std::array clearValues{}; clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} }; clearValues[1].depthStencil = { 1.0f, 0 }; renderPassInfo.clearValueCount = static_cast(clearValues.size()); renderPassInfo.pClearValues = clearValues.data(); vkCmdBeginRenderPass(pgc.context.commandBuffers[pgc.context.currentFrame], &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE); } void prism::render::Renderer::endRender() { vkCmdEndRenderPass(pgc.context.commandBuffers[pgc.context.currentFrame]); if (vkEndCommandBuffer(pgc.context.commandBuffers[pgc.context.currentFrame]) != VK_SUCCESS) { throw std::runtime_error("failed to record command buffer!"); } } void prism::render::Renderer::updateCamera(prism::scene::TransformComponent* transform, prism::scene::CameraComponent* camera) { prism::PGC::utils::CameraData* cameraData = pgc.getCameraDataPtr(); // Правильное преобразование координат: X→X, Y→Z, Z→-Y (для Vulkan) cameraData->pos = { transform->pos.x, transform->pos.y, transform->pos.z }; cameraData->look = { camera->look.x, camera->look.y, camera->look.z }; cameraData->fovy = camera->fovy; cameraData->aspect = camera->aspect; cameraData->zFar = camera->zFar; cameraData->zNear = camera->zNear; cameraData->useСurrentWindowAspect = camera->useCurrentWindowAspect; prism::PGC::CameraUBO cameraUbo{}; // Вычисляем точку, куда смотрит камера, на основе углов glm::vec3 direction; direction.x = cos(glm::radians(camera->look.x)) * cos(glm::radians(camera->look.y)); direction.y = sin(glm::radians(camera->look.y)); direction.z = sin(glm::radians(camera->look.x)) * cos(glm::radians(camera->look.y)); direction = glm::normalize(direction); glm::vec3 cameraPos = { transform->pos.x, transform->pos.y, transform->pos.z }; glm::vec3 cameraTarget = cameraPos + direction; cameraUbo.view = glm::lookAt( cameraPos, cameraTarget, glm::vec3(0.0f, 1.0f, 0.0f) // Up vector (Y вверх) ); if (pgc.context.cameraData.useСurrentWindowAspect) { cameraUbo.proj = glm::perspective( glm::radians(pgc.context.cameraData.fovy), pgc.context.swapChainExtent.width / (float)pgc.context.swapChainExtent.height, pgc.context.cameraData.zNear, pgc.context.cameraData.zFar ); } else { cameraUbo.proj = glm::perspective( glm::radians(pgc.context.cameraData.fovy), pgc.context.cameraData.aspect, pgc.context.cameraData.zNear, pgc.context.cameraData.zFar ); } cameraUbo.proj[1][1] *= -1; cameraUbo.viewProj = cameraUbo.proj * cameraUbo.view; cameraUbo.cameraPos = cameraPos; memcpy(pgc.context.uniformBuffers[pgc.context.currentFrame].cameraMapped, &cameraUbo, sizeof(cameraUbo)); } void prism::render::Renderer::updateObjectTransform(prism::scene::TransformComponent* transform, uint32_t transformId) { prism::PGC::ObjectUBO objectUbo{}; objectUbo.model = glm::mat4(1.0f); // Порядок: сначала масштаб, потом вращение, потом перенос objectUbo.model = glm::translate(objectUbo.model, glm::vec3(transform->pos.x, transform->pos.y, transform->pos.z)); objectUbo.model = objectUbo.model * glm::mat4_cast(glm::quat(glm::radians(glm::vec3{transform->rot.x, transform->rot.y, transform->rot.z}))); objectUbo.model = glm::scale(objectUbo.model, glm::vec3(transform->scale.x, transform->scale.y, transform->scale.z)); objectUbo.normals = glm::transpose(glm::inverse(objectUbo.model)); size_t offset = transformId * pgc.context.dynamicAlignment; memcpy((char*)pgc.context.uniformBuffers[pgc.context.currentFrame].objectMapped + offset, &objectUbo, sizeof(objectUbo)); } void prism::render::Renderer::bindDefault() { vkCmdBindPipeline(pgc.context.commandBuffers[pgc.context.currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pgc.context.graphicsPipeline); VkViewport viewport{}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = static_cast(pgc.context.swapChainExtent.width); viewport.height = static_cast(pgc.context.swapChainExtent.height); viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(pgc.context.commandBuffers[pgc.context.currentFrame], 0, 1, &viewport); VkRect2D scissor{}; scissor.offset = { 0, 0 }; scissor.extent = pgc.context.swapChainExtent; vkCmdSetScissor(pgc.context.commandBuffers[pgc.context.currentFrame], 0, 1, &scissor); VkBuffer vertexBuffers[] = { pgc.context.vertexBuffer }; VkDeviceSize offsets[] = { 0 }; vkCmdBindVertexBuffers(pgc.context.commandBuffers[pgc.context.currentFrame], 0, 1, vertexBuffers, offsets); vkCmdBindIndexBuffer(pgc.context.commandBuffers[pgc.context.currentFrame], pgc.context.indexBuffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindDescriptorSets(pgc.context.commandBuffers[pgc.context.currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pgc.context.pipelineLayout, 1, // set = 1 для текстур 1, &pgc.context.textureDescriptorSet, 0, nullptr); } void prism::render::Renderer::bindTransform(uint32_t transformId) { uint32_t dynamicOffset = transformId * static_cast(pgc.context.dynamicAlignment); vkCmdBindDescriptorSets(pgc.context.commandBuffers[pgc.context.currentFrame], VK_PIPELINE_BIND_POINT_GRAPHICS, pgc.context.pipelineLayout, 0, // set = 0 для ubo 1, &pgc.context.descriptorSets[pgc.context.currentFrame], 1, &dynamicOffset); } void prism::render::Renderer::pushTextureId(uint32_t textureId) { prism::PGC::PushConstants pushConstants{}; pushConstants.textureIndex = static_cast(textureId); vkCmdPushConstants( pgc.context.commandBuffers[pgc.context.currentFrame], pgc.context.pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, // Указываем, что push-константы используются во фрагментном шейдере 0, sizeof(prism::PGC::PushConstants), &pushConstants ); } void prism::render::Renderer::drawMesh(uint32_t meshId) { const PGC::Mesh& info = prism::PGC::MeshManager::getMeshInfo(&pgc.context, meshId); vkCmdDrawIndexed(pgc.context.commandBuffers[pgc.context.currentFrame], info.indexCount, 1, info.indexOffset, info.vertexOffset, 0); } prism::scene::TextureComponent prism::render::Renderer::addTexture(const std::string& texturePath) { return { PGC::TextureManager::addTexture(&pgc.context, texturePath) }; } void prism::render::Renderer::removeTexture(scene::TextureComponent texture) { PGC::TextureManager::removeTexture(&pgc.context, texture.texture); } prism::scene::MeshComponent prism::render::Renderer::addMesh(std::string texturePath) { return { PGC::MeshManager::addMesh(&pgc.context, texturePath)}; } void prism::render::Renderer::updateMeshes() { PGC::MeshManager::update(&pgc.context); } void prism::render::Renderer::clearMeshes() { PGC::MeshManager::clear(&pgc.context); } void prism::render::Renderer::awaitRenderingCompletion() { pgc.awaitRenderingCompletion(); } void prism::render::Renderer::destroy() { pgc.awaitRenderingCompletion(); pgc.cleanup(); } void prism::render::Renderer::setDefaultSettings() { settings.app.applicationName = SDL_GetWindowTitle(window->getSDLWindow()); settings.app.applicationVersion = { 1, 0, 0 }; settings.window = window->getSDLWindow(); VkPipelineColorBlendAttachmentState colorBlendAttachment{}; colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; colorBlendAttachment.blendEnable = VK_TRUE; colorBlendAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; colorBlendAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; colorBlendAttachment.colorBlendOp = VK_BLEND_OP_ADD; colorBlendAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; colorBlendAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO; colorBlendAttachment.alphaBlendOp = VK_BLEND_OP_ADD; settings.pipeline.colorBlend.attachments.push_back(colorBlendAttachment); settings.descriptorSetLayout = { { // Camera UBO (binding = 0, vertex stage) {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT}, // Object UBO (binding = 1, vertex stage) {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_VERTEX_BIT} }, 0 }; settings.textureDescriptorSetLayout = { { {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, settings.MAX_TEXTURES, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr, VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT | VK_DESCRIPTOR_BINDING_PARTIALLY_BOUND_BIT_EXT} }, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT }; }