From d8921bbc5f1ac4d721f5bf838684ba573a4294e5 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 25 Nov 2015 14:24:01 -1000 Subject: [PATCH] platform-sensitive system init --- specter/CMakeLists.txt | 1 + specter/MathLib | 2 +- specter/include/Specter/FontCache.hpp | 64 +++++++++-- specter/include/Specter/RootView.hpp | 6 +- specter/include/Specter/Specter.hpp | 2 + specter/include/Specter/TextView.hpp | 31 +++--- specter/include/Specter/View.hpp | 51 ++++++++- specter/include/Specter/ViewSystem.hpp | 32 ++++++ specter/lib/FontCache.cpp | 56 ++++++++-- specter/lib/RootView.cpp | 8 +- specter/lib/Specter.cpp | 30 ++++++ specter/lib/TextView.cpp | 141 +++++++++++++++++-------- specter/lib/View.cpp | 83 +++++++++++++++ 13 files changed, 419 insertions(+), 88 deletions(-) create mode 100644 specter/include/Specter/ViewSystem.hpp diff --git a/specter/CMakeLists.txt b/specter/CMakeLists.txt index 303c6e070..cf2b564d0 100644 --- a/specter/CMakeLists.txt +++ b/specter/CMakeLists.txt @@ -31,6 +31,7 @@ include_directories(include ${HECL_INCLUDE_DIR} ${BOO_INCLUDE_DIR} list(APPEND SPECTER_HEADERS include/Specter/Specter.hpp + include/Specter/ViewSystem.hpp include/Specter/View.hpp include/Specter/RootView.hpp include/Specter/TextView.hpp diff --git a/specter/MathLib b/specter/MathLib index 8d54a3c7b..dc5c903a0 160000 --- a/specter/MathLib +++ b/specter/MathLib @@ -1 +1 @@ -Subproject commit 8d54a3c7b5d76e32d9fb3ad57619a680b32909b7 +Subproject commit dc5c903a0dcabc57bdc7e83b3d037a2960e9bb73 diff --git a/specter/include/Specter/FontCache.hpp b/specter/include/Specter/FontCache.hpp index 4ac7fd5bb..a8971bdf1 100644 --- a/specter/include/Specter/FontCache.hpp +++ b/specter/include/Specter/FontCache.hpp @@ -62,6 +62,7 @@ public: { atUint32 m_unicodePoint; atUint32 m_layerIdx; + float m_layerFloat; float m_uv[4]; atInt8 m_leftPadding; atInt8 m_advance; @@ -69,19 +70,38 @@ public: atUint8 m_width; atUint8 m_height; atInt8 m_verticalOffset; - atInt16 m_kernIdx = -1; - }; - - struct KernAdj - { - atUint32 a; - atUint32 b; - atInt8 adj; }; private: std::vector m_glyphs; - std::vector m_kernAdjs; + std::unordered_map>> m_kernAdjs; + + struct TT_KernHead : Athena::io::DNA + { + DECL_DNA + Value length; + Value coverage; + }; + + struct TT_KernSubHead : Athena::io::DNA + { + DECL_DNA + Value nPairs; + Value searchRange; + Value entrySelector; + Value rangeShift; + }; + + struct TT_KernPair : Athena::io::DNA + { + DECL_DNA + Value left; + Value right; + Value value; + }; + + void buildKernTable(FT_Face face); + std::unordered_map m_glyphLookup; public: @@ -93,6 +113,32 @@ public: FontAtlas& operator=(const FontAtlas& other) = delete; uint32_t dpi() const {return m_dpi;} + + const Glyph* lookupGlyph(atUint32 charcode) const + { + auto search = m_glyphLookup.find(charcode); + if (search == m_glyphLookup.end()) + return nullptr; + return &m_glyphs[search->second]; + } + atInt16 lookupKern(atUint32 left, atUint32 right) const + { + auto leftSearch = m_glyphLookup.find(left); + if (leftSearch == m_glyphLookup.cend()) + return 0; + size_t leftIdx = leftSearch->second; + auto rightSearch = m_glyphLookup.find(right); + if (rightSearch == m_glyphLookup.cend()) + return 0; + size_t rightIdx = rightSearch->second; + auto pairSearch = m_kernAdjs.find(leftIdx); + if (pairSearch == m_kernAdjs.cend()) + return 0; + for (const std::pair& p : pairSearch->second) + if (p.first == rightIdx) + return p.second; + return 0; + } }; class FontCache diff --git a/specter/include/Specter/RootView.hpp b/specter/include/Specter/RootView.hpp index 41396df33..6742ac851 100644 --- a/specter/include/Specter/RootView.hpp +++ b/specter/include/Specter/RootView.hpp @@ -7,12 +7,11 @@ namespace Specter { +class ViewSystem; class RootView : public View, public boo::IWindowCallback { - FontCache& m_fontCache; boo::IWindow* m_window = nullptr; - float m_scale = 1.0; void resized(const boo::SWindowRect& rect); void mouseDown(const boo::SWindowCoord& coord, boo::EMouseButton button, boo::EModifierKey mods); @@ -36,8 +35,7 @@ class RootView : public View, public boo::IWindowCallback void draw(boo::IGraphicsCommandQueue* gfxQ); public: - RootView(FontCache& fontCache) : m_fontCache(fontCache) {} - void setWindow(boo::IWindow* window, float userScale); + RootView(ViewSystem& system, boo::IWindow* window); }; } diff --git a/specter/include/Specter/Specter.hpp b/specter/include/Specter/Specter.hpp index 73067c0b3..7450424ad 100644 --- a/specter/include/Specter/Specter.hpp +++ b/specter/include/Specter/Specter.hpp @@ -3,6 +3,7 @@ #include "View.hpp" #include "RootView.hpp" +#include "TextView.hpp" #include "Space.hpp" #include "Table.hpp" #include "Outliner.hpp" @@ -15,5 +16,6 @@ #include "Node.hpp" #include "NodeSocket.hpp" #include "FontCache.hpp" +#include "ViewSystem.hpp" #endif // SPECTER_HPP diff --git a/specter/include/Specter/TextView.hpp b/specter/include/Specter/TextView.hpp index 9398dccf2..0d9c1cf7e 100644 --- a/specter/include/Specter/TextView.hpp +++ b/specter/include/Specter/TextView.hpp @@ -10,43 +10,42 @@ namespace Specter { +class ViewSystem; class TextView : public View { boo::IGraphicsBufferD* m_glyphBuf; - int m_validDynamicSlots = 0; - size_t m_curGlyphCapacity; const FontAtlas& m_fontAtlas; - boo::IVertexFormat* m_vtxFmt = nullptr; /* OpenGL only */ + boo::IVertexFormat* m_bgVtxFmt = nullptr; /* OpenGL only */ public: class System { + friend class ViewSystem; friend class TextView; - boo::IGraphicsDataFactory* m_factory; - FontCache& m_fcache; - boo::IShaderPipeline* m_regular; - boo::IShaderPipeline* m_subpixel; - boo::IGraphicsBufferS* m_quadVBO; + FontCache* m_fcache = nullptr; + boo::IShaderPipeline* m_regular = nullptr; + boo::IShaderPipeline* m_subpixel = nullptr; boo::IVertexFormat* m_vtxFmt = nullptr; /* Not OpenGL */ - System(boo::IGraphicsDataFactory* factory, FontCache& fcache) - : m_factory(factory), m_fcache(fcache) {} - }; - static System BuildTextSystem(boo::GLDataFactory* factory, FontCache& fcache); + void init(boo::GLDataFactory* factory, FontCache* fcache); #if _WIN32 - static Shaders BuildTextSystem(boo::ID3DDataFactory* factory, FontCache& fcache); + void init(boo::ID3DDataFactory* factory, FontCache* fcache); #elif BOO_HAS_METAL - static Shaders BuildTextSystem(boo::MetalDataFactory* factory, FontCache& fcache); + void init(boo::MetalDataFactory* factory, FontCache* fcache); #endif + }; - TextView(System& system, FontTag font, size_t initGlyphCapacity=256); + TextView(ViewSystem& system, FontTag font, size_t initGlyphCapacity=256); struct RenderGlyph { - Zeus::CMatrix4f m_mvp; + Zeus::CVector3f m_pos[4]; + Zeus::CMatrix4f m_mv; Zeus::CVector3f m_uv[4]; Zeus::CColor m_color; + + RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, Zeus::CColor defaultColor); }; std::vector& accessGlyphs() {return m_glyphs;} void updateGlyphs() {m_validDynamicSlots = 0;} diff --git a/specter/include/Specter/View.hpp b/specter/include/Specter/View.hpp index 800d9cc70..c888d487e 100644 --- a/specter/include/Specter/View.hpp +++ b/specter/include/Specter/View.hpp @@ -7,16 +7,59 @@ #include "CTransform.hpp" #include "CColor.hpp" +#include +#include +#include + namespace Specter { +class ViewSystem; class View { -protected: - boo::SWindowRect m_viewport; - void bindViewport(boo::IGraphicsCommandQueue* gfxQ) {gfxQ->setViewport(m_viewport);} + boo::IGraphicsBufferD* m_bgVertBuf; + boo::IGraphicsBufferD* m_bgInstBuf; + boo::IVertexFormat* m_bgVtxFmt = nullptr; /* OpenGL only */ + boo::IShaderDataBinding* m_bgShaderBinding; + Zeus::CVector3f m_bgRect[4]; + Zeus::CColor m_bgColor; public: - virtual void draw(boo::IGraphicsCommandQueue* gfxQ)=0; + class System + { + friend class ViewSystem; + friend class View; + boo::IShaderPipeline* m_solidShader = nullptr; + boo::IVertexFormat* m_vtxFmt = nullptr; /* Not OpenGL */ + + void init(boo::GLDataFactory* factory); +#if _WIN32 + void init(boo::ID3DDataFactory* factory); +#elif BOO_HAS_METAL + void init(boo::MetalDataFactory* factory); +#endif + }; + + struct VertexBlock + { + Zeus::CMatrix4f m_mv; + }; +#define SPECTER_VIEW_VERT_BLOCK_GLSL\ + "uniform SpecterViewBlock\n"\ + "{\n"\ + " mat4 mv;\n"\ + "};\n" + +protected: + View(ViewSystem& system); + int m_validDynamicSlots = 0; + boo::IGraphicsBufferD* m_specterVertBlock; + + boo::SWindowRect m_absWindowRect; + void bindScissor(boo::IGraphicsCommandQueue* gfxQ) {gfxQ->setScissor(m_absWindowRect);} + +public: + void setBackground(Zeus::CColor color) {m_bgColor = color; m_validDynamicSlots = 0;} + virtual void draw(boo::IGraphicsCommandQueue* gfxQ); }; } diff --git a/specter/include/Specter/ViewSystem.hpp b/specter/include/Specter/ViewSystem.hpp new file mode 100644 index 000000000..3ffa18c3b --- /dev/null +++ b/specter/include/Specter/ViewSystem.hpp @@ -0,0 +1,32 @@ +#ifndef SPECTER_VIEWSYSTEM_HPP +#define SPECTER_VIEWSYSTEM_HPP + +#include "TextView.hpp" + +namespace Specter +{ +class ViewSystem +{ + template + void init(Factory* factory, FontCache* fcache) + { + m_viewSystem.init(factory); + m_textSystem.init(factory, fcache); + } + +public: + boo::IGraphicsDataFactory* m_factory = nullptr; + View::System m_viewSystem; + TextView::System m_textSystem; + + ViewSystem() = default; + ViewSystem(const ViewSystem& other) = delete; + ViewSystem(ViewSystem&& other) = default; + ViewSystem& operator=(const ViewSystem& other) = delete; + ViewSystem& operator=(ViewSystem&& other) = default; + + void init(boo::IGraphicsDataFactory* factory, FontCache* fcache); +}; +} + +#endif // SPECTER_VIEWSYSTEM_HPP diff --git a/specter/lib/FontCache.cpp b/specter/lib/FontCache.cpp index 2dbfb0b88..f6ac537bc 100644 --- a/specter/lib/FontCache.cpp +++ b/specter/lib/FontCache.cpp @@ -1,5 +1,6 @@ #include "Specter/FontCache.hpp" #include +#include #include #include FT_GZIP_H @@ -7,6 +8,7 @@ #include FT_OUTLINE_H #include #include +#include extern "C" const uint8_t DROIDSANS_PERMISSIVE[]; extern "C" size_t DROIDSANS_PERMISSIVE_SZ; @@ -14,6 +16,8 @@ extern "C" size_t DROIDSANS_PERMISSIVE_SZ; extern "C" const uint8_t BMONOFONT[]; extern "C" size_t BMONOFONT_SZ; +extern const FT_Driver_ClassRec tt_driver_class; + namespace Specter { static LogVisor::LogModule Log("Specter::FontCache"); @@ -121,6 +125,40 @@ static inline void GridFitGlyph(FT_GlyphSlot slot, FT_UInt& width, FT_UInt& heig height = slot->metrics.height >> 6; } +void FontAtlas::buildKernTable(FT_Face face) +{ + if (face->driver->clazz == &tt_driver_class) + { + TT_Face ttface = reinterpret_cast(face); + Athena::io::MemoryReader r(ttface->kern_table, ttface->kern_table_size); + std::unordered_map>>::iterator it = m_kernAdjs.end(); + atUint32 nSubs = r.readUint32Big(); + for (atUint32 i=0 ; i> 8 != 0) + { + r.seek(kernHead.length - 6, Athena::Current); + continue; + } + + TT_KernSubHead subHead; + subHead.read(r); + + for (atUint16 p=0 ; pfirst != pair.left) + if ((it = m_kernAdjs.find(pair.left)) == m_kernAdjs.end()) + it = m_kernAdjs.insert(std::make_pair(pair.left, std::vector>())).first; + it->second.emplace_back(pair.right, pair.value); + } + } + } +} + FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, bool subpixel, Athena::io::FileWriter& writer) : m_dpi(dpi) @@ -207,6 +245,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, Glyph& g = m_glyphs.back(); g.m_unicodePoint = charcode; g.m_layerIdx = fullTexmapLayers; + g.m_layerFloat = float(g.m_layerIdx); g.m_width = face->glyph->bitmap.width / 3; g.m_height = face->glyph->bitmap.rows; g.m_uv[0] = curLineWidth / float(TEXMAP_DIM); @@ -217,7 +256,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, g.m_advance = face->glyph->advance.x; g.m_rightPadding = 0; g.m_verticalOffset = face->glyph->metrics.horiBearingY / 64; - g.m_kernIdx = 0; if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) { totalHeight += curLineHeight + 1; @@ -278,6 +316,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, Glyph& g = m_glyphs.back(); g.m_unicodePoint = charcode; g.m_layerIdx = fullTexmapLayers; + g.m_layerFloat = float(g.m_layerIdx); g.m_width = face->glyph->bitmap.width; g.m_height = face->glyph->bitmap.rows; g.m_uv[0] = curLineWidth / float(TEXMAP_DIM); @@ -288,7 +327,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, g.m_advance = face->glyph->advance.x; g.m_rightPadding = 0; g.m_verticalOffset = face->glyph->metrics.horiBearingY >> 6; - g.m_kernIdx = 0; if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) { totalHeight += curLineHeight + 1; @@ -314,6 +352,8 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, boo::TextureFormat::I8, texmap.get(), bufSz); } + + buildKernTable(face); } FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, @@ -378,6 +418,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, Glyph& g = m_glyphs.back(); g.m_unicodePoint = charcode; g.m_layerIdx = fullTexmapLayers; + g.m_layerFloat = float(g.m_layerIdx); g.m_width = width; g.m_height = height; g.m_uv[0] = curLineWidth / float(TEXMAP_DIM); @@ -388,7 +429,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, g.m_advance = face->glyph->advance.x; g.m_rightPadding = 0; g.m_verticalOffset = face->glyph->metrics.horiBearingY / 64; - g.m_kernIdx = 0; if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) { totalHeight += curLineHeight + 1; @@ -428,8 +468,8 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, } else { - size_t count = TEXMAP_DIM * totalHeight; - texmap.reset(new GreyPixel[TEXMAP_DIM * totalHeight]); + size_t count = TEXMAP_DIM * finalHeight; + texmap.reset(new GreyPixel[TEXMAP_DIM * finalHeight]); bufSz = count * sizeof(GreyPixel); memset(texmap.get(), 0, bufSz); } @@ -449,6 +489,7 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, Glyph& g = m_glyphs.back(); g.m_unicodePoint = charcode; g.m_layerIdx = fullTexmapLayers; + g.m_layerFloat = float(g.m_layerIdx); g.m_width = width; g.m_height = height; g.m_uv[0] = curLineWidth / float(TEXMAP_DIM); @@ -459,7 +500,6 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, g.m_advance = face->glyph->advance.x; g.m_rightPadding = 0; g.m_verticalOffset = face->glyph->metrics.horiBearingY >> 6; - g.m_kernIdx = 0; if (curLineWidth + g.m_width + 1 > TEXMAP_DIM) { totalHeight += curLineHeight + 1; @@ -484,6 +524,8 @@ FontAtlas::FontAtlas(boo::IGraphicsDataFactory* gf, FT_Face face, uint32_t dpi, gf->newStaticArrayTexture(TEXMAP_DIM, finalHeight, fullTexmapLayers + 1, boo::TextureFormat::I8, texmap.get(), bufSz); } + + buildKernTable(face); } FontCache::Library::Library() @@ -513,7 +555,7 @@ FontTag FontCache::prepCustomFont(boo::IGraphicsDataFactory* gf, if (!face) Log.report(LogVisor::FatalError, "invalid freetype face"); - if (!face->charmap || face->charmap->encoding != ft_encoding_unicode) + if (!face->charmap || face->charmap->encoding != FT_ENCODING_UNICODE) Log.report(LogVisor::FatalError, "font does not contain a unicode char map"); /* Set size with FreeType */ diff --git a/specter/lib/RootView.cpp b/specter/lib/RootView.cpp index e42be35c6..a6ac96f23 100644 --- a/specter/lib/RootView.cpp +++ b/specter/lib/RootView.cpp @@ -3,11 +3,10 @@ namespace Specter { -void RootView::setWindow(boo::IWindow* window, float userScale) +RootView::RootView(ViewSystem& system, boo::IWindow* window) +: View(system), m_window(window) { - window->setCallback(this); - m_window = window; - m_scale = window->getVirtualPixelFactor() * userScale; + } void RootView::resized(const boo::SWindowRect& rect) @@ -76,6 +75,7 @@ void RootView::modKeyUp(boo::EModifierKey mod) void RootView::draw(boo::IGraphicsCommandQueue* gfxQ) { + View::draw(gfxQ); } } diff --git a/specter/lib/Specter.cpp b/specter/lib/Specter.cpp index e69de29bb..d17bacd8b 100644 --- a/specter/lib/Specter.cpp +++ b/specter/lib/Specter.cpp @@ -0,0 +1,30 @@ +#include "Specter/ViewSystem.hpp" + +namespace Specter +{ +static LogVisor::LogModule Log("Specter"); + +void ViewSystem::init(boo::IGraphicsDataFactory* factory, FontCache* fcache) +{ + m_factory = factory; + switch (factory->platform()) + { + case boo::IGraphicsDataFactory::Platform::OGL: + init(static_cast(factory), fcache); + break; +#if _WIN32 + case boo::IGraphicsDataFactory::Platform::D3D11: + case boo::IGraphicsDataFactory::Platform::D3D12: + init(static_cast(factory), fcache); + break; +#elif BOO_HAS_METAL + case boo::IGraphicsDataFactory::Platform::Metal: + init(static_cast(factory), fcache); + break; +#endif + default: + Log.report(LogVisor::FatalError, _S("unable to init view system for %s"), factory->platformName()); + } +} + +} diff --git a/specter/lib/TextView.cpp b/specter/lib/TextView.cpp index a547dac9a..db7b9e66c 100644 --- a/specter/lib/TextView.cpp +++ b/specter/lib/TextView.cpp @@ -1,18 +1,22 @@ #include "Specter/TextView.hpp" +#include "Specter/ViewSystem.hpp" #include "utf8proc.h" namespace Specter { static LogVisor::LogModule Log("Specter::TextView"); -TextView::System TextView::BuildTextSystem(boo::GLDataFactory* factory, FontCache& fcache) +void TextView::System::init(boo::GLDataFactory* factory, FontCache* fcache) { + m_fcache = fcache; + static const char* VS = "#version 330\n" - "layout(location=0) in vec3 posIn;\n" - "layout(location=1) in mat4 mvMtx;\n" - "layout(location=5) in vec3 uvIn[4];\n" - "layout(location=9) in vec4 colorIn;\n" + "layout(location=0) in vec3 posIn[4];\n" + "layout(location=4) in mat4 mvMtx;\n" + "layout(location=8) in vec3 uvIn[4];\n" + "layout(location=12) in vec4 colorIn;\n" + SPECTER_VIEW_VERT_BLOCK_GLSL "struct VertToFrag\n" "{\n" " vec3 uv;\n" @@ -23,7 +27,7 @@ TextView::System TextView::BuildTextSystem(boo::GLDataFactory* factory, FontCach "{\n" " vtf.uv = uvIn[gl_VertexID];\n" " vtf.color = colorIn;\n" - " gl_Position = mvMtx * vec4(posIn, 1.0);\n" + " gl_Position = mv * mvMtx * vec4(posIn[gl_VertexID], 1.0);\n" "}\n"; static const char* FSReg = @@ -59,56 +63,35 @@ TextView::System TextView::BuildTextSystem(boo::GLDataFactory* factory, FontCach " blendOut = texture(fontTex, vtf.uv);\n" "}\n"; - static float Quad[] = - { - 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 1.0, 1.0, 0.0, - 1.0, 0.0, 0.0, - }; + static const char* BlockNames[] = {"SpecterViewBlock"}; - System ret(factory, fcache); - - ret.m_regular = - factory->newShaderPipeline(VS, FSReg, 1, "fontTex", 0, nullptr, + m_regular = + factory->newShaderPipeline(VS, FSReg, 1, "fontTex", 1, BlockNames, boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha, true, true, false); - ret.m_subpixel = - factory->newShaderPipeline(VS, FSSubpixel, 1, "fontTex", 0, nullptr, + m_subpixel = + factory->newShaderPipeline(VS, FSSubpixel, 1, "fontTex", 1, BlockNames, boo::BlendFactor::SrcColor1, boo::BlendFactor::InvSrcColor1, true, true, false); - - ret.m_quadVBO = - factory->newStaticBuffer(boo::BufferUse::Vertex, Quad, sizeof(Quad), 1); - - return ret; } -#if _WIN32 -TextView::Shaders TextView::BuildTextSystem(boo::ID3DDataFactory* factory, FontCache& fcache) -{ -} - -#elif BOO_HAS_METAL -TextView::Shaders TextView::BuildTextSystem(boo::MetalDataFactory* factory, FontCache& fcache) -{ -} -#endif - -TextView::TextView(System& system, FontTag font, size_t initGlyphCapacity) -: m_curGlyphCapacity(initGlyphCapacity), - m_fontAtlas(system.m_fcache.lookupAtlas(font)) +TextView::TextView(ViewSystem& system, FontTag font, size_t initGlyphCapacity) +: View(system), + m_fontAtlas(system.m_textSystem.m_fcache->lookupAtlas(font)) { m_glyphBuf = system.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, sizeof(RenderGlyph), initGlyphCapacity); - if (!system.m_vtxFmt) + if (!system.m_textSystem.m_vtxFmt) { boo::VertexElementDescriptor vdescs[] = { - {system.m_quadVBO, nullptr, boo::VertexSemantic::Position}, + {m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 0}, + {m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 1}, + {m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 2}, + {m_glyphBuf, nullptr, boo::VertexSemantic::Position4 | boo::VertexSemantic::Instanced, 3}, {m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 0}, {m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 1}, {m_glyphBuf, nullptr, boo::VertexSemantic::ModelView | boo::VertexSemantic::Instanced, 2}, @@ -119,31 +102,95 @@ TextView::TextView(System& system, FontTag font, size_t initGlyphCapacity) {m_glyphBuf, nullptr, boo::VertexSemantic::UV4 | boo::VertexSemantic::Instanced, 3}, {m_glyphBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced} }; - m_vtxFmt = system.m_factory->newVertexFormat(10, vdescs); + m_bgVtxFmt = system.m_factory->newVertexFormat(13, vdescs); } m_glyphs.reserve(initGlyphCapacity); } +TextView::RenderGlyph::RenderGlyph(int& adv, const FontAtlas::Glyph& glyph, Zeus::CColor defaultColor) +{ + m_pos[0].assign(adv + glyph.m_leftPadding, glyph.m_verticalOffset + glyph.m_height, 0.f); + m_pos[1].assign(adv + glyph.m_leftPadding, glyph.m_verticalOffset, 0.f); + m_pos[2].assign(adv + glyph.m_leftPadding + glyph.m_width, glyph.m_verticalOffset + glyph.m_height, 0.f); + m_pos[3].assign(adv + glyph.m_leftPadding + glyph.m_width, glyph.m_verticalOffset, 0.f); + m_uv[0].assign(glyph.m_uv[0], glyph.m_uv[1], glyph.m_layerFloat); + m_uv[1].assign(glyph.m_uv[0], glyph.m_uv[3], glyph.m_layerFloat); + m_uv[2].assign(glyph.m_uv[2], glyph.m_uv[1], glyph.m_layerFloat); + m_uv[3].assign(glyph.m_uv[2], glyph.m_uv[3], glyph.m_layerFloat); + m_color = defaultColor; + adv += glyph.m_advance; +} + void TextView::typesetGlyphs(const std::string& str, Zeus::CColor defaultColor) { size_t rem = str.size(); - const utf8proc_uint8_t* it = str.data(); + const utf8proc_uint8_t* it = reinterpret_cast(str.data()); + utf8proc_int32_t lCh = -1; + m_glyphs.clear(); + m_glyphs.reserve(str.size()); + int adv = 0; + while (rem) { utf8proc_int32_t ch; utf8proc_ssize_t sz = utf8proc_iterate(it, -1, &ch); if (sz < 0) Log.report(LogVisor::FatalError, "invalid UTF-8 char"); + if (ch == '\n') + break; - m_fontAtlas + const FontAtlas::Glyph* glyph = m_fontAtlas.lookupGlyph(ch); + if (!glyph) + { + rem -= sz; + it += sz; + continue; + } + if (lCh != -1) + { + adv += m_fontAtlas.lookupKern(lCh, ch); + m_glyphs.emplace_back(adv, *glyph, defaultColor); + } + else + m_glyphs.emplace_back(adv, *glyph, defaultColor); + + lCh = ch; rem -= sz; it += sz; } + + m_validDynamicSlots = 0; } void TextView::typesetGlyphs(const std::wstring& str, Zeus::CColor defaultColor) { + wchar_t lCh = -1; + m_glyphs.clear(); + m_glyphs.reserve(str.size()); + int adv = 0; + + for (wchar_t ch : str) + { + if (ch == L'\n') + break; + + const FontAtlas::Glyph* glyph = m_fontAtlas.lookupGlyph(ch); + if (!glyph) + continue; + + if (lCh != -1) + { + adv += m_fontAtlas.lookupKern(lCh, ch); + m_glyphs.emplace_back(adv, *glyph, defaultColor); + } + else + m_glyphs.emplace_back(adv, *glyph, defaultColor); + + lCh = ch; + } + + m_validDynamicSlots = 0; } void TextView::colorGlyphs(Zeus::CColor newColor) @@ -158,6 +205,14 @@ void TextView::think() void TextView::draw(boo::IGraphicsCommandQueue* gfxQ) { + bindScissor(gfxQ); + int pendingSlot = 1 << gfxQ->pendingDynamicSlot(); + if ((m_validDynamicSlots & pendingSlot) == 0) + { + m_glyphBuf->load(m_glyphs.data(), m_glyphs.size() * sizeof(RenderGlyph)); + m_validDynamicSlots |= pendingSlot; + } + gfxQ->drawInstances(0, 4, m_glyphs.size()); } diff --git a/specter/lib/View.cpp b/specter/lib/View.cpp index aed11c1b0..f9e9d35db 100644 --- a/specter/lib/View.cpp +++ b/specter/lib/View.cpp @@ -1,6 +1,89 @@ #include "Specter/View.hpp" +#include "Specter/ViewSystem.hpp" namespace Specter { +void View::System::init(boo::GLDataFactory* factory) +{ + static const char* VS = + "#version 330\n" + "layout(location=0) in vec3 posIn;\n" + "layout(location=1) in vec4 colorIn;\n" + SPECTER_VIEW_VERT_BLOCK_GLSL + "struct VertToFrag\n" + "{\n" + " vec4 color;\n" + "};\n" + "out VertToFrag vtf;\n" + "void main()\n" + "{\n" + " vtf.color = colorIn;\n" + " gl_Position = mv * vec4(posIn[gl_VertexID], 1.0);\n" + "}\n"; + + static const char* FS = + "#version 330\n" + "struct VertToFrag\n" + "{\n" + " vec4 color;\n" + "};\n" + "in VertToFrag vtf;\n" + "layout(location=0) out vec4 colorOut;\n" + "void main()\n" + "{\n" + " colorOut = vtf.color;\n" + "}\n"; + + static const char* BlockNames[] = {"SpecterViewBlock"}; + + m_solidShader = factory->newShaderPipeline(VS, FS, 0, nullptr, 1, BlockNames, + boo::BlendFactor::SrcAlpha, boo::BlendFactor::InvSrcAlpha, + false, false, false); +} + +View::View(ViewSystem& system) +{ + m_bgVertBuf = + system.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, + sizeof(Zeus::CVector3f), 4); + + m_bgInstBuf = + system.m_factory->newDynamicBuffer(boo::BufferUse::Vertex, + sizeof(Zeus::CColor), 1); + + m_specterVertBlock = + system.m_factory->newDynamicBuffer(boo::BufferUse::Uniform, + sizeof(VertexBlock), 1); + + if (!system.m_viewSystem.m_vtxFmt) + { + boo::VertexElementDescriptor vdescs[] = + { + {m_bgVertBuf, nullptr, boo::VertexSemantic::Position4, 0}, + {m_bgInstBuf, nullptr, boo::VertexSemantic::Color | boo::VertexSemantic::Instanced} + }; + m_bgVtxFmt = system.m_factory->newVertexFormat(2, vdescs); + m_bgShaderBinding = + system.m_factory->newShaderDataBinding(system.m_viewSystem.m_solidShader, m_bgVtxFmt, + m_bgVertBuf, m_bgInstBuf, nullptr, 1, + (boo::IGraphicsBuffer**)&m_specterVertBlock, + 0, nullptr); + } + +} + +void View::draw(boo::IGraphicsCommandQueue* gfxQ) +{ + bindScissor(gfxQ); + int pendingSlot = 1 << gfxQ->pendingDynamicSlot(); + if ((m_validDynamicSlots & pendingSlot) == 0) + { + m_bgVertBuf->load(m_bgRect, sizeof(Zeus::CVector3f) * 4); + m_bgInstBuf->load(&m_bgColor, sizeof(Zeus::CColor)); + m_validDynamicSlots |= pendingSlot; + } + gfxQ->draw(0, 4); +} + }