~untaugh/meteormaker

dfff91780211421520f817d0a1141a038542b035 — Oskar Rundgren a month ago b771c09 master
Add text buffer class, and use ECS for text rendering.
M meteormaker.cc => meteormaker.cc +56 -25
@@ 2,7 2,6 @@

#include <cstdio>

#include <string>
#include <vector>
#include <thread>
#include <chrono>


@@ 15,6 14,7 @@
#include "texture.h"
#include "mouse.h"
#include "time.h"
#include "text_buffer.h"

int main(int argc, char **argv)
{


@@ 35,6 35,8 @@ void MeteorMaker::init()
  window = glfwCreateWindow(WIDTH, HEIGHT, "MeteorMaker", nullptr, nullptr);
  glfwSetCursorPosCallback(window, cursorPositionCallback);
  glfwSetMouseButtonCallback(window, mouseButtonCallback);
  glfwSetCharCallback(window, charCallback);
  glfwSetKeyCallback(window, keyCallback);
  render.init(window);
}



@@ 52,6 54,10 @@ void MeteorMaker::generateLoop(bool &finished, Meteor &meteor)

void MeteorMaker::loop()
{
  auto textBuffer = registry.create();
  registry.emplace<TextBuffer>(textBuffer);
  registry.emplace<TextInfo>(textBuffer, 0, 0, 1, true);

  auto meteorEntity = registry.create();
  registry.emplace<Position>(meteorEntity, 0, 0, 0);
  registry.emplace<Rotation>(meteorEntity, 0, 0, 0);


@@ 111,28 117,16 @@ void MeteorMaker::loop()
  glfwSetWindowUserPointer(window, &registry);

  auto meteorText = registry.create();
  auto &metInfo = registry.emplace<TextRenderCreateInfo>(meteorText);
  metInfo.size = 1.0f;
  metInfo.text = "Meteor";
  metInfo.x = 1.0f;
  metInfo.y = 0.0f;
  uint32_t metText1 = render.addText(metInfo);
  metInfo.size = 2.0f;
  metInfo.text = "rekaM";
  metInfo.x = 1.0f;
  metInfo.y = -0.5f;
  uint32_t metText2 = render.addText(metInfo);
  (void)metText1;
  (void)metText2;

  TextRenderCreateInfo fpsInfo = {};
  fpsInfo.size = 5.0f;
  fpsInfo.text = "123";
  fpsInfo.x = 0.9f;
  fpsInfo.y = -0.9f;
  uint32_t fpsText = render.addText(fpsInfo);
  (void)fpsText;
  render.textCommand();
  auto &meteorBuffer = registry.emplace<TextBuffer>(meteorText);
  registry.emplace<TextInfo>(meteorText, 1, 0,1);
  std::string s("Meteor\nMaker");
  meteorBuffer.set(s);

  auto fpsText = registry.create();
  auto &fpsBuffer = registry.emplace<TextBuffer>(fpsText);
  registry.emplace<TextInfo>(fpsText, 2, -4, 1);

  render.textCommand(registry);

  Physics physics;
  StarSystem starSystem;


@@ 151,10 145,13 @@ void MeteorMaker::loop()

    if (time.updateFps())
    {
      fpsInfo.text = std::to_string((uint32_t)time.fps());
      render.updateText(fpsText, fpsInfo);
      auto s = std::to_string((uint32_t)time.fps());

      fpsBuffer.set(s);
    }

    render.updateText(registry);

    physics.run(registry, time.getSeconds());
    starSystem.run(registry);



@@ 218,4 215,38 @@ void MeteorMaker::mouseButtonCallback(GLFWwindow* window, int button, int action
  }
}

void MeteorMaker::keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
  auto *registry = (entt::registry*)glfwGetWindowUserPointer(window);

  auto view = registry->view<TextBuffer>();

  for (auto &entity : view)
  {
    auto &textBuffer = registry->get<TextBuffer>(entity);

    if (key == GLFW_KEY_BACKSPACE && action == GLFW_PRESS)
      textBuffer.del();
    if (key == GLFW_KEY_ENTER && action == GLFW_PRESS)
      textBuffer.add('\n');
  }
}

void MeteorMaker::charCallback(GLFWwindow* window, unsigned int codepoint)
{
  auto *registry = (entt::registry*)glfwGetWindowUserPointer(window);

  auto view = registry->view<TextBuffer, TextInfo>();

  for (auto &entity : view)
  {
    auto &textInfo = registry->get<TextInfo>(entity);
    if (textInfo.selected)
    {
      auto &textBuffer = registry->get<TextBuffer>(entity);
      textBuffer.add(codepoint);
    }
  }
}

}

M meteormaker.h => meteormaker.h +3 -0
@@ 2,6 2,7 @@
#define METEORMAKER_H

#include <vector>
#include <string>

#include <GLFW/glfw3.h>
#include <entt/entt.hpp>


@@ 36,6 37,8 @@ class MeteorMaker
  entt::registry registry;
  static void cursorPositionCallback(GLFWwindow *window, double xpos, double ypos);
  static void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
  static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
  static void charCallback(GLFWwindow* window, unsigned int codepoint);
};

}

M outline.h => outline.h +5 -0
@@ 176,6 176,11 @@ public:
    }
  }

  ~OutlinePipeline()
  {
    vkDestroyPipeline(device, pipeline, nullptr);
  }

  const VkPipeline &get() const { return pipeline; };

private:

M render_vulkan.cc => render_vulkan.cc +92 -19
@@ 15,6 15,7 @@
#include "sprite.h"
#include "texture.h"
#include "meteor.h"
#include "text_buffer.h"

namespace meteormaker
{


@@ 340,17 341,10 @@ void RenderVulkan::update(entt::registry &registry)
  }
}

uint32_t RenderVulkan::addText(TextRenderCreateInfo &createInfo)
{
  uint32_t textId = renderTextsCount++;
  textInfos[textId] = createInfo;
  return textId;
}

void RenderVulkan::textCommand()
void RenderVulkan::textCommand(entt::registry &registry)
{
  createTextBuffers();
  textCommands = createTextCommands();
  textCommands = createTextCommands(registry);
}

uint32_t RenderVulkan::loadTextureArray(const char** paths, size_t count)


@@ 378,17 372,16 @@ void RenderVulkan::updateTexture()
  textureDescriptorSet = createTextureDescriptorSet();
}

void RenderVulkan::updateText(uint32_t id, TextRenderCreateInfo &createInfo)
void RenderVulkan::updateText(entt::registry &registry)
{
  vkDeviceWaitIdle(engine.device);
  textInfos[id] = createInfo;

  for (auto &command : textCommands)
  {
    vkFreeCommandBuffers(engine.device, engine.commandPool, 1, &command);
  }

  textCommands = createTextCommands();
  textCommands = createTextCommands(registry);
}

VkInstance EngineVulkan::createInstance() const


@@ 1121,6 1114,49 @@ VkDescriptorSet RenderVulkan::createDescriptorSet(VkDeviceSize size) const
  return descriptorSet;
}

VkDescriptorSet RenderVulkan::createTextDescriptorSet() const
{
  VkDescriptorSetAllocateInfo allocInfo = {};
  allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
  allocInfo.descriptorPool = engine.descriptorPool;
  allocInfo.descriptorSetCount = 1;
  allocInfo.pSetLayouts = textRender->textPipeline->getDescriptorSetLayout();

  VkDescriptorSet descriptorSet;

  if (vkAllocateDescriptorSets(engine.device, &allocInfo,
                               &descriptorSet) != VK_SUCCESS)
  {
    throw std::runtime_error("Failed to allocate descriptor set!");
  }

  VkDescriptorBufferInfo bufferInfo = {};
  bufferInfo.buffer = textRender->ubo->getBuffer();
  bufferInfo.offset = 0;
  bufferInfo.range = sizeof(glm::mat4);

  std::array<VkWriteDescriptorSet,1> descriptorWrites = {};

  descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
  descriptorWrites[0].dstSet = descriptorSet;
  descriptorWrites[0].dstBinding = 0;
  descriptorWrites[0].dstArrayElement = 0;
  descriptorWrites[0].descriptorType =
    VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  descriptorWrites[0].descriptorCount = 1;
  descriptorWrites[0].pBufferInfo = &bufferInfo;
  descriptorWrites[0].pImageInfo = nullptr;
  descriptorWrites[0].pTexelBufferView = nullptr;

  vkUpdateDescriptorSets(engine.device,
                         descriptorWrites.size(),
                         descriptorWrites.data(),
                         0,
                         nullptr);

  return descriptorSet;
}

VkDescriptorSet RenderVulkan::createTextureDescriptorSet() const
{
  VkDescriptorSetAllocateInfo allocInfo = {};


@@ 1205,12 1241,26 @@ void RenderVulkan::createTextBuffers()

  textRender = std::unique_ptr<TextRender>(new TextRender);

  textRender->ubo = std::unique_ptr<Buffer>(new Buffer(engine.device,
                                                       engine.physicalDevice,
                                                       sizeof(glm::mat4),
                                                       VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT));

  glm::mat4 transform = glm::scale(glm::mat4(1.0f), glm::vec3(1.0f, -1.0f, 1.0f));
  transform = glm::translate(transform, glm::vec3(-1.0f, 0.9f, 0.0f));
  float s = 0.3f;
  transform = glm::scale(transform, glm::vec3(s, s, s));


  textRender->ubo->update(&transform);

  //for (auto &textInfo : textInfos)
  {
    textRender->glyphs = std::unique_ptr<TextGlyphs>(new TextGlyphs(1.0f));
    textRender->textPipeline = std::unique_ptr<TextPipeline>(new TextPipeline(engine.device,
                                                                     engine.extent));

    textDescriptorSet = createTextDescriptorSet();

    for (auto &vec : textRender->glyphs->getVertices())
    {


@@ 1242,7 1292,7 @@ void RenderVulkan::createTextBuffers()
  textRender->textIndexBuffer->update(indices.data());
}

std::vector<VkCommandBuffer> RenderVulkan::createTextCommands()
std::vector<VkCommandBuffer> RenderVulkan::createTextCommands(entt::registry &registry)
{
  std::vector<VkCommandBuffer> commandBuffers(engine.framebuffers.size());



@@ 1296,13 1346,35 @@ std::vector<VkCommandBuffer> RenderVulkan::createTextCommands()
    vkCmdBindIndexBuffer(commandBuffers[i], textRender->textIndexBuffer->getBuffer(), 0,
                         VK_INDEX_TYPE_UINT32);

    for (auto &textInfo : textInfos)
    vkCmdBindDescriptorSets(commandBuffers[i],
                            VK_PIPELINE_BIND_POINT_GRAPHICS,
                            textRender->textPipeline->getPipelineLayout(),
                            0, 1, &textDescriptorSet,
                            0,
                            nullptr);

    auto view = registry.view<TextBuffer, TextInfo>();

    for (auto &entity : view)
    {
      auto &textBuffer = registry.get<TextBuffer>(entity);
      auto &textInfo = registry.get<TextInfo>(entity);

      TextAttributes attr = {};
      attr.x = textInfo.second.x;
      attr.y = textInfo.second.y;
      for (auto c : textInfo.second.text)
      attr.x = textInfo.x;
      attr.y = textInfo.y;

      for (auto c : textBuffer.get())
      {
        auto &glyph = textRender->glyphs->getGlyph(c);

        if (!glyph.render)
        {
          attr.x = textInfo.x;
          attr.y -= glyph.height;
          continue;
        }

        vkCmdSetStencilCompareMask(commandBuffers[i], VK_STENCIL_FACE_FRONT_AND_BACK, 0);
        vkCmdSetStencilWriteMask(commandBuffers[i], VK_STENCIL_FACE_FRONT_AND_BACK, 0xff);



@@ 1310,8 1382,6 @@ std::vector<VkCommandBuffer> RenderVulkan::createTextCommands()
                          VK_PIPELINE_BIND_POINT_GRAPHICS,
                          textRender->textPipeline->getPipeline(FILL));

        auto &glyph = textRender->glyphs->getGlyph(c);
        attr.x -= glyph.width;
        vkCmdPushConstants(commandBuffers[i],
                           textRender->textPipeline->getPipelineLayout(),
                           VK_SHADER_STAGE_VERTEX_BIT,


@@ 1353,6 1423,8 @@ std::vector<VkCommandBuffer> RenderVulkan::createTextCommands()
                  1,
                  glyph.boxOffset,
                  0);

        attr.x += glyph.width;
      }
    }



@@ 2016,6 2088,7 @@ void RenderVulkan::cleanSwapchain()
  vkFreeMemory(engine.device, depthImageMemory, nullptr);
  vkDestroyImage(engine.device, depthImage, nullptr);

  outlinePipeline.reset();
  spritePipeline.reset();

  for (size_t i = 0; i < engine.framebuffers.size(); i++)

M render_vulkan.h => render_vulkan.h +6 -6
@@ 89,9 89,8 @@ public:
  void update(entt::registry &registry);
  void loadShapes(std::vector<Shape> &shapes, entt::registry &registry);
  bool resized();
  uint32_t addText(TextRenderCreateInfo &createInfo);
  void updateText(uint32_t id, TextRenderCreateInfo &createInfo);
  void textCommand();
  void updateText(entt::registry &registry);
  void textCommand(entt::registry &registry);
  uint32_t loadTextureArray(const char** paths, size_t count);
  void updateTexture();
  void textureVertices();


@@ 115,8 114,6 @@ private:
  VkImageView depthImageView;
  bool wasResized = false;

  std::map<uint32_t, TextRenderCreateInfo> textInfos;
  uint32_t renderTextsCount = 0;
  std::unique_ptr<TextRender> textRender;
  std::vector<VkCommandBuffer> textCommands;



@@ 128,6 125,7 @@ private:
  std::vector<VkDeviceMemory> textureMemory;
  uint32_t textureCount = 0;
  VkDescriptorSet textureDescriptorSet;
  VkDescriptorSet textDescriptorSet;
  VkSampler sampler;

  uint32_t renderCount = 0;


@@ 137,6 135,7 @@ private:
                                VkImageLayout finalLayout) const;
  VkDescriptorSetLayout createDescriptorSetLayout() const;
  VkDescriptorSetLayout createTextureDescriptorSetLayout() const;
  VkDescriptorSetLayout createTextDescriptorSetLayout() const;
  VkPipelineLayout createPipelineLayout(VkDescriptorSetLayout &layout,
                                        std::vector<VkPushConstantRange> &pushConstantRanges) const;
  VkPipelineLayout createTexturePipelineLayout() const;


@@ 147,8 146,9 @@ private:
  VkDescriptorPool createDescriptorPool() const;
  VkDescriptorSet createDescriptorSet(VkDeviceSize size) const;
  VkDescriptorSet createTextureDescriptorSet() const;
  VkDescriptorSet createTextDescriptorSet() const;
  void createTextBuffers();
  std::vector<VkCommandBuffer> createTextCommands();
  std::vector<VkCommandBuffer> createTextCommands(entt::registry &registry);
  std::vector<VkCommandBuffer> createCommandBuffers(entt::registry &registry) const;
  std::vector<VkCommandBuffer> createTextureCommands() const;
  VkCommandBuffer createTextureCommand(uint32_t index) const;

M text.vert => text.vert +5 -1
@@ 4,12 4,16 @@
layout(location = 0) in vec2 inPosition;
layout(location = 1) out vec3 fragColor;

layout(binding = 0) uniform UniformBufferObject {
               mat4 transform;
} ubo;

layout(push_constant) uniform PushConsts {
  vec2 offset;
} pushConsts;

void main()
{
  gl_Position = vec4(inPosition + pushConsts.offset, 0.0, 1.0);
  gl_Position = ubo.transform * vec4(inPosition + pushConsts.offset, 0.0, 1.0);
  fragColor = vec3(0.0, 0.0, 0.0);
}

A text_buffer.h => text_buffer.h +22 -0
@@ 0,0 1,22 @@
#ifndef TEXTBUFFER_H
#define TEXTBUFFER_H

#include <string>

namespace meteormaker
{

class TextBuffer
{
public:
  std::string &get() { return buffer; };
  void set(std::string &s) { buffer = s; };
  void add(char c) { buffer.push_back(c); };
  void del() { if (buffer.size()) buffer.pop_back(); };
private:
  std::string buffer;
  uint32_t pos;
};

}
#endif

M text_correct.vert => text_correct.vert +5 -1
@@ 5,13 5,17 @@ layout(location = 0) in vec4 inPosition;
layout(location = 0) out vec2 outCtrl;
layout(location = 1) out vec3 fragColor;

layout(binding = 0) uniform UniformBufferObject {
               mat4 transform;
} ubo;

layout(push_constant) uniform PushConsts {
  vec2 offset;
} pushConsts;

void main()
{
  gl_Position = vec4(inPosition.xy + pushConsts.offset, 0.0, 1.0);
  gl_Position = ubo.transform * vec4(inPosition.xy + pushConsts.offset, 0.0, 1.0);
  fragColor = vec3(1.0, 0.0, 0.0);
  outCtrl = inPosition.zw;
}

M text_glyphs.h => text_glyphs.h +19 -2
@@ 4,6 4,7 @@
#include <map>
#include <vector>
#include <stdexcept>
#include <iostream>

#include <ft2build.h>
#include FT_FREETYPE_H


@@ 18,9 19,11 @@ struct GlyphInfo
  std::vector<uint32_t> sizes;
  uint32_t offset;
  float width;
  float height;
  uint32_t indexOffset;
  uint32_t indexSize;
  uint32_t boxOffset;
  bool render;
};

struct GlyphUserInfo


@@ 72,7 75,7 @@ public:
    funcs.shift = 0;
    funcs.delta = 0;

    for (char c='!'; c<='~'; c++)
    for (char c='\b'; c<='~'; c++)
    {
      loadChar(c);
    }


@@ 107,11 110,23 @@ private:
    FT_BBox box;
    FT_Outline_Get_CBox(&face->glyph->outline, &box);

    bool render = face->glyph->outline.n_contours > 0;
    float height = 0.0f;

    if (c == '\n')
    {
      render = false;
      height = face->glyph->metrics.vertAdvance/scale;
    }

    uint32_t indexSize = indices.size();
    glyphInfo.insert({c, {{},(uint32_t)vertices.size()/2,
                          face->glyph->advance.x/scale,
                          height,
                          indexSize,
                          0}});
                          0,
                          0,
                          render}});

    GlyphUserInfo userInfo;
    userInfo.info = &glyphInfo.at(c);


@@ 119,6 134,8 @@ private:
    userInfo.indices = &indices;
    userInfo.scale = scale;

    if (!render) return;

    error = FT_Outline_Decompose(&face->glyph->outline, &funcs, &userInfo);
    if (error)
    {

M text_pipeline.cc => text_pipeline.cc +10 -2
@@ 100,10 100,18 @@ VkRenderPass TextPipeline::createRenderPass() const
VkDescriptorSetLayout TextPipeline::createDescriptorSetLayout
(VkDevice device) const
{
  std::array<VkDescriptorSetLayoutBinding,1> bindings = {};

  bindings[0].binding = 0;
  bindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
  bindings[0].descriptorCount = 1;
  bindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
  bindings[0].pImmutableSamplers = nullptr;

  VkDescriptorSetLayoutCreateInfo setLayoutInfo = {};
  setLayoutInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
  setLayoutInfo.bindingCount = 0;
  setLayoutInfo.pBindings = nullptr;
  setLayoutInfo.bindingCount = bindings.size();
  setLayoutInfo.pBindings = bindings.data();

  VkDescriptorSetLayout setLayout;


M text_pipeline.h => text_pipeline.h +2 -0
@@ 33,6 33,8 @@ public:
    }
  };
  VkPipelineLayout getPipelineLayout() {return pipelineLayout;};
  VkDescriptorSetLayout *getDescriptorSetLayout(){return &setLayout;};

private:
  VkDevice device;
  VkRenderPass renderPass;

M text_render.cc => text_render.cc +1 -0
@@ 9,6 9,7 @@ TextRender::~TextRender()
  textPipeline.reset();
  textVertexBuffer.reset();
  textIndexBuffer.reset();
  ubo.reset();
}

}

M text_render.h => text_render.h +9 -0
@@ 21,6 21,14 @@ struct TextRenderCreateInfo
  float y;
};

struct TextInfo
{
  float x;
  float y;
  float size;
  bool selected;
};

class TextRender
{
public:


@@ 28,6 36,7 @@ public:
  std::unique_ptr<TextPipeline> textPipeline;
  std::unique_ptr<Buffer> textVertexBuffer;
  std::unique_ptr<Buffer> textIndexBuffer;
  std::unique_ptr<Buffer> ubo;
  std::unique_ptr<TextGlyphs> glyphs;
};