updated for design change
This commit is contained in:
parent
c97ab508ac
commit
c84c12b9e4
@ -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
|
||||
|
@ -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
|
||||
|
90
src/main.cpp
90
src/main.cpp
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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); */
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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; */
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
/// @}
|
||||
|
||||
|
||||
};
|
||||
|
@ -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>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user