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 }
get_geom_state
Returns the RenderState associated with the nth geom of the node.
Definition: geomNode.h:75
get_advance
Returns the distance by which the character pointer should be advanced after placing this character; ...
Definition: textGlyph.h:46
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_magfilter
Returns the filter mode of the texture for magnification.
Definition: texture.h:389
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int flatten_light()
Analyzes the geometry below this node and reports the number of vertices, triangles,...
Definition: nodePath.cxx:5495
PandaNode * get_child(size_t n) const
Returns the nth child of the node.
Definition: pandaNode.I:962
set_magfilter
Sets the filtering method that should be used when viewing the texture up close.
Definition: texture.h:389
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
This is an abstract base class for a family of classes that represent the fundamental geometry primit...
Definition: geomPrimitive.h:56
int clear_model_nodes()
Recursively walks through the scene graph at this level and below, looking for ModelNodes,...
Definition: nodePath.I:1974
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_quality_level
Returns the current quality_level hint.
Definition: texture.h:419
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
TextureCollection find_all_textures() const
Returns a list of a textures applied to geometry at this node and below.
Definition: nodePath.cxx:3918
get_minfilter
Returns the filter mode of the texture for minification.
Definition: texture.h:383
An encapsulation of a font; i.e.
Definition: textFont.h:32
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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...
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:563
size_t get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:953
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
set_minfilter
Sets the filtering method that should be used when viewing the texture from a distance.
Definition: texture.h:383
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A container for geometry primitives.
Definition: geom.h:54
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition: pandaNode.h:784
A representation of a single glyph (character) from a font.
Definition: textGlyph.h:28
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
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:535
get_num_textures
Returns the number of Textures in the collection.
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
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PT(TextFont) StaticTextFont
Returns a new copy of the same font.
set_compression
Requests that this particular Texture be compressed when it is loaded into texture memory.
Definition: texture.h:405
PandaNode * node() const
Returns the referenced node of the path.
Definition: nodePath.I:227
set_quality_level
Sets a hint to the renderer about the desired performance / quality tradeoff for this particular text...
Definition: texture.h:419
get_num_geoms
Returns the number of geoms in the node.
Definition: geomNode.h:71
This object provides a high-level interface for quickly reading a sequence of numeric values from a v...
float get_data1f()
Returns the data associated with the read row, expressed as a 1-component value, and advances the rea...
void set_mat(const LMatrix4 &mat)
Directly sets an arbitrary 4x4 transform matrix.
Definition: nodePath.cxx:1292
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
virtual bool is_geom_node() const
A simple downcast check.
Definition: pandaNode.cxx:2068
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:161
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
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:34
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_texture
Returns the nth Texture in the collection.