Panda3D
|
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 }