#include "GPUFontAtlasLayerCombo.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 namespace ofxVariableLab { void GPUFontAtlasLayerCombo::setup(const ComboIdentifier & identifier, AtlasLayerComboSettings settings){ this->identifier = identifier; this->settings = static_cast (settings); #ifdef TARGET_EMSCRIPTEN string shaderDir = "data/ofxGPUFont/shaders/DEBUG_ES3"; #else string shaderDir = "data/ofxGPUFont/shaders/GL3"; #endif shaderCatalog = std::make_unique (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(); cout << "Render type " << r->getType() << endl; } 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 _variationText; for(const auto & layer : layers){ std::string layerText = layer->getProps().text; totalCharacters += layerText.length(); _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){ if(layer->getType() == LayerType::GPUFONT){ shared_ptr gpuFontLayer = static_pointer_cast (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){ // TODO: handle removing child text from buffer shared_ptr gpuFontLayer = static_pointer_cast (layer); layers.erase( std::remove_if( layers.begin(), layers.end(), [gpuFontLayer](shared_ptr 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 > & GPUFontAtlasLayerCombo::getLayers(){ return layers; } void GPUFontAtlasLayerCombo::draw(){ OFX_PROFILER_FUNCTION(); GLuint location; int width = ofGetWidth(); int height = ofGetHeight(); 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; float superLineHeight = 0; 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 vertices; std::vector indices; vertices.resize(totalCharacters * 4); indices.resize(totalCharacters * 6); std::vector outerBoundingBoxes; for(const auto & layer : layers){ OFX_PROFILER_SCOPE("draw layer"); float lineHeight = font->getLineHeight(layer->getProps().fontSize_px); float ascender = font->getAscender(layer->getProps().fontSize_px); superLineHeight = lineHeight; ofxGPUFont::Font::BoundingBox bb; std::vector bbs; float advanceY = 0; font->collectBoundingBoxes(layer->getVariationText(), layer->getVariationTextAppearance(), bb, bbs, advanceY, true, layer->getProps().fontSize_px); glm::vec4 transformOrigin; getAndApplyTransformOrigin(transformOrigin, layer->getOuterNode(), layer->getInnerNode(), bb, ascender, advanceY, layer->getProps()); bb.multiply(layer->getInnerNode().getGlobalTransformMatrix()); ofVboMesh mesh; mesh.addVertices({bb.p0, bb.p1, bb.p2, bb.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); 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); 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); font->collectVerticesAndIndices(mirrorInnerNode, layer->getVariationTextAppearance(), bbs_mirror_xy, vertices, indices); } font->collectVerticesAndIndices(layer->getInnerNode(), layer->getVariationTextAppearance(), bbs, vertices, indices); } { OFX_PROFILER_SCOPE("font->draw()"); font->draw(vertices, indices); } glUseProgram(currentProgram); 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); ofDrawBitmapStringHighlight( "fps: " + ofToString(ofGetFrameRate()) + "\n" + "font: " + this->identifier.fontPath + "\n" + "lineHEight: " + ofToString(superLineHeight) + "\n" + "antiAliasingWindowSize: " + ofToString(antiAliasingWindowSize) + "\n" + "SSAA: " + (enableSuperSamplingAntiAliasing ? "on" : "off") + "\n" , 20, 20); } 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); } }