slight refactor
This commit is contained in:
parent
470b831c61
commit
ee47b7649a
7 changed files with 8 additions and 739 deletions
|
@ -88,7 +88,7 @@ start. I hope to capture something essential.)DONE";
|
|||
#else
|
||||
string shaderDir = "data/ofxGPUFont/shaders/GL3";
|
||||
#endif
|
||||
shaderCatalog = std::make_unique <ShaderCatalog>(shaderDir);
|
||||
shaderCatalog = std::make_unique <ofxGPUFont::ShaderCatalog>(shaderDir);
|
||||
//backgroundShader = shaderCatalog->get("background");
|
||||
fontShader = shaderCatalog->get("font");
|
||||
|
||||
|
@ -252,7 +252,7 @@ void ofApp::keyReleased(int key){
|
|||
#else
|
||||
string shaderDir = "data/ofxGPUFont/shaders/GL3";
|
||||
#endif
|
||||
shaderCatalog = std::make_unique <ShaderCatalog>(shaderDir);
|
||||
shaderCatalog = std::make_unique <ofxGPUFont::ShaderCatalog>(shaderDir);
|
||||
//backgroundShader = shaderCatalog->get("background");
|
||||
fontShader = shaderCatalog->get("font");
|
||||
}
|
||||
|
|
|
@ -46,9 +46,9 @@ class ofApp : public ofBaseApp {
|
|||
// because OpenGL still requires a non-zero VAO to be bound for the draw call.
|
||||
GLuint emptyVAO;
|
||||
|
||||
std::unique_ptr <ShaderCatalog> shaderCatalog;
|
||||
std::unique_ptr <ofxGPUFont::ShaderCatalog> shaderCatalog;
|
||||
//std::shared_ptr <ShaderCatalog::Entry> backgroundShader;
|
||||
std::shared_ptr <ShaderCatalog::Entry> fontShader;
|
||||
std::shared_ptr <ofxGPUFont::ShaderCatalog::Entry> fontShader;
|
||||
|
||||
ofxGPUFont::Font::BoundingBox bb;
|
||||
|
||||
|
|
18
src/glm.hpp
18
src/glm.hpp
|
@ -1,18 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#define GLM_FORCE_RADIANS
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <glm/gtx/hash.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
|
@ -1,717 +0,0 @@
|
|||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <defer.hpp>
|
||||
|
||||
#include "ofMain.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_MULTIPLE_MASTERS_H
|
||||
|
||||
#include "glm.hpp"
|
||||
|
||||
#include "shader_catalog.hpp"
|
||||
|
||||
#define F26DOT6_TO_DOUBLE(x) (1 / 64. * double(x))
|
||||
#define F16DOT16_TO_DOUBLE(x) (1 / 65536. * double(x))
|
||||
#define DOUBLE_TO_F16DOT16(x) FT_Fixed(65536. * x)
|
||||
|
||||
#include "font.hpp"
|
||||
|
||||
float wght = 700.0;
|
||||
float wghtStep = 10.0;
|
||||
std::string currentFontPath = "";
|
||||
|
||||
struct Transform {
|
||||
float fovy = glm::radians(60.0f);
|
||||
float distance = 0.42f;
|
||||
glm::mat3 rotation = glm::mat3(1.0f);
|
||||
glm::vec3 position = glm::vec3(0.0f);
|
||||
|
||||
glm::mat4 getProjectionMatrix(float aspect){
|
||||
return glm::perspective( /* fovy = */ glm::radians(60.0f), aspect, 0.002f, 12.000f);
|
||||
}
|
||||
|
||||
glm::mat4 getViewMatrix(){
|
||||
auto translation = glm::translate(position);
|
||||
return glm::lookAt(glm::vec3(0, 0, distance), glm::vec3(0, 0, 0), glm::vec3(0, 1, 0)) * glm::mat4(rotation) * translation;
|
||||
}
|
||||
};
|
||||
|
||||
struct DragController {
|
||||
enum class Action {
|
||||
NONE,
|
||||
TRANSLATE,
|
||||
ROTATE_TURNTABLE,
|
||||
ROTATE_TRACKBALL
|
||||
};
|
||||
|
||||
Transform * transform = nullptr;
|
||||
int activeButton = -1;
|
||||
Action activeAction = Action::NONE;
|
||||
|
||||
double dragX, dragY;
|
||||
double wrapX, wrapY;
|
||||
double virtualX, virtualY;
|
||||
glm::vec3 dragTarget;
|
||||
|
||||
void reset(){
|
||||
// Reset transform.
|
||||
*transform = Transform{};
|
||||
|
||||
// Cancel active action, if any.
|
||||
activeButton = -1;
|
||||
activeAction = Action::NONE;
|
||||
}
|
||||
|
||||
bool unprojectMousePositionToXYPlane(GLFWwindow * window, double x, double y, glm::vec3 & result){
|
||||
int iwidth = 0, iheight = 0;
|
||||
glfwGetWindowSize(window, &iwidth, &iheight);
|
||||
|
||||
double width = iwidth;
|
||||
double height = iheight;
|
||||
|
||||
glm::mat4 projection = transform->getProjectionMatrix(float(width / height));
|
||||
glm::mat4 view = transform->getViewMatrix();
|
||||
|
||||
double relX = x / width * 2.0 - 1.0;
|
||||
double relY = y / height * 2.0 - 1.0;
|
||||
|
||||
glm::vec4 clipPos = glm::vec4(float(relX), -float(relY), 0.5f, 1.0f);
|
||||
glm::vec4 worldPos = glm::inverse(projection * view) * clipPos;
|
||||
worldPos *= 1.0f / worldPos.w;
|
||||
|
||||
glm::vec3 pos = glm::vec3(glm::column(glm::inverse(view), 3));
|
||||
glm::vec3 dir = glm::normalize(glm::vec3(worldPos) - pos);
|
||||
float t = -pos.z / dir.z;
|
||||
|
||||
result = pos + t * dir;
|
||||
return t > 0.0f;
|
||||
}
|
||||
|
||||
void onMouseButton(GLFWwindow * window, int button, int action, int mods){
|
||||
if(action == GLFW_PRESS && activeButton == -1){
|
||||
activeButton = button;
|
||||
|
||||
if(mods & GLFW_MOD_CONTROL){
|
||||
activeAction = Action::TRANSLATE;
|
||||
}else{
|
||||
if(activeButton == GLFW_MOUSE_BUTTON_2){
|
||||
activeAction = Action::TRANSLATE;
|
||||
}else if(activeButton == GLFW_MOUSE_BUTTON_3){
|
||||
activeAction = Action::ROTATE_TURNTABLE;
|
||||
}else{
|
||||
activeAction = Action::ROTATE_TRACKBALL;
|
||||
}
|
||||
}
|
||||
|
||||
glfwGetCursorPos(window, &dragX, &dragY);
|
||||
wrapX = std::numeric_limits <double>::quiet_NaN();
|
||||
wrapY = std::numeric_limits <double>::quiet_NaN();
|
||||
virtualX = dragX;
|
||||
virtualY = dragY;
|
||||
|
||||
glm::vec3 target;
|
||||
bool ok = unprojectMousePositionToXYPlane(window, dragX, dragY, target);
|
||||
dragTarget = ok ? target : glm::vec3();
|
||||
}else if(action == GLFW_RELEASE && activeButton == button){
|
||||
activeButton = -1;
|
||||
activeAction = Action::NONE;
|
||||
dragX = 0.0;
|
||||
dragY = 0.0;
|
||||
wrapX = std::numeric_limits <double>::quiet_NaN();
|
||||
wrapY = std::numeric_limits <double>::quiet_NaN();
|
||||
virtualX = 0.0;
|
||||
virtualY = 0.0;
|
||||
dragTarget = glm::vec3();
|
||||
}
|
||||
}
|
||||
|
||||
void onCursorPos(GLFWwindow * window, double x, double y){
|
||||
if(activeAction == Action::NONE){
|
||||
return;
|
||||
}
|
||||
|
||||
int iwidth = 0, iheight = 0;
|
||||
glfwGetWindowSize(window, &iwidth, &iheight);
|
||||
|
||||
double width = iwidth;
|
||||
double height = iheight;
|
||||
|
||||
double deltaX = x - dragX;
|
||||
double deltaY = y - dragY;
|
||||
|
||||
if(!std::isnan(wrapX) && !std::isnan(wrapY)){
|
||||
double wrapDeltaX = x - wrapX;
|
||||
double wrapDeltaY = y - wrapY;
|
||||
if(wrapDeltaX * wrapDeltaX + wrapDeltaY * wrapDeltaY < deltaX * deltaX + deltaY * deltaY){
|
||||
deltaX = wrapDeltaX;
|
||||
deltaY = wrapDeltaY;
|
||||
wrapX = std::numeric_limits <double>::quiet_NaN();
|
||||
wrapY = std::numeric_limits <double>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
dragX = x;
|
||||
dragY = y;
|
||||
|
||||
double targetX = x;
|
||||
double targetY = y;
|
||||
bool changed = false;
|
||||
if(targetX < 0){
|
||||
targetX += width - 1;
|
||||
changed = true;
|
||||
}else if(targetX >= width){
|
||||
targetX -= width - 1;
|
||||
changed = true;
|
||||
}
|
||||
if(targetY < 0){
|
||||
targetY += height - 1;
|
||||
changed = true;
|
||||
}else if(targetY >= height){
|
||||
targetY -= height - 1;
|
||||
changed = true;
|
||||
}
|
||||
if(changed){
|
||||
glfwSetCursorPos(window, targetX, targetY);
|
||||
wrapX = targetX;
|
||||
wrapY = targetY;
|
||||
}
|
||||
|
||||
if(activeAction == Action::TRANSLATE){
|
||||
virtualX += deltaX;
|
||||
virtualY += deltaY;
|
||||
|
||||
glm::vec3 target;
|
||||
bool ok = unprojectMousePositionToXYPlane(window, virtualX, virtualY, target);
|
||||
if(ok){
|
||||
float x = transform->position.x;
|
||||
float y = transform->position.y;
|
||||
glm::vec3 delta = target - dragTarget;
|
||||
transform->position.x = glm::clamp(x + delta.x, -4.0f, 4.0f);
|
||||
transform->position.y = glm::clamp(y + delta.y, -4.0f, 4.0f);
|
||||
}
|
||||
}else if(activeAction == Action::ROTATE_TURNTABLE){
|
||||
double size = glm::min(width, height);
|
||||
glm::mat3 rx = glm::rotate(float(deltaX / size * glm::pi <double>()), glm::vec3(0, 0, 1));
|
||||
glm::mat3 ry = glm::rotate(float(deltaY / size * glm::pi <double>()), glm::vec3(1, 0, 0));
|
||||
transform->rotation = ry * transform->rotation * rx;
|
||||
}else if(activeAction == Action::ROTATE_TRACKBALL){
|
||||
double size = glm::min(width, height);
|
||||
glm::mat3 rx = glm::rotate(float(deltaX / size * glm::pi <double>()), glm::vec3(0, 1, 0));
|
||||
glm::mat3 ry = glm::rotate(float(deltaY / size * glm::pi <double>()), glm::vec3(1, 0, 0));
|
||||
transform->rotation = ry * rx * transform->rotation;
|
||||
}
|
||||
}
|
||||
|
||||
void onScroll(GLFWwindow * window, double xOffset, double yOffset){
|
||||
float factor = glm::clamp(1.0 - float(yOffset) / 10.0, 0.1, 1.9);
|
||||
transform->distance = glm::clamp(transform->distance * factor, 0.010f, 10.000f);
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
FT_Library library;
|
||||
|
||||
Transform transform;
|
||||
DragController dragController;
|
||||
|
||||
// Empty VAO used when the vertex shader has no input and only uses gl_VertexID,
|
||||
// because OpenGL still requires a non-zero VAO to be bound for the draw call.
|
||||
GLuint emptyVAO;
|
||||
|
||||
std::unique_ptr <ShaderCatalog> shaderCatalog;
|
||||
std::shared_ptr <ShaderCatalog::Entry> backgroundShader;
|
||||
std::shared_ptr <ShaderCatalog::Entry> fontShader;
|
||||
|
||||
std::unique_ptr <Font> mainFont;
|
||||
std::unique_ptr <Font> helpFont;
|
||||
std::vector <std::unique_ptr <Font> > otherFonts;
|
||||
std::vector <float> otherWghts;
|
||||
std::vector <float> otherSteps;
|
||||
static const int N_OTHER_FONTS = 5;
|
||||
|
||||
constexpr float helpFontBaseSize = 20.0f;
|
||||
|
||||
int antiAliasingWindowSize = 1;
|
||||
bool enableSuperSamplingAntiAliasing = true;
|
||||
bool enableControlPointsVisualization = false;
|
||||
|
||||
bool showHelp = true;
|
||||
|
||||
Font::BoundingBox bb;
|
||||
std::string mainTexta =
|
||||
R"DONE(AVM AAIn the center of Fedora, that gray stone metropolis, stands a metal building
|
||||
[from Invisible Cities by Italo Calvino])DONE";
|
||||
std::string mainText =
|
||||
R"DONE(In the center of Fedora, that gray stone metropolis, stands a metal building
|
||||
with a crystal globe in every room. Looking into each globe, you see a blue
|
||||
city, the model of a different Fedora. These are the forms the city could have
|
||||
taken if, for one reason or another, it had not become what we see today. In
|
||||
every age someone, looking at Fedora as it was, imagined a way of making it the
|
||||
ideal city, but while he constructed his miniature model, Fedora was already no
|
||||
longer the same as before, and what had been until yesterday a possible future
|
||||
became only a toy in a glass globe.
|
||||
|
||||
The building with the globes is now Fedora's museum: every inhabitant visits it,
|
||||
chooses the city that corresponds to his desires, contemplates it, imagining his
|
||||
reflection in the medusa pond that would have collected the waters of the canal
|
||||
(if it had not been dried up), the view from the high canopied box along the
|
||||
avenue reserved for elephants (now banished from the city), the fun of sliding
|
||||
down the spiral, twisting minaret (which never found a pedestal from which to
|
||||
rise).
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
The building with the globes is now Fedora's museum: every inhabitant visits it,
|
||||
chooses the city that corresponds to his desires, contemplates it, imagining his
|
||||
reflection in the medusa pond that would have collected the waters of the canal
|
||||
(if it had not been dried up), the view from the high canopied box along the
|
||||
avenue reserved for elephants (now banished from the city), the fun of sliding
|
||||
down the spiral, twisting minaret (which never found a pedestal from which to
|
||||
rise).
|
||||
|
||||
The building with the globes is now Fedora's museum: every inhabitant visits it,
|
||||
chooses the city that corresponds to his desires, contemplates it, imagining his
|
||||
reflection in the medusa pond that would have collected the waters of the canal
|
||||
(if it had not been dried up), the view from the high canopied box along the
|
||||
avenue reserved for elephants (now banished from the city), the fun of sliding
|
||||
down the spiral, twisting minaret (which never found a pedestal from which to
|
||||
rise).
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
|
||||
On the map of your empire, O Great Khan, there must be room both for the big,
|
||||
stone Fedora and the little Fedoras in glass globes. Not because they are all
|
||||
equally real, but because they are only assumptions. The one contains what is
|
||||
accepted as necessary when it is not yet so; the others, what is imagined as
|
||||
possible and, a moment later, is possible no longer.
|
||||
[from Invisible Cities by Italo Calvino])DONE";
|
||||
|
||||
}
|
||||
|
||||
void updateWght(float & value, float & step){
|
||||
value += step;
|
||||
|
||||
if(value >= 700.0){
|
||||
if(step > 0){
|
||||
step *= -1;
|
||||
}
|
||||
value += step;
|
||||
}
|
||||
if(value <= 100.0){
|
||||
if(step < 0){
|
||||
step *= -1;
|
||||
}
|
||||
value += step;
|
||||
}
|
||||
}
|
||||
|
||||
int currentIndexx = -1;
|
||||
|
||||
static std::unique_ptr <Font> loadFont(const std::string & filename, float worldSize = 1.0f, bool hinting = false){
|
||||
std::string error;
|
||||
FT_Face face = Font::loadFace(library, filename, error);
|
||||
float & wghtValue = currentIndexx < 0 ? wght : otherWghts[currentIndexx];
|
||||
bool success = Font::setFontVariationAxis(library, face, "Weight", wghtValue);
|
||||
//if(success){
|
||||
//std::cout << "lol, success?" << wght << std::endl;
|
||||
//}else{
|
||||
//std::cout << "godverdomme" << wght << std::endl;
|
||||
//}
|
||||
if(error != ""){
|
||||
std::cerr << "[font] failed to load " << filename << ": " << error << std::endl;
|
||||
return std::unique_ptr <Font>{};
|
||||
}
|
||||
|
||||
return std::make_unique <Font>(face, worldSize, hinting);
|
||||
}
|
||||
|
||||
static void tryUpdateMainFont(const std::string & filename){
|
||||
{
|
||||
currentIndexx = -1;
|
||||
auto font = loadFont(filename, 0.05f);
|
||||
if(!font){
|
||||
return;
|
||||
}
|
||||
|
||||
font->dilation = 0.1f;
|
||||
|
||||
font->prepareGlyphsForText(mainText);
|
||||
|
||||
mainFont = std::move(font);
|
||||
bb = mainFont->measure(0, 0, mainText);
|
||||
}
|
||||
updateWght(wght, wghtStep);
|
||||
|
||||
for(int i = 0; i < N_OTHER_FONTS; ++i){
|
||||
if(otherWghts.size() == i){
|
||||
int w = i * 213456;
|
||||
while(w > 700){
|
||||
w -= 700;
|
||||
}
|
||||
otherWghts.push_back(float(w));
|
||||
otherSteps.push_back(float(wghtStep));
|
||||
}
|
||||
currentIndexx = i;
|
||||
auto font = loadFont(filename, 0.05f);
|
||||
if(!font){
|
||||
return;
|
||||
}
|
||||
updateWght(otherWghts[i], otherSteps[i]);
|
||||
//std::cout << "updateWght(" << otherWghts[i] << "," << otherSteps[i] << ");" << std::endl;
|
||||
|
||||
font->dilation = 0.1f;
|
||||
|
||||
font->prepareGlyphsForText(mainText);
|
||||
|
||||
if(otherFonts.size() == i){
|
||||
otherFonts.push_back(std::move(font));
|
||||
}else{
|
||||
otherFonts[i] = std::move(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mouseButtonCallback(GLFWwindow * window, int button, int action, int mods){
|
||||
dragController.onMouseButton(window, button, action, mods);
|
||||
}
|
||||
|
||||
static void cursorPosCallback(GLFWwindow * window, double x, double y){
|
||||
dragController.onCursorPos(window, x, y);
|
||||
}
|
||||
|
||||
static void scrollCallback(GLFWwindow * window, double xOffset, double yOffset){
|
||||
dragController.onScroll(window, xOffset, yOffset);
|
||||
}
|
||||
|
||||
static void keyCallback(GLFWwindow * window, int key, int scancode, int action, int mods){
|
||||
if(action != GLFW_PRESS){
|
||||
return;
|
||||
}
|
||||
switch(key){
|
||||
case GLFW_KEY_R:
|
||||
dragController.reset();
|
||||
break;
|
||||
|
||||
case GLFW_KEY_C:
|
||||
enableControlPointsVisualization = !enableControlPointsVisualization;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_A:
|
||||
enableSuperSamplingAntiAliasing = !enableSuperSamplingAntiAliasing;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_0:
|
||||
antiAliasingWindowSize = 0;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_1:
|
||||
antiAliasingWindowSize = 1;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_2:
|
||||
antiAliasingWindowSize = 20;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_3:
|
||||
antiAliasingWindowSize = 40;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_S:
|
||||
antiAliasingWindowSize = 1;
|
||||
enableSuperSamplingAntiAliasing = true;
|
||||
break;
|
||||
|
||||
case GLFW_KEY_H:
|
||||
showHelp = !showHelp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void dropCallback(GLFWwindow * window, int pathCount, const char * paths[]){
|
||||
if(pathCount == 0){
|
||||
return;
|
||||
}
|
||||
currentFontPath = paths[0];
|
||||
tryUpdateMainFont(paths[0]);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[]){
|
||||
if(!glfwInit()){
|
||||
std::cerr << "ERROR: failed to initialize GLFW" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
|
||||
glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE);
|
||||
|
||||
GLFWwindow * window = glfwCreateWindow(1600, 900, "GPU Font Rendering Demo", nullptr, nullptr);
|
||||
if(!window){
|
||||
std::cerr << "ERROR: failed to create GLFW window" << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
if(!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){
|
||||
std::cerr << "ERROR: failed to initialize OpenGL context" << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
|
||||
{
|
||||
FT_Error error = FT_Init_FreeType(&library);
|
||||
if(error){
|
||||
std::cerr << "ERROR: failed to initialize FreeType" << std::endl;
|
||||
glfwTerminate();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
dragController.transform = &transform;
|
||||
glfwSetMouseButtonCallback(window, mouseButtonCallback);
|
||||
glfwSetCursorPosCallback(window, cursorPosCallback);
|
||||
glfwSetScrollCallback(window, scrollCallback);
|
||||
glfwSetKeyCallback(window, keyCallback);
|
||||
glfwSetDropCallback(window, dropCallback);
|
||||
|
||||
glGenVertexArrays(1, &emptyVAO);
|
||||
|
||||
shaderCatalog = std::make_unique <ShaderCatalog>("shaders");
|
||||
backgroundShader = shaderCatalog->get("background");
|
||||
fontShader = shaderCatalog->get("font");
|
||||
|
||||
currentFontPath = "fonts/SourceSerifPro-Regular.otf";
|
||||
tryUpdateMainFont(currentFontPath);
|
||||
|
||||
{
|
||||
float xscale, yscale;
|
||||
glfwGetWindowContentScale(window, &xscale, &yscale);
|
||||
float worldSize = std::ceil(helpFontBaseSize * yscale);
|
||||
helpFont = loadFont("fonts/SourceSansPro-Semibold.otf", worldSize, true);
|
||||
}
|
||||
|
||||
while(!glfwWindowShouldClose(window)){
|
||||
tryUpdateMainFont(currentFontPath);
|
||||
shaderCatalog->update();
|
||||
|
||||
glfwPollEvents();
|
||||
|
||||
int width, height;
|
||||
glfwGetFramebufferSize(window, &width, &height);
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
|
||||
GLuint location;
|
||||
|
||||
glm::mat4 projection = transform.getProjectionMatrix((float)width / height);
|
||||
glm::mat4 view = transform.getViewMatrix();
|
||||
glm::mat4 model = glm::mat4(1.0f);
|
||||
|
||||
{ // Draw background.
|
||||
GLuint program = backgroundShader->program;
|
||||
glUseProgram(program);
|
||||
glBindVertexArray(emptyVAO);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
glBindVertexArray(0);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
// Uses premultiplied-alpha.
|
||||
glEnable(GL_BLEND);
|
||||
glBlendEquation(GL_FUNC_ADD);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
for(int i = N_OTHER_FONTS - 1; i >= 0; i--){
|
||||
if(otherFonts.size() > i){
|
||||
//std::cout << "drawing " << i << " with " << otherWghts[i] << std::endl;
|
||||
auto & otherFont = otherFonts[i];
|
||||
GLuint program = fontShader->program;
|
||||
glUseProgram(program);
|
||||
|
||||
otherFont->program = program;
|
||||
otherFont->drawSetup();
|
||||
|
||||
location = glGetUniformLocation(program, "projection");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection));
|
||||
location = glGetUniformLocation(program, "view");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(view));
|
||||
location = glGetUniformLocation(program, "model");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(model));
|
||||
|
||||
location = glGetUniformLocation(program, "color");
|
||||
float r = float(i) / N_OTHER_FONTS;
|
||||
float g = sin(42.143 * float(i) + otherWghts[i] * 0.0001) * 0.5 + 0.5;
|
||||
float b = cos(3.143 * float(i) + otherWghts[i] * 0.0005) * 0.1 + 0.1;
|
||||
glUniform4f(location, r, 1.0f - r, 1.0f - r, 1.0f);
|
||||
float z = (i + 1) * -0.1;
|
||||
location = glGetUniformLocation(program, "z");
|
||||
glUniform1f(location, z);
|
||||
|
||||
location = glGetUniformLocation(program, "antiAliasingWindowSize");
|
||||
glUniform1f(location, (float)antiAliasingWindowSize);
|
||||
location = glGetUniformLocation(program, "enableSuperSamplingAntiAliasing");
|
||||
glUniform1i(location, enableSuperSamplingAntiAliasing);
|
||||
location = glGetUniformLocation(program, "enableControlPointsVisualization");
|
||||
glUniform1i(location, enableControlPointsVisualization);
|
||||
|
||||
float cx = 0.5f * (bb.minX + bb.maxX);
|
||||
float cy = 0.5f * (bb.minY + bb.maxY);
|
||||
otherFont->draw(-cx, -cy, 0, mainText);
|
||||
glUseProgram(0);
|
||||
}
|
||||
}
|
||||
if(mainFont){
|
||||
GLuint program = fontShader->program;
|
||||
glUseProgram(program);
|
||||
|
||||
mainFont->program = program;
|
||||
mainFont->drawSetup();
|
||||
|
||||
location = glGetUniformLocation(program, "projection");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection));
|
||||
location = glGetUniformLocation(program, "view");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(view));
|
||||
location = glGetUniformLocation(program, "model");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(model));
|
||||
float z = 0;
|
||||
location = glGetUniformLocation(program, "z");
|
||||
glUniform1f(location, z);
|
||||
|
||||
location = glGetUniformLocation(program, "color");
|
||||
glUniform4f(location, 1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
location = glGetUniformLocation(program, "antiAliasingWindowSize");
|
||||
glUniform1f(location, (float)antiAliasingWindowSize);
|
||||
location = glGetUniformLocation(program, "enableSuperSamplingAntiAliasing");
|
||||
glUniform1i(location, enableSuperSamplingAntiAliasing);
|
||||
location = glGetUniformLocation(program, "enableControlPointsVisualization");
|
||||
glUniform1i(location, enableControlPointsVisualization);
|
||||
|
||||
float cx = 0.5f * (bb.minX + bb.maxX);
|
||||
float cy = 0.5f * (bb.minY + bb.maxY);
|
||||
mainFont->draw(-cx, -cy, 0, mainText);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
if(helpFont && showHelp){
|
||||
GLuint program = fontShader->program;
|
||||
glUseProgram(program);
|
||||
|
||||
helpFont->program = program;
|
||||
helpFont->drawSetup();
|
||||
|
||||
glm::mat4 projection = glm::ortho(0.0f, (float)width, 0.0f, (float)height, -1.0f, 1.0f);
|
||||
glm::mat4 view = glm::mat4(1.0f);
|
||||
glm::mat4 model = glm::mat4(1.0f);
|
||||
|
||||
location = glGetUniformLocation(program, "projection");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(projection));
|
||||
location = glGetUniformLocation(program, "view");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(view));
|
||||
location = glGetUniformLocation(program, "model");
|
||||
glUniformMatrix4fv(location, 1, false, glm::value_ptr(model));
|
||||
|
||||
location = glGetUniformLocation(program, "color");
|
||||
float r = 200, g = 35, b = 220, a = 0.8;
|
||||
glUniform4f(location, r * a / 255.0f, g * a / 255.0f, b * a / 255.0f, a);
|
||||
|
||||
location = glGetUniformLocation(program, "antiAliasingWindowSize");
|
||||
glUniform1f(location, 1.0f);
|
||||
location = glGetUniformLocation(program, "enableSuperSamplingAntiAliasing");
|
||||
glUniform1i(location, true);
|
||||
location = glGetUniformLocation(program, "enableControlPointsVisualization");
|
||||
glUniform1i(location, false);
|
||||
|
||||
std::stringstream stream;
|
||||
stream << "Drag and drop a .ttf or .otf file to change the font\n";
|
||||
stream << "\n";
|
||||
stream << "right drag (or CTRL drag) - move\n";
|
||||
stream << "left drag - trackball rotate\n";
|
||||
stream << "middle drag - turntable rotate\n";
|
||||
stream << "scroll wheel - zoom\n";
|
||||
stream << "\n";
|
||||
stream << "0, 1, 2, 3 - change anti-aliasing window size: " << antiAliasingWindowSize << " pixel" << ((antiAliasingWindowSize != 1) ? "s" : "") << "\n";
|
||||
stream << glfwGetKeyName(GLFW_KEY_A, 0) << " - " << (enableSuperSamplingAntiAliasing ? "disable" : "enable") << " 2D anti-aliasing\n";
|
||||
stream << "(using another ray along the y-axis)\n";
|
||||
stream << glfwGetKeyName(GLFW_KEY_S, 0) << " - reset anti-aliasing settings\n";
|
||||
stream << glfwGetKeyName(GLFW_KEY_C, 0) << " - " << (enableControlPointsVisualization ? "disable" : "enable") << " control points\n";
|
||||
stream << glfwGetKeyName(GLFW_KEY_R, 0) << " - reset view\n";
|
||||
stream << glfwGetKeyName(GLFW_KEY_H, 0) << " - toggle help\n";
|
||||
|
||||
std::string helpText = stream.str();
|
||||
helpFont->prepareGlyphsForText(helpText);
|
||||
|
||||
float xscale, yscale;
|
||||
glfwGetWindowContentScale(window, &xscale, &yscale);
|
||||
helpFont->setWorldSize(std::ceil(helpFontBaseSize * yscale));
|
||||
|
||||
auto bb = helpFont->measure(0, 0, helpText);
|
||||
helpFont->draw(10 - bb.minX, height - 10 - bb.maxY, 0, helpText);
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
glfwSwapBuffers(window);
|
||||
}
|
||||
|
||||
// Clean up OpenGL resources before termination.
|
||||
mainFont = nullptr;
|
||||
helpFont = nullptr;
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
|
@ -14,6 +14,7 @@
|
|||
// UpdateList keeps track of which entries need to be updated.
|
||||
// The actual update is slightly delayed to avoid reading a partially written file.
|
||||
// It is threadsafe to allow safe communication with the asynchronous file watcher callback.
|
||||
namespace ofxGPUFont {
|
||||
class UpdateList {
|
||||
std::mutex mutex;
|
||||
std::unordered_map <std::string, std::chrono::steady_clock::time_point> updates;
|
||||
|
@ -189,3 +190,4 @@ std::shared_ptr <ShaderCatalog::Entry> ShaderCatalog::get(const std::string & na
|
|||
void ShaderCatalog::update(){
|
||||
impl->update();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
// example.frag are loaded and linked together to form the "example" program).
|
||||
// Whenever a shader file changes on disk, the corresponding program is
|
||||
// recompiled and relinked.
|
||||
namespace ofxGPUFont {
|
||||
class ShaderCatalog {
|
||||
public:
|
||||
struct Entry {
|
||||
|
@ -30,3 +31,4 @@ class ShaderCatalog {
|
|||
class Impl;
|
||||
std::unique_ptr <Impl> impl;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue