add bounding box for text drawable and add possibility to share data among different instances

This commit is contained in:
ohyzha
2024-08-22 13:27:08 +03:00
parent 31390ec9ae
commit 4fce5fd1de
2 changed files with 75 additions and 35 deletions

View File

@@ -30,7 +30,9 @@ namespace OpenVulkano::Scene
sdfDefaultShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/text"); sdfDefaultShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/text");
sdfDefaultShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); sdfDefaultShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription());
sdfDefaultShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); sdfDefaultShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING);
sdfDefaultShader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING); DescriptorSetLayoutBinding desc = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
desc.stageFlags = ShaderProgramType::FRAGMENT;
sdfDefaultShader.AddDescriptorSetLayoutBinding(desc);
sdfDefaultShader.alphaBlend = true; sdfDefaultShader.alphaBlend = true;
sdfDefaultShader.cullMode = CullMode::NONE; sdfDefaultShader.cullMode = CullMode::NONE;
once = false; once = false;
@@ -48,7 +50,9 @@ namespace OpenVulkano::Scene
msdfDefaultShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/msdfText"); msdfDefaultShader.AddShaderProgram(OpenVulkano::ShaderProgramType::FRAGMENT, "Shader/msdfText");
msdfDefaultShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription()); msdfDefaultShader.AddVertexInputDescription(OpenVulkano::Vertex::GetVertexInputDescription());
msdfDefaultShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING); msdfDefaultShader.AddDescriptorSetLayoutBinding(Texture::DESCRIPTOR_SET_LAYOUT_BINDING);
msdfDefaultShader.AddDescriptorSetLayoutBinding(UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING); DescriptorSetLayoutBinding desc = UniformBuffer::DESCRIPTOR_SET_LAYOUT_BINDING;
desc.stageFlags = ShaderProgramType::FRAGMENT;
msdfDefaultShader.AddDescriptorSetLayoutBinding(desc);
msdfDefaultShader.alphaBlend = true; msdfDefaultShader.alphaBlend = true;
msdfDefaultShader.cullMode = CullMode::NONE; msdfDefaultShader.cullMode = CullMode::NONE;
once = false; once = false;
@@ -56,6 +60,13 @@ namespace OpenVulkano::Scene
return msdfDefaultShader; return msdfDefaultShader;
} }
TextDrawable::TextDrawable(const TextConfig& config)
{
m_cfg = config;
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
m_uniBuffer.binding.stageFlags = ShaderProgramType::FRAGMENT;
}
TextDrawable::TextDrawable(const Array<char>& atlasMetadata, const TextConfig& config) TextDrawable::TextDrawable(const Array<char>& atlasMetadata, const TextConfig& config)
: TextDrawable(atlasMetadata, nullptr, config) : TextDrawable(atlasMetadata, nullptr, config)
{ {
@@ -79,20 +90,22 @@ namespace OpenVulkano::Scene
std::memcpy(&metadataBytes, atlasMetadata.Data() + (atlasMetadata.Size() - sizeof(uint32_t) - sizeof(uint64_t)), std::memcpy(&metadataBytes, atlasMetadata.Data() + (atlasMetadata.Size() - sizeof(uint32_t) - sizeof(uint64_t)),
sizeof(uint64_t)); sizeof(uint64_t));
uint64_t offsetToMetadata = atlasMetadata.Size() - metadataBytes - sizeof(uint32_t) - sizeof(uint64_t); uint64_t offsetToMetadata = atlasMetadata.Size() - metadataBytes - sizeof(uint32_t) - sizeof(uint64_t);
m_atlasData = std::make_shared<AtlasData>();
if (isPacked) if (isPacked)
{ {
m_texture = Texture(); m_atlasData->texture = Texture();
m_material.texture = &m_texture.value(); m_material.texture = &m_atlasData->texture;
m_img = Image::IImageLoader::loadData((const uint8_t*) atlasMetadata.Data(), m_atlasData->img = Image::IImageLoader::loadData((const uint8_t*) atlasMetadata.Data(),
offsetToMetadata); offsetToMetadata);
m_material.texture->format = m_img->dataFormat; m_material.texture->format = m_atlasData->img->dataFormat;
m_material.texture->resolution = m_img->resolution; m_material.texture->resolution = m_atlasData->img->resolution;
m_material.texture->size = m_img->data.Size(); m_material.texture->size = m_atlasData->img->data.Size();
m_material.texture->textureBuffer = m_img->data.Data(); m_material.texture->textureBuffer = m_atlasData->img->data.Data();
} }
else else
{ {
if (atlasTex == nullptr) { throw std::runtime_error("Atlas texture cannot be null with non-packed atlas metadata"); } if (atlasTex == nullptr) { throw std::runtime_error("Atlas texture cannot be null with non-packed atlas metadata"); }
m_atlasData->texture = *atlasTex;
m_material.texture = atlasTex; m_material.texture = atlasTex;
} }
@@ -101,7 +114,7 @@ namespace OpenVulkano::Scene
size_t readMetadataBytes = 0; size_t readMetadataBytes = 0;
uint32_t unicode = 0; uint32_t unicode = 0;
std::memcpy(&m_meta, atlasMetadata.Data() + read_bytes, sizeof(AtlasMetadata)); std::memcpy(&m_atlasData->meta, atlasMetadata.Data() + read_bytes, sizeof(AtlasMetadata));
read_bytes += sizeof(AtlasMetadata); read_bytes += sizeof(AtlasMetadata);
readMetadataBytes += sizeof(AtlasMetadata); readMetadataBytes += sizeof(AtlasMetadata);
while (readMetadataBytes < metadataBytes) while (readMetadataBytes < metadataBytes)
@@ -109,24 +122,27 @@ namespace OpenVulkano::Scene
std::memcpy(&unicode, atlasMetadata.Data() + read_bytes, sizeof(uint32_t)); std::memcpy(&unicode, atlasMetadata.Data() + read_bytes, sizeof(uint32_t));
read_bytes += sizeof(uint32_t); read_bytes += sizeof(uint32_t);
readMetadataBytes += sizeof(uint32_t); readMetadataBytes += sizeof(uint32_t);
GlyphInfo& info = m_glyphs[unicode]; GlyphInfo& info = m_atlasData->glyphs[unicode];
std::memcpy(&info, atlasMetadata.Data() + read_bytes, sizeof(GlyphInfo)); std::memcpy(&info, atlasMetadata.Data() + read_bytes, sizeof(GlyphInfo));
read_bytes += sizeof(GlyphInfo); read_bytes += sizeof(GlyphInfo);
readMetadataBytes += sizeof(GlyphInfo); readMetadataBytes += sizeof(GlyphInfo);
} }
m_cfg = config; m_cfg = config;
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
m_uniBuffer.binding.stageFlags = ShaderProgramType::FRAGMENT;
} }
TextDrawable::TextDrawable(const std::map<uint32_t, GlyphInfo>& glyphData, Texture* atlasTex, TextDrawable::TextDrawable(const std::shared_ptr<AtlasData>& atlasData, const TextConfig& config)
const TextConfig& config)
{ {
if (!atlasTex) { throw std::runtime_error("Atlas texture is nullptr"); } if (!atlasData || atlasData->glyphs.empty() || !atlasData->texture.textureBuffer)
if (glyphData.empty()) { throw std::runtime_error("Glyphs are not loaded"); } {
m_material.texture = atlasTex; throw std::runtime_error("Cannot initialize text drawable with empty atlas data");
m_glyphs = glyphData; }
m_atlasData = atlasData;
m_material.texture = &atlasData->texture;
m_cfg = config; m_cfg = config;
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
m_uniBuffer.binding.stageFlags = ShaderProgramType::FRAGMENT;
} }
TextDrawable::TextDrawable(IFontAtlasGenerator* fontAtlasGenerator, const TextConfig& config) TextDrawable::TextDrawable(IFontAtlasGenerator* fontAtlasGenerator, const TextConfig& config)
@@ -137,6 +153,7 @@ namespace OpenVulkano::Scene
m_cfg = config; m_cfg = config;
m_material.texture = const_cast<Texture*>(&m_fontAtlasGenerator->GetAtlas()); m_material.texture = const_cast<Texture*>(&m_fontAtlasGenerator->GetAtlas());
m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3); m_uniBuffer.Init(sizeof(TextConfig), &m_cfg, 3);
m_uniBuffer.binding.stageFlags = ShaderProgramType::FRAGMENT;
} }
void TextDrawable::GenerateText(const std::string& text, const Math::Vector3f& pos) void TextDrawable::GenerateText(const std::string& text, const Math::Vector3f& pos)
@@ -170,23 +187,16 @@ namespace OpenVulkano::Scene
std::map<uint32_t, GlyphInfo>* symbols; std::map<uint32_t, GlyphInfo>* symbols;
if (m_fontAtlasGenerator) if (m_fontAtlasGenerator)
{ {
if (m_fontAtlasGenerator->GetGlyphsInfo().empty() && !m_glyphs.empty()) // just in case if FontAtlasGenerator changed it's atlas
{ m_material.texture = const_cast<Texture*>(&m_fontAtlasGenerator->GetAtlas());
// texture is set in ctor symbols = &m_fontAtlasGenerator->GetGlyphsInfo();
symbols = &m_glyphs;
}
else
{
// just in case if FontAtlasGenerator changed it's atlas
m_material.texture = const_cast<Texture*>(&m_fontAtlasGenerator->GetAtlas());
symbols = &m_fontAtlasGenerator->GetGlyphsInfo();
}
meta = &m_fontAtlasGenerator->GetAtlasMetadata(); meta = &m_fontAtlasGenerator->GetAtlasMetadata();
} }
else else
{ {
symbols = &m_glyphs; m_material.texture = &m_atlasData->texture;
meta = &m_meta; symbols = &m_atlasData->glyphs;
meta = &m_atlasData->meta;
} }
const Texture& atlasTex = *m_material.texture; const Texture& atlasTex = *m_material.texture;
@@ -196,6 +206,8 @@ namespace OpenVulkano::Scene
const double lineHeight = meta->lineHeight; const double lineHeight = meta->lineHeight;
double posY = pos.y; double posY = pos.y;
int i = 0; int i = 0;
Math::Vector3f bmin(pos), bmax(pos);
bool firstGlyph = true;
while (begin != end) while (begin != end)
{ {
uint32_t c = utf8::next(begin, end); uint32_t c = utf8::next(begin, end);
@@ -251,11 +263,29 @@ namespace OpenVulkano::Scene
// TODO: change to lower value(or ideally remove completely) to avoid overlapping and make less space between symbols // TODO: change to lower value(or ideally remove completely) to avoid overlapping and make less space between symbols
// when setting for depth comparison operator will be available( <= ) // when setting for depth comparison operator will be available( <= )
cursorX += info.advance + 0.08; cursorX += info.advance + 0.08;
if (firstGlyph)
{
bmin.x = m_geometry.vertices[vIdx].position.x;
firstGlyph = false;
}
bmax.x = std::max(bmax.x, m_geometry.vertices[vIdx + 1].position.x);
bmax.y = std::max(bmax.y, m_geometry.vertices[vIdx + 2].position.y);
bmin.y = std::min(bmin.y, m_geometry.vertices[vIdx + 1].position.y);
++i; ++i;
} }
m_bbox.Init(bmin, bmax);
SimpleDrawable::Init(m_shader, &m_geometry, &m_material, &m_uniBuffer); SimpleDrawable::Init(m_shader, &m_geometry, &m_material, &m_uniBuffer);
} }
void TextDrawable::SetAtlasData(const std::shared_ptr<AtlasData>& atlasData)
{
if (!atlasData || atlasData->glyphs.empty() || !atlasData->texture.textureBuffer)
{
throw std::runtime_error("Cannot initialize text drawable with empty atlas data");
}
m_atlasData = atlasData;
}
void TextDrawable::SetFontAtlasGenerator(IFontAtlasGenerator* fontAtlasGenerator) void TextDrawable::SetFontAtlasGenerator(IFontAtlasGenerator* fontAtlasGenerator)
{ {
if (!fontAtlasGenerator || fontAtlasGenerator->GetGlyphsInfo().empty()) if (!fontAtlasGenerator || fontAtlasGenerator->GetGlyphsInfo().empty())

View File

@@ -33,32 +33,42 @@ namespace OpenVulkano::Scene
//bool sdfMultiChannel = false; //bool sdfMultiChannel = false;
}; };
struct AtlasData
{
std::map<uint32_t, GlyphInfo> glyphs;
AtlasMetadata meta;
std::unique_ptr<Image::Image> img;
Texture texture;
};
class TextDrawable : public SimpleDrawable class TextDrawable : public SimpleDrawable
{ {
public: public:
static Shader& GetSdfDefaultShader(); static Shader& GetSdfDefaultShader();
static Shader& GetMsdfDefaultShader(); static Shader& GetMsdfDefaultShader();
TextDrawable(const TextConfig& config = TextConfig());
TextDrawable(const Array<char>& atlasMetadata, const TextConfig& config = TextConfig()); TextDrawable(const Array<char>& atlasMetadata, const TextConfig& config = TextConfig());
TextDrawable(const std::string& atlasMetadataFile, const TextConfig& config = TextConfig()); TextDrawable(const std::string& atlasMetadataFile, const TextConfig& config = TextConfig());
TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config = TextConfig()); TextDrawable(const std::string& atlasMetadataFile, Texture* atlasTex, const TextConfig& config = TextConfig());
TextDrawable(const Array<char>& atlasMetadata, Texture* atlasTex, const TextConfig& config = TextConfig()); TextDrawable(const Array<char>& atlasMetadata, Texture* atlasTex, const TextConfig& config = TextConfig());
TextDrawable(const std::map<uint32_t, GlyphInfo>& glyphData, Texture* atlasTex, const TextConfig& config = TextConfig()); TextDrawable(const std::shared_ptr<AtlasData>& atlasData, const TextConfig& config = TextConfig());
TextDrawable(IFontAtlasGenerator* fontAtlasGenerator, const TextConfig& config = TextConfig()); TextDrawable(IFontAtlasGenerator* fontAtlasGenerator, const TextConfig& config = TextConfig());
void GenerateText(const std::string& text, const Math::Vector3f& pos = Math::Vector3f(0.f)); void GenerateText(const std::string& text, const Math::Vector3f& pos = Math::Vector3f(0.f));
void SetConfig(const TextConfig& cfg) { m_cfg = cfg; } void SetConfig(const TextConfig& cfg) { m_cfg = cfg; }
void SetShader(Shader* shader) { m_shader = shader; } void SetShader(Shader* shader) { m_shader = shader; }
void SetAtlasData(const std::shared_ptr<AtlasData>& atlasData);
Math::AABB& GetBoundingBox() { return m_bbox; }
TextConfig& GetConfig() { return m_cfg; } TextConfig& GetConfig() { return m_cfg; }
Shader* GetShader() { return m_shader; } Shader* GetShader() { return m_shader; }
std::shared_ptr<AtlasData> GetAtlasData() { return m_atlasData; }
void SetFontAtlasGenerator(IFontAtlasGenerator* fontAtlasGenerator); void SetFontAtlasGenerator(IFontAtlasGenerator* fontAtlasGenerator);
IFontAtlasGenerator* GetFontAtlasGenerator() { return m_fontAtlasGenerator; } IFontAtlasGenerator* GetFontAtlasGenerator() { return m_fontAtlasGenerator; }
private: private:
Geometry m_geometry; Geometry m_geometry;
Material m_material; Material m_material;
UniformBuffer m_uniBuffer; UniformBuffer m_uniBuffer;
std::map<uint32_t, GlyphInfo> m_glyphs; std::shared_ptr<AtlasData> m_atlasData;
AtlasMetadata m_meta; Math::AABB m_bbox;
std::unique_ptr<Image::Image> m_img;
std::optional<Texture> m_texture;
IFontAtlasGenerator* m_fontAtlasGenerator = nullptr; IFontAtlasGenerator* m_fontAtlasGenerator = nullptr;
Shader* m_shader = nullptr; Shader* m_shader = nullptr;
TextConfig m_cfg; TextConfig m_cfg;