updated for design change

This commit is contained in:
matthias@arch 2022-11-14 23:00:13 +01:00
parent c97ab508ac
commit c84c12b9e4
11 changed files with 240 additions and 131 deletions

View File

@ -1,3 +1,3 @@
CompileFlags: # Tweak the parse settings
Add: [ -std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I., -Iexternal, -Irenderer, -Ishader, -Iutility, -I../external, -I../renderer, -I../shader, -I../utility,]
Add: [ -std=c++2a, -Wall, -I/usr/include/freetype2, -I.., -I., -Iexternal, -Irenderer, -Ishader, -Iutility, -Idrawables, -Ivulkan_draw_strategies, -I../external, -I../renderer, -I../shader, -I../utility, -I../drawables, -I../vulkan_draw_strategies ]
# https://clangd.llvm.org/config

View File

@ -2362,7 +2362,7 @@ UML_LIMIT_NUM_FIELDS = 10
# The default value is: NO.
# This tag requires that the tag UML_LOOK is set to YES.
DOT_UML_DETAILS = NO
DOT_UML_DETAILS = YES
# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters
# to display on a single line. If the actual line length exceeds this threshold
@ -2569,4 +2569,4 @@ GENERATE_LEGEND = YES
# plantuml temporary files.
# The default value is: YES.
DOT_CLEANUP = YES
DOT_CLEANUP = NO

View File

@ -1,8 +1,11 @@
#include "shape.hpp"
#include "ds_vk_rectangle.hpp"
#include "print_draw_strategy.hpp"
#include "vulkan_instance.hpp"
#include "renderer2D.hpp"
#include "renderer3D.hpp"
#include "vulkan_settings.hpp"
#include "font.hpp"
#include <chrono>
#include <ratio>
@ -10,14 +13,13 @@
namespace gz::vlk {
int mainLoop() {
LogCreateInfo logCI {
Log log(LogCreateInfo {
.logfile = "main.log",
.storeLog = false,
.prefix = "Main",
.prefixColor = Color::BG_RED,
.timeColor = settings::VULKAN_MESSAGE_TIME_COLOR,
};
Log log(std::move(logCI));
});
gz::SettingsManagerCreateInfo<VULKAN_SETTINGS_MAN_TYPES> smCI {
.filepath = settings::CONFIG_FILE,
@ -27,6 +29,7 @@ namespace gz::vlk {
.throwExceptionWhenNewValueNotAllowed = true,
};
log("Creating instance");
VulkanInstance vulkanInstance(smCI);
vulkanInstance.init();
TextureManager textureManager(vulkanInstance);
@ -34,42 +37,53 @@ namespace gz::vlk {
log("Startup complete. Drawing shapes");
Rectangle rect9(-200, -400, 200, 200, { 0.0f, 1.0f, 0.0f}, "entities/sheep.png");
Rectangle rect1( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "blocks/leaves.png");
Rectangle rect2( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "blocks/crate.png");
Rectangle rect3( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "blocks/dirt.png");
Rectangle rect5( 90, 91, 92, 93, { 1.0f, 0.9f, 0.8f}, "items/sword.png");
Rectangle rect6( 190, 191, 192, 193, { 0.7f, 0.6f, 0.5f}, "items/crossbow.png");
Rectangle rect4(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "blocks/ice.png");
Rectangle rect7( 32, 120, 400, 400, { 0.0f, 0.0f, 1.0f}, "special/hotbar.png");
Rectangle rect8(-600, -600, 800, 400, { 1.0f, 0.0f, 0.0f}, "special/func_nocol.png");
Rectangle rect10(-400, -400, 800, 800, { 0.0f, 1.0f, 0.0f}, "atlas");
r2D.drawShape(&rect1);
r2D.drawShape(&rect2);
r2D.drawShape(&rect3);
r2D.drawShape(&rect4);
r2D.drawShape(&rect5);
r2D.drawShape(&rect6);
r2D.drawShape(&rect7);
r2D.drawShape(&rect8);
r2D.drawShape(&rect9);
r2D.drawShape(&rect10);
log("Drawing complete. Filling r2D with shapes.");
r2D.fillVertexBufferWithShapes();
r2D.fillIndexBufferWithShapes();
BufferManager<Vertex2D, uint32_t> bufferManager(vulkanInstance);
auto createRect = [&r2D, &bufferManager](auto posX, auto posY, auto width, auto height, const auto& texture) {
return Rectangle(posX, posY, width, height, std::make_unique<RectangleTexturedVkDS>(RectangleTexturedVkDS(r2D, bufferManager, texture)));
/* return Rectangle(posX, poxY, width, height, std::make_unique<PrintDrawStrategy<Rectangle>>()); */
};
std::vector<Rectangle> rects;
rects.emplace_back(createRect( 0, 0, 48, 48, "blocks/leaves.png"));
rects.emplace_back(createRect( -190,-191, 192, 193, "blocks/crate.png"));
rects.emplace_back(createRect( 32, 120, 400, 400, "blocks/dirt.png"));
rects.emplace_back(createRect( 90, 91, 92, 93, "items/sword.png"));
rects.emplace_back(createRect( 190, 191, 192, 193, "items/crossbow.png"));
rects.emplace_back(createRect(-600, -600, 800, 400, "blocks/ice.png"));
rects.emplace_back(createRect( 32, 400, 400, 100, "special/hotbar.png"));
rects.emplace_back(createRect(-600, -600, 800, 400, "special/func_nocol.png"));
rects.emplace_back(createRect(-200, -400, 200, 200, "entities/sheep.png"));
rects.emplace_back(createRect(-600, 0, 600, 600, "atlas_0"));
log("Initializing complete.");
Renderer3D r3D(vulkanInstance, textureManager);
/* gz::FontManager fm("fonts"); */
/* Renderer3D r3D(vulkanInstance, textureManager); */
/* gz::vlk::FontManager fm(vulkanInstance, "fonts"); */
/* fm.loadFont("menu.ttf"); */
/* /1* fm.getFaces().at("menu.ttf"). *1/ */
try {
/* fm.getFaces().at("menu.ttf"). */
/* try */
{
uint32_t imageIndex;
/* std::chrono::time_point now = std::chrono::system_clock::now(); */
while (! glfwWindowShouldClose(vulkanInstance.getWindow())) {
bool first2d = false;
int count = 0;
while (!glfwWindowShouldClose(vulkanInstance.getWindow())) {
glfwPollEvents();
imageIndex = vulkanInstance.beginFrameDraw();
r2D.drawFrame(imageIndex);
r3D.drawFrame(imageIndex);
uint32_t currentFrame = vulkanInstance.getCurrentFrame();
r2D.beginFrameDraw(imageIndex, currentFrame);
for (auto& shape : rects) {
shape.draw();
}
if (count > 100) {
/* r2D.drawFrame(imageIndex); */
r2D.endFrameDraw(imageIndex, currentFrame);
/* r3D.drawFrame(imageIndex); */
}
else {
/* r3D.drawFrame(imageIndex); */
r2D.endFrameDraw(imageIndex, currentFrame);
}
count = count < 200 ? ++count : 0;
/* first2d = !first2d; */
vulkanInstance.endFrameDraw(imageIndex);
auto SLEEP_TIME = std::chrono::milliseconds(1000 / vulkanInstance.getSettings().get<uint32_t>("framerate"));
std::this_thread::sleep_for(SLEEP_TIME);
@ -80,10 +94,10 @@ namespace gz::vlk {
}
vulkanInstance.cleanup();
}
catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
}
/* catch (const std::exception& e) { */
/* std::cerr << e.what() << std::endl; */
/* return 1; */
/* } */
return 0;
}

View File

@ -27,6 +27,7 @@ namespace gz::vlk {
class Renderer {
public:
Renderer(VulkanInstance& instance, TextureManager& textureManager) : vk(instance), textureManager(textureManager) {};
TextureManager& getTextureManager() { return textureManager; };
protected:
/**
* @brief Cleanup vulkan resources held by this class

View File

@ -1,7 +1,6 @@
#include "renderer2D.hpp"
#include "exceptions.hpp"
#include "shape.hpp"
#include "vulkan_allocator.hpp"
#include "vulkan_instance.hpp"
#include "texture_manager.hpp"
@ -38,7 +37,7 @@ namespace gz::vlk {
// INIT & CLEANUP
//
Renderer2D::Renderer2D(VulkanInstance& instance, TextureManager& textureManager)
: Renderer(instance, textureManager), shapeBufferManager(instance)
: Renderer(instance, textureManager)
{
LogCreateInfo logCI {
.logfile = "renderer2D.log",
@ -87,6 +86,11 @@ namespace gz::vlk {
//
// SWAPCHAIN DEPENDANT
//
const vk::Extent2D& Renderer2D::getScExtent() const {
return vk.getScExtent();
}
void Renderer2D::initSwapChainDependantResources() {
createRenderPass();
createImageResources();
@ -311,6 +315,8 @@ namespace gz::vlk {
// RENDERING
//
void Renderer2D::beginFrameDraw(uint32_t imageIndex, uint32_t currentFrame) {
this->currentFrame = currentFrame;
this->imageIndex = imageIndex;
commandBuffers[currentFrame].reset();
vk::CommandBufferBeginInfo commandBufferBI {
/* .flags = 0, */
@ -414,48 +420,37 @@ namespace gz::vlk {
/* vk.destroyBuffer(stagingBuffer, stagingBufferMemory); */
/* } */
void Renderer2D::drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame) {
/* rLog.log0("drawShape:", shape->getVertexBufferInfo(), shape->getIndexBufferInfo()); */
commandBuffers[currentFrame].bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline);
void Renderer2D::drawShape(BufferManager<Vertex2D, uint32_t>& bufferManager, const BufferInfo& vertexBufferInfo, const BufferInfo& indexBufferInfo, const uint32_t textureAtlasIndex) {
/* rLog.log0("drawShape:", vertexBufferInfo, indexBufferInfo, "textureAtlasIndex:", textureAtlasIndex); */
auto& cmdBuffer = commandBuffers.at(currentFrame);
cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].pipeline);
uint32_t descriptorCount = 1;
uint32_t firstSet = 0;
uint32_t dynamicOffsetCount = 0;
uint32_t* dynamicOffsets = nullptr;
commandBuffers[currentFrame].bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets);
cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelines[R2Render].layout, firstSet, descriptorCount, &textureManager.getDescriptorSet(), dynamicOffsetCount, dynamicOffsets);
vk::Buffer vertexBuffers[] = { shapeBufferManager.getVertexBuffer(shape->getVertexBufferInfo()) };
vk::DeviceSize vertexOffsets[] = { shape->getVertexBufferInfo().offset };
vk::Buffer vertexBuffers[] = { bufferManager.getVertexBuffer(vertexBufferInfo) };
vk::DeviceSize vertexOffsets[] = { vertexBufferInfo.offset };
uint32_t bindingCount = 1;
uint32_t binding = 0;
commandBuffers[currentFrame].bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets);
cmdBuffer.bindVertexBuffers(binding, bindingCount, vertexBuffers, vertexOffsets);
vk::Buffer indexBuffers = { shapeBufferManager.getIndexBuffer(shape->getIndexBufferInfo()) };
vk::DeviceSize indexOffset = shape->getIndexBufferInfo().offset;
vk::Buffer indexBuffers = { bufferManager.getIndexBuffer(indexBufferInfo) };
vk::DeviceSize indexOffset = indexBufferInfo.offset;
// TODO use correct index type!
commandBuffers[currentFrame].bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32);
cmdBuffer.bindIndexBuffer(indexBuffers, indexOffset, vk::IndexType::eUint32);
uint32_t pushConstant = shape->getTexureAtlasIndex();
uint32_t pushConstant = textureAtlasIndex;
vk::ShaderStageFlags flags(vk::ShaderStageFlagBits::eFragment);
commandBuffers[currentFrame].pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant);
cmdBuffer.pushConstants(pipelines[R2Render].layout, flags, NO_OFFSET, sizeof(uint32_t), &pushConstant);
uint32_t indexCount = shape->getIndexBufferInfo().count;
uint32_t indexCount = indexBufferInfo.count;
uint32_t instanceCount = 1;
uint32_t firstIndex = 0;
uint32_t firstInstance = 0;
commandBuffers[currentFrame].drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance);
cmdBuffer.drawIndexed(indexCount, instanceCount, firstIndex, NO_OFFSET, firstInstance);
}
void Renderer2D::initializeShape(Shape* shape) {
// make indices valid
/* shape->setIndexOffset(shapesVerticesCount); */
shape->normalizeVertices(vk.getScExtent().width, vk.getScExtent().height);
shape->setTextureCoordinates(textureManager);
shape->initalizeBufferInfos(shapeBufferManager);
}
} // namespace gz::vlk

View File

@ -1,8 +1,9 @@
#pragma once
#include "buffer_manager.hpp"
#include "renderer.hpp"
#include "shape.hpp"
#include "text.hpp"
#include "vulkan_allocator.hpp"
#include "vulkan_util.hpp"
@ -54,8 +55,8 @@ namespace gz::vlk {
*/
void endFrameDraw(uint32_t imageIndex, uint32_t currentFrame);
void drawShape(Shape* shape, uint32_t imageIndex, uint32_t currentFrame);
void initializeShape(Shape* shape);
void drawShape(BufferManager<Vertex2D, uint32_t>& bufferManager, const BufferInfo& vertexBufferInfo, const BufferInfo& indexBufferInfo, const uint32_t textureAtlasIndex);
const vk::Extent2D& getScExtent() const;
/**
* @brief Copies the vertices from shapes into the vertex buffer, using a staging buffer
*/
@ -64,6 +65,8 @@ namespace gz::vlk {
/// @}
private:
uint32_t imageIndex;
uint32_t currentFrame;
/**
* @brief Destroy all vulkan objects owned by this object
* @details:
@ -74,11 +77,6 @@ namespace gz::vlk {
*/
void cleanup();
/* std::vector<Shape> shapes; */
/* size_t shapesVerticesCount = 0; */
/* size_t shapesIndicesCount = 0; */
BufferManager<Vertex2D, uint32_t> shapeBufferManager;
/* void recordCommandBuffer(uint32_t imageIndex, uint32_t currentFrame); */
/**

View File

@ -9,8 +9,7 @@
#include <cstdio>
#include <cstring>
#include <gz-util/exceptions.hpp>
#include <gz-util/util/string_concepts.hpp>
#include <gz-util/util/string_conversion.hpp>
#include <gz-util/string/conversion.hpp>
#include <ratio>
#include <glm/glm.hpp>
@ -23,8 +22,9 @@ std::string TextureImageArea::toString() const {
//
// INIT & CLEANUP
//
TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY)
TextureAtlas::TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat)
: vk(instance),
imageFormat(imageFormat),
slotWidth(slotWidth), slotHeight(slotHeight),
slotCountX(slotCountX), slotCountY(slotCountY)
{
@ -59,12 +59,12 @@ void TextureAtlas::cleanup() {
void TextureAtlas::createImageResources() {
vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, vk::Format::eR8G8B8A8Srgb, vk::ImageTiling::eLinear,
vk.createImage(slotWidth * slotCountX, slotHeight * slotCountY, imageFormat, vk::ImageTiling::eLinear,
vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled,
vk::MemoryPropertyFlagBits::eDeviceLocal,
textureImage, textureImageMemory);
vk::CommandBuffer cmdBuffer = vk.beginSingleTimeCommands(POOL_GRAPHICS);
vk.transitionImageLayout(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &cmdBuffer);
vk.transitionImageLayout(textureImage, imageFormat, vk::ImageLayout::eUndefined, vk::ImageLayout::eTransferDstOptimal, &cmdBuffer);
vk::ImageSubresourceRange subresourceRange {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
@ -73,10 +73,10 @@ void TextureAtlas::createImageResources() {
.layerCount = 1,
};
cmdBuffer.clearColorImage(textureImage, vk::ImageLayout::eTransferDstOptimal, &settings::missingTextureColor, 1, &subresourceRange);
vk.transitionImageLayout(textureImage, vk::Format::eR8G8B8A8Srgb, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, &cmdBuffer);
vk.transitionImageLayout(textureImage, imageFormat, vk::ImageLayout::eTransferDstOptimal, vk::ImageLayout::eShaderReadOnlyOptimal, &cmdBuffer);
vk.endSingleTimeCommands(cmdBuffer, POOL_GRAPHICS);
vk.createImageView(vk::Format::eR8G8B8A8Srgb, textureImage, textureImageView, vk::ImageAspectFlagBits::eColor);
vk.createImageView(imageFormat, textureImage, textureImageView, vk::ImageAspectFlagBits::eColor);
vk.createTextureSampler(textureSampler);
}
@ -84,7 +84,7 @@ void TextureAtlas::createImageResources() {
//
// ADD TEXTURE
//
std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height) {
std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16_t width, uint16_t height, uint8_t bytesPerPixel) {
if (freeAreas.empty()) {
throw Exception("No texture slots available", "TextureAtlas::addTexture");
}
@ -151,7 +151,7 @@ std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16
#ifdef LOG_LEVEL_0
tLog.log0("addTexture: Adding texture at position x,y=(", x, y, "), with slotCountX,Y=(", slotsX, slotsY, "), with dimensions w,h=(", width, height, ")");
#endif
blitTextureOnImage(pixels, x, y, width, height);
blitTextureOnImage(pixels, width, height, bytesPerPixel, x, y);
// topleft normalized: slot / slotCount
// bottomright normalized: (slot + (textureSize/slotCountize)) / slotCount
return { glm::vec2(static_cast<float>(x) / slotCountX, static_cast<float>(y) / slotCountY),
@ -160,9 +160,8 @@ std::pair<glm::vec2, glm::vec2> TextureAtlas::addTexture(uint8_t* pixels, uint16
}
void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight) {
constexpr size_t BYTES_PER_PIXEL = 4;
vk::DeviceSize imageSize = textureWidth * textureHeight * BYTES_PER_PIXEL;
void TextureAtlas::blitTextureOnImage(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel, uint16_t slotX, uint16_t slotY) {
vk::DeviceSize imageSize = textureWidth * textureHeight * bytesPerPixel;
vk::Buffer stagingBuffer;
MemoryInfo stagingBufferMemory;
vk.createBuffer(imageSize, vk::BufferUsageFlagBits::eTransferSrc,

View File

@ -3,6 +3,7 @@
#include "vulkan_allocator.hpp"
#include <gz-util/log.hpp>
#include <glm/glm.hpp>
#include <vulkan/vulkan_enums.hpp>
#define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
@ -52,16 +53,25 @@ namespace gz::vlk {
* -# @ref createImageResources "create texture image, view and sampler"
*
* The textureImage will have the dimensions slotWidth * slotCountX, slotHeight * slotCountY
*
* @param instance Reference to a VulkanInstance
* @param slotWidth Width of a texture slot in pixels
* @param slotHeight Height of a texture slot in pixels
* @param slotCountX Number of slots in a row
* @param slotCountY Number of slots in a column
* @param imageFormat The format of the image. Must match the bytesPerPixel parameter in addTexture()
*/
TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY);
TextureAtlas(VulkanInstance& instance, uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb);
/**
* @brief Add a texture to the atlas
* @param pixels Buffer containing the bytes. Size of the buffer must be `textureWidth * textureHeight * bytesPerPixel`
* @param textureWidth Width in pixels
* @param textureHeight Height in pixels
* @param bytesPerPixel Number of bytes per pixel. Must be compatible to the imageFormat passed to the constructor
* @returns The topleft and bottomright texture coordinates in the image of the atlas
* @throws Exception when there is no space in the atlas to add the texture
*/
std::pair<glm::vec2, glm::vec2> addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight);
[[ nodiscard ]] std::pair<glm::vec2, glm::vec2> addTexture(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel);
const vk::ImageView& getTextureImageView() const { return textureImageView; }
const vk::Sampler& getTextureSampler() const { return textureSampler; }
@ -75,13 +85,14 @@ namespace gz::vlk {
private:
VulkanInstance& vk;
void blitTextureOnImage(uint8_t* pixels, uint16_t slotX, uint16_t slotY, uint16_t textureWidth, uint16_t textureHeight);
void blitTextureOnImage(uint8_t* pixels, uint16_t textureWidth, uint16_t textureHeight, uint8_t bytesPerPixel, uint16_t slotX, uint16_t slotY);
/**
* @brief Create textureImage, textureImageView and textureSampler
* @details
* the textureImage is created with missingTextureColor and then transitioned to SHADER_READ_ONLY_OPTIMAL layout.
*/
void createImageResources();
vk::Format imageFormat;
vk::Image textureImage;
MemoryInfo textureImageMemory;
vk::ImageView textureImageView;

View File

@ -1,10 +1,19 @@
#include "texture_manager.hpp"
#include "exceptions.hpp"
#include "vulkan_instance.hpp"
#include "vulkan_util.hpp"
#include <glm/detail/qualifier.hpp>
#include <glm/glm.hpp>
#include <gz-util/exceptions.hpp>
#include <gz-util/string/to_string.hpp>
namespace gz::vlk {
std::string TextureInfo::toString() const {
return "<atlas: " + gz::toString(atlas) + ", tl: " + gz::toString(texCoordTopLeft) + ", br: " + gz::toString(texCoordBottomRight) + ">";
}
TextureManager::TextureManager(VulkanInstance& instance)
: vk(instance)
{
@ -18,7 +27,7 @@ namespace gz::vlk {
tLog = Log(std::move(logCI));
vk.registerCleanupCallback(std::bind(&TextureManager::cleanup, this));
atlases.emplace_back(TextureAtlas(vk, 24, 24, 24, 24));
addAtlas(24, 24, 24, 24);
createDescriptorResources();
VulkanInstance::registerObjectUsingVulkan("TextureManager",
&descriptorSetLayout, &descriptorPool, &descriptorSet);
@ -35,31 +44,75 @@ namespace gz::vlk {
}
void TextureManager::getTexCoords(const std::string& textureName, glm::vec2& texCoords) {
if (!textureInfos.contains(textureName)) {
loadTextureFromFile(textureName);
TextureAtlasIndex TextureManager::addAtlas(uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat) {
TextureAtlasIndex index = atlases.size();
atlases.emplace_back(TextureAtlas(vk, slotWidth, slotHeight, slotCountX, slotCountY, imageFormat));
// add special "atlas_X" texture
textures.insert( { std::string("atlas_") + gz::toString(index), index });
textureInfos.emplace_back(TextureInfo{ .atlas = index, .texCoordTopLeft = glm::vec2(0, 0), .texCoordBottomRight = glm::vec2(1, 1) });
return index;
}
void TextureManager::isValidTexture(const Texture& texture) const {
if (texture < textureInfos.size()) {
return;
}
else {
throw InvalidArgument("Texture handle '" + gz::toString(texture) + "' is invalid.", "TextureManager::isValidTexture");
}
TextureInfo& texI = textureInfos[textureName];
tLog.log0("getTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight);
}
Texture TextureManager::getTexture(const std::string& textureName) {
if (!textures.contains(textureName)) {
return loadTextureFromFile(textureName);
}
else {
return textures.at(textureName);
}
}
void TextureManager::setTexCoords(const Texture& texture, glm::vec2& texCoords) {
isValidTexture(texture);
TextureInfo& texI = textureInfos.at(texture);
tLog.log0("setTexCoords", texCoords, "textureInfo: topleft", texI.texCoordTopLeft, "botright", texI.texCoordBottomRight);
texCoords = texI.texCoordTopLeft + texCoords * (texI.texCoordBottomRight - texI.texCoordTopLeft);
}
void TextureManager::loadTextureFromFile(const std::string& textureName) {
//
// ATLASES
//
const TextureAtlas& TextureManager::getTextureAtlas(const Texture& texture) const {
isValidTexture(texture);
return atlases.at(textureInfos.at(texture).atlas);
}
uint32_t TextureManager::getTextureAtlasIndex(const Texture& texture) const {
isValidTexture(texture);
return textureInfos.at(texture).atlas;
}
Texture TextureManager::loadTextureFromFile(const std::string& textureName) {
int textureWidth, textureHeight, textureChannels;
stbi_uc* pixels = stbi_load(("textures/" + textureName).c_str(), &textureWidth, &textureHeight, &textureChannels, STBI_rgb_alpha);
if (!pixels) {
throw Exception("Failed to load texture: textures/" + textureName, "TextureManager::loadTexture");
}
TextureInfo& texI = textureInfos[textureName];
Texture texture = textureInfos.size();
textureInfos.emplace_back();
TextureInfo& texI = textureInfos.back();
texI.atlas = 0;
// TODO
auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight);
static constexpr uint8_t BYTES_PER_PIXEL = 4;
auto texCoords = atlases.at(0).addTexture(pixels, textureWidth, textureHeight, BYTES_PER_PIXEL);
stbi_image_free(pixels);
tLog.log0("TextureManager::loadTextureFromFile: TexCoords of new image:", texCoords.first, texCoords.second);
texI.texCoordTopLeft = std::move(texCoords.first),
texI.texCoordBottomRight = std::move(texCoords.second),
tLog.log0("TextureManager::loadTextureFromFile: After loading:", atlases.at(0));
tLog.log0("loadTextureFromFile: textureInfos", textureInfos);
return texture;
}

View File

@ -4,20 +4,34 @@
#include "stb_image.h"
#include <gz-util/util/string.hpp>
#include <gz-util/string/utility.hpp>
#define LOG_SUBLOGS
#include <gz-util/log.hpp>
#include <glm/fwd.hpp>
#include <unordered_map>
namespace gz::vlk {
// TODO use this texture handle
using Texture = uint32_t;
using TextureAtlasIndex = uint32_t;
struct TextureInfo {
uint32_t atlas;
TextureAtlasIndex atlas;
glm::vec2 texCoordTopLeft;
glm::vec2 texCoordBottomRight;
std::string toString() const;
};
/// Defined in vulkan_instance.hpp
class VulkanInstance;
/**
* @details
* Manages multiple @ref TextureAtlas "texture atlases", which each hold one image that contains muiltiple textures
* @section tm_descriptors
* The TextureManager has provides a descriptor set for accessing the texture atlases:
* Bindings:
* -0. Array of combined image samplers (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) of length (getAtlasCount())
*/
class TextureManager {
public:
/**
@ -27,48 +41,72 @@ namespace gz::vlk {
* -# @ref createDescriptor "create descriptor set layout, pool and set
*/
TextureManager(VulkanInstance& instance);
void getTexCoords(const std::string& textureName, glm::vec2& texCoords);
/**
* @brief Get a texture handle for a texture. Loads the texture from file if not already loaded
*/
Texture getTexture(const std::string& textureName);
/**
* @brief Set the texture coordiantes for the texture
* @details
* @param texCoords
* The texture coordinates to set. texCoords should be from (0,0) to (1,1) and will be
* set to the according texture coordinate of texture.
*/
void setTexCoords(const Texture& texture, glm::vec2& texCoords);
/* void getTexCoords(const std::string& textureName, glm::vec2& texCoords); */
const vk::DescriptorSet& getDescriptorSet() const { return descriptorSet; }
const vk::DescriptorSetLayout& getDescriptorSetLayout() const { return descriptorSetLayout; }
// TODO
/// @todo take texture as argument
const TextureAtlas& getTextureAtlas() const { return atlases.at(0); }
/**
* @brief Get the atlas that texture is in
* @throws InvalidArgument if the texture is not present
*/
const TextureAtlas& getTextureAtlas(const Texture& texture) const;
/* const TextureAtlas& getTextureAtlas(const std::string& texture) const; */
/**
* @brief Get the index of the atlas that texture is in
* @throws InvalidArgument if the texture is not present
*/
TextureAtlasIndex getTextureAtlasIndex(const Texture& texture) const;
/* TextureAtlasIndex getTextureAtlasIndex(const std::string& texture) const; */
/**
* @brief Get the number of atlases.
* @details Needed for specialization constant in shaders using sampler arrays
*/
uint32_t getAtlasCount() const { return static_cast<uint32_t>(atlases.size()); }
/**
* @brief Add a new atlas and a special "atlas_X" texture (where X is the returned index)
*/
TextureAtlasIndex addAtlas(uint16_t slotWidth, uint16_t slotHeight, uint16_t slotCountX, uint16_t slotCountY, vk::Format imageFormat = vk::Format::eR8G8B8A8Srgb);
private:
void cleanup();
void loadTextureFromFile(const std::string& textureName);
// TODO index currently has no meaning
/**
* @brief Check if texture handle is valid
* @throws InvalidArgument if texture is not valid
*/
void isValidTexture(const Texture& texture) const;
Texture loadTextureFromFile(const std::string& textureName);
std::vector<TextureAtlas> atlases;
/**
* @brief Contains textureName : TextureInfo pairs
* @brief Contains textureName : Texture pairs
*/
util::unordered_string_map<TextureInfo> textureInfos;
util::unordered_string_map<Texture> textures;
std::vector<TextureInfo> textureInfos;
VulkanInstance& vk;
Log tLog;
/**
* @name Create desciptors
* @details These functions create a desciptor with bindings for
* -# Array of combined image samplers (DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER) at binding 0. Array length is atlas count
* @{
*/
/**
* @name Create desciptors
* @see tm_descriptors
*/
/// @{
vk::DescriptorSetLayout descriptorSetLayout;
vk::DescriptorPool descriptorPool;
vk::DescriptorSet descriptorSet;
/* std::vector<vk::DescriptorSet> descriptorSets; */
/**
* @brief Create a descriptor set, layout and pool
* @details
* Bindings:
* -0. texture sampler array COMBINED_IMAGE_SAMPLER[atlasCount]
*/
void createDescriptorResources();
/* const uint32_t bindingCombinedImageSampler = 1; */
/**
* @}
*/
/// @}
};

View File

@ -2,7 +2,7 @@
#define VULKAN_HPP_NO_CONSTRUCTORS
#include <vulkan/vulkan.hpp>
#include <gz-util/util/string_conversion.hpp>
#include <gz-util/string/to_string.hpp>
#include <glm/gtc/matrix_transform.hpp>