Panda3D
staticTextFont.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file staticTextFont.cxx
10  * @author drose
11  * @date 2001-05-03
12  */
13 
14 #include "staticTextFont.h"
15 #include "config_text.h"
16 
17 #include "geom.h"
18 #include "geomNode.h"
19 #include "geomVertexReader.h"
20 #include "geomPoints.h"
21 #include "renderState.h"
22 #include "dcast.h"
23 
24 // Temporary
25 #include "textureCollection.h"
26 #include "nodePath.h"
27 
28 TypeHandle StaticTextFont::_type_handle;
29 
30 /**
31  * The constructor expects the root node to a model generated via egg-mkfont,
32  * which consists of a set of models, one per each character in the font.
33  *
34  * If a CoordinateSystem value is specified, it informs the font of the
35  * coordinate system in which this model was generated. "up" in this
36  * coordinate system will be the direction of the top of the letters.
37  */
39 StaticTextFont(PandaNode *font_def, CoordinateSystem cs) {
40  nassertv(font_def != nullptr);
41  _font = font_def;
42  _cs = cs;
43  _glyphs.clear();
44 
45  if (_cs == CS_default) {
46  _cs = get_default_coordinate_system();
47  }
48 
49  NodePath np(font_def);
50  if (_cs != CS_zup_right) {
51  // We have to convert the entire font to CS_zup_right before we can use
52  // it, because the text subsystem assumes the glyphs are stored in
53  // CS_zup_right.
54  NodePath temp_root("root");
55  NodePath temp_child = temp_root.attach_new_node("child");
56  np = np.copy_to(temp_child);
57  LMatrix4 mat = LMatrix4::convert_mat(_cs, CS_zup_right);
58  temp_child.set_mat(mat);
59  temp_root.clear_model_nodes();
60  temp_root.flatten_light();
61  np = temp_root.get_child(0).get_child(0);
62  _font = np.node();
63  _cs = CS_zup_right;
64  }
65 
66  // If there is no explicit quality level or filter settings on the textures
67  // in the static font, set the appropriate defaults for text.
69  int num_textures = tc.get_num_textures();
70  for (int i = 0; i < num_textures; ++i) {
71  Texture *tex = tc.get_texture(i);
72 
73  // Don't compress font textures. Though there's a relatively high bang-
74  // for-the-buck in compressing them, there's an increased risk that broken
75  // graphics drivers will fail to render the text properly, causing
76  // troubles for a user who then won't be able to navigate the options
77  // menus to disable texture compression.
78  tex->set_compression(Texture::CM_off);
79 
80  if (tex->get_quality_level() == Texture::QL_default) {
81  tex->set_quality_level(text_quality_level);
82  }
83  if (tex->get_minfilter() == SamplerState::FT_default) {
84  tex->set_minfilter(text_minfilter);
85  }
86  if (tex->get_magfilter() == SamplerState::FT_default) {
87  tex->set_magfilter(text_magfilter);
88  }
89  }
90 
91  find_characters(_font, RenderState::make_empty());
92  _is_valid = !_glyphs.empty();
93 
94  // Check for an explicit space width.
95  int character = 32;
96  Glyphs::iterator gi = _glyphs.find(character);
97  if (gi != _glyphs.end()) {
98  TextGlyph *glyph = (*gi).second;
99  _space_advance = glyph->get_advance();
100  }
101 
102  set_name(font_def->get_name());
103 }
104 
105 /**
106  * Returns a new copy of the same font.
107  */
108 PT(TextFont) StaticTextFont::
109 make_copy() const {
110  return new StaticTextFont(_font);
111 }
112 
113 /**
114  *
115  */
116 void StaticTextFont::
117 write(std::ostream &out, int indent_level) const {
118  indent(out, indent_level)
119  << "StaticTextFont " << get_name() << "; "
120  << _glyphs.size() << " characters available in font:\n";
121  Glyphs::const_iterator gi;
122 
123  // Figure out which symbols we have. We collect lowercase letters,
124  // uppercase letters, and digits together for the user's convenience.
125  static const int num_letters = 26;
126  static const int num_digits = 10;
127  bool lowercase[num_letters];
128  bool uppercase[num_letters];
129  bool digits[num_digits];
130 
131  memset(lowercase, 0, sizeof(bool) * num_letters);
132  memset(uppercase, 0, sizeof(bool) * num_letters);
133  memset(digits, 0, sizeof(bool) * num_digits);
134 
135  int count_lowercase = 0;
136  int count_uppercase = 0;
137  int count_digits = 0;
138 
139  for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
140  int ch = (*gi).first;
141  if (ch < 128) {
142  if (islower(ch)) {
143  count_lowercase++;
144  lowercase[ch - 'a'] = true;
145 
146  } else if (isupper(ch)) {
147  count_uppercase++;
148  uppercase[ch - 'A'] = true;
149 
150  } else if (isdigit(ch)) {
151  count_digits++;
152  digits[ch - '0'] = true;
153  }
154  }
155  }
156 
157  if (count_lowercase == num_letters) {
158  indent(out, indent_level + 2)
159  << "All lowercase letters\n";
160 
161  } else if (count_lowercase > 0) {
162  indent(out, indent_level + 2)
163  << "Some lowercase letters: ";
164  for (int i = 0; i < num_letters; i++) {
165  if (lowercase[i]) {
166  out << (char)(i + 'a');
167  }
168  }
169  out << "\n";
170  }
171 
172  if (count_uppercase == num_letters) {
173  indent(out, indent_level + 2)
174  << "All uppercase letters\n";
175 
176  } else if (count_uppercase > 0) {
177  indent(out, indent_level + 2)
178  << "Some uppercase letters: ";
179  for (int i = 0; i < num_letters; i++) {
180  if (uppercase[i]) {
181  out << (char)(i + 'A');
182  }
183  }
184  out << "\n";
185  }
186 
187  if (count_digits == num_digits) {
188  indent(out, indent_level + 2)
189  << "All digits\n";
190 
191  } else if (count_digits > 0) {
192  indent(out, indent_level + 2)
193  << "Some digits: ";
194  for (int i = 0; i < num_digits; i++) {
195  if (digits[i]) {
196  out << (char)(i + '0');
197  }
198  }
199  out << "\n";
200  }
201 
202  for (gi = _glyphs.begin(); gi != _glyphs.end(); ++gi) {
203  int ch = (*gi).first;
204  if (ch >= 128 || !isalnum(ch)) {
205  indent(out, indent_level + 2)
206  << ch;
207  if (ch < isprint(ch)) {
208  out << " = '" << (char)ch << "'\n";
209  }
210  }
211  }
212 }
213 
214 /**
215  * Gets the glyph associated with the given character code, as well as an
216  * optional scaling parameter that should be applied to the glyph's geometry
217  * and advance parameters. Returns true if the glyph exists, false if it does
218  * not. Even if the return value is false, the value for glyph might be
219  * filled in with a printable glyph.
220  */
221 bool StaticTextFont::
222 get_glyph(int character, CPT(TextGlyph) &glyph) {
223  Glyphs::const_iterator gi = _glyphs.find(character);
224  if (gi == _glyphs.end()) {
225  // No definition for this character.
226  glyph = get_invalid_glyph();
227  return false;
228  }
229 
230  glyph = (*gi).second;
231  return true;
232 }
233 
234 /**
235  * Given that 'root' is a PandaNode containing at least a polygon and a point
236  * which define the character's appearance and kern position, respectively,
237  * recursively walk the hierarchy and root and locate those two Geoms.
238  */
239 void StaticTextFont::
240 find_character_gsets(PandaNode *root, CPT(Geom) &ch, CPT(Geom) &dot,
241  const RenderState *&state, const RenderState *net_state) {
242  CPT(RenderState) next_net_state = net_state->compose(root->get_state());
243 
244  if (root->is_geom_node()) {
245  GeomNode *geode = DCAST(GeomNode, root);
246 
247  for (int i = 0; i < geode->get_num_geoms(); i++) {
248  const Geom *geom = geode->get_geom(i);
249 
250  bool found_points = false;
251  for (size_t j = 0; j < geom->get_num_primitives() && !found_points; ++j) {
252  const GeomPrimitive *primitive = geom->get_primitive(j);
253  if (primitive->is_of_type(GeomPoints::get_class_type())) {
254  dot = geom;
255  found_points = true;
256  }
257  }
258  if (!found_points) {
259  // If it doesn't have any points, it must be the regular letter.
260  ch = geom;
261  state = next_net_state->compose(geode->get_geom_state(i));
262  }
263  }
264 
265  } else {
266  PandaNode::Children cr = root->get_children();
267  int num_children = cr.get_num_children();
268  for (int i = 0; i < num_children; i++) {
269  find_character_gsets(cr.get_child(i), ch, dot, state,
270  next_net_state);
271  }
272  }
273 }
274 
275 /**
276  * Walk the hierarchy beginning at the indicated root and locate any nodes
277  * whose names are just integers. These are taken to be characters, and their
278  * definitions and kern informations are retrieved.
279  */
280 void StaticTextFont::
281 find_characters(PandaNode *root, const RenderState *net_state) {
282  CPT(RenderState) next_net_state = net_state->compose(root->get_state());
283  std::string name = root->get_name();
284 
285  bool all_digits = !name.empty();
286  const char *p = name.c_str();
287  while (all_digits && *p != '\0') {
288  // VC++ complains if we treat an int as a bool, so we have to do this != 0
289  // comparison on the int isdigit() function to shut it up.
290  all_digits = (isdigit(*p) != 0);
291  p++;
292  }
293 
294  if (all_digits) {
295  int character = atoi(name.c_str());
296  CPT(Geom) ch;
297  CPT(Geom) dot;
298  const RenderState *state = nullptr;
299  find_character_gsets(root, ch, dot, state, next_net_state);
300  PN_stdfloat width = 0.0;
301  if (dot != nullptr) {
302  // Get the first vertex from the "dot" geoset. This will be the origin
303  // of the next character.
304  GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
305  width = reader.get_data1f();
306  }
307 
308  _glyphs[character] = new TextGlyph(character, ch, state, width);
309 
310  } else if (name == "ds") {
311  // The group "ds" is a special node that indicates the font's design size,
312  // or line height.
313 
314  CPT(Geom) ch;
315  CPT(Geom) dot;
316  const RenderState *state = nullptr;
317  find_character_gsets(root, ch, dot, state, next_net_state);
318  if (dot != nullptr) {
319  // Get the first vertex from the "dot" geoset. This will be the design
320  // size indicator.
321  GeomVertexReader reader(dot->get_vertex_data(), InternalName::get_vertex());
322  LVecBase3 data = reader.get_data3();
323  _line_height = data[2];
324  _total_poly_margin = data[0];
325  _space_advance = 0.25f * _line_height;
326  }
327 
328  } else {
329  PandaNode::Children cr = root->get_children();
330  int num_children = cr.get_num_children();
331  for (int i = 0; i < num_children; i++) {
332  find_characters(cr.get_child(i), next_net_state);
333  }
334  }
335 }
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.h:75
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
A container for geometry primitives.
Definition: geom.h:54
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
NodePath copy_to(const NodePath &other, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Functions like instance_to(), except a deep copy is made of the referenced node and all of its descen...
Definition: nodePath.cxx:539
int flatten_light()
Analyzes the geometry below this node and reports the number of vertices, triangles,...
Definition: nodePath.cxx:5587
void set_mat(const LMatrix4 &mat)
Directly sets an arbitrary 4x4 transform matrix.
Definition: nodePath.cxx:1359
int clear_model_nodes()
Recursively walks through the scene graph at this level and below, looking for ModelNodes,...
Definition: nodePath.I:1984
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
NodePath get_child(int n, Thread *current_thread=Thread::get_current_thread()) const
Returns a NodePath representing the nth child of the referenced node.
Definition: nodePath.I:337
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
Definition: nodePath.cxx:4000
NodePath attach_new_node(PandaNode *node, int sort=0, Thread *current_thread=Thread::get_current_thread()) const
Attaches a new node, with or without existing parents, to the scene graph below the referenced node o...
Definition: nodePath.cxx:600
PandaNode * get_child(size_t n) const
Returns the nth child of the node.
Definition: pandaNode.I:962
size_t get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:953
A basic node of the scene graph or data graph.
Definition: pandaNode.h:65
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2062
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:782
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
StaticTextFont(PandaNode *font_def, CoordinateSystem cs=CS_default)
The constructor expects the root node to a model generated via egg-mkfont, which consists of a set of...
An encapsulation of a font; i.e.
Definition: textFont.h:32
TextGlyph * get_invalid_glyph()
Returns a special glyph that can be used as a placeholder for any character not in the font.
Definition: textFont.cxx:92
A representation of a single glyph (character) from a font.
Definition: textGlyph.h:28
get_advance
Returns the distance by which the character pointer should be advanced after placing this character; ...
Definition: textGlyph.h:46
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
get_num_textures
Returns the number of Textures in the collection.
get_texture
Returns the nth Texture in the collection.
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
set_compression
Requests that this particular Texture be compressed when it is loaded into texture memory.
Definition: texture.h:413
get_minfilter
Returns the filter mode of the texture for minification.
Definition: texture.h:391
set_quality_level
Sets a hint to the renderer about the desired performance / quality tradeoff for this particular text...
Definition: texture.h:427
get_magfilter
Returns the filter mode of the texture for magnification.
Definition: texture.h:397
get_quality_level
Returns the current quality_level hint.
Definition: texture.h:427
set_minfilter
Sets the filtering method that should be used when viewing the texture from a distance.
Definition: texture.h:391
set_magfilter
Sets the filtering method that should be used when viewing the texture up close.
Definition: texture.h:397
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(TextFont) StaticTextFont
Returns a new copy of the same font.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.