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