Program Listing for File renderer.cpp

Return to documentation for file (PrismEngine/src/renderer.cpp)

#include "renderer.h"
#include "meshManager.h"
#include <gtc/quaternion.hpp>


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.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<VkClearValue, 2> clearValues{};
    clearValues[0].color = { {0.0f, 0.0f, 0.0f, 1.0f} };
    clearValues[1].depthStencil = { 1.0f, 0 };

    renderPassInfo.clearValueCount = static_cast<uint32_t>(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;
    cameraUbo.ambientColor = camera->ambientColor;
    cameraUbo.ambientIntensity = camera->ambientIntensity;

    memcpy(pgc.context.uniformBuffers[pgc.context.currentFrame].cameraMapped, &cameraUbo, sizeof(cameraUbo));
}

void prism::render::Renderer::updateInstances(std::vector<InstanceData> renderData)
{
    std::vector<prism::PGC::ObjectSSBO> allObjectsData;
    for (auto& obj : renderData) {
        prism::PGC::ObjectSSBO data;
        data.model = glm::mat4(1.0f);
        data.model = glm::translate(data.model, glm::vec3(obj.transform->pos.x, obj.transform->pos.y, obj.transform->pos.z));
        data.model = data.model * glm::mat4_cast(glm::quat(glm::radians(glm::vec3{ obj.transform->rot.x, obj.transform->rot.y, obj.transform->rot.z })));
        data.model = glm::scale(data.model, glm::vec3(obj.transform->scale.x, obj.transform->scale.y, obj.transform->scale.z));

        data.normals = glm::transpose(glm::inverse(data.model));

        data.texture = obj.texture->texture;
        allObjectsData.push_back(data);
    }

    memcpy((char*)pgc.context.storageBuffers[pgc.context.currentFrame].objectMapped,
        allObjectsData.data(),
        allObjectsData.size() * sizeof(prism::PGC::ObjectSSBO));
}

void prism::render::Renderer::updateLights(LightData* lightData)
{
    prism::PGC::CameraUBO* cameraUBO = static_cast<prism::PGC::CameraUBO*>(pgc.context.uniformBuffers[pgc.context.currentFrame].cameraMapped);
    cameraUBO->pointLightCount = static_cast<uint32_t>(lightData->pointLights.size());
    cameraUBO->directionalLightCount = static_cast<uint32_t>(lightData->directionalLights.size());

    if (!lightData->pointLights.empty()) {
        memcpy(pgc.context.storageBuffers[pgc.context.currentFrame].pointLightsMapped,
            lightData->pointLights.data(),
            lightData->pointLights.size() * sizeof(prism::scene::PointLightComponent));
    }

    if (!lightData->directionalLights.empty()) {
        memcpy(pgc.context.storageBuffers[pgc.context.currentFrame].directionalLightsMapped,
            lightData->directionalLights.data(),
            lightData->directionalLights.size() * sizeof(prism::scene::DirectionalLightComponents));
    }
}

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<float>(pgc.context.swapChainExtent.width);
    viewport.height = static_cast<float>(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::bindObjectsData()
{
    vkCmdBindDescriptorSets(pgc.context.commandBuffers[pgc.context.currentFrame],
        VK_PIPELINE_BIND_POINT_GRAPHICS,
        pgc.context.pipelineLayout,
        0,
        1,
        &pgc.context.descriptorSets[pgc.context.currentFrame],
        0, nullptr);
}

void prism::render::Renderer::drawMesh(uint32_t meshId, uint32_t instanceCount, uint32_t firstIndex)
{
    const PGC::Mesh& info = pgc.meshManager.getMeshInfo(meshId);
    vkCmdDrawIndexed(pgc.context.commandBuffers[pgc.context.currentFrame], info.indexCount, instanceCount, info.indexOffset, info.vertexOffset, firstIndex);

}

prism::TextureId prism::render::Renderer::addTexture(const std::string& texturePath)
{
    return pgc.textureStorage.load(texturePath);
}

bool prism::render::Renderer::removeTexture(TextureId texture)
{
    return pgc.textureStorage.remove(texture);
}

void prism::render::Renderer::remodeMaterial(scene::MaterialComponent material)
{
    if (!material.texture)
    {
        pgc.textureStorage.remove(material.texture);
    }
}

void prism::render::Renderer::clearTextures()
{
    pgc.textureStorage.cleanup();
}

prism::scene::MeshComponent prism::render::Renderer::addMesh(std::string texturePath)
{
    return { pgc.meshManager.addMesh(texturePath)};
}

void prism::render::Renderer::updateMeshes()
{
    pgc.meshManager.update();
}

void prism::render::Renderer::clearMeshes()
{
    pgc.meshManager.clear();
}

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();

    settings.descriptorSetLayout = {
    {
            // Camera UBO
            {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT},

            // Objects SSBO
            {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT },
            // PointLightsSSBO
            {2, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT},
            // DirectionalLightsSSBO
            {3, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_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
    };
}