Moved to strategy design patter for 2D drawables

This commit is contained in:
matthias@arch 2022-11-14 22:55:14 +01:00
parent a367fa9cd6
commit 4d1f8bb3c7
14 changed files with 359 additions and 139 deletions

View File

@ -0,0 +1,14 @@
#pragma once
namespace gz {
/**
* @brief Base class for draw strategies for type T
*/
template<typename T>
class DrawStrategy {
public:
virtual void draw() = 0;
virtual void update(const T&) = 0;
virtual ~DrawStrategy() {};
};
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "draw_strategy_base.hpp"
#include <iostream>
#include <gz-util/string/to_string.hpp>
namespace gz {
/**
* @brief Print the object to stdout when draw() is called
* @details
* When update() is called, to object is converted to string and stored in tAsString.
* When draw() is called, tAsString is passed to `std::cout`.
*/
template<gz::ConvertibleToString T>
class PrintDrawStrategy : public DrawStrategy<T> {
public:
void draw() override;
void update(const T& t) override;
private:
std::string tAsString;
};
template<gz::ConvertibleToString T>
void PrintDrawStrategy<T>::draw() {
std::cout << tAsString << '\n';
}
template<gz::ConvertibleToString T>
void PrintDrawStrategy<T>::update(const T& t) {
tAsString = gz::toString(t);
}
} // namespace gz

View File

@ -0,0 +1,23 @@
#include "rectangle.hpp"
#include "texture_manager.hpp"
#include <gz-util/string/to_string.hpp>
namespace gz {
Rectangle::Rectangle(float left, float top, float width, float height, std::unique_ptr<DrawStrategy<Rectangle>>&& drawStrategy)
: position(left, top), size(width, height), drawStrategy(std::move(drawStrategy))
{
update();
}
std::string Rectangle::toString() const {
return "<Rectangle pos: " + gz::toString(position) + ", size: " + gz::toString(size) + ">";
}
void Rectangle::update() {
drawStrategy->update(*this);
}
} // namespace gz::vlk

View File

@ -0,0 +1,27 @@
#pragma once
#include "vertex.hpp"
#include "draw_strategy_base.hpp"
#include <memory>
namespace gz {
class Rectangle {
public:
Rectangle(float top, float left, float width, float height, std::unique_ptr<DrawStrategy<Rectangle>>&& drawStrategy);
inline const glm::vec2& getPosition() const { return position; }
inline void setPosition(const glm::vec2& v) { position = v; }
inline void setPosition(glm::vec2&& v) { position = std::move(v); }
inline const glm::vec2& getSize() const { return size; }
inline void setSize(const glm::vec2& v) { size = v; }
inline void setSize(glm::vec2&& v) { size = std::move(v); }
void draw() { drawStrategy->draw(); };
std::string toString() const;
private:
void update();
glm::vec2 position;
glm::vec2 size;
std::unique_ptr<DrawStrategy<Rectangle>> drawStrategy;
};
} // namespace gz

25
src/drawables/text.cpp Normal file
View File

@ -0,0 +1,25 @@
#include "text.hpp"
#include "font.hpp"
namespace gz::vlk {
Text::Text(Font font, std::string&& text)
: text(std::move(text)), font(font)
{
}
std::string Text::toString() const {
return "<Text: '" + text + "'>";
}
void Text::setTextureCoordinates(FontManager& fontManager) {
for (char c : text) {
fontManager.getTexCoords(font, c, vertices[0].texCoord);
fontManager.getTexCoords(font, c, vertices[1].texCoord);
fontManager.getTexCoords(font, c, vertices[2].texCoord);
fontManager.getTexCoords(font, c, vertices[3].texCoord);
}
// must be after getTexCoords, since that might load the texture
}
} // namespace gz::vlk

25
src/drawables/text.hpp Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "buffer_manager.hpp"
#include <string>
#include "font.hpp"
namespace gz::vlk {
class Text {
public:
Text(Font font, std::string&& text);
private:
std::string text;
void setTextureCoordinates(FontManager& fontManager);
uint32_t textureAtlasIndex;
Font font;
std::vector<Vertex2D> vertices;
std::vector<uint32_t> indices;
BufferInfo vertexBufferInfo;
BufferInfo indexBufferInfo;
std::string toString() const;
};
} // namespace gz::vlk

View File

@ -1,72 +0,0 @@
#include "shape.hpp"
#include "buffer_manager.hpp"
#include "texture_manager.hpp"
namespace gz::vlk {
Rectangle::Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color, std::string&& texture)
: top(top), left(left), width(width), height(height), color(color)
{
this->texture = std::move(texture);
generateVertices();
}
void Rectangle::generateVertices() {
vertices.clear();
vertices.emplace_back(Vertex2D{glm::vec2(top, left), color, glm::vec2(0, 0)});
vertices.emplace_back(Vertex2D{glm::vec2(top, left + width), color, glm::vec2(0, 1)});
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left + width), color, glm::vec2(1, 1)});
vertices.emplace_back(Vertex2D{glm::vec2(top + height, left), color, glm::vec2(1, 0)});
indices = { 0, 1, 2, 2, 3, 0 };
/* indices = { 2, 1, 0, 2, 3, 0 }; */
}
void Shape::setIndexOffset(uint32_t offset) {
for (size_t i = 0; i < indices.size(); i++) {
indices[i] += offset;
}
}
void Shape::normalizeVertices(float width, float height) {
for (size_t i = 0; i < vertices.size(); i++) {
vertices[i].pos.x /= width;
vertices[i].pos.y /= height;
}
}
void Shape::initalizeBufferInfos(BufferManager<Vertex2D, uint32_t>& bufferManager) {
assert(vertexBufferInfo.index == BUFFER_NOT_INITIALIZED);
std::tie(vertexBufferInfo, indexBufferInfo) = bufferManager.addVertices(vertices, indices);
}
void Rectangle::setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {
vertices[0].texCoord = topLeft;
vertices[1].texCoord.x = bottomRight.x;
vertices[1].texCoord.y = topLeft.y;
vertices[2].texCoord = bottomRight;
vertices[3].texCoord.x = topLeft.x;
vertices[3].texCoord.y = bottomRight.y;
}
void Rectangle::setTextureCoordinates(TextureManager& textureManager) {
if (texture != "atlas") {
textureManager.getTexCoords(texture, vertices[0].texCoord);
textureManager.getTexCoords(texture, vertices[1].texCoord);
textureManager.getTexCoords(texture, vertices[2].texCoord);
textureManager.getTexCoords(texture, vertices[3].texCoord);
textureAtlasIndex = textureManager.getTextureAtlasIndex(texture);
}
else {
textureAtlasIndex = 0;
}
// must be after getTexCoords, since that might load the texture
}
} // namespace gz::vlk

View File

@ -1,67 +0,0 @@
#pragma once
#include "buffer_manager.hpp"
#include "vertex.hpp"
#include <cstdint>
namespace gz::vlk {
// defined in texture_manager.hpp
class TextureManager;
/**
* @brief Base class for shapes
* @details
* In this implementaiton, a shape is a 2D object made of several vertices.
* Each shape has its own vertex and index buffers, which are managed by a BufferManager.
* Their texture is managed by a TextureManager.
* @todo free resources and rule of 5
*/
class Shape {
public:
const std::vector<Vertex2D>& getVertices() const { return vertices; }
const std::vector<uint32_t>& getIndices() const { return indices; }
const std::string& getTexture() const { return texture; }
uint32_t getTexureAtlasIndex() const { return textureAtlasIndex; }
const BufferInfo& getVertexBufferInfo() const { return vertexBufferInfo; }
const BufferInfo& getIndexBufferInfo() const { return indexBufferInfo; }
/**
* @brief Add an offset to all indices (useful when putting multiple shapes in the same vertex buffer)
*/
void setIndexOffset(uint32_t offset);
/**
* @brief Normalize the vertices, so that (1, 1) is (width, height)
*/
void normalizeVertices(float width, float height);
virtual void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) {};
virtual void setTextureCoordinates(TextureManager& textureManager) {};
bool buffersInitalized() const { return vertexBufferInfo.index != BUFFER_NOT_INITIALIZED; }
void initalizeBufferInfos(BufferManager<Vertex2D, uint32_t>& bufferManager);
virtual ~Shape() {};
protected:
std::string texture = "texture.png";
uint32_t textureAtlasIndex;
std::vector<Vertex2D> vertices;
std::vector<uint32_t> indices;
BufferInfo vertexBufferInfo;
BufferInfo indexBufferInfo;
};
class Rectangle : public Shape {
public:
Rectangle(float top, float left, uint32_t width, uint32_t height, glm::vec3 color, std::string&& texture);
void setTextureCoordinates(glm::vec2 topLeft, glm::vec2 bottomRight) override;
/**
* @brief Get the correct texture coordinates from a TextureManager
* @details
* If the texture is "atlas", the texture coordinates will remain at (0,0), (0,1), (1, 1), (1, 0) and
* thus show the entire atlas.
*/
void setTextureCoordinates(TextureManager& textureManager) override;
private:
float top, left;
uint32_t width, height;
glm::vec3 color;
void generateVertices();
};
} // namespace gz::vlk

View File

@ -0,0 +1,34 @@
#include "ds_vk_base.hpp"
#include "renderer2D.hpp"
namespace gz::vlk {
void VulkanDS::setIndexOffset(uint32_t offset) {
for (size_t i = 0; i < indices.size(); i++) {
indices[i] += offset;
}
}
void VulkanDS::normalizeVertices(float width, float height) {
for (size_t i = 0; i < vertices.size(); i++) {
vertices[i].pos.x /= width;
vertices[i].pos.y /= height;
}
}
void VulkanDS::updateBufferInfos() {
assert(vertexBufferInfo.index == BUFFER_NOT_INITIALIZED);
std::tie(vertexBufferInfo, indexBufferInfo) = bufferManager.get().addVertices(vertices, indices);
}
VulkanTexturedDS::VulkanTexturedDS(TextureManager& textureManager, Texture texture)
: texture(texture), textureAtlasIndex(textureManager.getTextureAtlasIndex(texture)), textureManager(textureManager)
{
}
} // namespace gz::vlk

View File

@ -0,0 +1,62 @@
#pragma once
#include "draw_strategy_base.hpp"
#include "buffer_manager.hpp"
#include "texture_manager.hpp"
namespace gz::vlk {
class Renderer2D;
class TextureManager;
/**
* @brief Draw rectangles using the vlk::Renderer2D
*
* @todo free resources and rule of 5
*/
class VulkanDS {
public:
VulkanDS(Renderer2D& renderer, BufferManager<Vertex2D, uint32_t>& bufferManager)
: renderer(renderer), bufferManager(bufferManager) {};
const std::vector<Vertex2D>& getVertices() const { return vertices; }
const std::vector<uint32_t>& getIndices() const { return indices; }
const BufferInfo& getVertexBufferInfo() const { return vertexBufferInfo; }
const BufferInfo& getIndexBufferInfo() const { return indexBufferInfo; }
/**
* @brief Add an offset to all indices (useful when putting multiple shapes in the same vertex buffer)
*/
void setIndexOffset(uint32_t offset);
/**
* @brief Normalize the vertices, so that (1, 1) is (width, height)
*/
void normalizeVertices(float width, float height);
inline bool buffersInitalized() const { return vertexBufferInfo.index != BUFFER_NOT_INITIALIZED; }
virtual ~VulkanDS() {};
protected:
/**
* @todo free resources
*/
void updateBufferInfos();
std::vector<Vertex2D> vertices;
std::vector<uint32_t> indices;
BufferInfo vertexBufferInfo;
BufferInfo indexBufferInfo;
std::reference_wrapper<Renderer2D> renderer;
std::reference_wrapper<BufferManager<Vertex2D, uint32_t>> bufferManager;
};
class VulkanTexturedDS {
public:
VulkanTexturedDS(TextureManager& textureManager, Texture texture);
inline const Texture& getTexture() const { return texture; }
inline uint32_t getTexureAtlasIndex() const { return textureAtlasIndex; }
virtual ~VulkanTexturedDS() {};
protected:
Texture texture;
TextureAtlasIndex textureAtlasIndex;
std::reference_wrapper<TextureManager> textureManager;
};
} // namespace gz::vlk

View File

@ -0,0 +1,45 @@
#include "ds_vk_rectangle.hpp"
#include "texture_manager.hpp"
#include "renderer2D.hpp"
namespace gz::vlk {
RectangleTexturedVkDS::RectangleTexturedVkDS(Renderer2D& renderer, BufferManager<Vertex2D, uint32_t>& bufferManager, const std::string& textureName)
: VulkanDS(renderer, bufferManager),
VulkanTexturedDS(this->renderer.get().getTextureManager(), this->renderer.get().getTextureManager().getTexture(textureName))
{
}
void RectangleTexturedVkDS::update(const Rectangle& rect) {
// update buffers
vertices.clear();
const auto& pos = rect.getPosition();
const auto& size = rect.getSize();
glm::vec3 color(0, 0, 0);
// topleft, topright, botright, botleft -> clockwise
vertices.emplace_back(Vertex2D{pos, color, glm::vec2(0, 0)});
vertices.emplace_back(Vertex2D{glm::vec2(pos.x + size.x, pos.y), color, glm::vec2(1, 0)});
vertices.emplace_back(Vertex2D{glm::vec2(pos.x + size.x, pos.y + size.y), color, glm::vec2(1, 1)});
vertices.emplace_back(Vertex2D{glm::vec2(pos.x, pos.y + size.y), color, glm::vec2(0, 1)});
/* indices = { 0, 1, 2, 2, 3, 0 }; */
/* indices = { 2, 1, 0, 2, 3, 0 }; */
indices = { 0, 2, 1, 0, 3, 2 };
normalizeVertices(renderer.get().getScExtent().width, renderer.get().getScExtent().height);
// update texture
TextureManager& tm = textureManager.get();
tm.setTexCoords(texture, vertices[0].texCoord);
tm.setTexCoords(texture, vertices[1].texCoord);
tm.setTexCoords(texture, vertices[2].texCoord);
tm.setTexCoords(texture, vertices[3].texCoord);
updateBufferInfos();
}
void RectangleTexturedVkDS::draw() {
renderer.get().drawShape(bufferManager.get(), vertexBufferInfo, indexBufferInfo, textureAtlasIndex);
}
} // namespace gz::vlk

View File

@ -0,0 +1,27 @@
#pragma once
#include "rectangle.hpp"
#include "ds_vk_base.hpp"
namespace gz::vlk {
/**
* @details
* Rectangle has vertex and index buffers from vlk::BufferManager
* and a texture from vlk::TextureManager
*/
class RectangleTexturedVkDS : public DrawStrategy<Rectangle>, public VulkanDS, public VulkanTexturedDS {
public:
RectangleTexturedVkDS(Renderer2D& renderer, BufferManager<Vertex2D, uint32_t>& bufferManager, const std::string& textureName);
/**
* @todo
*/
void draw() override;
/**
* @brief Set the new vertices and indices
*/
void update(const Rectangle& rect) override;
private:
void setTextureCoordinates();
};
} // namespace gz::vlk

View File

@ -0,0 +1,19 @@
#include "ds_vk_text.hpp"
namespace gz::vlk {
void TextVkDS::draw() {
}
void TextVkDS::update(const Text& text) {
}
void TextVkDS::setTextureCoordinates() {
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "text.hpp"
#include "ds_vk_base.hpp"
namespace gz::vlk {
class TextVkDS : public DrawStrategy<Text>, public VulkanDS, public VulkanTexturedDS {
public:
TextVkDS(Renderer2D& renderer, BufferManager<Vertex2D, uint32_t>& bufferManager, const std::string& textureName);
/**
* @todo
*/
void draw() override;
/**
* @brief Set the new vertices and indices
*/
void update(const Text& text) override;
private:
void setTextureCoordinates();
};
} // namespace gz::vlk