work on text
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
|
||||
#include "utils/Objects/Body.h"
|
||||
|
||||
#define STB_TRUETYPE_IMPLIMENTATION
|
||||
#include <stb/stb_truetype.h>
|
||||
|
||||
#include <GL/glew.h>
|
||||
@@ -12,25 +11,162 @@ namespace Archimedes {
|
||||
|
||||
class Text {
|
||||
|
||||
GLuint tex;
|
||||
unsigned int tex;
|
||||
|
||||
std::vector<unsigned char> ttf;
|
||||
stbtt_bakedchar cdata[96];
|
||||
|
||||
std::string path;
|
||||
std::string fontPath;
|
||||
|
||||
std::string vs = "#version 330 core\n"
|
||||
"layout (location = 0) in vec3 aPos;\n"
|
||||
"layout (location = 1) in vec2 aTex;\n"
|
||||
"uniform mat4 model;\n"
|
||||
"uniform mat4 view;\n"
|
||||
"uniform mat4 proj;\n"
|
||||
"out vec2 TexCoord;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" gl_Position = proj * view * model * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
|
||||
" TexCoord = aTex;\n"
|
||||
"}\0";
|
||||
|
||||
std::string fs = "#version 330 core\n"
|
||||
"out vec4 FragColor;\n"
|
||||
"in vec2 TexCoord;\n"
|
||||
"uniform sampler2D tex;\n"
|
||||
"uniform vec4 color;\n"
|
||||
"void main()\n"
|
||||
"{\n"
|
||||
" FragColor = vec4(0.6f, 0.1f, 0.1f, texture(tex, TexCoord).a);\n"
|
||||
//" FragColor = vec4(1.0f);\n"
|
||||
"}\n\0";
|
||||
|
||||
|
||||
public:
|
||||
Shader shader;
|
||||
Texture texture;
|
||||
|
||||
Text(std::string p) : path(p) {}
|
||||
Text() {}
|
||||
~Text() {}
|
||||
|
||||
void textInit() {
|
||||
//////////////////////////////////////
|
||||
|
||||
// Load a TTF file into a texture and return the character data.
|
||||
stbtt_packedchar* LoadFont(const char* filename) {
|
||||
// Load TTF file into memory.
|
||||
std::ifstream file(filename, std::ios::ate | std::ios::binary);
|
||||
size_t fileSize = (size_t)file.tellg();
|
||||
char* ttfData = (char*)calloc(fileSize + 1, sizeof(char));
|
||||
file.seekg(0);
|
||||
file.read(ttfData, fileSize);
|
||||
file.close();
|
||||
|
||||
// Pack TTF into pixel data using stb_truetype.
|
||||
stbtt_pack_context packContext;
|
||||
stbtt_packedchar *charData = (stbtt_packedchar*)calloc(126, sizeof(stbtt_packedchar));
|
||||
unsigned char* pixels = (unsigned char*)calloc(1024 * 1024, sizeof(char));
|
||||
stbtt_PackBegin(&packContext, pixels, 1024, 1024, 1024, 1, NULL);
|
||||
|
||||
// Pack unicode codepoints 0 to 125 into the texture and character data. If a different starting
|
||||
// point than 0 is picked then lookups in charData array must be offset by that number.
|
||||
// With 0-125 the uppercase A will be at charData[65].
|
||||
// With 32-125 the uppercase A will be at charData[65-32].
|
||||
stbtt_PackFontRange(&packContext, (unsigned char*)ttfData, 0, 200.0f, 0, 125, charData);
|
||||
stbtt_PackEnd(&packContext);
|
||||
|
||||
// Create OpenGL texture with the font pack pixel data.
|
||||
// Only uses one color channel since font data is a monochrome alpha mask.
|
||||
GLuint fontTexture;
|
||||
glGenTextures(1, &fontTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, fontTexture);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 1024, 1024, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
|
||||
return charData;
|
||||
}
|
||||
|
||||
// Calculate pixel width of a string.
|
||||
float TextWidth(const char* text, float height, stbtt_packedchar *charData) {
|
||||
float w = 0.0f;
|
||||
|
||||
for (int i = 0; i < strlen(text); i++) {
|
||||
stbtt_packedchar c = charData[text[i]];
|
||||
w += c.xadvance;
|
||||
}
|
||||
// Scale width by rendered height to texture generated height ratio for final size.
|
||||
return w * (height / 200.0f);
|
||||
}
|
||||
|
||||
|
||||
// Render string using glDrawArrays.
|
||||
// X indicates the leftmost, center or rightmost point of the based on align (0:left, 1:center, 2:rigght).
|
||||
// Y is the baseline of the text.
|
||||
void RenderString(const char* text, float x, float y, float height, int align, stbtt_packedchar *charData, GLuint program) {
|
||||
// Scale of actual text compared to size on stb_truetype generated texture.
|
||||
float scale = height / 200.0f;
|
||||
|
||||
// Resolution of window/display for shader position calculations.
|
||||
float resolution[2] = { 1024, 1024 };
|
||||
|
||||
float position[2] = {x, y};
|
||||
if (align == 1) {
|
||||
// Center align
|
||||
position[0] -= TextWidth(text, height, charData) / 2.0f;
|
||||
} else if (align == 2) {
|
||||
// Right align
|
||||
position[0] -= TextWidth(text, height, charData);
|
||||
}
|
||||
|
||||
glUniform2fv(glGetUniformLocation(program, "resolution"), 1, resolution);
|
||||
for (int i = 0; i < strlen(text); i++) {
|
||||
// Lookup current character data from stb_truetype.
|
||||
stbtt_packedchar c = charData[text[i]];
|
||||
|
||||
// Position of character in texture.
|
||||
float charPosition[4] = {static_cast<float>(c.x0), static_cast<float>(c.y0), static_cast<float>(c.x1), static_cast<float>(c.y1)};
|
||||
|
||||
// Find the actual size of character since fonts can be variable width.
|
||||
// 'M' usually is wider than '!', 'L' taller than 'o', etc.
|
||||
// Calculated by substracting start-offset from end-offset.
|
||||
// xoff is start offset from the left.
|
||||
// xoff2 is end offset from the left.
|
||||
// yoff is start offset from the baseline (will be negative if above baseline).
|
||||
// yoff2 is end offset from the baseline.
|
||||
float size[2] = {(c.xoff2 - c.xoff) * scale, (c.yoff2 - c.yoff) * scale};
|
||||
|
||||
// The actual position of character based on its offset form left/baseline.
|
||||
float renderPos[2] = {position[0] + c.xoff * scale, position[1] - c.yoff2 * scale};
|
||||
|
||||
glUniform4fv(glGetUniformLocation(program, "charPosition"), 1, charPosition);
|
||||
glUniform2fv(glGetUniformLocation(program, "position"), 1, renderPos);
|
||||
glUniform2fv(glGetUniformLocation(program, "size"), 1, size);
|
||||
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
||||
|
||||
// Advance x position to start of next character.
|
||||
position[0] += c.xadvance * scale;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////
|
||||
|
||||
void textInit(std::string p) {
|
||||
|
||||
fontPath = p;
|
||||
|
||||
ttf.reserve(1<<20);
|
||||
|
||||
unsigned char temp_bitmap[1024*1024];
|
||||
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
std::ifstream file(fontPath, std::ios::binary);
|
||||
|
||||
file.seekg(0, std::ios::end);
|
||||
auto&& size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
ttf.reserve(size);
|
||||
|
||||
if(!file.is_open()) {
|
||||
std::cout << "ttf won't open!\n";
|
||||
@@ -43,28 +179,40 @@ namespace Archimedes {
|
||||
|
||||
file.close();
|
||||
|
||||
stbtt_BakeFontBitmap(ttf.data(),0, 64.0, temp_bitmap,1024,1024, 32,96, cdata);
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 1024,1024,0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||
stbtt_BakeFontBitmap(ttf.data(),0, 200.0f, temp_bitmap,1024,1024, 32,96, cdata);
|
||||
|
||||
std::vector<unsigned char> bin;
|
||||
bin.reserve(1024 * 1024);
|
||||
|
||||
for(unsigned char c : temp_bitmap) {
|
||||
bin.push_back(c);
|
||||
}
|
||||
|
||||
shader = Shader(vs, fs, Shader::LoadType::FromSource);
|
||||
texture = Texture(bin, 1024, 1024);
|
||||
}
|
||||
|
||||
Body* genText(float x, float y, std::string text) {
|
||||
Body* genText(std::vector<std::string> texts) {
|
||||
// assume orthographic projection with units = screen pixels, origin at top left
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
|
||||
std::vector<float> v;
|
||||
std::vector<unsigned int> i;
|
||||
|
||||
float x = 0, y = 0, oX = 0, dY = 0;
|
||||
|
||||
std::string text = "";
|
||||
|
||||
for(std::string s : texts) {
|
||||
text += s + " ";
|
||||
}
|
||||
|
||||
for(unsigned int j = 0; j < text.length(); j++) {
|
||||
if (text[j] >= 32) {
|
||||
stbtt_aligned_quad q;
|
||||
stbtt_GetBakedQuad(cdata, 512,512, text[j]-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9
|
||||
|
||||
|
||||
v.push_back(q.x0);
|
||||
v.push_back(q.y0);
|
||||
v.push_back(0.0f);
|
||||
@@ -72,8 +220,7 @@ namespace Archimedes {
|
||||
v.push_back(q.s0);
|
||||
v.push_back(q.t0);
|
||||
|
||||
i.push_back(j * 4);
|
||||
|
||||
|
||||
|
||||
v.push_back(q.x1);
|
||||
v.push_back(q.y0);
|
||||
@@ -81,10 +228,9 @@ namespace Archimedes {
|
||||
|
||||
v.push_back(q.s1);
|
||||
v.push_back(q.t0);
|
||||
|
||||
i.push_back(j * 4 + 1);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
v.push_back(q.x1);
|
||||
v.push_back(q.y1);
|
||||
@@ -92,10 +238,9 @@ namespace Archimedes {
|
||||
|
||||
v.push_back(q.s1);
|
||||
v.push_back(q.t1);
|
||||
|
||||
i.push_back(j * 4 + 2);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
v.push_back(q.x0);
|
||||
v.push_back(q.y1);
|
||||
@@ -104,15 +249,27 @@ namespace Archimedes {
|
||||
v.push_back(q.s0);
|
||||
v.push_back(q.t1);
|
||||
|
||||
i.push_back(j * 4);
|
||||
i.push_back(j * 4 + 1);
|
||||
i.push_back(j * 4 + 2);
|
||||
i.push_back(j * 4 + 2);
|
||||
i.push_back(j * 4 + 3);
|
||||
i.push_back(j * 4);
|
||||
|
||||
dY = q.y1 - q.y0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return new Body(
|
||||
VertexBuffer(v),
|
||||
VertexBuffer(v),
|
||||
IndexArray(i),
|
||||
VertexLayout(),
|
||||
Shader()
|
||||
VertexLayout({
|
||||
LayoutElement(LayoutElement::Type::Float, 3, 5 * sizeof(float), 0),
|
||||
LayoutElement(LayoutElement::Type::Float, 2, 5 * sizeof(float), 3 * sizeof(float))
|
||||
}),
|
||||
shader,
|
||||
texture
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -122,10 +279,10 @@ namespace Archimedes {
|
||||
class JObject {
|
||||
|
||||
public:
|
||||
JObject() = default;
|
||||
JObject() {}
|
||||
|
||||
void load(std::string path) {
|
||||
std::ifstream file(path);
|
||||
void load(std::string pathJSON, std::string pathFont) {
|
||||
std::ifstream file(pathJSON);
|
||||
if (!file.is_open()) goodJSON = false;
|
||||
document = json::parse(file);
|
||||
file.close();
|
||||
@@ -136,6 +293,8 @@ namespace Archimedes {
|
||||
cameras = document["Cameras"];
|
||||
lights = document["Lights"];
|
||||
actions = document["Actions"];
|
||||
|
||||
text.textInit(pathFont);
|
||||
}
|
||||
|
||||
bool isValid() const { return goodJSON; }
|
||||
@@ -157,8 +316,9 @@ namespace Archimedes {
|
||||
return new Body(
|
||||
VertexBuffer(v),
|
||||
IndexArray(i),
|
||||
VertexLayout(),
|
||||
layout,
|
||||
shader,
|
||||
std::nullopt,
|
||||
RenderMode::ConnectedLinesLooped
|
||||
);
|
||||
|
||||
@@ -197,8 +357,9 @@ namespace Archimedes {
|
||||
return new Body(
|
||||
VertexBuffer(v),
|
||||
IndexArray(i),
|
||||
VertexLayout(),
|
||||
layout,
|
||||
shader,
|
||||
std::nullopt,
|
||||
RenderMode::ConnectedLinesLooped
|
||||
);
|
||||
}
|
||||
@@ -237,8 +398,9 @@ namespace Archimedes {
|
||||
return new Body(
|
||||
VertexBuffer(v),
|
||||
IndexArray(i),
|
||||
VertexLayout(),
|
||||
layout,
|
||||
shader,
|
||||
std::nullopt,
|
||||
RenderMode::ConnectedLinesLooped
|
||||
);
|
||||
}
|
||||
@@ -262,24 +424,25 @@ namespace Archimedes {
|
||||
return new Body(
|
||||
VertexBuffer(v),
|
||||
IndexArray(i),
|
||||
VertexLayout(),
|
||||
layout,
|
||||
shader,
|
||||
std::nullopt,
|
||||
RenderMode::ConnectedLinesLooped
|
||||
);
|
||||
}
|
||||
|
||||
Body* createText(json& element, std::string name) {
|
||||
std::vector<float> v;
|
||||
std::vector<unsigned int> i;
|
||||
|
||||
//do something
|
||||
json lines = element["text"];
|
||||
|
||||
return new Body(
|
||||
VertexBuffer(v),
|
||||
IndexArray(i),
|
||||
VertexLayout(),
|
||||
shader
|
||||
);
|
||||
std::vector<std::string> theText;
|
||||
for (json::const_iterator it = lines.cbegin(); it != lines.cend(); it++)
|
||||
{
|
||||
std::string line = *it;
|
||||
theText.push_back(line);
|
||||
}
|
||||
|
||||
return text.genText(theText);
|
||||
}
|
||||
|
||||
void buildObjects() {
|
||||
@@ -303,7 +466,7 @@ namespace Archimedes {
|
||||
objectMap[name] = createPoly(element, name);
|
||||
}
|
||||
if (type == "TEXT") {
|
||||
objectMap[name] = nullptr;
|
||||
objectMap[name] = createText(element, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -355,8 +518,13 @@ namespace Archimedes {
|
||||
|
||||
std::unordered_map<std::string, Body*> objectMap;
|
||||
Shader shader;
|
||||
|
||||
Text text;
|
||||
private:
|
||||
|
||||
Archimedes::VertexLayout layout = Archimedes::VertexLayout({
|
||||
Archimedes::LayoutElement(Archimedes::LayoutElement::Type::Float, 3, 0, 0)
|
||||
});
|
||||
|
||||
std::unordered_map<std::string, Body> templateMap;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user