Revision 35efca7662eafe5c0648fbb4e97a7bc1eda84322 authored by Christoph Neuhauser on 23 October 2023, 22:10:04 UTC, committed by Christoph Neuhauser on 23 October 2023, 22:10:04 UTC
1 parent ef25cab
Raw File
WorldMapRenderer.cpp
/*
 * BSD 2-Clause License
 *
 * Copyright (c) 2023, Christoph Neuhauser
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <boost/algorithm/string/replace.hpp>
#include <tiffio.h>

#include <Utils/AppSettings.hpp>
#include <Utils/File/Archive.hpp>
#include <Graphics/Vulkan/Buffers/Framebuffer.hpp>
#include <Graphics/Vulkan/Render/Renderer.hpp>
#include <Graphics/Vulkan/Render/ComputePipeline.hpp>
#include <ImGui/Widgets/PropertyEditor.hpp>
#include <ImGui/imgui_custom.h>

#include "Utils/InternalState.hpp"
#include "Widgets/ViewManager.hpp"
#include "Volume/VolumeData.hpp"
#include "RenderingModes.hpp"
#include "WorldMapRenderer.hpp"

// Include Curl after Renderer.hpp, as NaNHandling::IGNORE conflicts with windows.h.
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <curl/curl.h>

static size_t writeDataCallbackCurl(void *pointer, size_t size, size_t numMembers, void *stream) {
    size_t written = fwrite(pointer, size, numMembers, (FILE*)stream);
    return written;
}

bool downloadFile(const std::string &url, const std::string &localFileName) {
    CURL* curlHandle = curl_easy_init();
    if (!curlHandle) {
        return false;
    }
    CURLcode curlErrorCode = CURLE_OK;

    char* compressedUrl = curl_easy_escape(curlHandle, url.c_str(), int(url.size()));
    std::string fixedUrl = compressedUrl;
    boost::replace_all(fixedUrl, "%3A", ":");
    boost::replace_all(fixedUrl, "%2F", "/");
    std::cout << "Starting to download \"" << fixedUrl << "\"..." << std::endl;

    curl_easy_setopt(curlHandle, CURLOPT_URL, fixedUrl.c_str());
    //curl_easy_setopt(curlHandle, CURLOPT_VERBOSE, 1L);
    curl_easy_setopt(curlHandle, CURLOPT_NOPROGRESS, 0L);
    curl_easy_setopt(curlHandle, CURLOPT_FOLLOWLOCATION, 1L);
    curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, writeDataCallbackCurl);
    FILE* pagefile = fopen(localFileName.c_str(), "wb");
    if (pagefile) {
        curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, pagefile);
        curlErrorCode = curl_easy_perform(curlHandle);
        if (curlErrorCode != CURLE_OK) {
            fclose(pagefile);
            curl_free(compressedUrl);
            curl_easy_cleanup(curlHandle);
            return false;
        }
        fclose(pagefile);
    }

    curl_free(compressedUrl);
    curl_easy_cleanup(curlHandle);
    return true;
}

const char* const WORLD_MAP_QUALITY_NAMES[] = {
        "Low", "Medium", "High"
};
const char* const WORLD_MAP_NAMES[] = {
        "HYP_50M_SR_W", "HYP_LR_SR_OB_DR", "HYP_HR_SR_OB_DR"
};
const char* const WORLD_MAP_URLS[] = {
        "https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/raster/HYP_50M_SR_W.zip",
        "https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/raster/HYP_LR_SR_OB_DR.zip",
        "https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/10m/raster/HYP_HR_SR_OB_DR.zip"
};

WorldMapRenderer::WorldMapRenderer(ViewManager* viewManager)
        : Renderer(RENDERING_MODE_NAMES[int(RENDERING_MODE_WORLD_MAP_RENDERER)], viewManager) {
}

WorldMapRenderer::~WorldMapRenderer() {
}

void WorldMapRenderer::initialize() {
    Renderer::initialize();
}

void WorldMapRenderer::ensureWorldMapFileExists() {
    const std::string mapsDirectory = sgl::AppSettings::get()->getDataDirectory() + "Maps/";
    const std::string mapName = std::string(WORLD_MAP_NAMES[int(worldMapQuality)]);
    worldMapFilePath = mapsDirectory + mapName + "tif";
    const std::string worldMapArchivePath = mapsDirectory + mapName + ".zip";
    if (sgl::FileUtils::get()->exists(worldMapFilePath)) {
        return;
    }

    if (!sgl::FileUtils::get()->directoryExists(mapsDirectory)) {
        sgl::FileUtils::get()->ensureDirectoryExists(mapsDirectory);
    }
    std::string worldMapUrl = WORLD_MAP_URLS[int(worldMapQuality)];
    if (!downloadFile(worldMapUrl, worldMapArchivePath)) {
        sgl::Logfile::get()->writeWarning("Warning: Downloading " + mapName + ".zip with CURL failed.", true);
    }

    std::unordered_map<std::string, sgl::ArchiveEntry> files;
    auto retType = sgl::loadAllFilesFromArchive(worldMapArchivePath, files, false);
    if (retType != sgl::ARCHIVE_FILE_LOAD_SUCCESSFUL) {
        sgl::Logfile::get()->writeWarning("Warning: " + mapName + ".zip could not be opened.", true);
        return;
    }
    auto it = files.find(mapName + ".tif");
    if (it == files.end()) {
        sgl::Logfile::get()->writeWarning(
                "Warning: " + mapName + ".zip does not contain " + mapName + ".tif.", true);
        sgl::FileUtils::get()->removeFile(worldMapArchivePath);
        return;
    }

    auto mapArchiveEntry = it->second;
    FILE* mapFile = fopen(worldMapFilePath.c_str(), "wb");
    if (!mapFile) {
        sgl::Logfile::get()->writeWarning("Warning: Could not create file " + mapName + ".tif.", true);
        sgl::FileUtils::get()->removeFile(worldMapArchivePath);
        return;
    }
    fwrite(mapArchiveEntry.bufferData.get(), 1, mapArchiveEntry.bufferSize, mapFile);
    fclose(mapFile);

    sgl::FileUtils::get()->removeFile(worldMapArchivePath);
    hasCheckedWorldMapExists = true;
}

void WorldMapRenderer::setVolumeData(VolumeDataPtr& _volumeData, bool isNewData) {
    if (!volumeData) {
        isNewData = true;
    }
    volumeData = _volumeData;

    if (isNewData) {
        // Free old data first.
        indexBuffer = {};
        vertexPositionBuffer = {};
        vertexNormalBuffer = {};
        worldMapTexture = {};
        for (auto& worldMapRasterPass : worldMapRasterPasses) {
            worldMapRasterPass->setVolumeData(volumeData, isNewData);
            worldMapRasterPass->setRenderData(
                    indexBuffer, vertexPositionBuffer, vertexNormalBuffer, vertexTexCoordBuffer, worldMapTexture);
        }

        std::vector<uint32_t> triangleIndices;
        std::vector<glm::vec3> vertexPositions;
        std::vector<glm::vec3> vertexNormals;
        std::vector<glm::vec2> vertexTexCoords;
        createWorldMapTexture();
        createGeometryData(triangleIndices, vertexPositions, vertexNormals, vertexTexCoords);

        if (triangleIndices.empty()) {
            return;
        }

        sgl::vk::Device* device = renderer->getDevice();
        indexBuffer = std::make_shared<sgl::vk::Buffer>(
                device, sizeof(uint32_t) * triangleIndices.size(), triangleIndices.data(),
                VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
                VMA_MEMORY_USAGE_GPU_ONLY);
        vertexPositionBuffer = std::make_shared<sgl::vk::Buffer>(
                device, sizeof(glm::vec3) * vertexPositions.size(), vertexPositions.data(),
                VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
                VMA_MEMORY_USAGE_GPU_ONLY);
        vertexNormalBuffer = std::make_shared<sgl::vk::Buffer>(
                device, sizeof(glm::vec3) * vertexNormals.size(), vertexNormals.data(),
                VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
                VMA_MEMORY_USAGE_GPU_ONLY);
        vertexTexCoordBuffer = std::make_shared<sgl::vk::Buffer>(
                device, sizeof(glm::vec2) * vertexTexCoords.size(), vertexTexCoords.data(),
                VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
                VMA_MEMORY_USAGE_GPU_ONLY);
    }

    for (auto& worldMapRasterPass : worldMapRasterPasses) {
        worldMapRasterPass->setVolumeData(volumeData, isNewData);
        worldMapRasterPass->setRenderData(
                indexBuffer, vertexPositionBuffer, vertexNormalBuffer, vertexTexCoordBuffer, worldMapTexture);
    }
}

void WorldMapRenderer::onFieldRemoved(FieldType fieldType, int fieldIdx) {
}

void WorldMapRenderer::createGeometryData(
        std::vector<uint32_t>& triangleIndices, std::vector<glm::vec3>& vertexPositions,
        std::vector<glm::vec3>& vertexNormals, std::vector<glm::vec2>& vertexTexCoords) {
    sgl::AABB3 aabb = volumeData->getBoundingBoxRendering();
    std::vector<glm::vec3> polygonPoints;

    const float* latData = nullptr;
    const float* lonData = nullptr;
    volumeData->getLatLonData(latData, lonData);
    int xs = volumeData->getGridSizeX();
    int ys = volumeData->getGridSizeY();
    int numVertices = xs * ys;

    vertexPositions.reserve(numVertices);
    vertexNormals.reserve(numVertices);
    vertexTexCoords.reserve(numVertices);
    for (int y = 0; y < ys; y++) {
        for (int x = 0; x < xs; x++) {
            vertexPositions.emplace_back(
                    aabb.min.x + (aabb.max.x - aabb.min.x) * float(x) / float(xs - 1),
                    aabb.min.y + (aabb.max.y - aabb.min.y) * float(y) / float(ys - 1),
                    aabb.min.z);
            vertexNormals.emplace_back(0.0f, 0.0f, 1.0f);
            float x_norm = (lonData ? lonData[x + y * xs] : 0.0f) / 360.0f + 0.5f;
            float y_norm = (latData ? latData[x + y * xs] : 0.0f) / 180.0f + 0.5f;
            float texCoordX = (x_norm - minNormX) / (maxNormX - minNormX);
            float texCoordY = (y_norm - minNormY) / (maxNormY - minNormY);
            vertexTexCoords.emplace_back(texCoordX, texCoordY);
        }
    }

    triangleIndices.reserve(6 * (xs - 1) * (ys - 1));
    for (int y = 0; y < ys - 1; y++) {
        for (int x = 0; x < xs - 1; x++) {
            triangleIndices.push_back(x     + y       * xs);
            triangleIndices.push_back(x + 1 + y       * xs);
            triangleIndices.push_back(x     + (y + 1) * xs);
            triangleIndices.push_back(x + 1 + y       * xs);
            triangleIndices.push_back(x + 1 + (y + 1) * xs);
            triangleIndices.push_back(x     + (y + 1) * xs);
        }
    }
}

void WorldMapRenderer::createWorldMapTexture() {
    if (!hasCheckedWorldMapExists) {
        ensureWorldMapFileExists();
    }

    if (!volumeData->getHasLatLonData()) {
        sgl::Logfile::get()->writeWarning(
                "Warning in WorldMapRenderer::createWorldMapTexture: The volume has no lat/lon data.");
        return;
    }
    if (!sgl::FileUtils::get()->exists(worldMapFilePath)) {
        return;
    }

    const float* latData = nullptr;
    const float* lonData = nullptr;
    volumeData->getLatLonData(latData, lonData);
    int xs = volumeData->getGridSizeX();
    int ys = volumeData->getGridSizeY();

    TIFF* tif = TIFFOpen(worldMapFilePath.c_str(), "r");

    uint16_t tiffEncoding = 0;
    TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffEncoding);
    if (tiffEncoding == COMPRESSION_JBIG) {
        sgl::Logfile::get()->throwError("Error: JBIG encoding is disabled to comply with the license of JBIG-KIT.");
    }

    uint32_t imageWidth, imageHeight;
    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imageHeight);

    minNormX = std::numeric_limits<float>::max();
    maxNormX = std::numeric_limits<float>::lowest();
    minNormY = std::numeric_limits<float>::max();
    maxNormY = std::numeric_limits<float>::lowest();
    for (int y = 0; y < ys; y++) {
        for (int x = 0; x < xs; x++) {
            float x_norm = lonData[x + y * xs] / 360.0f + 0.5f;
            float y_norm = latData[x + y * xs] / 180.0f + 0.5f;
            minNormX = std::min(minNormX, x_norm);
            maxNormX = std::max(maxNormX, x_norm);
            minNormY = std::min(minNormY, y_norm);
            maxNormY = std::max(maxNormY, y_norm);
        }
    }
    xl = uint32_t(std::floor(float(imageWidth - 1) * minNormX));
    yl = uint32_t(std::floor(float(imageHeight - 1) * minNormY));
    xu = uint32_t(std::ceil(float(imageWidth - 1) * maxNormX));
    yu = uint32_t(std::ceil(float(imageHeight - 1) * maxNormY));
    minNormX = float(xl) / float(imageWidth - 1);
    minNormY = float(yl) / float(imageHeight - 1);
    maxNormX = float(xu) / float(imageWidth - 1);
    maxNormY = float(yu) / float(imageHeight - 1);
    regionImageWidth = xu - xl + 1;
    regionImageHeight = yu - yl + 1;

    auto* imageDataRgba = reinterpret_cast<uint32_t*>(_TIFFmalloc(imageWidth * imageHeight * sizeof(uint32_t)));
    if (imageDataRgba == nullptr) {
        sgl::Logfile::get()->throwError("Error: _TIFFmalloc failed!");
    }
    if (!TIFFReadRGBAImage(tif, imageWidth, imageHeight, imageDataRgba, 0)) {
        _TIFFfree(imageDataRgba);
        sgl::Logfile::get()->throwError("Error: TIFFReadRGBAImage failed!");
    }

    sgl::vk::ImageSettings imageSettings{};
    imageSettings.width = uint32_t(regionImageWidth);
    imageSettings.height = uint32_t(regionImageHeight);
    sgl::vk::ImageSamplerSettings imageSamplerSettings{};
    worldMapTexture = std::make_shared<sgl::vk::Texture>(
            renderer->getDevice(), imageSettings, imageSamplerSettings);

    const auto worldMapSizeBytes = size_t(4) * size_t(regionImageWidth) * size_t(regionImageHeight);
    auto* worldMapData = new uint8_t[worldMapSizeBytes];

    for (uint32_t y = 0; y < regionImageHeight; y++) {
        for (uint32_t x = 0; x < regionImageWidth; x++) {
            uint32_t color = imageDataRgba[xl + x + (yl + y) * imageWidth];
            int r = TIFFGetR(color);
            int g = TIFFGetG(color);
            int b = TIFFGetB(color);
            auto writeIdx = (x + y * regionImageWidth) * 4;
            worldMapData[writeIdx] = r;
            worldMapData[writeIdx + 1] = g;
            worldMapData[writeIdx + 2] = b;
            worldMapData[writeIdx + 3] = 255;
        }
    }

    _TIFFfree(imageDataRgba);
    TIFFClose(tif);

    worldMapTexture->getImage()->uploadData(worldMapSizeBytes, worldMapData);
    delete[] worldMapData;

    /*int xs = volumeData->getGridSizeX();
    int ys = volumeData->getGridSizeY();
    sgl::vk::ImageSettings imageSettings{};
    imageSettings.width = uint32_t(xs);
    imageSettings.height = uint32_t(ys);
    sgl::vk::ImageSamplerSettings imageSamplerSettings{};
    worldMapTexture = std::make_shared<sgl::vk::Texture>(
            renderer->getDevice(), imageSettings, imageSamplerSettings);

    const auto worldMapSizeBytes = size_t(4 * xs * ys);
    auto* worldMapData = new uint8_t[worldMapSizeBytes];

    TIFF* tif = TIFFOpen(worldMapFilePath.c_str(), "r");

    uint16_t tiffEncoding = 0;
    TIFFGetFieldDefaulted(tif, TIFFTAG_COMPRESSION, &tiffEncoding);
    if (tiffEncoding == COMPRESSION_JBIG) {
        sgl::Logfile::get()->throwError("Error: JBIG encoding is disabled to comply with the license of JBIG-KIT.");
    }

    uint32_t imageWidth, imageHeight;

    TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imageWidth);
    TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imageHeight);
    auto* imageDataRgba = reinterpret_cast<uint32_t*>(_TIFFmalloc(imageWidth * imageHeight * sizeof(uint32_t)));
    if (imageDataRgba == nullptr) {
        sgl::Logfile::get()->throwError("Error: _TIFFmalloc failed!");
    }
    if (!TIFFReadRGBAImage(tif, imageWidth, imageHeight, imageDataRgba, 0)) {
        _TIFFfree(imageDataRgba);
        sgl::Logfile::get()->throwError("Error: TIFFReadRGBAImage failed!");
    }

    for (int y = 0; y < ys; y++) {
        for (int x = 0; x < xs; x++) {
            float x_norm = lonData[x + y * xs] / 360.0f + 0.5f;
            float y_norm = latData[x + y * xs] / 180.0f + 0.5f;
            auto x_in = uint32_t(std::clamp(int(std::round(x_norm * float(imageWidth))), 0, int(imageWidth) - 1));
            auto y_in = uint32_t(std::clamp(int(std::round(y_norm * float(imageHeight))), 0, int(imageHeight) - 1));
            uint32_t color = imageDataRgba[x_in + y_in * imageWidth];
            int r = TIFFGetR(color);
            int g = TIFFGetG(color);
            int b = TIFFGetB(color);
            int writeIdx = (x + y * xs) * 4;
            worldMapData[writeIdx] = r;
            worldMapData[writeIdx + 1] = g;
            worldMapData[writeIdx + 2] = b;
            worldMapData[writeIdx + 3] = 255;
        }
    }

    _TIFFfree(imageDataRgba);
    TIFFClose(tif);

    worldMapTexture->getImage()->uploadData(worldMapSizeBytes, worldMapData);
    delete[] worldMapData;*/
}

void WorldMapRenderer::recreateSwapchainView(uint32_t viewIdx, uint32_t width, uint32_t height) {
    worldMapRasterPasses.at(viewIdx)->recreateSwapchain(width, height);
}

void WorldMapRenderer::renderViewImpl(uint32_t viewIdx) {
    if (indexBuffer && worldMapTexture) {
        worldMapRasterPasses.at(viewIdx)->render();
    }
}

void WorldMapRenderer::addViewImpl(uint32_t viewIdx) {
    auto worldMapRasterPass = std::make_shared<WorldMapRasterPass>(renderer, viewManager->getViewSceneData(viewIdx));
    if (volumeData) {
        worldMapRasterPass->setVolumeData(volumeData, true);
        worldMapRasterPass->setRenderData(
                indexBuffer, vertexPositionBuffer, vertexNormalBuffer, vertexTexCoordBuffer, worldMapTexture);
    }
    worldMapRasterPass->setLightingFactor(lightingFactor);
    worldMapRasterPasses.push_back(worldMapRasterPass);
}

void WorldMapRenderer::removeViewImpl(uint32_t viewIdx) {
    worldMapRasterPasses.erase(worldMapRasterPasses.begin() + viewIdx);
}

void WorldMapRenderer::renderGuiImpl(sgl::PropertyEditor& propertyEditor) {
    if (propertyEditor.addSliderFloat("Lighting Factor", &lightingFactor, 0.0f, 1.0f)) {
        for (auto& worldMapRasterPass : worldMapRasterPasses) {
            worldMapRasterPass->setLightingFactor(lightingFactor);
        }
        reRender = true;
    }
    if (propertyEditor.addCombo(
            "Resolution", (int*)&worldMapQuality, WORLD_MAP_QUALITY_NAMES, IM_ARRAYSIZE(WORLD_MAP_QUALITY_NAMES))) {
        hasCheckedWorldMapExists = false;
        createWorldMapTexture();
        for (auto& worldMapRasterPass : worldMapRasterPasses) {
            worldMapRasterPass->setRenderData(
                    indexBuffer, vertexPositionBuffer, vertexNormalBuffer, vertexTexCoordBuffer, worldMapTexture);
        }
        reRender = true;
    }
}

void WorldMapRenderer::setSettings(const SettingsMap& settings) {
    Renderer::setSettings(settings);
    if (settings.getValueOpt("lighting_factor", lightingFactor)) {
        for (auto& worldMapRasterPass : worldMapRasterPasses) {
            worldMapRasterPass->setLightingFactor(lightingFactor);
        }
        reRender = true;
    }
    std::string worldMapQualityString;
    if (settings.getValueOpt("world_map_quality", worldMapQualityString)) {
        for (int i = 0; i < IM_ARRAYSIZE(WORLD_MAP_QUALITY_NAMES); i++) {
            if (worldMapQualityString == WORLD_MAP_QUALITY_NAMES[i]) {
                worldMapQuality = WorldMapQuality(i);
                break;
            }
        }
        hasCheckedWorldMapExists = false;
        createWorldMapTexture();
        for (auto& worldMapRasterPass : worldMapRasterPasses) {
            worldMapRasterPass->setRenderData(
                    indexBuffer, vertexPositionBuffer, vertexNormalBuffer, vertexTexCoordBuffer, worldMapTexture);
        }
        reRender = true;
    }
}

void WorldMapRenderer::getSettings(SettingsMap& settings) {
    Renderer::getSettings(settings);
    settings.addKeyValue("lighting_factor", lightingFactor);
    settings.addKeyValue("world_map_quality", WORLD_MAP_QUALITY_NAMES[int(worldMapQuality)]);
}



WorldMapRasterPass::WorldMapRasterPass(sgl::vk::Renderer* renderer, SceneData* sceneData)
        : RasterPass(renderer), sceneData(sceneData), camera(&sceneData->camera) {
    rendererUniformDataBuffer = std::make_shared<sgl::vk::Buffer>(
            device, sizeof(RenderSettingsData),
            VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_GPU_ONLY);
}

void WorldMapRasterPass::setVolumeData(VolumeDataPtr& _volumeData, bool isNewData) {
    this->volumeData = _volumeData;

    sgl::AABB3 aabb = volumeData->getBoundingBoxRendering();
    renderSettingsData.minBoundingBox = aabb.min;
    renderSettingsData.maxBoundingBox = aabb.max;

    dataDirty = true;
}

void WorldMapRasterPass::setRenderData(
        const sgl::vk::BufferPtr& _indexBuffer, const sgl::vk::BufferPtr& _vertexPositionBuffer,
        const sgl::vk::BufferPtr& _vertexNormalBuffer, const sgl::vk::BufferPtr& _vertexTexCoordBuffer,
        const sgl::vk::TexturePtr& _worldMapTexture) {
    indexBuffer = _indexBuffer;
    vertexPositionBuffer = _vertexPositionBuffer;
    vertexNormalBuffer = _vertexNormalBuffer;
    vertexTexCoordBuffer = _vertexTexCoordBuffer;
    worldMapTexture = _worldMapTexture;

    setDataDirty();
}

void WorldMapRasterPass::loadShader() {
    sgl::vk::ShaderManager->invalidateShaderCache();
    std::map<std::string, std::string> preprocessorDefines;
    volumeData->getPreprocessorDefines(preprocessorDefines);
    shaderStages = sgl::vk::ShaderManager->getShaderStages(
            {"WorldMap.Vertex", "WorldMap.Fragment"}, preprocessorDefines);
}

void WorldMapRasterPass::setGraphicsPipelineInfo(sgl::vk::GraphicsPipelineInfo& pipelineInfo) {
    pipelineInfo.setInputAssemblyTopology(sgl::vk::PrimitiveTopology::TRIANGLE_LIST);
    pipelineInfo.setCullMode(sgl::vk::CullMode::CULL_NONE);
    pipelineInfo.setVertexBufferBindingByLocationIndex("vertexPosition", sizeof(glm::vec3));
    pipelineInfo.setVertexBufferBindingByLocationIndex("vertexNormal", sizeof(glm::vec3));
    pipelineInfo.setVertexBufferBindingByLocationIndex("vertexTexCoord", sizeof(glm::vec2));
}

void WorldMapRasterPass::createRasterData(sgl::vk::Renderer* renderer, sgl::vk::GraphicsPipelinePtr& graphicsPipeline) {
    rasterData = std::make_shared<sgl::vk::RasterData>(renderer, graphicsPipeline);
    volumeData->setRenderDataBindings(rasterData);
    rasterData->setIndexBuffer(indexBuffer);
    rasterData->setVertexBuffer(vertexPositionBuffer, "vertexPosition");
    rasterData->setVertexBuffer(vertexNormalBuffer, "vertexNormal");
    rasterData->setVertexBuffer(vertexTexCoordBuffer, "vertexTexCoord");
    rasterData->setStaticBuffer(rendererUniformDataBuffer, "RendererUniformDataBuffer");
    rasterData->setStaticTexture(worldMapTexture, "worldMapTexture");
}

void WorldMapRasterPass::recreateSwapchain(uint32_t width, uint32_t height) {
    framebuffer = std::make_shared<sgl::vk::Framebuffer>(device, width, height);

    sgl::vk::AttachmentState attachmentState;
    attachmentState.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
    attachmentState.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    attachmentState.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
    framebuffer->setColorAttachment(
            (*sceneData->sceneTexture)->getImageView(), 0, attachmentState,
            sceneData->clearColor->getFloatColorRGBA());

    sgl::vk::AttachmentState depthAttachmentState;
    depthAttachmentState.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;
    depthAttachmentState.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
    depthAttachmentState.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
    framebuffer->setDepthStencilAttachment(
            (*sceneData->sceneDepthTexture)->getImageView(), depthAttachmentState, 1.0f);

    framebufferDirty = true;
    dataDirty = true;
}

void WorldMapRasterPass::_render() {
    renderSettingsData.cameraPosition = (*camera)->getPosition();
    rendererUniformDataBuffer->updateData(
            sizeof(RenderSettingsData), &renderSettingsData, renderer->getVkCommandBuffer());
    renderer->insertMemoryBarrier(
            VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_UNIFORM_READ_BIT,
            VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);

    sceneData->switchColorState(RenderTargetAccess::RASTERIZER);
    if (sceneData->useDepthBuffer) {
        sceneData->switchDepthState(RenderTargetAccess::RASTERIZER);
    }

    RasterPass::_render();
}
back to top