flightgear/utils/fgpanel/FGTextLayer.cxx
2022-10-20 20:29:11 +08:00

308 lines
10 KiB
C++

//
// Written by David Megginson, started January 2000.
// Adopted for standalone fgpanel application by Torsten Dreyer, August 2009
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#include "ApplicationProperties.hxx"
#include "GL_utils.hxx"
#include "FGTextLayer.hxx"
SGPath FGTextLayer::The_Font_Path;
FGFontCache FGTextLayer::The_Font_Cache;
GLuint FGTextLayer::Text_Layer_Program_Object (0);
GLint FGTextLayer::Text_Layer_Position_Loc (0);
GLint FGTextLayer::Text_Layer_Tex_Coord_Loc (0);
GLint FGTextLayer::Text_Layer_MVP_Loc (0);
GLint FGTextLayer::Text_Layer_Sampler_Loc (0);
GLint FGTextLayer::Text_Layer_Color_Loc (0);
bool
FGTextLayer::Init () {
const char V_Text_Layer_Shader_Str[] =
#ifdef _RPI
"#version 100 \n"
"attribute vec4 a_position; \n"
"attribute vec2 a_tex_coord; \n"
"varying vec2 v_tex_coord; \n"
#else
"#version 130 \n"
"in vec4 a_position; \n"
"in vec2 a_tex_coord; \n"
"out vec2 v_tex_coord; \n"
#endif
"uniform mat4 u_mvp_matrix; \n"
"void main () { \n"
" gl_Position = u_mvp_matrix * a_position; \n"
" v_tex_coord = a_tex_coord; \n"
"} \n";
const char F_Text_Layer_Shader_Str[] =
#ifdef _RPI
"#version 100 \n"
"precision mediump float; \n"
"varying vec2 v_tex_coord; \n"
#else
"#version 130 \n"
"in vec2 v_tex_coord; \n"
#endif
"uniform sampler2D u_texture; \n"
"uniform vec4 u_color; \n"
"void main () { \n"
" gl_FragColor = vec4 (1, 1, 1, \n"
" texture2D (u_texture, v_tex_coord).a) * \n"
" u_color; \n"
"} \n";
Text_Layer_Program_Object = GL_utils::instance ().load_program (V_Text_Layer_Shader_Str,
F_Text_Layer_Shader_Str);
if (Text_Layer_Program_Object == 0) {
return false;
}
// Get the attribute locations
Text_Layer_Position_Loc = glGetAttribLocation (Text_Layer_Program_Object, "a_position");
Text_Layer_Tex_Coord_Loc = glGetAttribLocation (Text_Layer_Program_Object, "a_tex_coord");
// Get the uniform locations
Text_Layer_MVP_Loc = glGetUniformLocation (Text_Layer_Program_Object, "u_mvp_matrix");
// Get the sampler location
Text_Layer_Sampler_Loc = glGetUniformLocation (Text_Layer_Program_Object, "u_texture");
// Get the color location
Text_Layer_Color_Loc = glGetUniformLocation (Text_Layer_Program_Object, "u_color");
return true;
}
FGTextLayer::FGTextLayer (const int w, const int h) :
FGInstrumentLayer (w, h),
m_pointSize (0.0),
m_font_name () {
m_then.stamp ();
m_color[0] = m_color[1] = m_color[2] = 0.0;
m_color[3] = 1.0;
}
FGTextLayer::~FGTextLayer () {
for (chunk_list::iterator It = m_chunks.begin ();
It != m_chunks.end ();
++It) {
delete *It;
}
}
void
FGTextLayer::draw () {
if (test ()) {
glUseProgram (Text_Layer_Program_Object);
glUniform4fv (Text_Layer_Color_Loc, 1, m_color);
transform ();
GL_utils::instance ().glMatrixMode (GL_utils::GL_UTILS_PROJECTION);
GL_utils::instance ().glPushMatrix ();
GL_utils::instance ().glMultMatrixf
(*reinterpret_cast <GLfloat (*)[4][4]>
(GL_utils::instance ().get_top_matrix (GL_utils::GL_UTILS_MODELVIEW)));
glUniformMatrix4fv (Text_Layer_MVP_Loc,
1,
GL_FALSE,
GL_utils::instance ().get_top_matrix (GL_utils::GL_UTILS_PROJECTION));
GLuint Glyph_Texture;
if (!The_Font_Cache.Set_Font (m_font_name, m_pointSize, Glyph_Texture)) {
SG_LOG (SG_COCKPIT, SG_ALERT, "Missing font : " << m_font_name << " " << m_pointSize);
}
m_now.stamp ();
const long diff ((m_now - m_then).toUSecs ());
if (diff > 100000 || diff < 0 ) {
// ( diff < 0 ) is a sanity check and indicates our time stamp
// difference math probably overflowed. We can handle a max
// difference of 35.8 minutes since the returned value is in
// usec. So if the panel is left off longer than that we can
// over flow the math with it is turned back on. This (diff <
// 0) catches that situation, get's us out of trouble, and
// back on track.
recalc_value ();
m_then = m_now;
}
glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, Glyph_Texture);
glUniform1i (Text_Layer_Sampler_Loc, 0);
int X (0);
int Y (0);
int Previous_X (0);
int Previous_Y (0);
int Left, Bottom, W, H;
double X1, Y1, X2, Y2;
for (string::iterator It = m_value.begin (); It != m_value.end (); ++It) {
if (The_Font_Cache.Get_Char (*It,
X, Y,
Left, Bottom,
W, H,
X1, Y1,
X2, Y2)) {
const GLfloat v_Vertices[] = {
float (Previous_X + Left), float (Previous_Y + Bottom), 0.0f, // Position 0 (bottom left corner)
float (X1), float (Y2), // TexCoord 0 (bottom left corner)
float (Previous_X + Left + W), float (Previous_Y + Bottom), 0.0f, // Position 1 (bottom right corner)
float (X2), float (Y2), // TexCoord 1 (bottom right corner)
float (Previous_X + Left + W), float (Previous_Y + Bottom + H), 0.0f, // Position 2 (top right corner)
float (X2), float (Y1), // TexCoord 2 (top right corner)
float (Previous_X + Left), float (Previous_Y + Bottom + H), 0.0f, // Position 3 (top left corner)
float (X1), float (Y1) }; // TexCoord 3 (top left corner)
glVertexAttribPointer (Text_Layer_Position_Loc,
3,
GL_FLOAT,
GL_FALSE,
5 * sizeof (GLfloat),
v_Vertices);
glVertexAttribPointer (Text_Layer_Tex_Coord_Loc,
2,
GL_FLOAT,
GL_FALSE,
5 * sizeof (GLfloat),
&v_Vertices[3]);
glEnableVertexAttribArray (Text_Layer_Position_Loc);
glEnableVertexAttribArray (Text_Layer_Tex_Coord_Loc);
const GLushort indices[] = {0, 1, 2, 0, 2, 3};
glDrawElements (GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);
Previous_X = X;
Previous_Y = Y;
}
}
GL_utils::instance ().glPopMatrix ();
GL_utils::instance ().glMatrixMode (GL_utils::GL_UTILS_MODELVIEW);
}
}
void
FGTextLayer::addChunk (FGTextLayer::Chunk * const chunk) {
m_chunks.push_back (chunk);
}
void
FGTextLayer::setColor (const float r,
const float g,
const float b) {
m_color[0] = r;
m_color[1] = g;
m_color[2] = b;
m_color[3] = 1.0;
}
void
FGTextLayer::setPointSize (const float size) {
m_pointSize = size;
}
void
FGTextLayer::setFontName (const string &name) {
if (The_Font_Path.isNull ()) {
char *Env_Path = ::getenv ("FG_FONTS");
if (Env_Path != NULL) {
The_Font_Path = SGPath::fromEnv (Env_Path);
} else {
The_Font_Path = ApplicationProperties::GetRootPath ("Fonts");
}
}
m_font_name = The_Font_Path.local8BitStr () + "/" + name + ".ttf";
}
void
FGTextLayer::recalc_value () const {
m_value = "";
for (chunk_list::const_iterator It = m_chunks.begin ();
It != m_chunks.end ();
++It) {
m_value += (*It)->getValue ();
}
}
////////////////////////////////////////////////////////////////////////
// Implementation of FGTextLayer::Chunk.
////////////////////////////////////////////////////////////////////////
FGTextLayer::Chunk::Chunk (const string &text,
const string &fmt) :
m_type (FGTextLayer::TEXT),
m_text (text),
m_fmt (fmt),
m_mult (1.0),
m_offs (0.0),
m_trunc (false) {
if (m_fmt.empty ()) {
m_fmt = "%s";
}
}
FGTextLayer::Chunk::Chunk (const ChunkType type,
const SGPropertyNode *node,
const string &fmt,
const float mult,
const float offs,
const bool truncation) :
m_type (type),
m_node (node),
m_fmt (fmt),
m_mult (mult),
m_offs (offs),
m_trunc (truncation) {
if (m_fmt.empty ()) {
if (type == TEXT_VALUE) {
m_fmt = "%s";
} else {
m_fmt = "%.2f";
}
}
}
const char *
FGTextLayer::Chunk::getValue () const {
if (test ()) {
m_buf[0] = '\0';
switch (m_type) {
case TEXT:
sprintf (m_buf, m_fmt.c_str (), m_text.c_str ());
break;
case TEXT_VALUE:
sprintf (m_buf, m_fmt.c_str (), m_node->getStringValue ());
break;
case DOUBLE_VALUE:
double d (m_offs + m_node->getFloatValue() * m_mult);
if (m_trunc) {
d = (d < 0) ? -floor (-d) : floor (d);
}
sprintf (m_buf, m_fmt.c_str(), d);
break;
}
return m_buf;
} else {
return "";
}
}