Panda3D

staticTextFont.cxx

00001 // Filename: staticTextFont.cxx
00002 // Created by:  drose (03May01)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "staticTextFont.h"
00016 #include "config_text.h"
00017 
00018 #include "geom.h"
00019 #include "geomNode.h"
00020 #include "geomVertexReader.h"
00021 #include "geomPoints.h"
00022 #include "renderState.h"
00023 #include "dcast.h"
00024 
00025 // Temporary
00026 #include "textureCollection.h"
00027 #include "nodePath.h"
00028 
00029 TypeHandle StaticTextFont::_type_handle;
00030 
00031 ////////////////////////////////////////////////////////////////////
00032 //     Function: StaticTextFont::Constructor
00033 //       Access: Published
00034 //  Description: The constructor expects the root node to a model
00035 //               generated via egg-mkfont, which consists of a set of
00036 //               models, one per each character in the font.
00037 ////////////////////////////////////////////////////////////////////
00038 StaticTextFont::
00039 StaticTextFont(PandaNode *font_def) {
00040   nassertv(font_def != (PandaNode *)NULL);
00041   _font = font_def;
00042   _glyphs.clear();
00043 
00044   // If there is no explicit quality level or filter settings on the
00045   // textures in the static font, set the appropriate defaults for
00046   // text.
00047   NodePath np(font_def);
00048   TextureCollection tc = np.find_all_textures();
00049   int num_textures = tc.get_num_textures();
00050   for (int i = 0; i < num_textures; ++i) {
00051     Texture *tex = tc.get_texture(i);
00052     
00053     // Don't compress font textures.  Though there's a relatively high
00054     // bang-for-the-buck in compressing them, there's an increased
00055     // risk that broken graphics drivers will fail to render the text
00056     // properly, causing troubles for a user who then won't be able to
00057     // navigate the options menus to disable texture compression.
00058     tex->set_compression(Texture::CM_off);
00059 
00060     if (tex->get_quality_level() == Texture::QL_default) {
00061       tex->set_quality_level(text_quality_level);
00062     }
00063     if (tex->get_minfilter() == Texture::FT_default) {
00064       tex->set_minfilter(text_minfilter);
00065     }
00066     if (tex->get_magfilter() == Texture::FT_default) {
00067       tex->set_magfilter(text_magfilter);
00068     }
00069   }
00070 
00071   find_characters(font_def, RenderState::make_empty());
00072   _is_valid = !_glyphs.empty();
00073 
00074   set_name(font_def->get_name());
00075 }
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: StaticTextFont::make_copy
00079 //       Access: Published
00080 //  Description: Returns a new copy of the same font.
00081 ////////////////////////////////////////////////////////////////////
00082 PT(TextFont) StaticTextFont::
00083 make_copy() const {
00084   return new StaticTextFont(_font);
00085 }
00086 
00087 ////////////////////////////////////////////////////////////////////
00088 //     Function: StaticTextFont::write
00089 //       Access: Published, Virtual
00090 //  Description:
00091 ////////////////////////////////////////////////////////////////////
00092 void StaticTextFont::
00093 write(ostream &out, int indent_level) const {
00094   indent(out, indent_level)
00095     << "StaticTextFont " << get_name() << "; "
00096     << _glyphs.size() << " characters available in font:\n";
00097   Glyphs::const_iterator gi;
00098   
00099   // Figure out which symbols we have.  We collect lowercase letters,
00100   // uppercase letters, and digits together for the user's
00101   // convenience.
00102   static const int num_letters = 26;
00103   static const int num_digits = 10;
00104   bool lowercase[num_letters];
00105   bool uppercase[num_letters];
00106   bool digits[num_digits];
00107 
00108   memset(lowercase, 0, sizeof(bool) * num_letters);
00109   memset(uppercase, 0, sizeof(bool) * num_letters);
00110   memset(digits, 0, sizeof(bool) * num_digits);
00111 
00112   int count_lowercase = 0;
00113   int count_uppercase = 0;
00114   int count_digits = 0;
00115 
00116   for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
00117     int ch = (*gi).first;
00118     if (ch < 128) {
00119       if (islower(ch)) {
00120         count_lowercase++;
00121         lowercase[ch - 'a'] = true;
00122         
00123       } else if (isupper(ch)) {
00124         count_uppercase++;
00125         uppercase[ch - 'A'] = true;
00126         
00127       } else if (isdigit(ch)) {
00128         count_digits++;
00129         digits[ch - '0'] = true;
00130       }
00131     }
00132   }
00133 
00134   if (count_lowercase == num_letters) {
00135     indent(out, indent_level + 2)
00136       << "All lowercase letters\n";
00137 
00138   } else if (count_lowercase > 0) {
00139     indent(out, indent_level + 2)
00140       << "Some lowercase letters: ";
00141     for (int i = 0; i < num_letters; i++) {
00142       if (lowercase[i]) {
00143         out << (char)(i + 'a');
00144       }
00145     }
00146     out << "\n";
00147   }
00148 
00149   if (count_uppercase == num_letters) {
00150     indent(out, indent_level + 2)
00151       << "All uppercase letters\n";
00152 
00153   } else if (count_uppercase > 0) {
00154     indent(out, indent_level + 2)
00155       << "Some uppercase letters: ";
00156     for (int i = 0; i < num_letters; i++) {
00157       if (uppercase[i]) {
00158         out << (char)(i + 'A');
00159       }
00160     }
00161     out << "\n";
00162   }
00163 
00164   if (count_digits == num_digits) {
00165     indent(out, indent_level + 2)
00166       << "All digits\n";
00167 
00168   } else if (count_digits > 0) {
00169     indent(out, indent_level + 2)
00170       << "Some digits: ";
00171     for (int i = 0; i < num_digits; i++) {
00172       if (digits[i]) {
00173         out << (char)(i + '0');
00174       }
00175     }
00176     out << "\n";
00177   }
00178 
00179   for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
00180     int ch = (*gi).first;
00181     if (ch >= 128 || !isalnum(ch)) {
00182       indent(out, indent_level + 2)
00183         << ch;
00184       if (ch < isprint(ch)) {
00185         out << " = '" << (char)ch << "'\n";
00186       }
00187     }
00188   }
00189 }
00190 
00191 ////////////////////////////////////////////////////////////////////
00192 //     Function: StaticTextFont::get_glyph
00193 //       Access: Public, Virtual
00194 //  Description: Gets the glyph associated with the given character
00195 //               code, as well as an optional scaling parameter that
00196 //               should be applied to the glyph's geometry and advance
00197 //               parameters.  Returns true if the glyph exists, false
00198 //               if it does not.  Even if the return value is false,
00199 //               the value for glyph might be filled in with a
00200 //               printable glyph.
00201 ////////////////////////////////////////////////////////////////////
00202 bool StaticTextFont::
00203 get_glyph(int character, const TextGlyph *&glyph) {
00204   Glyphs::const_iterator gi = _glyphs.find(character);
00205   if (gi == _glyphs.end()) {
00206     // No definition for this character.
00207     glyph = get_invalid_glyph();
00208     return false;
00209   }
00210 
00211   glyph = (*gi).second;
00212   return true;
00213 }
00214 
00215 ////////////////////////////////////////////////////////////////////
00216 //     Function: StaticTextFont::find_character_gsets
00217 //       Access: Private
00218 //  Description: Given that 'root' is a PandaNode containing at least
00219 //               a polygon and a point which define the character's
00220 //               appearance and kern position, respectively,
00221 //               recursively walk the hierarchy and root and locate
00222 //               those two Geoms.
00223 ////////////////////////////////////////////////////////////////////
00224 void StaticTextFont::
00225 find_character_gsets(PandaNode *root, CPT(Geom) &ch, CPT(Geom) &dot,
00226                      const RenderState *&state, const RenderState *net_state) {
00227   CPT(RenderState) next_net_state = net_state->compose(root->get_state());
00228 
00229   if (root->is_geom_node()) {
00230     GeomNode *geode = DCAST(GeomNode, root);
00231 
00232     for (int i = 0; i < geode->get_num_geoms(); i++) {
00233       const Geom *geom = geode->get_geom(i);
00234 
00235       bool found_points = false;
00236       for (int j = 0; j < geom->get_num_primitives() && !found_points; j++) {
00237         const GeomPrimitive *primitive = geom->get_primitive(j);
00238         if (primitive->is_of_type(GeomPoints::get_class_type())) {
00239           dot = geom;
00240           found_points = true;
00241         }
00242       }
00243       if (!found_points) {
00244         // If it doesn't have any points, it must be the regular
00245         // letter.
00246         ch = geom;
00247         state = next_net_state->compose(geode->get_geom_state(i));
00248       }
00249     }
00250 
00251   } else {
00252     PandaNode::Children cr = root->get_children();
00253     int num_children = cr.get_num_children();
00254     for (int i = 0; i < num_children; i++) {
00255       find_character_gsets(cr.get_child(i), ch, dot, state,
00256                            next_net_state);
00257     }
00258   }
00259 }
00260 
00261 ////////////////////////////////////////////////////////////////////
00262 //     Function: StaticTextFont::find_characters
00263 //       Access: Private
00264 //  Description: Walk the hierarchy beginning at the indicated root
00265 //               and locate any nodes whose names are just integers.
00266 //               These are taken to be characters, and their
00267 //               definitions and kern informations are retrieved.
00268 ////////////////////////////////////////////////////////////////////
00269 void StaticTextFont::
00270 find_characters(PandaNode *root, const RenderState *net_state) {
00271   CPT(RenderState) next_net_state = net_state->compose(root->get_state());
00272   string name = root->get_name();
00273 
00274   bool all_digits = !name.empty();
00275   const char *p = name.c_str();
00276   while (all_digits && *p != '\0') {
00277     // VC++ complains if we treat an int as a bool, so we have to do
00278     // this != 0 comparison on the int isdigit() function to shut it
00279     // up.
00280     all_digits = (isdigit(*p) != 0);
00281     p++;
00282   }
00283 
00284   if (all_digits) {
00285     int character = atoi(name.c_str());
00286     CPT(Geom) ch;
00287     CPT(Geom) dot;
00288     const RenderState *state = NULL;
00289     find_character_gsets(root, ch, dot, state, next_net_state);
00290     if (ch != (Geom *)NULL && dot != (Geom *)NULL) {
00291       // Get the first vertex from the "dot" geoset.  This will be the
00292       // origin of the next character.
00293       GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
00294       float width = reader.get_data1f();
00295 
00296       _glyphs[character] = new TextGlyph(character, ch, state, width);
00297     }
00298 
00299   } else if (name == "ds") {
00300     // The group "ds" is a special node that indicates the font's
00301     // design size, or line height.
00302 
00303     CPT(Geom) ch;
00304     CPT(Geom) dot;
00305     const RenderState *state = NULL;
00306     find_character_gsets(root, ch, dot, state, next_net_state);
00307     if (dot != (Geom *)NULL) {
00308       // Get the first vertex from the "dot" geoset.  This will be the
00309       // design size indicator.
00310       GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
00311       _line_height = reader.get_data3f()[2];
00312       _space_advance = 0.25f * _line_height;
00313     }
00314 
00315   } else {
00316     PandaNode::Children cr = root->get_children();
00317     int num_children = cr.get_num_children();
00318     for (int i = 0; i < num_children; i++) {
00319       find_characters(cr.get_child(i), next_net_state);
00320     }
00321   }
00322 }
 All Classes Functions Variables Enumerations