GPUFont with layers

This commit is contained in:
jrkb 2023-04-13 17:15:35 +02:00
parent dce5e7c57a
commit 70eb4f97f4
8 changed files with 236 additions and 109 deletions

View file

@ -19,6 +19,9 @@
namespace ofxVariableLab {
struct AtlasLayerComboSettings {
};
struct ComboIdentifier {
std::string fontPath = "data/celines-fonts/Version-2-var.ttf";
Layer::Type type = Layer::MSDFGEN;
@ -31,9 +34,11 @@ struct ComboIdentifier {
class AtlasLayerCombo {
public:
virtual void setup(const ComboIdentifier & identifier) = 0;
virtual void setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings) = 0;
virtual void update() = 0;
virtual void careForChild(shared_ptr <Layer> layer) = 0;
virtual void abandonChild(shared_ptr <Layer> layer) = 0;
virtual const ComboIdentifier & getIdentifier() const = 0;
virtual void setVFlip(VFlipState vFlipState) = 0;

View file

@ -1,5 +1,6 @@
#include "GPUFontAtlasLayerCombo.h"
#include "Utils.h"
#include "font.hpp"
#include "ofColor.h"
#include "ofEvents.h"
#include "ofGraphics.h"
@ -7,8 +8,10 @@
namespace ofxVariableLab {
void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier){
void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings){
this->identifier = identifier;
this->settings = static_cast <GPUFontAtlasLayerComboSettings &>(settings);
#ifdef TARGET_EMSCRIPTEN
string shaderDir = "data/ofxGPUFont/shaders/DEBUG_ES3";
#else
@ -25,16 +28,14 @@ void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier){
ofExit();
}
}
currentFontPath = "data/celines-fonts/Version-2-var.ttf";
mainText = "whatever";
ofxGPUFont::tryUpdateMainFont(library,
currentFontPath,
this->identifier.fontPath,
mainText,
font,
bb);
this->settings.gpuTextureOffset);
font->listFontVariationAxes(fontVariationAxesParameters, library);
cout << currentFontPath << " : fontVariationAxes :" << endl;
cout << this->identifier.fontPath << " : fontVariationAxes :" << endl;
for(const auto & axis : fontVariationAxesParameters){
cout << std::fixed << std::setprecision(10) << axis.name << " :\n"
<< "\tmin: " << std::to_string(axis.minValue)
@ -49,29 +50,76 @@ void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier){
cout << "Render type " << r->getType() << endl;
}
void GPUFontAtlasLayerCombo::update(){
//#ifndef TARGET_OPENGLES
//if(ofGetFrameNum() % 10 == 0){
//shaderCatalog->requestUpdate("font");
//}else if(ofGetFrameNum() % 10 == 5){
//shaderCatalog->update();
//float animationSpeed = 1.0;
//float threshold = 1.0;
//for(int i = 0; i < fontVariationAxes.size(); i++){
//ofxGPUFont::Font::FontVariationAxis & axis = fontVariationAxes[i];
//ofxGPUFont::Font::FontVariationAxisParameters & axisParams = fontVariationAxesParameters[i];
//double newValue = ofMap(sin(ofGetElapsedTimef() * animationSpeed),
//-1, 1,
//axisParams.minValue, axisParams.maxValue);
//if(abs(newValue - axis.value) > threshold){
//font->setFontVariationAxis(library, axis.name, newValue);
//axis.value = newValue;
//font->prepareGlyphsForText(mainText, true);
//}
//#endif
float animationSpeed = 1.0;
float threshold = 1.0;
for(int i = 0; i < fontVariationAxes.size(); i++){
ofxGPUFont::Font::FontVariationAxis & axis = fontVariationAxes[i];
ofxGPUFont::Font::FontVariationAxisParameters & axisParams = fontVariationAxesParameters[i];
double newValue = ofMap(sin(ofGetElapsedTimef() * animationSpeed),
-1, 1,
axisParams.minValue, axisParams.maxValue);
if(abs(newValue - axis.value) > threshold){
font->setFontVariationAxis(library, axis.name, newValue);
axis.value = newValue;
font->prepareGlyphsForText(mainText, true);
}
//}
if(isDirty){
//ofxGPUFont::tryUpdateMainFont(library,
//this->identifier.fontPath,
//mainText,
//font,
//this->settings.gpuTextureOffset);
font->prepareGlyphsForText(mainText, true);
isDirty = false;
}
}
void GPUFontAtlasLayerCombo::careForChild(shared_ptr <Layer> layer){
if(layer->getType() == Layer::GPUFONT){
shared_ptr <GPUFontLayer> gpuFontLayer = static_pointer_cast <GPUFontLayer>(layer);
layers.push_back(gpuFontLayer);
std::string oldText = mainText;
std::string layerText = gpuFontLayer->getProps().text;
std::string text = oldText + layerText;
totalCharacters += layerText.length();
removeDuplicateCharacters(text);
// check if we added any characters
// TODO: check for variation variations
if(text.length() != oldText.length()){
mainText = text;
isDirty = true;
}
}else{
ofLogError("GPUFontAtlasLayerCombo::careForChild()") << __LINE__ << ": child is not recognized as Layer::GPUFONT";
}
}
void GPUFontAtlasLayerCombo::abandonChild(shared_ptr <Layer> layer){
// TODO: handle removing child text from buffer
shared_ptr <GPUFontLayer> gpuFontLayer = static_pointer_cast <GPUFontLayer>(layer);
layers.erase(
std::remove_if(
layers.begin(),
layers.end(),
[gpuFontLayer](shared_ptr <GPUFontLayer> const & l)
{
return l == gpuFontLayer;
}), layers.end());
// update the text
// this doesn't happen very often, so let's just
// iterate over all layers and calculate fresh
std::string text = "";
totalCharacters = 0;
for(const auto & layer : layers){
std::string layerText = layer->getProps().text;
totalCharacters += layerText.length();
text += layerText;
}
removeDuplicateCharacters(text);
mainText = text;
// NOTE: we do not want to update buffers now,
// as it's probably more costly than just keeping
// the old glyphs around...
}
const ComboIdentifier & GPUFontAtlasLayerCombo::getIdentifier() const {
return identifier;
@ -89,13 +137,6 @@ void GPUFontAtlasLayerCombo::draw(){
int height = ofGetHeight();
glm::vec2 mouse = glm::vec2(ofGetMouseX(), ofGetMouseY());
float targetFontSize = 128;
float width_unit = 1;
float height_unit = (float)height / width;
//ofClear(125, 255, 125, 255);
//glm::mat4 projection = transform.getProjectionMatrix((float)width / height);
glm::mat4 projection = transform.getOrthoProjectionMatrix(width,
height,
@ -158,37 +199,20 @@ void GPUFontAtlasLayerCombo::draw(){
//mouse.y = 0;
std::vector <ofxGPUFont::Font::BufferVertex> vertices;
std::vector <int32_t> indices;
string text = "whatever";
int n_texts = 240;
vertices.resize(text.length() * n_texts * 4);
indices.resize(text.length() * n_texts * 6);
cx = 0.5f * (bb.minX + bb.maxX);
cy = 0.5f * (bb.minY + bb.maxY);
for(int i = 0; i < n_texts - 1; i++){
float pipi = ((PI * 2 * i) / (n_texts - 1));
float shift = (400 * cos(ofGetElapsedTimef() * 0.125 + pipi));
float shift_x = (600 * sin(ofGetElapsedTimef() * 0.125 + pipi));
float shift_o = (100 * sin(ofGetElapsedTimef() * 0.4 + pipi));
font->collectVerticesAndIndices(
glm::vec3(mouse.x + shift_x,
mouse.y + shift + shift_o,
0),
text,
vertices,
indices,
true,
int(ofMap(sin(ofGetElapsedTimef() + pipi), -1, 1, 42, 96)));
for(const auto & layer : layers){
font->collectVerticesAndIndices(glm::vec3(layer->getProps().x,
layer->getProps().y,
0),
layer->getProps().text,
vertices, indices, true,
layer->getProps().fontSize_px);
}
font->collectVerticesAndIndices(
glm::vec3(mouse.x,
mouse.y,
0),
text,
vertices,
indices,
true,
ofMap(sin(ofGetElapsedTimef() * 0.25f), -1.0f, 1.0f, 42.0f, 96.0f));
vertices.resize(totalCharacters * 4);
indices.resize(totalCharacters * 6);
//cx = 0.5f * (bb.minX + bb.maxX);
//cy = 0.5f * (bb.minY + bb.maxY);
font->draw(vertices, indices);
//ofRectangle rectangle(0, 0, width, height);
//ofDrawRectangle(rectangle);
@ -208,9 +232,7 @@ void GPUFontAtlasLayerCombo::draw(){
ofDrawBitmapStringHighlight(
"fps: " + ofToString(ofGetFrameRate()) + "\n"
+ "font: " + currentFontPath + "\n"
+ "cx: " + ofToString(cx) + "\n"
+ "cy: " + ofToString(cy) + "\n"
+ "font: " + this->identifier.fontPath + "\n"
, 20, 20);
}

View file

@ -10,6 +10,9 @@
#endif
namespace ofxVariableLab {
struct GPUFontAtlasLayerComboSettings : public AtlasLayerComboSettings {
int gpuTextureOffset = 0;
};
class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
struct Transform {
enum Origin {
@ -53,9 +56,11 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
}
};
public:
void setup(const ComboIdentifier & identifier) override;
void setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings = GPUFontAtlasLayerComboSettings()) override;
void update() override;
void careForChild(shared_ptr <Layer> layer) override;
void abandonChild(shared_ptr <Layer> layer) override;
const ComboIdentifier & getIdentifier() const override;
void setVFlip(VFlipState vFlipState) override;
@ -63,8 +68,11 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
void draw();
shared_ptr <ofxGPUFont::Font> font;
string mainText = "";
bool isDirty = false;
private:
GPUFontAtlasLayerComboSettings settings;
FT_Library library;
vector <shared_ptr <GPUFontLayer> > layers;
ComboIdentifier identifier;
@ -76,12 +84,8 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
//std::shared_ptr <ShaderCatalog::Entry> backgroundShader;
std::shared_ptr <ofxGPUFont::ShaderCatalog::Entry> fontShader;
ofxGPUFont::Font::BoundingBox bb;
Transform transform;
string currentFontPath;
string mainText;
// Size of the window (in pixels) used for 1-dimensional anti-aliasing along each rays.
// 0 - no anti-aliasing
// 1 - normal anti-aliasing
@ -97,7 +101,7 @@ class GPUFontAtlasLayerCombo : public AtlasLayerCombo {
std::vector <ofxGPUFont::Font::FontVariationAxis> fontVariationAxes;
VFlipState vFlip;
bool isDirty = false;
int totalCharacters = 0;
};
}

View file

@ -1,5 +1,6 @@
#include "LayerComposition.h"
#include "Atlas.h"
#include "GPUFontAtlasLayerCombo.h"
#include "MsdfLayer.h"
#include "Utils.h"
#include "ofUtils.h"
@ -8,10 +9,10 @@
namespace ofxVariableLab {
void LayerComposition::setup(){
auto combo = make_shared <GPUFontAtlasLayerCombo>();
ComboIdentifier comboIdentifier = {"lol", Layer::GPUFONT};
combo->setup(comboIdentifier);
atlasLayerCombos[comboIdentifier] = combo;
//auto combo = make_shared <GPUFontAtlasLayerCombo>();
//ComboIdentifier comboIdentifier = {"lol", Layer::GPUFONT};
//combo->setup(comboIdentifier);
//atlasLayerCombos[comboIdentifier] = combo;
}
void LayerComposition::update(){
@ -35,40 +36,66 @@ LayerID LayerComposition::addLayer(const ComboIdentifier & identifier,
const Layer::Props & props,
const std::vector <FontVariation> & variations){
string layerID = "";
if(atlasLayerCombos.count(identifier) <= 0){
switch(identifier.type){
case Layer::GPUFONT: {
ofLogError("LayerComposition::addLayer")
<< "GPUFont not implemented" << endl;
break;
switch(identifier.type){
case Layer::GPUFONT: {
shared_ptr <GPUFontAtlasLayerCombo> combo;
auto comboIterator = atlasLayerCombos.find(identifier);
if(comboIterator == atlasLayerCombos.end()){
// we don't have one yet
// so let's create it
combo = make_shared <GPUFontAtlasLayerCombo>();
GPUFontAtlasLayerComboSettings settings;
settings.gpuTextureOffset = nextGpuTextureOffset;
combo->setup(identifier, settings);
nextGpuTextureOffset++;
atlasLayerCombos[identifier] = combo;
}else{
// use existing combo
combo = dynamic_pointer_cast <GPUFontAtlasLayerCombo>(comboIterator->second);
}
default:
case Layer::MSDFGEN: {
// TODO: put most stuff in combo setup
auto combo = make_shared <MsdfAtlasLayerCombo>();
combo->setup(identifier); // TODO: add here text and variations
auto layer = make_shared <MsdfLayer>();
layer->vFlip = vFlipState;
layer->setProps(props);
layer->setup();
std::vector <ofxMsdfgen::FontVariation> msdfVariations;
for(const auto & v : variations){
msdfVariations.push_back({v.name, v.value});
}
// TODO: do not add Variation to atlas, but to combo,
// so we know it's dirty
combo->atlas->addVariations(msdfVariations);
combo->careForChild(layer);
layers[layer->getId()] = layer;
atlasLayerCombos[identifier] = std::move(combo);
layerID = layer->getId();
auto layer = make_shared <GPUFontLayer>();
layer->setProps(props);
combo->careForChild(layer);
layerID = layer->getId();
layers[layerID] = layer;
break;
}
default:
case Layer::MSDFGEN: {
// TODO: put most stuff in combo setup
auto combo = make_shared <MsdfAtlasLayerCombo>();
combo->setup(identifier); // TODO: add here text and variations
auto layer = make_shared <MsdfLayer>();
layer->vFlip = vFlipState;
layer->setProps(props);
layer->setup();
std::vector <ofxMsdfgen::FontVariation> msdfVariations;
for(const auto & v : variations){
msdfVariations.push_back({v.name, v.value});
}
}
// TODO: do not add Variation to atlas, but to combo,
// so we know it's dirty
combo->atlas->addVariations(msdfVariations);
combo->careForChild(layer);
layers[layer->getId()] = layer;
atlasLayerCombos[identifier] = std::move(combo);
layerID = layer->getId();
}
}
return layerID;
}
void LayerComposition::removeLayer(const LayerID & id){
auto layer = getLayer(id);
for(auto & it : atlasLayerCombos){
it.second->abandonChild(layer);
}
layers.erase(id);
}
shared_ptr <Layer> LayerComposition::getLayer(const LayerID & layerID){
return layers[layerID];
}

View file

@ -6,6 +6,7 @@
#include "MsdfLayer.h"
#include "GPUFontAtlasLayerCombo.h"
#include "GPUFontLayer.h"
#include "Utils.h"
namespace ofxVariableLab {
@ -20,14 +21,22 @@ class LayerComposition {
LayerID addLayer(const ComboIdentifier & identifier,
const Layer::Props & props,
const std::vector <FontVariation> & variations);
shared_ptr <ofxVariableLab::Layer> getLayer(const LayerID & layerID);
void removeLayer(const LayerID & id); // TODO: make bool, to catch nonexisting
shared_ptr <Layer> getLayer(const LayerID & layerID);
const unordered_map <ComboIdentifier, shared_ptr <AtlasLayerCombo> > & getAtlasLayerCombos() const;
void setVFlip(bool vFlip);
private:
VFlipState vFlipState = V_FLIP_UNKNOWN;
unordered_map <LayerID, shared_ptr <ofxVariableLab::Layer> > layers;
unordered_map <LayerID, shared_ptr <Layer> > layers;
unordered_map <ComboIdentifier, shared_ptr <AtlasLayerCombo> > atlasLayerCombos;
/**
* @brief lastGpuTextureOffset GL_TEXTURE0 + offset
* we want to different textures for different fonts
* this value is incremented/decremented when adding/removing GPUFont layers
*/
int nextGpuTextureOffset = 0;
//unordered_map <LayerIdentifier, shared_ptr <ofxVariableLab::Layer> > layers;
};

View file

@ -1,16 +1,19 @@
#include "MsdfAtlasLayerCombo.h"
#include "AtlasLayerCombo.h"
#include "Utils.h"
namespace ofxVariableLab {
void MsdfAtlasLayerCombo::setup(const ComboIdentifier & layerIdentifier){
void MsdfAtlasLayerCombo::setup(const ComboIdentifier & layerIdentifier,
AtlasLayerComboSettings settings){
this->identifier = layerIdentifier;
this->settings = static_cast <MsdfAtlasLayerComboSettings &>(settings);
ofxMsdfgen::AtlasSettings atlasSettings;
//settings.characters = "ABCDEFGHIJKL";
atlasSettings.scale = 64;
atlasSettings.minimumScale = 64;
atlasSettings.scale = this->settings.scale;
atlasSettings.minimumScale = this->settings.minimumScale;
atlasSettings.characters = "";
atlasSettings.maxInterpolationStepSize = 50.0;
atlasSettings.maxInterpolationStepSize = this->settings.maxInterpolationStepSize;
atlas = make_shared <ofxMsdfgen::Atlas>();
@ -45,6 +48,17 @@ void MsdfAtlasLayerCombo::careForChild(shared_ptr <Layer> layer){
}
layers.push_back(msdfLayer);
}
void MsdfAtlasLayerCombo::abandonChild(shared_ptr <Layer> layer){
shared_ptr <MsdfLayer> msdfLayer = dynamic_pointer_cast <MsdfLayer>(layer);
layers.erase(
std::remove_if(
layers.begin(),
layers.end(),
[msdfLayer](shared_ptr <MsdfLayer> const & l)
{
return l == msdfLayer;
}), layers.end());
}
const ComboIdentifier & MsdfAtlasLayerCombo::getIdentifier() const {
return identifier;

View file

@ -5,11 +5,18 @@
#include "Utils.h"
namespace ofxVariableLab {
struct MsdfAtlasLayerComboSettings : public AtlasLayerComboSettings {
float scale = 64;
float minimumScale = 64;
float maxInterpolationStepSize = 50.0;
};
class MsdfAtlasLayerCombo : public AtlasLayerCombo {
public:
void setup(const ComboIdentifier & identifier) override;
void setup(const ComboIdentifier & identifier,
AtlasLayerComboSettings settings = MsdfAtlasLayerComboSettings()) override;
void update() override;
void careForChild(shared_ptr <Layer> layer) override;
void abandonChild(shared_ptr <Layer> layer) override;
const ComboIdentifier & getIdentifier() const override;
void setVFlip(VFlipState vFlipState) override;
const ofImage & getAtlasImage();
@ -18,6 +25,7 @@ class MsdfAtlasLayerCombo : public AtlasLayerCombo {
shared_ptr <ofxMsdfgen::Atlas> atlas;
private:
MsdfAtlasLayerComboSettings settings;
vector <shared_ptr <MsdfLayer> > layers;
vector <ofxMsdfgen::GlyphGeometry> glyphGeometries;
shared_ptr <ofShader> msdfShader;

View file

@ -1,6 +1,8 @@
#pragma once
#include <algorithm>
#include <string>
#include <set>
#include <glm/glm.hpp>
namespace ofxVariableLab {
@ -22,6 +24,42 @@ inline void hash_combine(std::size_t & seed, const T & v){
std::hash <T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
//template <class ForwardIt, class UnaryPredicate>
//ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPredicate p){
//first = std::find_if(first, last, p);
//if(first != last){
//for(ForwardIt i = first; ++i != last;){
//if(!p(*i)){
//*first++ = std::move(*i);
//}
//}
//}
//return first;
//}
inline void removeDuplicateCharacters(std::string & str){
std::set <char> chars;
str.erase(
std::remove_if(
str.begin(),
str.end(),
[&chars](char i){
// If encountered character, remove this one.
if(chars.count(i)){
return true;
}
// Otherwise, mark this character encountered and don't remove.
chars.insert(i);
return false;
}
),
str.end()
);
}
enum VFlipState {
V_FLIP_UNKNOWN = 0,
V_FLIP_ON,