839 lines
35 KiB
C++
839 lines
35 KiB
C++
#include "GPUFontAtlasLayerCombo.h"
|
|
#include "GPUFontLayer.h"
|
|
#include "Utils.h"
|
|
#include "font.hpp"
|
|
#include "ofAppRunner.h"
|
|
#include "ofColor.h"
|
|
#include "ofEvents.h"
|
|
#include "ofGraphics.h"
|
|
#include "ofRectangle.h"
|
|
#include "ofUtils.h"
|
|
#include "quaternion.hpp"
|
|
#include <GL/gl.h>
|
|
|
|
namespace ofxVariableLab {
|
|
|
|
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
|
|
string shaderDir = "data/ofxGPUFont/shaders/GL3";
|
|
#endif
|
|
shaderCatalog = std::make_unique <ofxGPUFont::ShaderCatalog>(shaderDir);
|
|
//backgroundShader = shaderCatalog->get("background");
|
|
fontShader = shaderCatalog->get("font");
|
|
|
|
{
|
|
FT_Error error = FT_Init_FreeType(&library);
|
|
if(error){
|
|
std::cerr << "ERROR: failed to initialize FreeType" << std::endl;
|
|
ofExit();
|
|
}
|
|
}
|
|
ofxGPUFont::initializeFont(library,
|
|
this->identifier.fontPath,
|
|
font,
|
|
this->settings.gpuTextureOffset);
|
|
|
|
font->listFontVariationAxes(fontVariationAxesParameters, library);
|
|
//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)
|
|
//<< "\tmax: " << std::to_string(axis.maxValue)
|
|
//<< "\tdefault: " << std::to_string(axis.defaultValue) << endl;
|
|
fontVariationAxes.push_back({axis.name, axis.defaultValue});
|
|
}
|
|
|
|
transform = Transform();
|
|
|
|
auto & r = ofGetCurrentRenderer();
|
|
}
|
|
void GPUFontAtlasLayerCombo::update(){
|
|
OFX_PROFILER_FUNCTION();
|
|
for(const auto & layer : layers){
|
|
layer->update();
|
|
if(layer->isDirtyDirty()){
|
|
isDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
if(isDirty){
|
|
std::string text = "";
|
|
totalCharacters = 0;
|
|
std::vector <ofxGPUFont::GlyphIdentity> _variationText;
|
|
for(const auto & layer : layers){
|
|
std::string layerText = layer->getProps().text;
|
|
int mirrorMultiplier = 1;
|
|
if(layer->getProps().mirror_x){
|
|
mirrorMultiplier += 1;
|
|
}
|
|
if(layer->getProps().mirror_y){
|
|
mirrorMultiplier += 1;
|
|
}
|
|
if(layer->getProps().mirror_xy){
|
|
mirrorMultiplier += 1;
|
|
}
|
|
totalCharacters += layerText.length() * mirrorMultiplier;
|
|
_variationText.insert(_variationText.end(),
|
|
layer->getVariationText().begin(),
|
|
layer->getVariationText().end());
|
|
|
|
text += layerText;
|
|
layer->setDirtyDirty(false); // technically not clean yet
|
|
// but this is synchronous
|
|
// and will happen, so we can avoid
|
|
// another loop
|
|
}
|
|
removeDuplicateCharacters(text);
|
|
mainText = text;
|
|
variationText = std::move(_variationText);
|
|
font->prepareGlyphsForText(variationText,
|
|
library,
|
|
true);
|
|
isDirty = false;
|
|
}
|
|
}
|
|
void GPUFontAtlasLayerCombo::careForChild(shared_ptr <Layer> layer){
|
|
if(layer->getType() == LayerType::GPUFONT){
|
|
shared_ptr <GPUFontLayer> gpuFontLayer = static_pointer_cast <GPUFontLayer>(layer);
|
|
gpuFontLayer->setMomsComboIdentifier(identifier);
|
|
gpuFontLayer->isWithNewMom();
|
|
gpuFontLayer->setDirtyDirty(true);
|
|
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;
|
|
}
|
|
layers.push_back(std::move(gpuFontLayer));
|
|
}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());
|
|
|
|
isDirty = true;
|
|
}
|
|
const ComboIdentifier & GPUFontAtlasLayerCombo::getIdentifier() const {
|
|
return identifier;
|
|
}
|
|
void GPUFontAtlasLayerCombo::setVFlip(VFlipState vFlipState){
|
|
vFlip = vFlipState;
|
|
}
|
|
bool GPUFontAtlasLayerCombo::hasChildren(){
|
|
return layers.size() > 0;
|
|
}
|
|
const vector <shared_ptr <GPUFontLayer> > & GPUFontAtlasLayerCombo::getLayers(){
|
|
return layers;
|
|
}
|
|
void GPUFontAtlasLayerCombo::draw(int width, int height){
|
|
OFX_PROFILER_FUNCTION();
|
|
GLuint location;
|
|
|
|
glm::vec2 mouse = glm::vec2(ofGetMouseX(), ofGetMouseY());
|
|
|
|
//glm::mat4 projection = transform.getProjectionMatrix((float)width / height);
|
|
glm::mat4 projection = transform.getOrthoProjectionMatrix(width,
|
|
height,
|
|
Transform::TOP_LEFT);
|
|
//Transform::CENTERED);
|
|
glm::mat4 view = transform.getViewMatrix();
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
//glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // background color
|
|
//glClear(GL_COLOR_BUFFER_BIT); // clear background with background color
|
|
|
|
// Uses premultiplied-alpha.
|
|
ofDisableDepthTest();
|
|
glEnable(GL_BLEND);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
//glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
|
|
//ofEnableAlphaBlending();
|
|
|
|
int currentProgram;
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
|
|
|
|
//antiAliasingWindowSize = ofMap(ofGetMouseX(), 0, ofGetWidth(), 1, 3);
|
|
//enableSuperSamplingAntiAliasing = ofGetMouseY() > ofGetHeight() / 2;
|
|
if(font){
|
|
OFX_PROFILER_SCOPE("draw font");
|
|
fontShaderProgram = fontShader->program;
|
|
glUseProgram(fontShaderProgram);
|
|
|
|
font->program = fontShaderProgram;
|
|
font->drawSetup();
|
|
|
|
location = glGetUniformLocation(fontShaderProgram, "projection");
|
|
glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection));
|
|
|
|
location = glGetUniformLocation(fontShaderProgram, "view");
|
|
glUniformMatrix4fv(location, 1, false, glm::value_ptr(view));
|
|
location = glGetUniformLocation(fontShaderProgram, "model");
|
|
glUniformMatrix4fv(location, 1, false, glm::value_ptr(model));
|
|
float z = 0;
|
|
location = glGetUniformLocation(fontShaderProgram, "z");
|
|
glUniform1f(location, z);
|
|
|
|
|
|
location = glGetUniformLocation(fontShaderProgram, "antiAliasingWindowSize");
|
|
glUniform1f(location, (float)antiAliasingWindowSize);
|
|
location = glGetUniformLocation(fontShaderProgram, "enableSuperSamplingAntiAliasing");
|
|
glUniform1i(location, enableSuperSamplingAntiAliasing);
|
|
location = glGetUniformLocation(fontShaderProgram, "enableControlPointsVisualization");
|
|
glUniform1i(location, enableControlPointsVisualization);
|
|
|
|
std::vector <ofxGPUFont::Font::BufferVertex> vertices;
|
|
std::vector <int32_t> indices;
|
|
|
|
vertices.resize(totalCharacters * 4);
|
|
indices.resize(totalCharacters * 6);
|
|
|
|
std::vector <ofVboMesh> outerBoundingBoxes;
|
|
ofRectangle r;
|
|
for(const auto & layer : layers){
|
|
OFX_PROFILER_SCOPE("draw layer");
|
|
float ascender = font->getAscender(layer->getProps().fontSize_px);
|
|
ofxGPUFont::Font::BoundingBox bb;
|
|
std::vector <ofxGPUFont::Font::BoundingBox> bbs;
|
|
std::vector <ofxGPUFont::Font::BoundingBox> mirror_bbs;
|
|
float advanceY = 0;
|
|
font->collectBoundingBoxes(layer->getVariationText(),
|
|
layer->getVariationTextAppearance(),
|
|
bb, bbs, advanceY,
|
|
true, layer->getProps().fontSize_px,
|
|
layer->getProps().lineHeight);
|
|
float lineHeight = font->getLineHeight(layer->getProps().fontSize_px) * layer->getProps().lineHeight;
|
|
if(layer->getProps().width > 0){
|
|
int n_wraps = wrapBoundingBoxes(bbs,
|
|
layer->getVariationText(),
|
|
layer->getProps().width,
|
|
lineHeight);
|
|
float min_x = FLT_MAX;
|
|
float min_y = FLT_MAX;
|
|
float max_x = -FLT_MAX;
|
|
float max_y = -FLT_MAX;
|
|
for(const auto & sbb : bbs){
|
|
min_x = min(sbb.p0.x, min_x);
|
|
min_y = min(sbb.p0.y, min_y);
|
|
max_x = max(sbb.p2.x, max_x);
|
|
max_y = max(sbb.p2.y, max_y);
|
|
}
|
|
bb.p0.x = min_x;
|
|
bb.p0.y = min_y;
|
|
bb.p1.x = max_x;
|
|
bb.p1.y = min_y;
|
|
bb.p2.x = max_x;
|
|
bb.p2.y = max_y;
|
|
bb.p3.x = min_x;
|
|
bb.p3.y = max_y;
|
|
|
|
if(ofGetFrameNum() % 600 == 0){
|
|
cout
|
|
<< "n_wraps: " << ofToString(n_wraps) << endl
|
|
<< "layer->getProps().width: " << ofToString(layer->getProps().width) << endl
|
|
;
|
|
|
|
}
|
|
}
|
|
glm::vec4 transformOrigin;
|
|
getAndApplyTransformOrigin(transformOrigin,
|
|
layer->getOuterNode(),
|
|
layer->getInnerNode(),
|
|
bb,
|
|
ascender,
|
|
advanceY,
|
|
layer->getProps());
|
|
|
|
auto bb_mirror_none = bb;
|
|
bb_mirror_none.multiply(layer->getInnerNode().getGlobalTransformMatrix());
|
|
|
|
mirror_bbs.push_back(std::move(bb_mirror_none));
|
|
//layer->setBoundingBox(bb);
|
|
ofVboMesh mesh;
|
|
mesh.addVertices({bb_mirror_none.p0,
|
|
bb_mirror_none.p1,
|
|
bb_mirror_none.p2,
|
|
bb_mirror_none.p3,
|
|
glm::vec4(layer->getProps().x, layer->getProps().y, 0, 1),
|
|
transformOrigin
|
|
});
|
|
mesh.addColors({
|
|
ofColor::red,
|
|
ofColor::red,
|
|
ofColor::red,
|
|
ofColor::red,
|
|
ofColor::white,
|
|
ofColor::green,
|
|
});
|
|
outerBoundingBoxes.push_back(mesh);
|
|
|
|
if(layer->getProps().mirror_x){
|
|
auto bbs_mirror_x = bbs;
|
|
ofNode mirrorOuterNode = layer->getOuterNode();
|
|
ofNode mirrorInnerNode = layer->getInnerNode();
|
|
mirrorInnerNode.setParent(mirrorOuterNode);
|
|
mirrorOuterNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
mirrorOuterNode.rotateDeg(layer->getProps().rotation * -1, glm::vec3(0, 0, 1));
|
|
mirrorOuterNode.setPosition(transformOrigin);
|
|
mirrorOuterNode.move(layer->getProps().mirror_x_distance * -1, 0, 0);
|
|
mirrorOuterNode.setScale(-1, 1, 1);
|
|
|
|
auto bb_mirror_x = bb;
|
|
bb_mirror_x.multiply(mirrorInnerNode.getGlobalTransformMatrix());
|
|
mirror_bbs.push_back(std::move(bb_mirror_x));
|
|
font->collectVerticesAndIndices(mirrorInnerNode,
|
|
layer->getVariationTextAppearance(),
|
|
bbs_mirror_x,
|
|
vertices,
|
|
indices);
|
|
}
|
|
if(layer->getProps().mirror_y){
|
|
auto bbs_mirror_y = bbs;
|
|
ofNode mirrorOuterNode = layer->getOuterNode();
|
|
ofNode mirrorInnerNode = layer->getInnerNode();
|
|
mirrorInnerNode.setParent(mirrorOuterNode);
|
|
mirrorOuterNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
mirrorOuterNode.rotateDeg(layer->getProps().rotation * -1, glm::vec3(0, 0, 1));
|
|
mirrorOuterNode.setPosition(transformOrigin);
|
|
mirrorOuterNode.move(0, layer->getProps().mirror_y_distance * -1, 0);
|
|
mirrorOuterNode.setScale(1, -1, 1);
|
|
|
|
auto bb_mirror_y = bb;
|
|
bb_mirror_y.multiply(mirrorInnerNode.getGlobalTransformMatrix());
|
|
mirror_bbs.push_back(std::move(bb_mirror_y));
|
|
font->collectVerticesAndIndices(mirrorInnerNode,
|
|
layer->getVariationTextAppearance(),
|
|
bbs_mirror_y,
|
|
vertices,
|
|
indices);
|
|
}
|
|
if(layer->getProps().mirror_xy){
|
|
auto bbs_mirror_xy = bbs;
|
|
ofNode mirrorOuterNode = layer->getOuterNode();
|
|
ofNode mirrorInnerNode = layer->getInnerNode();
|
|
mirrorInnerNode.setParent(mirrorOuterNode);
|
|
mirrorOuterNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
mirrorOuterNode.rotateDeg(layer->getProps().rotation * 1, glm::vec3(0, 0, 1));
|
|
mirrorOuterNode.setPosition(transformOrigin);
|
|
mirrorOuterNode.move(layer->getProps().mirror_x_distance * -1,
|
|
layer->getProps().mirror_y_distance * -1, 0);
|
|
mirrorOuterNode.setScale(-1, -1, 1);
|
|
|
|
auto bb_mirror_xy = bb;
|
|
bb_mirror_xy.multiply(mirrorInnerNode.getGlobalTransformMatrix());
|
|
mirror_bbs.push_back(std::move(bb_mirror_xy));
|
|
font->collectVerticesAndIndices(mirrorInnerNode,
|
|
layer->getVariationTextAppearance(),
|
|
bbs_mirror_xy,
|
|
vertices,
|
|
indices);
|
|
}
|
|
font->collectVerticesAndIndices(layer->getInnerNode(),
|
|
layer->getVariationTextAppearance(),
|
|
bbs,
|
|
vertices,
|
|
indices);
|
|
|
|
float min_x = FLT_MAX;
|
|
float max_x = -FLT_MAX;
|
|
float min_y = FLT_MAX;
|
|
float max_y = -FLT_MAX;
|
|
for(const auto & mbb : mirror_bbs){
|
|
min_x = min(min_x, min(mbb.p0.x, min(mbb.p1.x, min(mbb.p2.x, mbb.p3.x))));
|
|
max_x = max(max_x, max(mbb.p0.x, max(mbb.p1.x, max(mbb.p2.x, mbb.p3.x))));
|
|
min_y = min(min_y, min(mbb.p0.y, min(mbb.p1.y, min(mbb.p2.y, mbb.p3.y))));
|
|
max_y = max(max_y, max(mbb.p0.y, max(mbb.p1.y, max(mbb.p2.y, mbb.p3.y))));
|
|
}
|
|
float width = max_x - min_x;
|
|
float height = max_y - min_y;
|
|
layer->setBoundingBox(Layer::BoundingBox{min_x, min_y, width, height});
|
|
|
|
r = ofRectangle(layer->getProps().x, layer->getProps().y, layer->getProps().width, 800);
|
|
}
|
|
|
|
{
|
|
OFX_PROFILER_SCOPE("font->draw()");
|
|
font->draw(vertices, indices);
|
|
}
|
|
|
|
glUseProgram(currentProgram);
|
|
//ofPushStyle();
|
|
//ofSetColor(ofColor::green);
|
|
//ofNoFill();
|
|
//ofDrawRectangle(r);
|
|
//ofPopStyle();
|
|
//ofPushStyle();
|
|
//ofSetColor(ofColor::white);
|
|
//for(const auto & b : outerBoundingBoxes){
|
|
//int i = 0;
|
|
//for(const auto & v : b.getVertices()){
|
|
//ofSetColor(b.getColors()[i]);
|
|
//ofDrawCircle(v, 2);
|
|
//ofDrawBitmapString(ofToString(i), 0, 0);
|
|
//i++;
|
|
//}
|
|
//b.drawVertices();
|
|
//}
|
|
//ofFill();
|
|
//ofPopStyle();
|
|
}
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
void GPUFontAtlasLayerCombo::draw(int width,
|
|
int height,
|
|
const shared_ptr <GPUFontLayer> & layer){
|
|
OFX_PROFILER_FUNCTION();
|
|
GLuint location;
|
|
|
|
glm::vec2 mouse = glm::vec2(ofGetMouseX(), ofGetMouseY());
|
|
|
|
//glm::mat4 projection = transform.getProjectionMatrix((float)width / height);
|
|
glm::mat4 projection = transform.getOrthoProjectionMatrix(width,
|
|
height,
|
|
Transform::TOP_LEFT);
|
|
//Transform::CENTERED);
|
|
glm::mat4 view = transform.getViewMatrix();
|
|
glm::mat4 model = glm::mat4(1.0f);
|
|
|
|
//glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // background color
|
|
//glClear(GL_COLOR_BUFFER_BIT); // clear background with background color
|
|
|
|
// Uses premultiplied-alpha.
|
|
ofDisableDepthTest();
|
|
glEnable(GL_BLEND);
|
|
glBlendEquation(GL_FUNC_ADD);
|
|
//glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE);
|
|
//ofEnableAlphaBlending();
|
|
|
|
int currentProgram;
|
|
glGetIntegerv(GL_CURRENT_PROGRAM, ¤tProgram);
|
|
|
|
//antiAliasingWindowSize = ofMap(ofGetMouseX(), 0, ofGetWidth(), 1, 3);
|
|
//enableSuperSamplingAntiAliasing = ofGetMouseY() > ofGetHeight() / 2;
|
|
if(font){
|
|
OFX_PROFILER_SCOPE("draw font");
|
|
fontShaderProgram = fontShader->program;
|
|
glUseProgram(fontShaderProgram);
|
|
|
|
font->program = fontShaderProgram;
|
|
font->drawSetup();
|
|
|
|
location = glGetUniformLocation(fontShaderProgram, "projection");
|
|
glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection));
|
|
|
|
location = glGetUniformLocation(fontShaderProgram, "view");
|
|
glUniformMatrix4fv(location, 1, false, glm::value_ptr(view));
|
|
location = glGetUniformLocation(fontShaderProgram, "model");
|
|
glUniformMatrix4fv(location, 1, false, glm::value_ptr(model));
|
|
float z = 0;
|
|
location = glGetUniformLocation(fontShaderProgram, "z");
|
|
glUniform1f(location, z);
|
|
|
|
|
|
location = glGetUniformLocation(fontShaderProgram, "antiAliasingWindowSize");
|
|
glUniform1f(location, (float)antiAliasingWindowSize);
|
|
location = glGetUniformLocation(fontShaderProgram, "enableSuperSamplingAntiAliasing");
|
|
glUniform1i(location, enableSuperSamplingAntiAliasing);
|
|
location = glGetUniformLocation(fontShaderProgram, "enableControlPointsVisualization");
|
|
glUniform1i(location, enableControlPointsVisualization);
|
|
|
|
std::vector <ofxGPUFont::Font::BufferVertex> vertices;
|
|
std::vector <int32_t> indices;
|
|
|
|
vertices.resize(totalCharacters * 4);
|
|
indices.resize(totalCharacters * 6);
|
|
|
|
std::vector <ofVboMesh> outerBoundingBoxes;
|
|
ofRectangle r;
|
|
|
|
//for(const auto & layer : layers)
|
|
{
|
|
OFX_PROFILER_SCOPE("draw layer");
|
|
float ascender = font->getAscender(layer->getProps().fontSize_px);
|
|
ofxGPUFont::Font::BoundingBox bb;
|
|
std::vector <ofxGPUFont::Font::BoundingBox> bbs;
|
|
std::vector <ofxGPUFont::Font::BoundingBox> mirror_bbs;
|
|
float advanceY = 0;
|
|
font->collectBoundingBoxes(layer->getVariationText(),
|
|
layer->getVariationTextAppearance(),
|
|
bb, bbs, advanceY,
|
|
true, layer->getProps().fontSize_px,
|
|
layer->getProps().lineHeight);
|
|
float lineHeight = font->getLineHeight(layer->getProps().fontSize_px) * layer->getProps().lineHeight;
|
|
if(layer->getProps().width > 0){
|
|
int n_wraps = wrapBoundingBoxes(bbs,
|
|
layer->getVariationText(),
|
|
layer->getProps().width,
|
|
lineHeight);
|
|
// could be optimized by:
|
|
// - min_x = bbs[0].p0.x
|
|
// - max_x = min_x + layer->getProps().width
|
|
// - min_y = bbs[0].p0.y
|
|
// - max_y = lineHeight * (n_wraps + 1)
|
|
float min_x = FLT_MAX;
|
|
float min_y = FLT_MAX;
|
|
float max_x = -FLT_MAX;
|
|
float max_y = -FLT_MAX;
|
|
for(const auto & sbb : bbs){
|
|
min_x = min(sbb.p0.x, min_x);
|
|
min_y = min(sbb.p0.y, min_y);
|
|
max_x = max(sbb.p2.x, max_x);
|
|
max_y = max(sbb.p2.y, max_y);
|
|
}
|
|
bb.p0.x = min_x;
|
|
bb.p0.y = min_y;
|
|
bb.p1.x = max_x;
|
|
bb.p1.y = min_y;
|
|
bb.p2.x = max_x;
|
|
bb.p2.y = max_y;
|
|
bb.p3.x = min_x;
|
|
bb.p3.y = max_y;
|
|
|
|
if(ofGetFrameNum() % 600 == 0){
|
|
cout
|
|
<< "n_wraps: " << ofToString(n_wraps) << endl
|
|
<< "layer->getProps().width: " << ofToString(layer->getProps().width) << endl
|
|
;
|
|
|
|
}
|
|
}
|
|
glm::vec4 transformOrigin;
|
|
getAndApplyTransformOrigin(transformOrigin,
|
|
layer->getOuterNode(),
|
|
layer->getInnerNode(),
|
|
bb,
|
|
ascender,
|
|
advanceY,
|
|
layer->getProps());
|
|
|
|
auto bb_mirror_none = bb;
|
|
bb_mirror_none.multiply(layer->getInnerNode().getGlobalTransformMatrix());
|
|
|
|
mirror_bbs.push_back(std::move(bb_mirror_none));
|
|
//layer->setBoundingBox(bb);
|
|
ofVboMesh mesh;
|
|
mesh.addVertices({bb_mirror_none.p0,
|
|
bb_mirror_none.p1,
|
|
bb_mirror_none.p2,
|
|
bb_mirror_none.p3,
|
|
glm::vec4(layer->getProps().x, layer->getProps().y, 0, 1),
|
|
transformOrigin
|
|
});
|
|
mesh.addColors({
|
|
ofColor::red,
|
|
ofColor::red,
|
|
ofColor::red,
|
|
ofColor::red,
|
|
ofColor::white,
|
|
ofColor::green,
|
|
});
|
|
outerBoundingBoxes.push_back(mesh);
|
|
|
|
if(layer->getProps().mirror_x){
|
|
auto bbs_mirror_x = bbs;
|
|
ofNode mirrorOuterNode = layer->getOuterNode();
|
|
ofNode mirrorInnerNode = layer->getInnerNode();
|
|
mirrorInnerNode.setParent(mirrorOuterNode);
|
|
mirrorOuterNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
mirrorOuterNode.rotateDeg(layer->getProps().rotation * -1, glm::vec3(0, 0, 1));
|
|
mirrorOuterNode.setPosition(transformOrigin);
|
|
mirrorOuterNode.move(layer->getProps().mirror_x_distance * -1, 0, 0);
|
|
mirrorOuterNode.setScale(-1, 1, 1);
|
|
|
|
auto bb_mirror_x = bb;
|
|
bb_mirror_x.multiply(mirrorInnerNode.getGlobalTransformMatrix());
|
|
mirror_bbs.push_back(std::move(bb_mirror_x));
|
|
font->collectVerticesAndIndices(mirrorInnerNode,
|
|
layer->getVariationTextAppearance(),
|
|
bbs_mirror_x,
|
|
vertices,
|
|
indices);
|
|
}
|
|
if(layer->getProps().mirror_y){
|
|
auto bbs_mirror_y = bbs;
|
|
ofNode mirrorOuterNode = layer->getOuterNode();
|
|
ofNode mirrorInnerNode = layer->getInnerNode();
|
|
mirrorInnerNode.setParent(mirrorOuterNode);
|
|
mirrorOuterNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
mirrorOuterNode.rotateDeg(layer->getProps().rotation * -1, glm::vec3(0, 0, 1));
|
|
mirrorOuterNode.setPosition(transformOrigin);
|
|
mirrorOuterNode.move(0, layer->getProps().mirror_y_distance * -1, 0);
|
|
mirrorOuterNode.setScale(1, -1, 1);
|
|
|
|
auto bb_mirror_y = bb;
|
|
bb_mirror_y.multiply(mirrorInnerNode.getGlobalTransformMatrix());
|
|
mirror_bbs.push_back(std::move(bb_mirror_y));
|
|
font->collectVerticesAndIndices(mirrorInnerNode,
|
|
layer->getVariationTextAppearance(),
|
|
bbs_mirror_y,
|
|
vertices,
|
|
indices);
|
|
}
|
|
if(layer->getProps().mirror_xy){
|
|
auto bbs_mirror_xy = bbs;
|
|
ofNode mirrorOuterNode = layer->getOuterNode();
|
|
ofNode mirrorInnerNode = layer->getInnerNode();
|
|
mirrorInnerNode.setParent(mirrorOuterNode);
|
|
mirrorOuterNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
mirrorOuterNode.rotateDeg(layer->getProps().rotation * 1, glm::vec3(0, 0, 1));
|
|
mirrorOuterNode.setPosition(transformOrigin);
|
|
mirrorOuterNode.move(layer->getProps().mirror_x_distance * -1,
|
|
layer->getProps().mirror_y_distance * -1, 0);
|
|
mirrorOuterNode.setScale(-1, -1, 1);
|
|
|
|
auto bb_mirror_xy = bb;
|
|
bb_mirror_xy.multiply(mirrorInnerNode.getGlobalTransformMatrix());
|
|
mirror_bbs.push_back(std::move(bb_mirror_xy));
|
|
font->collectVerticesAndIndices(mirrorInnerNode,
|
|
layer->getVariationTextAppearance(),
|
|
bbs_mirror_xy,
|
|
vertices,
|
|
indices);
|
|
}
|
|
font->collectVerticesAndIndices(layer->getInnerNode(),
|
|
layer->getVariationTextAppearance(),
|
|
bbs,
|
|
vertices,
|
|
indices);
|
|
|
|
float min_x = FLT_MAX;
|
|
float max_x = -FLT_MAX;
|
|
float min_y = FLT_MAX;
|
|
float max_y = -FLT_MAX;
|
|
for(const auto & mbb : mirror_bbs){
|
|
min_x = min(min_x, min(mbb.p0.x, min(mbb.p1.x, min(mbb.p2.x, mbb.p3.x))));
|
|
max_x = max(max_x, max(mbb.p0.x, max(mbb.p1.x, max(mbb.p2.x, mbb.p3.x))));
|
|
min_y = min(min_y, min(mbb.p0.y, min(mbb.p1.y, min(mbb.p2.y, mbb.p3.y))));
|
|
max_y = max(max_y, max(mbb.p0.y, max(mbb.p1.y, max(mbb.p2.y, mbb.p3.y))));
|
|
}
|
|
float width = max_x - min_x;
|
|
float height = max_y - min_y;
|
|
layer->setBoundingBox(Layer::BoundingBox{min_x, min_y, width, height});
|
|
|
|
r = ofRectangle(layer->getProps().x, layer->getProps().y, layer->getProps().width, 800);
|
|
}
|
|
|
|
{
|
|
OFX_PROFILER_SCOPE("font->draw()");
|
|
font->draw(vertices, indices);
|
|
}
|
|
|
|
glUseProgram(currentProgram);
|
|
//ofPushStyle();
|
|
//ofSetColor(ofColor::green);
|
|
//ofNoFill();
|
|
//ofDrawRectangle(r);
|
|
//ofPopStyle();
|
|
//ofPushStyle();
|
|
//ofSetColor(ofColor::white);
|
|
//for(const auto & b : outerBoundingBoxes){
|
|
//int i = 0;
|
|
//for(const auto & v : b.getVertices()){
|
|
//ofSetColor(b.getColors()[i]);
|
|
//ofDrawCircle(v, 2);
|
|
//ofDrawBitmapString(ofToString(i), 0, 0);
|
|
//i++;
|
|
//}
|
|
//b.drawVertices();
|
|
//}
|
|
//ofFill();
|
|
//ofPopStyle();
|
|
}
|
|
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
void GPUFontAtlasLayerCombo::getAndApplyTransformOrigin(glm::vec4 & transformOrigin,
|
|
ofNode & momNode,
|
|
ofNode & node,
|
|
const ofxGPUFont::Font::BoundingBox & bb,
|
|
const float ascender,
|
|
const float advanceY,
|
|
const Layer::Props & props){
|
|
switch(props.transformOrigin){
|
|
default:
|
|
case Layer::TransformOrigin::TOP_LEFT: {
|
|
node.setPosition(glm::vec3(0,
|
|
ascender,
|
|
0));
|
|
transformOrigin = glm::vec4(props.x,
|
|
props.y - ascender,
|
|
0, 1);
|
|
break;
|
|
}
|
|
|
|
case Layer::TransformOrigin::TOP_RIGHT: {
|
|
float moveX = bb.p1.x - bb.p0.x;
|
|
node.setPosition(glm::vec3(-moveX,
|
|
ascender,
|
|
0));
|
|
transformOrigin = glm::vec4(props.x + moveX,
|
|
props.y - ascender,
|
|
0, 1);
|
|
break;
|
|
}
|
|
|
|
case Layer::TransformOrigin::CENTER: {
|
|
float moveX = (bb.p2.x - bb.p0.x) * 0.5 + bb.p0.x;
|
|
float moveY = (bb.p2.y - bb.p0.y) * 0.5 - bb.p2.y;
|
|
node.setPosition(glm::vec3(-moveX,
|
|
moveY,
|
|
0));
|
|
transformOrigin = glm::vec4(props.x + moveX,
|
|
props.y - moveY,
|
|
0, 1);
|
|
break;
|
|
}
|
|
|
|
case Layer::TransformOrigin::BOTTOM_LEFT: {
|
|
float moveY = ascender - advanceY;
|
|
node.setPosition(glm::vec3(0,
|
|
moveY,
|
|
0));
|
|
transformOrigin = glm::vec4(props.x,
|
|
props.y - moveY,
|
|
0, 1);
|
|
break;
|
|
}
|
|
|
|
case Layer::TransformOrigin::BOTTOM_RIGHT: {
|
|
float moveY = ascender - (advanceY);
|
|
float moveX = (bb.p2.x - bb.p0.x);
|
|
node.setPosition(glm::vec3(-moveX,
|
|
moveY,
|
|
0));
|
|
transformOrigin = glm::vec4(props.x + moveX,
|
|
props.y - moveY,
|
|
0, 1);
|
|
break;
|
|
}
|
|
}
|
|
momNode.setOrientation(glm::quat(1, 0, 0, 0));
|
|
momNode.rotateDeg(props.rotation, glm::vec3(0, 0, 1));
|
|
momNode.setPosition(transformOrigin);
|
|
}
|
|
int GPUFontAtlasLayerCombo::wrapBoundingBoxes(std::vector <ofxGPUFont::Font::BoundingBox> & bbs,
|
|
const std::vector <ofxGPUFont::GlyphIdentity> & _variationText,
|
|
int width,
|
|
float advanceY){
|
|
|
|
int i = 0;
|
|
int vi = 0;
|
|
int wrapIndex = -1;
|
|
int n_wraps = 0;
|
|
bool firstToBreak = true;
|
|
float collectedWrapWidth = 0;
|
|
|
|
for(ofxGPUFont::Font::BoundingBox & bb : bbs){
|
|
bool hasGoodCharacter = false;
|
|
while(!hasGoodCharacter && vi < variationText.size() - 1){
|
|
if(variationText[vi].charcode == '\0'
|
|
|| variationText[vi].charcode == '\r'
|
|
|| variationText[vi].charcode == '\n'){
|
|
wrapIndex = -1;
|
|
collectedWrapWidth = 0;
|
|
vi++;
|
|
}else if(variationText[vi].charcode == ' '){ // TODO: any whitespace
|
|
wrapIndex = i;
|
|
vi++;
|
|
}else{
|
|
hasGoodCharacter = true;
|
|
}
|
|
}
|
|
if(variationText[vi].charcode == '-'){
|
|
wrapIndex = i;
|
|
}
|
|
//float xxx = bb.p1.x - collectedWrapWidth;
|
|
//if(ofGetFrameNum() % 600 == 0){
|
|
//cout << "WRAP INDEX " << char(variationText[vi].charcode) << ":" << ofToString(wrapIndex) << " xxx: " << ofToString(xxx) << " cww: " << ofToString(collectedWrapWidth) << endl;
|
|
//}
|
|
if(bb.p1.x - collectedWrapWidth > width){
|
|
if(wrapIndex >= 0){
|
|
if(wrapIndex == i){
|
|
float wrapWidth = bb.p0.x;
|
|
collectedWrapWidth = wrapWidth;
|
|
n_wraps++;
|
|
bb.p0.x -= wrapWidth;
|
|
bb.p1.x -= wrapWidth;
|
|
bb.p2.x -= wrapWidth;
|
|
bb.p3.x -= wrapWidth;
|
|
bb.p0.y += (n_wraps) * advanceY;
|
|
bb.p1.y += (n_wraps) * advanceY;
|
|
bb.p2.y += (n_wraps) * advanceY;
|
|
bb.p3.y += (n_wraps) * advanceY;
|
|
}else{
|
|
if(firstToBreak){
|
|
collectedWrapWidth = bbs[wrapIndex].p0.x;
|
|
n_wraps++;
|
|
for(int w = wrapIndex; w <= i; w++){
|
|
bbs[w].p0.x -= collectedWrapWidth;
|
|
bbs[w].p1.x -= collectedWrapWidth;
|
|
bbs[w].p2.x -= collectedWrapWidth;
|
|
bbs[w].p3.x -= collectedWrapWidth;
|
|
bbs[w].p0.y += (n_wraps) * advanceY;
|
|
bbs[w].p1.y += (n_wraps) * advanceY;
|
|
bbs[w].p2.y += (n_wraps) * advanceY;
|
|
bbs[w].p3.y += (n_wraps) * advanceY;
|
|
}
|
|
}else{
|
|
bb.p0.x -= collectedWrapWidth;
|
|
bb.p1.x -= collectedWrapWidth;
|
|
bb.p2.x -= collectedWrapWidth;
|
|
bb.p3.x -= collectedWrapWidth;
|
|
bb.p0.y += (n_wraps) * advanceY;
|
|
bb.p1.y += (n_wraps) * advanceY;
|
|
bb.p2.y += (n_wraps) * advanceY;
|
|
bb.p3.y += (n_wraps) * advanceY;
|
|
}
|
|
}
|
|
firstToBreak = false;
|
|
}
|
|
}else{
|
|
bb.p0.x -= collectedWrapWidth;
|
|
bb.p1.x -= collectedWrapWidth;
|
|
bb.p2.x -= collectedWrapWidth;
|
|
bb.p3.x -= collectedWrapWidth;
|
|
bb.p0.y += (n_wraps) * advanceY;
|
|
bb.p1.y += (n_wraps) * advanceY;
|
|
bb.p2.y += (n_wraps) * advanceY;
|
|
bb.p3.y += (n_wraps) * advanceY;
|
|
}
|
|
|
|
vi++;
|
|
i++;
|
|
}
|
|
return n_wraps;
|
|
}
|
|
//void GPUFontAtlasLayerCombo::cullBoundingBoxes(std::vector <ofxGPUFont::Font::BoundingBox> & bbs,
|
|
//int width, int height){
|
|
//}
|
|
//bool GPUFontAtlasLayerCombo::isInside(const ofxGPUFont::Font::BoundingBox & bb,
|
|
//int width, int height){
|
|
//}
|
|
//bool GPUFontAtlasLayerCombo::isInside(const glm::vec4 & vertex,
|
|
//int width, int height){
|
|
//}
|
|
|
|
}
|