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