Panda3D
 All Classes Functions Variables Enumerations
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   // Check for an explicit space width.
00075   int character = 32;
00076   Glyphs::iterator gi = _glyphs.find(character);
00077   if (gi != _glyphs.end()) {
00078     TextGlyph *glyph = (*gi).second;
00079     _space_advance = glyph->get_advance();
00080   }
00081 
00082   set_name(font_def->get_name());
00083 }
00084 
00085 ////////////////////////////////////////////////////////////////////
00086 //     Function: StaticTextFont::make_copy
00087 //       Access: Published
00088 //  Description: Returns a new copy of the same font.
00089 ////////////////////////////////////////////////////////////////////
00090 PT(TextFont) StaticTextFont::
00091 make_copy() const {
00092   return new StaticTextFont(_font);
00093 }
00094 
00095 ////////////////////////////////////////////////////////////////////
00096 //     Function: StaticTextFont::write
00097 //       Access: Published, Virtual
00098 //  Description:
00099 ////////////////////////////////////////////////////////////////////
00100 void StaticTextFont::
00101 write(ostream &out, int indent_level) const {
00102   indent(out, indent_level)
00103     << "StaticTextFont " << get_name() << "; "
00104     << _glyphs.size() << " characters available in font:\n";
00105   Glyphs::const_iterator gi;
00106   
00107   // Figure out which symbols we have.  We collect lowercase letters,
00108   // uppercase letters, and digits together for the user's
00109   // convenience.
00110   static const int num_letters = 26;
00111   static const int num_digits = 10;
00112   bool lowercase[num_letters];
00113   bool uppercase[num_letters];
00114   bool digits[num_digits];
00115 
00116   memset(lowercase, 0, sizeof(bool) * num_letters);
00117   memset(uppercase, 0, sizeof(bool) * num_letters);
00118   memset(digits, 0, sizeof(bool) * num_digits);
00119 
00120   int count_lowercase = 0;
00121   int count_uppercase = 0;
00122   int count_digits = 0;
00123 
00124   for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
00125     int ch = (*gi).first;
00126     if (ch < 128) {
00127       if (islower(ch)) {
00128         count_lowercase++;
00129         lowercase[ch - 'a'] = true;
00130         
00131       } else if (isupper(ch)) {
00132         count_uppercase++;
00133         uppercase[ch - 'A'] = true;
00134         
00135       } else if (isdigit(ch)) {
00136         count_digits++;
00137         digits[ch - '0'] = true;
00138       }
00139     }
00140   }
00141 
00142   if (count_lowercase == num_letters) {
00143     indent(out, indent_level + 2)
00144       << "All lowercase letters\n";
00145 
00146   } else if (count_lowercase > 0) {
00147     indent(out, indent_level + 2)
00148       << "Some lowercase letters: ";
00149     for (int i = 0; i < num_letters; i++) {
00150       if (lowercase[i]) {
00151         out << (char)(i + 'a');
00152       }
00153     }
00154     out << "\n";
00155   }
00156 
00157   if (count_uppercase == num_letters) {
00158     indent(out, indent_level + 2)
00159       << "All uppercase letters\n";
00160 
00161   } else if (count_uppercase > 0) {
00162     indent(out, indent_level + 2)
00163       << "Some uppercase letters: ";
00164     for (int i = 0; i < num_letters; i++) {
00165       if (uppercase[i]) {
00166         out << (char)(i + 'A');
00167       }
00168     }
00169     out << "\n";
00170   }
00171 
00172   if (count_digits == num_digits) {
00173     indent(out, indent_level + 2)
00174       << "All digits\n";
00175 
00176   } else if (count_digits > 0) {
00177     indent(out, indent_level + 2)
00178       << "Some digits: ";
00179     for (int i = 0; i < num_digits; i++) {
00180       if (digits[i]) {
00181         out << (char)(i + '0');
00182       }
00183     }
00184     out << "\n";
00185   }
00186 
00187   for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
00188     int ch = (*gi).first;
00189     if (ch >= 128 || !isalnum(ch)) {
00190       indent(out, indent_level + 2)
00191         << ch;
00192       if (ch < isprint(ch)) {
00193         out << " = '" << (char)ch << "'\n";
00194       }
00195     }
00196   }
00197 }
00198 
00199 ////////////////////////////////////////////////////////////////////
00200 //     Function: StaticTextFont::get_glyph
00201 //       Access: Public, Virtual
00202 //  Description: Gets the glyph associated with the given character
00203 //               code, as well as an optional scaling parameter that
00204 //               should be applied to the glyph's geometry and advance
00205 //               parameters.  Returns true if the glyph exists, false
00206 //               if it does not.  Even if the return value is false,
00207 //               the value for glyph might be filled in with a
00208 //               printable glyph.
00209 ////////////////////////////////////////////////////////////////////
00210 bool StaticTextFont::
00211 get_glyph(int character, const TextGlyph *&glyph) {
00212   Glyphs::const_iterator gi = _glyphs.find(character);
00213   if (gi == _glyphs.end()) {
00214     // No definition for this character.
00215     glyph = get_invalid_glyph();
00216     return false;
00217   }
00218 
00219   glyph = (*gi).second;
00220   return true;
00221 }
00222 
00223 ////////////////////////////////////////////////////////////////////
00224 //     Function: StaticTextFont::find_character_gsets
00225 //       Access: Private
00226 //  Description: Given that 'root' is a PandaNode containing at least
00227 //               a polygon and a point which define the character's
00228 //               appearance and kern position, respectively,
00229 //               recursively walk the hierarchy and root and locate
00230 //               those two Geoms.
00231 ////////////////////////////////////////////////////////////////////
00232 void StaticTextFont::
00233 find_character_gsets(PandaNode *root, CPT(Geom) &ch, CPT(Geom) &dot,
00234                      const RenderState *&state, const RenderState *net_state) {
00235   CPT(RenderState) next_net_state = net_state->compose(root->get_state());
00236 
00237   if (root->is_geom_node()) {
00238     GeomNode *geode = DCAST(GeomNode, root);
00239 
00240     for (int i = 0; i < geode->get_num_geoms(); i++) {
00241       const Geom *geom = geode->get_geom(i);
00242 
00243       bool found_points = false;
00244       for (int j = 0; j < geom->get_num_primitives() && !found_points; j++) {
00245         const GeomPrimitive *primitive = geom->get_primitive(j);
00246         if (primitive->is_of_type(GeomPoints::get_class_type())) {
00247           dot = geom;
00248           found_points = true;
00249         }
00250       }
00251       if (!found_points) {
00252         // If it doesn't have any points, it must be the regular
00253         // letter.
00254         ch = geom;
00255         state = next_net_state->compose(geode->get_geom_state(i));
00256       }
00257     }
00258 
00259   } else {
00260     PandaNode::Children cr = root->get_children();
00261     int num_children = cr.get_num_children();
00262     for (int i = 0; i < num_children; i++) {
00263       find_character_gsets(cr.get_child(i), ch, dot, state,
00264                            next_net_state);
00265     }
00266   }
00267 }
00268 
00269 ////////////////////////////////////////////////////////////////////
00270 //     Function: StaticTextFont::find_characters
00271 //       Access: Private
00272 //  Description: Walk the hierarchy beginning at the indicated root
00273 //               and locate any nodes whose names are just integers.
00274 //               These are taken to be characters, and their
00275 //               definitions and kern informations are retrieved.
00276 ////////////////////////////////////////////////////////////////////
00277 void StaticTextFont::
00278 find_characters(PandaNode *root, const RenderState *net_state) {
00279   CPT(RenderState) next_net_state = net_state->compose(root->get_state());
00280   string name = root->get_name();
00281 
00282   bool all_digits = !name.empty();
00283   const char *p = name.c_str();
00284   while (all_digits && *p != '\0') {
00285     // VC++ complains if we treat an int as a bool, so we have to do
00286     // this != 0 comparison on the int isdigit() function to shut it
00287     // up.
00288     all_digits = (isdigit(*p) != 0);
00289     p++;
00290   }
00291 
00292   if (all_digits) {
00293     int character = atoi(name.c_str());
00294     CPT(Geom) ch;
00295     CPT(Geom) dot;
00296     const RenderState *state = NULL;
00297     find_character_gsets(root, ch, dot, state, next_net_state);
00298     PN_stdfloat width = 0.0;
00299     if (dot != (Geom *)NULL) {
00300       // Get the first vertex from the "dot" geoset.  This will be the
00301       // origin of the next character.
00302       GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
00303       width = reader.get_data1f();
00304     }
00305 
00306     _glyphs[character] = new TextGlyph(character, ch, state, width);
00307 
00308   } else if (name == "ds") {
00309     // The group "ds" is a special node that indicates the font's
00310     // design size, or line height.
00311 
00312     CPT(Geom) ch;
00313     CPT(Geom) dot;
00314     const RenderState *state = NULL;
00315     find_character_gsets(root, ch, dot, state, next_net_state);
00316     if (dot != (Geom *)NULL) {
00317       // Get the first vertex from the "dot" geoset.  This will be the
00318       // design size indicator.
00319       GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
00320       _line_height = reader.get_data3()[2];
00321       _space_advance = 0.25f * _line_height;
00322     }
00323 
00324   } else {
00325     PandaNode::Children cr = root->get_children();
00326     int num_children = cr.get_num_children();
00327     for (int i = 0; i < num_children; i++) {
00328       find_characters(cr.get_child(i), next_net_state);
00329     }
00330   }
00331 }
 All Classes Functions Variables Enumerations