summaryrefslogtreecommitdiffstats
path: root/dev/MinGfx/src/text_shader.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dev/MinGfx/src/text_shader.cc238
1 files changed, 238 insertions, 0 deletions
diff --git a/dev/MinGfx/src/text_shader.cc b/dev/MinGfx/src/text_shader.cc
new file mode 100644
index 0000000..ca99953
--- /dev/null
+++ b/dev/MinGfx/src/text_shader.cc
@@ -0,0 +1,238 @@
+/*
+ Copyright (c) 2017,2018 Regents of the University of Minnesota.
+ All Rights Reserved.
+ See corresponding header file for details.
+ */
+
+#include "text_shader.h"
+
+#include "platform.h"
+#include <fstream>
+
+// disable warnings for this 3rd party code
+#pragma warning (push, 0)
+#define STB_RECT_PACK_IMPLEMENTATION
+#include "stb_rect_pack.h"
+#define STB_TRUETYPE_IMPLEMENTATION
+#include <stb_truetype.h>
+#pragma warning (pop)
+
+
+namespace mingfx {
+
+// Reference implementation: https://github.com/nothings/stb/blob/master/tests/oversample/main.c
+
+
+TextShader::TextShader() : native_font_size_(0.0)
+{
+ for (int i = 0; i < 128; i++) {
+ chardata_[i] = stbtt_packedchar();
+ }
+}
+
+TextShader::~TextShader() {
+}
+
+
+bool TextShader::Init(const std::string &filename, int font_size) {
+ // load shader
+ shader_.AddVertexShaderFromFile(Platform::FindMinGfxShaderFile("text.vert"));
+ shader_.AddFragmentShaderFromFile(Platform::FindMinGfxShaderFile("text.frag"));
+ shader_.LinkProgram();
+
+ // load font
+ native_font_size_ = (float)font_size;
+ std::ifstream is(filename.c_str(), std::ifstream::binary);
+ if (is) {
+ is.seekg(0, is.end);
+ int length = (int)is.tellg();
+ is.seekg(0, is.beg);
+
+ char *ttf_buffer = new char[length];
+ is.read(ttf_buffer, length);
+ if (is)
+ std::cout << "all characters read successfully.";
+ else
+ std::cout << "error: only " << is.gcount() << " could be read";
+ is.close();
+
+ // todo: calc an appropriate pow of 2 size given the font_size
+ int atlas_width = 1024;
+ int atlas_height = 1024;
+
+ stbtt_pack_context pc;
+ unsigned char *bitmap = new unsigned char[(size_t)atlas_width * atlas_height];
+
+ stbtt_PackBegin(&pc, bitmap, atlas_width, atlas_height, 0, 1, NULL);
+ stbtt_PackSetOversampling(&pc, 2, 2);
+ stbtt_PackFontRange(&pc, (unsigned char*)ttf_buffer, 0, (float)font_size, 32, 95, chardata_+32);
+ stbtt_PackEnd(&pc);
+
+ // convert to 4-channel since that is all that Texture2D currently supports
+ unsigned char *bitmap4D = new unsigned char[(size_t)4 * atlas_width * atlas_height];
+ for (int i=0; i < atlas_width * atlas_height; i++) {
+ bitmap4D[4*i + 0] = bitmap[i];
+ bitmap4D[4*i + 1] = bitmap[i];
+ bitmap4D[4*i + 2] = bitmap[i];
+ bitmap4D[4*i + 3] = bitmap[i];
+ }
+
+ atlas_.InitFromBytes(atlas_width, atlas_height, bitmap4D);
+
+ delete [] ttf_buffer;
+ delete [] bitmap;
+ delete [] bitmap4D;
+
+ return true;
+ }
+ else {
+ std::cerr << "TextShader: Error font file does not exist: " << filename << std::endl;
+ return false;
+ }
+
+}
+
+
+void TextShader::Draw3D(const Matrix4 &model, const Matrix4 &view, const Matrix4 &projection,
+ const std::string &text, TextFormat format, bool cache)
+{
+ MeshData *md = NULL;
+ std::map<std::string, MeshData>::iterator it = cache_.find(text);
+ if (it != cache_.end()) {
+ // use an existing cached mesh
+ md = &(it->second);
+ }
+ else {
+ // need to create a new mesh, add a new one to the cache or use the tmp_mesh
+ if (cache) {
+ MeshData new_md;
+ cache_[text] = new_md;
+ md = &(cache_[text]);
+ }
+ else {
+ md = &tmp_md_;
+ }
+
+ // set appropriate vertices and texcoords for this text string
+ SetTextMesh(text, md);
+ }
+
+ Vector3 offset;
+ if (format.h_align == HorizAlign::HORIZ_ALIGN_LEFT) {
+ offset[0] = 0;
+ }
+ else if (format.h_align == HorizAlign::HORIZ_ALIGN_CENTER) {
+ offset[0] = -0.5f * (md->max[0] - md->min[0]);
+ }
+ else if (format.h_align == HorizAlign::HORIZ_ALIGN_RIGHT) {
+ offset[0] = -(md->max[0] - md->min[0]);
+ }
+
+ if (format.v_align == VertAlign::VERT_ALIGN_TOP) {
+ offset[1] = -md->max[1];
+ }
+ else if (format.v_align == VertAlign::VERT_ALIGN_CENTER) {
+ offset[1] = -0.5f * md->max[1];
+ }
+ else if (format.v_align == VertAlign::VERT_ALIGN_BASELINE) {
+ offset[1] = 0;
+ }
+ else if (format.v_align == VertAlign::VERT_ALIGN_BOTTOM) {
+ offset[1] = -md->min[1];
+ }
+
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ shader_.UseProgram();
+ Matrix4 mvp = projection * view * model;
+ shader_.SetUniform("mvp_matrix", mvp);
+ shader_.SetUniform("scale", format.size / native_font_size_);
+ shader_.SetUniform("offset", offset);
+ shader_.SetUniform("color", format.color);
+ shader_.BindTexture("font_atlas", atlas_);
+ md->mesh.Draw();
+ shader_.StopProgram();
+
+ glEnable(GL_CULL_FACE);
+}
+
+
+void TextShader::SetTextMesh(const std::string &text, MeshData *md) {
+ std::vector<Point3> verts;
+ std::vector<Point2> uvs;
+ std::vector<unsigned int> indices;
+
+ const char *c = text.c_str();
+ float x = 0.0;
+ float y = 0.0;
+ while (*c) {
+ stbtt_aligned_quad q;
+ stbtt_GetPackedQuad(chardata_, atlas_.width(), atlas_.height(), *c++, &x, &y, &q, 0);
+
+ // top left
+ verts.push_back(Point3(q.x0, -q.y0, 0.0));
+ uvs.push_back(Point2(q.s0, q.t0));
+ // top right
+ verts.push_back(Point3(q.x1, -q.y0, 0.0));
+ uvs.push_back(Point2(q.s1, q.t0));
+ // bot right
+ verts.push_back(Point3(q.x1, -q.y1, 0.0));
+ uvs.push_back(Point2(q.s1, q.t1));
+ // bot left
+ verts.push_back(Point3(q.x0, -q.y1, 0.0));
+ uvs.push_back(Point2(q.s0, q.t1));
+
+
+ indices.push_back((unsigned int)verts.size()-2);
+ indices.push_back((unsigned int)verts.size()-3);
+ indices.push_back((unsigned int)verts.size()-4);
+
+ indices.push_back((unsigned int)verts.size()-2);
+ indices.push_back((unsigned int)verts.size()-4);
+ indices.push_back((unsigned int)verts.size()-1);
+ }
+
+ md->mesh.SetVertices(verts);
+ md->mesh.SetTexCoords(0, uvs);
+ md->mesh.SetIndices(indices);
+
+ md->min = Point2(verts[0][0], verts[0][1]);
+ md->max = md->min;
+ for (int i=0; i<verts.size(); i++) {
+ Point3 p = verts[i];
+ if (p[0] < md->min[0]) md->min[0] = p[0];
+ if (p[0] > md->max[0]) md->max[0] = p[0];
+ if (p[1] < md->min[1]) md->min[1] = p[1];
+ if (p[1] > md->max[1]) md->max[1] = p[1];
+ }
+}
+
+Vector2 TextShader::TextExtents(const std::string &text, TextFormat format, bool cache) {
+ MeshData *md = NULL;
+ std::map<std::string, MeshData>::iterator it = cache_.find(text);
+ if (it != cache_.end()) {
+ // use an existing cached mesh
+ md = &(it->second);
+ }
+ else {
+ // need to create a new mesh, add a new one to the cache or use the tmp_mesh
+ if (cache) {
+ MeshData new_md;
+ cache_[text] = new_md;
+ md = &(cache_[text]);
+ }
+ else {
+ md = &tmp_md_;
+ }
+
+ // set appropriate vertices and texcoords for this text string
+ SetTextMesh(text, md);
+ }
+
+ return format.size / native_font_size_ * (md->max - md->min);
+}
+
+
+} // end namespace \ No newline at end of file