Panda3D
textNode.cxx
1 // Filename: textNode.cxx
2 // Created by: drose (13Mar02)
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 "textNode.h"
16 #include "textGlyph.h"
17 #include "stringDecoder.h"
18 #include "config_text.h"
19 #include "textAssembler.h"
20 
21 #include "compose_matrix.h"
22 #include "geom.h"
23 #include "geomLinestrips.h"
24 #include "geomPoints.h"
25 #include "geomTristrips.h"
26 #include "geomVertexWriter.h"
27 #include "geomNode.h"
28 #include "pnotify.h"
29 #include "transformState.h"
30 #include "colorAttrib.h"
31 #include "colorScaleAttrib.h"
32 #include "cullBinAttrib.h"
33 #include "textureAttrib.h"
34 #include "transparencyAttrib.h"
35 #include "sceneGraphReducer.h"
36 #include "indent.h"
37 #include "cullTraverser.h"
38 #include "cullTraverserData.h"
39 #include "geometricBoundingVolume.h"
40 #include "accumulatedAttribs.h"
41 #include "renderState.h"
42 #include "renderModeAttrib.h"
43 #include "decalEffect.h"
44 #include "dcast.h"
45 #include "bamFile.h"
46 #include "zStream.h"
47 #include "pStatCollector.h"
48 #include "pStatTimer.h"
49 #include "boundingSphere.h"
50 
51 #include <stdio.h>
52 
53 TypeHandle TextNode::_type_handle;
54 
55 PStatCollector TextNode::_text_generate_pcollector("*:Generate Text");
56 
57 ////////////////////////////////////////////////////////////////////
58 // Function: TextNode::Constructor
59 // Access: Published
60 // Description:
61 ////////////////////////////////////////////////////////////////////
62 TextNode::
63 TextNode(const string &name) : PandaNode(name) {
64  set_cull_callback();
65 
66  _flags = 0;
67  _max_rows = 0;
68  _usage_hint = GeomEnums::UH_static;
69  _flatten_flags = 0;
70  if (text_flatten) {
71  _flatten_flags |= FF_strong;
72  }
73  if (text_dynamic_merge) {
74  _flatten_flags |= FF_dynamic_merge;
75  }
76 
77  if (text_small_caps) {
78  set_small_caps(true);
79  }
80 
81  _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
82  _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
83 
84  _frame_width = 1.0f;
85 
86  _frame_ul.set(0.0f, 0.0f);
87  _frame_lr.set(0.0f, 0.0f);
88  _card_ul.set(0.0f, 0.0f);
89  _card_lr.set(0.0f, 0.0f);
90 
91  _transform = LMatrix4::ident_mat();
92  _coordinate_system = CS_default;
93 
94  _ul3d.set(0.0f, 0.0f, 0.0f);
95  _lr3d.set(0.0f, 0.0f, 0.0f);
96 }
97 
98 ////////////////////////////////////////////////////////////////////
99 // Function: TextNode::Copy Constructor
100 // Access: Published
101 // Description: It's sort of a copy constructor: it copies the
102 // indicated TextProperties, without copying a complete
103 // TextNode.
104 ////////////////////////////////////////////////////////////////////
105 TextNode::
106 TextNode(const string &name, const TextProperties &copy) :
107  PandaNode(name), TextProperties(copy)
108 {
109  _flags = 0;
110  _max_rows = 0;
111  _usage_hint = GeomEnums::UH_static;
112 
113  _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
114  _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
115 
116  _frame_width = 1.0f;
117 
118  _frame_ul.set(0.0f, 0.0f);
119  _frame_lr.set(0.0f, 0.0f);
120  _card_ul.set(0.0f, 0.0f);
121  _card_lr.set(0.0f, 0.0f);
122 
123  _transform = LMatrix4::ident_mat();
124  _coordinate_system = CS_default;
125 
126  _ul3d.set(0.0f, 0.0f, 0.0f);
127  _lr3d.set(0.0f, 0.0f, 0.0f);
128 }
129 
130 ////////////////////////////////////////////////////////////////////
131 // Function: TextNode::Copy Constructor
132 // Access: Published
133 // Description: OK, this is a true copy constructor.
134 ////////////////////////////////////////////////////////////////////
135 TextNode::
136 TextNode(const TextNode &copy) :
137  PandaNode(copy),
138  TextEncoder(copy),
139  TextProperties(copy),
140  _card_texture(copy._card_texture),
141  _frame_color(copy._frame_color),
142  _card_color(copy._card_color),
143  _flags(copy._flags),
144  _max_rows(copy._max_rows),
145  _usage_hint(GeomEnums::UH_static),
146  _frame_width(copy._frame_width),
147  _card_border_size(copy._card_border_size),
148  _card_border_uv_portion(copy._card_border_uv_portion),
149  _frame_ul(copy._frame_ul),
150  _frame_lr(copy._frame_lr),
151  _card_ul(copy._card_ul),
152  _card_lr(copy._card_lr),
153  _transform(copy._transform),
154  _coordinate_system(copy._coordinate_system),
155  _ul3d(copy._ul3d),
156  _lr3d(copy._lr3d)
157 {
158  invalidate_with_measure();
159 }
160 
161 ////////////////////////////////////////////////////////////////////
162 // Function: TextNode::make_copy
163 // Access: Protected, Virtual
164 // Description: Returns a newly-allocated Node that is a shallow copy
165 // of this one. It will be a different Node pointer,
166 // but its internal data may or may not be shared with
167 // that of the original Node.
168 ////////////////////////////////////////////////////////////////////
169 PandaNode *TextNode::
170 make_copy() const {
171  return new TextNode(*this);
172 }
173 
174 ////////////////////////////////////////////////////////////////////
175 // Function: TextNode::Destructor
176 // Access: Published
177 // Description:
178 ////////////////////////////////////////////////////////////////////
179 TextNode::
180 ~TextNode() {
181 }
182 
183 ////////////////////////////////////////////////////////////////////
184 // Function: TextNode::calc_width
185 // Access: Published
186 // Description: Returns the width of a single character of the font,
187 // or 0.0 if the character is not known. This may be a
188 // wide character (greater than 255).
189 ////////////////////////////////////////////////////////////////////
190 PN_stdfloat TextNode::
191 calc_width(wchar_t character) const {
192  TextFont *font = get_font();
193  if (font == (TextFont *)NULL) {
194  return 0.0f;
195  }
196 
197  return TextAssembler::calc_width(character, *this);
198 }
199 
200 ////////////////////////////////////////////////////////////////////
201 // Function: TextNode::has_exact_character
202 // Access: Published
203 // Description: Returns true if the named character exists in the
204 // font exactly as named, false otherwise. Note that
205 // because Panda can assemble glyphs together
206 // automatically using cheesy accent marks, this is not
207 // a reliable indicator of whether a suitable glyph can
208 // be rendered for the character. For that, use
209 // has_character() instead.
210 //
211 // This returns true for whitespace and Unicode
212 // whitespace characters (if they exist in the font),
213 // but returns false for characters that would render
214 // with the "invalid glyph". It also returns false for
215 // characters that would be synthesized within Panda,
216 // but see has_character().
217 ////////////////////////////////////////////////////////////////////
218 bool TextNode::
219 has_exact_character(wchar_t character) const {
220  TextFont *font = get_font();
221  if (font == (TextFont *)NULL) {
222  return false;
223  }
224 
225  return TextAssembler::has_exact_character(character, *this);
226 }
227 
228 ////////////////////////////////////////////////////////////////////
229 // Function: TextNode::has_character
230 // Access: Published
231 // Description: Returns true if the named character exists in the
232 // font or can be synthesized by Panda, false otherwise.
233 // (Panda can synthesize some accented characters by
234 // combining similar-looking glyphs from the font.)
235 //
236 // This returns true for whitespace and Unicode
237 // whitespace characters (if they exist in the font),
238 // but returns false for characters that would render
239 // with the "invalid glyph".
240 ////////////////////////////////////////////////////////////////////
241 bool TextNode::
242 has_character(wchar_t character) const {
243  TextFont *font = get_font();
244  if (font == (TextFont *)NULL) {
245  return false;
246  }
247 
248  return TextAssembler::has_character(character, *this);
249 }
250 
251 ////////////////////////////////////////////////////////////////////
252 // Function: TextNode::is_whitespace
253 // Access: Published
254 // Description: Returns true if the indicated character represents
255 // whitespace in the font, or false if anything visible
256 // will be rendered for it.
257 //
258 // This returns true for whitespace and Unicode
259 // whitespace characters (if they exist in the font),
260 // and returns false for any other characters, including
261 // characters that do not exist in the font (these would
262 // be rendered with the "invalid glyph", which is
263 // visible).
264 //
265 // Note that this function can be reliably used to
266 // identify Unicode whitespace characters only if the
267 // font has all of the whitespace characters defined.
268 // It will return false for any character not in the
269 // font, even if it is an official Unicode whitespace
270 // character.
271 ////////////////////////////////////////////////////////////////////
272 bool TextNode::
273 is_whitespace(wchar_t character) const {
274  TextFont *font = get_font();
275  if (font == (TextFont *)NULL) {
276  return false;
277  }
278 
279  return TextAssembler::is_whitespace(character, *this);
280 }
281 
282 ////////////////////////////////////////////////////////////////////
283 // Function: TextNode::calc_width
284 // Access: Published
285 // Description: Returns the width of a line of text of arbitrary
286 // characters. The line should not include the newline
287 // character or any embedded control characters like \1
288 // or \3.
289 ////////////////////////////////////////////////////////////////////
290 PN_stdfloat TextNode::
291 calc_width(const wstring &line) const {
292  PN_stdfloat width = 0.0f;
293 
294  wstring::const_iterator si;
295  for (si = line.begin(); si != line.end(); ++si) {
296  width += calc_width(*si);
297  }
298 
299  return width;
300 }
301 
302 ////////////////////////////////////////////////////////////////////
303 // Function: TextNode::output
304 // Access: Public, Virtual
305 // Description:
306 ////////////////////////////////////////////////////////////////////
307 void TextNode::
308 output(ostream &out) const {
309  PandaNode::output(out);
310 
311  check_rebuild();
312  int geom_count = 0;
313  if (_internal_geom != (PandaNode *)NULL) {
314  geom_count = count_geoms(_internal_geom);
315  }
316 
317  out << " (" << geom_count << " geoms)";
318 }
319 
320 ////////////////////////////////////////////////////////////////////
321 // Function: TextNode::write
322 // Access: Published, Virtual
323 // Description:
324 ////////////////////////////////////////////////////////////////////
325 void TextNode::
326 write(ostream &out, int indent_level) const {
327  PandaNode::write(out, indent_level);
328  TextProperties::write(out, indent_level + 2);
329  indent(out, indent_level + 2)
330  << "transform is: " << *TransformState::make_mat(_transform) << "\n";
331  indent(out, indent_level + 2)
332  << "in coordinate system " << _coordinate_system << "\n";
333  indent(out, indent_level + 2)
334  << "text is " << get_text() << "\n";
335 }
336 
337 ////////////////////////////////////////////////////////////////////
338 // Function: TextNode::generate
339 // Access: Published
340 // Description: Generates the text, according to the parameters
341 // indicated within the TextNode, and returns a Node
342 // that may be parented within the tree to represent it.
343 ////////////////////////////////////////////////////////////////////
344 PT(PandaNode) TextNode::
345 generate() {
346  PStatTimer timer(_text_generate_pcollector);
347  if (text_cat.is_debug()) {
348  text_cat.debug()
349  << "Rebuilding " << get_type() << " " << get_name()
350  << " with '" << get_text() << "'\n";
351  }
352 
353  // The strategy here will be to assemble together a bunch of
354  // letters, instanced from the letter hierarchy of font_def, into
355  // our own little hierarchy.
356 
357  // There will be one root over the whole text block, that
358  // contains the transform passed in. Under this root there will be
359  // another node for each row, that moves the row into the right place
360  // horizontally and vertically, and for each row, there is another
361  // node for each character.
362 
363  _ul3d.set(0.0f, 0.0f, 0.0f);
364  _lr3d.set(0.0f, 0.0f, 0.0f);
365 
366  // Now build a new sub-tree for all the text components.
367  string name = get_text();
368  size_t newline = name.find('\n');
369  if (newline != string::npos) {
370  name = name.substr(0, newline);
371  }
372  PT(PandaNode) root = new PandaNode(name);
373 
374  if (!has_text()) {
375  return root;
376  }
377 
378  TextFont *font = get_font();
379  if (font == (TextFont *)NULL) {
380  return root;
381  }
382 
383  // Compute the overall text transform matrix. We build the text in
384  // a Z-up coordinate system and then convert it to whatever the user
385  // asked for.
386  LMatrix4 mat =
387  LMatrix4::convert_mat(CS_zup_right, _coordinate_system) *
388  _transform;
389 
390  CPT(TransformState) transform = TransformState::make_mat(mat);
391  root->set_transform(transform);
392 
393  wstring wtext = get_wtext();
394 
395  // Assemble the text.
396  TextAssembler assembler(this);
397  assembler.set_properties(*this);
398  assembler.set_max_rows(_max_rows);
399  assembler.set_usage_hint(_usage_hint);
400  assembler.set_dynamic_merge((_flatten_flags & FF_dynamic_merge) != 0);
401  bool all_set = assembler.set_wtext(wtext);
402  if (all_set) {
403  // No overflow.
404  _flags &= ~F_has_overflow;
405  } else {
406  // Overflow.
407  _flags |= F_has_overflow;
408  }
409 
410  PT(PandaNode) text_root = assembler.assemble_text();
411  _text_ul = assembler.get_ul();
412  _text_lr = assembler.get_lr();
413  _num_rows = assembler.get_num_rows();
414  _wordwrapped_wtext = assembler.get_wordwrapped_wtext();
415 
416  // Parent the text in.
417  PT(PandaNode) text = new PandaNode("text");
418  root->add_child(text, get_draw_order() + 2);
419  text->add_child(text_root);
420 
421  // Save the bounding-box information about the text in a form
422  // friendly to the user.
423  const LVector2 &ul = assembler.get_ul();
424  const LVector2 &lr = assembler.get_lr();
425  _ul3d.set(ul[0], 0.0f, ul[1]);
426  _lr3d.set(lr[0], 0.0f, lr[1]);
427 
428  _ul3d = _ul3d * _transform;
429  _lr3d = _lr3d * _transform;
430 
431  // Incidentally, that means we don't need to measure the text now.
432  _flags &= ~F_needs_measure;
433 
434  // Now flatten our hierarchy to get rid of the transforms we put in,
435  // applying them to the vertices.
436 
437  NodePath root_np(root);
438  if (_flatten_flags & FF_strong) {
439  root_np.flatten_strong();
440  } else if (_flatten_flags & FF_medium) {
441  root_np.flatten_medium();
442  } else if (_flatten_flags & FF_light) {
443  root_np.flatten_light();
444  }
445 
446  // Now deal with the decorations.
447 
448  if (has_card()) {
449  PT(PandaNode) card_root;
450  if (has_card_border()) {
451  card_root = make_card_with_border();
452  } else {
453  card_root = make_card();
454  }
455  card_root->set_transform(transform);
456  card_root->set_attrib(ColorAttrib::make_flat(get_card_color()));
457  if (get_card_color()[3] != 1.0f) {
458  card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
459  }
460  if (has_card_texture()) {
461  card_root->set_attrib(TextureAttrib::make(get_card_texture()));
462  }
463 
464  if (has_bin()) {
465  card_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order()));
466  }
467 
468  // We always apply attribs down to the card vertices.
470  gr.apply_attribs(card_root);
471 
472  // In order to decal the text onto the card, the card must
473  // become the parent of the text.
474  card_root->add_child(root);
475  root = card_root;
476 
477  if (get_card_decal()) {
478  card_root->set_effect(DecalEffect::make());
479  }
480  }
481 
482  if (has_frame()) {
483  PT(PandaNode) frame_root = make_frame();
484  frame_root->set_transform(transform);
485  root->add_child(frame_root, get_draw_order() + 1);
486  frame_root->set_attrib(ColorAttrib::make_flat(get_frame_color()));
487  if (get_frame_color()[3] != 1.0f) {
488  frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
489  }
490 
491  if (has_bin()) {
492  frame_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order() + 1));
493  }
494 
496  gr.apply_attribs(frame_root);
497  }
498 
499  return root;
500 }
501 
502 ////////////////////////////////////////////////////////////////////
503 // Function: TextNode::get_internal_geom
504 // Access: Published
505 // Description: Returns the actual node that is used internally to
506 // render the text, if the TextNode is parented within
507 // the scene graph.
508 //
509 // In general, you should not call this method. Call
510 // generate() instead if you want to get a handle to
511 // geometry that represents the text. This method is
512 // provided as a debugging aid only.
513 ////////////////////////////////////////////////////////////////////
516  // Output a nuisance warning to discourage the naive from calling
517  // this method accidentally.
518  text_cat.info()
519  << "TextNode::get_internal_geom() called.\n";
520  check_rebuild();
521  return _internal_geom;
522 }
523 
524 ////////////////////////////////////////////////////////////////////
525 // Function: TextNode::get_unsafe_to_apply_attribs
526 // Access: Public, Virtual
527 // Description: Returns the union of all attributes from
528 // SceneGraphReducer::AttribTypes that may not safely be
529 // applied to the vertices of this node. If this is
530 // nonzero, these attributes must be dropped at this
531 // node as a state change.
532 //
533 // This is a generalization of safe_to_transform().
534 ////////////////////////////////////////////////////////////////////
535 int TextNode::
537  // We have no way to apply these kinds of attributes to our
538  // TextNode, so insist they get dropped into the PandaNode's basic
539  // state.
540  return
541  SceneGraphReducer::TT_tex_matrix |
542  SceneGraphReducer::TT_other;
543 }
544 
545 ////////////////////////////////////////////////////////////////////
546 // Function: TextNode::apply_attribs_to_vertices
547 // Access: Public, Virtual
548 // Description: Applies whatever attributes are specified in the
549 // AccumulatedAttribs object (and by the attrib_types
550 // bitmask) to the vertices on this node, if
551 // appropriate. If this node uses geom arrays like a
552 // GeomNode, the supplied GeomTransformer may be used to
553 // unify shared arrays across multiple different nodes.
554 //
555 // This is a generalization of xform().
556 ////////////////////////////////////////////////////////////////////
557 void TextNode::
558 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
559  GeomTransformer &transformer) {
560  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
561  const LMatrix4 &mat = attribs._transform->get_mat();
562  _transform *= mat;
563 
564  if ((_flags & F_needs_measure) == 0) {
565  // If we already have a measure, transform it too. We don't
566  // need to invalidate the 2-d parts, since that's not affected
567  // by the transform anyway.
568  _ul3d = _ul3d * mat;
569  _lr3d = _lr3d * mat;
570  }
571  }
572  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
573  if (attribs._color != (const RenderAttrib *)NULL) {
574  const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
575  if (ca->get_color_type() == ColorAttrib::T_flat) {
576  const LColor &c = ca->get_color();
577  set_text_color(c);
578  set_frame_color(c);
579  set_card_color(c);
580  set_shadow_color(c);
581  }
582  }
583  }
584  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
585  if (attribs._color_scale != (const RenderAttrib *)NULL) {
586  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
587  const LVecBase4 &s = csa->get_scale();
588  if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
589  LVecBase4 tc = get_text_color();
590  tc[0] *= s[0];
591  tc[1] *= s[1];
592  tc[2] *= s[2];
593  tc[3] *= s[3];
594  set_text_color(tc);
595  LVecBase4 sc = get_shadow_color();
596  sc[0] *= s[0];
597  sc[1] *= s[1];
598  sc[2] *= s[2];
599  sc[3] *= s[3];
600  set_shadow_color(sc);
601  LVecBase4 fc = get_frame_color();
602  fc[0] *= s[0];
603  fc[1] *= s[1];
604  fc[2] *= s[2];
605  fc[3] *= s[3];
606  set_frame_color(fc);
607  LVecBase4 cc = get_card_color();
608  cc[0] *= s[0];
609  cc[1] *= s[1];
610  cc[2] *= s[2];
611  cc[3] *= s[3];
612  set_card_color(cc);
613  }
614  }
615  }
616 
617  // Now propagate the attributes down to our already-generated
618  // geometry, if we have any.
619  if ((_flags & F_needs_rebuild) == 0 &&
620  _internal_geom != (PandaNode *)NULL) {
622  gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer);
623  }
624 }
625 
626 ////////////////////////////////////////////////////////////////////
627 // Function: TextNode::calc_tight_bounds
628 // Access: Public, Virtual
629 // Description: This is used to support
630 // NodePath::calc_tight_bounds(). It is not intended to
631 // be called directly, and it has nothing to do with the
632 // normal Panda bounding-volume computation.
633 //
634 // If the node contains any geometry, this updates
635 // min_point and max_point to enclose its bounding box.
636 // found_any is to be set true if the node has any
637 // geometry at all, or left alone if it has none. This
638 // method may be called over several nodes, so it may
639 // enter with min_point, max_point, and found_any
640 // already set.
641 ////////////////////////////////////////////////////////////////////
642 CPT(TransformState) TextNode::
643 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
644  const TransformState *transform, Thread *current_thread) const {
645  CPT(TransformState) next_transform =
646  PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform,
647  current_thread);
648 
649  check_rebuild();
650 
651  if (_internal_geom != (PandaNode *)NULL) {
652  _internal_geom->calc_tight_bounds(min_point, max_point,
653  found_any, next_transform, current_thread);
654  }
655 
656  return next_transform;
657 }
658 
659 ////////////////////////////////////////////////////////////////////
660 // Function: TextNode::cull_callback
661 // Access: Protected, Virtual
662 // Description: This function will be called during the cull
663 // traversal to perform any additional operations that
664 // should be performed at cull time. This may include
665 // additional manipulation of render state or additional
666 // visible/invisible decisions, or any other arbitrary
667 // operation.
668 //
669 // Note that this function will *not* be called unless
670 // set_cull_callback() is called in the constructor of
671 // the derived class. It is necessary to call
672 // set_cull_callback() to indicated that we require
673 // cull_callback() to be called.
674 //
675 // By the time this function is called, the node has
676 // already passed the bounding-volume test for the
677 // viewing frustum, and the node's transform and state
678 // have already been applied to the indicated
679 // CullTraverserData object.
680 //
681 // The return value is true if this node should be
682 // visible, or false if it should be culled.
683 ////////////////////////////////////////////////////////////////////
684 bool TextNode::
685 cull_callback(CullTraverser *trav, CullTraverserData &data) {
686  check_rebuild();
687  if (_internal_geom != (PandaNode *)NULL) {
688  // Render the text with this node.
689  CullTraverserData next_data(data, _internal_geom);
690  trav->traverse(next_data);
691  }
692 
693  // Now continue to render everything else below this node.
694  return true;
695 }
696 
697 ////////////////////////////////////////////////////////////////////
698 // Function: TextNode::is_renderable
699 // Access: Public, Virtual
700 // Description: Returns true if there is some value to visiting this
701 // particular node during the cull traversal for any
702 // camera, false otherwise. This will be used to
703 // optimize the result of get_net_draw_show_mask(), so
704 // that any subtrees that contain only nodes for which
705 // is_renderable() is false need not be visited.
706 ////////////////////////////////////////////////////////////////////
707 bool TextNode::
708 is_renderable() const {
709  return true;
710 }
711 
712 ////////////////////////////////////////////////////////////////////
713 // Function: TextNode::compute_internal_bounds
714 // Access: Protected, Virtual
715 // Description: Called when needed to recompute the node's
716 // _internal_bound object. Nodes that contain anything
717 // of substance should redefine this to do the right
718 // thing.
719 ////////////////////////////////////////////////////////////////////
720 void TextNode::
722  int &internal_vertices,
723  int pipeline_stage,
724  Thread *current_thread) const {
725  // First, get ourselves a fresh, empty bounding volume.
726  PT(BoundingVolume) bound = new BoundingSphere;
727 
729 
730  // Now enclose the bounding box around the text. We can do this
731  // without actually generating the text, if we have at least
732  // measured it.
733  check_measure();
734 
735  LPoint3 vertices[8];
736  vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
737  vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
738  vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
739  vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
740  vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
741  vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
742  vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
743  vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
744 
745  gbv->around(vertices, vertices + 8);
746 
747  internal_bounds = bound;
748  internal_vertices = 0; // TODO: estimate this better.
749 }
750 
751 ////////////////////////////////////////////////////////////////////
752 // Function: TextNode::r_prepare_scene
753 // Access: Protected, Virtual
754 // Description: The recursive implementation of prepare_scene().
755 // Don't call this directly; call
756 // PandaNode::prepare_scene() or
757 // NodePath::prepare_scene() instead.
758 ////////////////////////////////////////////////////////////////////
759 void TextNode::
761  GeomTransformer &transformer, Thread *current_thread) {
762  check_rebuild();
763 
764  PandaNode *child = _internal_geom;
765  if (child != (PandaNode *)NULL) {
766  CPT(RenderState) child_state = node_state->compose(child->get_state());
767  child->r_prepare_scene(gsg, child_state, transformer, current_thread);
768  }
769 
770  PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread);
771 }
772 
773 ////////////////////////////////////////////////////////////////////
774 // Function: TextNode::do_rebuild
775 // Access: Private
776 // Description: Removes any existing children of the TextNode, and
777 // adds the newly generated text instead.
778 ////////////////////////////////////////////////////////////////////
779 void TextNode::
780 do_rebuild() {
781  _flags &= ~(F_needs_rebuild | F_needs_measure);
782  _internal_geom = generate();
783 }
784 
785 
786 ////////////////////////////////////////////////////////////////////
787 // Function: TextNode::do_measure
788 // Access: Private
789 // Description: Can be called in lieu of do_rebuild() to measure the
790 // text and set up the bounding boxes properly without
791 // actually assembling it.
792 ////////////////////////////////////////////////////////////////////
793 void TextNode::
794 do_measure() {
795  // We no longer make this a special case.
796  do_rebuild();
797 }
798 
799 ////////////////////////////////////////////////////////////////////
800 // Function: TextNode::make_frame
801 // Access: Private
802 // Description: Creates a frame around the text.
803 ////////////////////////////////////////////////////////////////////
804 PT(PandaNode) TextNode::
805 make_frame() {
806  PT(GeomNode) frame_node = new GeomNode("frame");
807 
808  LVector4 dimensions = get_frame_actual();
809  PN_stdfloat left = dimensions[0];
810  PN_stdfloat right = dimensions[1];
811  PN_stdfloat bottom = dimensions[2];
812  PN_stdfloat top = dimensions[3];
813 
814  CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _frame_width);
815  CPT(RenderState) state = RenderState::make(thick);
816 
817  PT(GeomVertexData) vdata = new GeomVertexData
818  ("text", GeomVertexFormat::get_v3(), get_usage_hint());
819  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
820 
821  vertex.add_data3(left, 0.0f, top);
822  vertex.add_data3(left, 0.0f, bottom);
823  vertex.add_data3(right, 0.0f, bottom);
824  vertex.add_data3(right, 0.0f, top);
825 
826  PT(GeomLinestrips) frame = new GeomLinestrips(get_usage_hint());
827  frame->add_consecutive_vertices(0, 4);
828  frame->add_vertex(0);
829  frame->close_primitive();
830 
831  PT(Geom) geom = new Geom(vdata);
832  geom->add_primitive(frame);
833  frame_node->add_geom(geom, state);
834 
835  if (get_frame_corners()) {
836  PT(GeomPoints) corners = new GeomPoints(get_usage_hint());
837  corners->add_consecutive_vertices(0, 4);
838  PT(Geom) geom2 = new Geom(vdata);
839  geom2->add_primitive(corners);
840  frame_node->add_geom(geom2, state);
841  }
842 
843  return frame_node.p();
844 }
845 
846 ////////////////////////////////////////////////////////////////////
847 // Function: TextNode::make_card
848 // Access: Private
849 // Description: Creates a card behind the text.
850 ////////////////////////////////////////////////////////////////////
851 PT(PandaNode) TextNode::
852 make_card() {
853  PT(GeomNode) card_node = new GeomNode("card");
854 
855  LVector4 dimensions = get_card_actual();
856  PN_stdfloat left = dimensions[0];
857  PN_stdfloat right = dimensions[1];
858  PN_stdfloat bottom = dimensions[2];
859  PN_stdfloat top = dimensions[3];
860 
861  PT(GeomVertexData) vdata = new GeomVertexData
862  ("text", GeomVertexFormat::get_v3t2(), get_usage_hint());
863  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
864  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
865 
866  vertex.add_data3(left, 0.0f, top);
867  vertex.add_data3(left, 0.0f, bottom);
868  vertex.add_data3(right, 0.0f, top);
869  vertex.add_data3(right, 0.0f, bottom);
870 
871  texcoord.add_data2(0.0f, 1.0f);
872  texcoord.add_data2(0.0f, 0.0f);
873  texcoord.add_data2(1.0f, 1.0f);
874  texcoord.add_data2(1.0f, 0.0f);
875 
876  PT(GeomTristrips) card = new GeomTristrips(get_usage_hint());
877  card->add_consecutive_vertices(0, 4);
878  card->close_primitive();
879 
880  PT(Geom) geom = new Geom(vdata);
881  geom->add_primitive(card);
882 
883  card_node->add_geom(geom);
884 
885  return card_node.p();
886 }
887 
888 
889 ////////////////////////////////////////////////////////////////////
890 // Function: TextNode::make_card_with_border
891 // Access: Private
892 // Description: Creates a card behind the text with a specified border
893 // for button edge or what have you.
894 ////////////////////////////////////////////////////////////////////
895 PT(PandaNode) TextNode::
896 make_card_with_border() {
897  PT(GeomNode) card_node = new GeomNode("card");
898 
899  LVector4 dimensions = get_card_actual();
900  PN_stdfloat left = dimensions[0];
901  PN_stdfloat right = dimensions[1];
902  PN_stdfloat bottom = dimensions[2];
903  PN_stdfloat top = dimensions[3];
904 
905  // we now create three tri-strips instead of one
906  // with vertices arranged as follows:
907  //
908  // 1 3 5 7 - one
909  // 2 4 6 8 / \ two
910  // 9 11 13 15 \ /
911  // 10 12 14 16 - three
912  //
913 
914  PT(GeomVertexData) vdata = new GeomVertexData
915  ("text", GeomVertexFormat::get_v3t2(), get_usage_hint());
916  GeomVertexWriter vertex(vdata, InternalName::get_vertex());
917  GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
918 
919  // verts 1,2,3,4
920  vertex.add_data3(left, 0.02, top);
921  vertex.add_data3(left, 0.02, top - _card_border_size);
922  vertex.add_data3(left + _card_border_size, 0.02, top);
923  vertex.add_data3(left + _card_border_size, 0.02,
924  top - _card_border_size);
925  // verts 5,6,7,8
926  vertex.add_data3(right - _card_border_size, 0.02, top);
927  vertex.add_data3(right - _card_border_size, 0.02,
928  top - _card_border_size);
929  vertex.add_data3(right, 0.02, top);
930  vertex.add_data3(right, 0.02, top - _card_border_size);
931  // verts 9,10,11,12
932  vertex.add_data3(left, 0.02, bottom + _card_border_size);
933  vertex.add_data3(left, 0.02, bottom);
934  vertex.add_data3(left + _card_border_size, 0.02,
935  bottom + _card_border_size);
936  vertex.add_data3(left + _card_border_size, 0.02, bottom);
937  // verts 13,14,15,16
938  vertex.add_data3(right - _card_border_size, 0.02,
939  bottom + _card_border_size);
940  vertex.add_data3(right - _card_border_size, 0.02, bottom);
941  vertex.add_data3(right, 0.02, bottom + _card_border_size);
942  vertex.add_data3(right, 0.02, bottom);
943 
944  texcoord.add_data2(0.0f, 1.0f); //1
945  texcoord.add_data2(0.0f, 1.0f - _card_border_uv_portion); //2
946  texcoord.add_data2(0.0f + _card_border_uv_portion, 1.0f); //3
947  texcoord.add_data2(0.0f + _card_border_uv_portion,
948  1.0f - _card_border_uv_portion); //4
949  texcoord.add_data2(1.0f -_card_border_uv_portion, 1.0f); //5
950  texcoord.add_data2(1.0f -_card_border_uv_portion,
951  1.0f - _card_border_uv_portion); //6
952  texcoord.add_data2(1.0f, 1.0f); //7
953  texcoord.add_data2(1.0f, 1.0f - _card_border_uv_portion); //8
954 
955  texcoord.add_data2(0.0f, _card_border_uv_portion); //9
956  texcoord.add_data2(0.0f, 0.0f); //10
957  texcoord.add_data2(_card_border_uv_portion, _card_border_uv_portion); //11
958  texcoord.add_data2(_card_border_uv_portion, 0.0f); //12
959 
960  texcoord.add_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13
961  texcoord.add_data2(1.0f - _card_border_uv_portion, 0.0f);//14
962  texcoord.add_data2(1.0f, _card_border_uv_portion);//15
963  texcoord.add_data2(1.0f, 0.0f);//16
964 
965  PT(GeomTristrips) card = new GeomTristrips(get_usage_hint());
966 
967  // tristrip #1
968  card->add_consecutive_vertices(0, 8);
969  card->close_primitive();
970 
971  // tristrip #2
972  card->add_vertex(1);
973  card->add_vertex(8);
974  card->add_vertex(3);
975  card->add_vertex(10);
976  card->add_vertex(5);
977  card->add_vertex(12);
978  card->add_vertex(7);
979  card->add_vertex(14);
980  card->close_primitive();
981 
982  // tristrip #3
983  card->add_consecutive_vertices(8, 8);
984  card->close_primitive();
985 
986  PT(Geom) geom = new Geom(vdata);
987  geom->add_primitive(card);
988 
989  card_node->add_geom(geom);
990 
991  return card_node.p();
992 }
993 
994 ////////////////////////////////////////////////////////////////////
995 // Function: TextNode::count_geoms
996 // Access: Private, Static
997 // Description: Recursively counts the number of Geoms at the
998 // indicated node and below. Strictly for reporting
999 // this count on output.
1000 ////////////////////////////////////////////////////////////////////
1001 int TextNode::
1002 count_geoms(PandaNode *node) {
1003  int num_geoms = 0;
1004 
1005  if (node->is_geom_node()) {
1006  GeomNode *geom_node = DCAST(GeomNode, node);
1007  num_geoms += geom_node->get_num_geoms();
1008  }
1009 
1010  Children children = node->get_children();
1011  for (int i = 0; i < children.get_num_children(); ++i) {
1012  num_geoms += count_geoms(children.get_child(i));
1013  }
1014 
1015  return num_geoms;
1016 }
int get_num_children() const
Returns the number of children of the node.
Definition: pandaNode.I:1163
static const LMatrix4f & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:903
A basic node of the scene graph or data graph.
Definition: pandaNode.h:72
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
bool has_character(wchar_t character) const
Returns true if the named character exists in the font or can be synthesized by Panda, false otherwise.
Definition: textNode.cxx:242
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:60
static bool has_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font or can be synthesized by Panda, false otherwise.
const LColor & get_color() const
If the type is T_flat or T_off, this returns the color that will be applied to geometry.
Definition: colorAttrib.I:58
Defines a series of disconnected points.
Definition: geomPoints.h:25
const LVecBase4 & get_scale() const
Returns the scale to be applied to colors.
void set_small_caps(bool small_caps)
Sets the small_caps flag.
Definition: textNode.I:788
This class can be used to convert text between multiple representations, e.g.
Definition: textEncoder.h:37
const LVector2 & get_ul() const
Returns the upper-left corner of the assembled text, in 2-d text coordinates.
This defines a bounding sphere, consisting of a center and a radius.
TextFont * get_font() const
Returns the font currently in use, if any.
void add_data2(PN_stdfloat x, PN_stdfloat y)
Sets the write row to a particular 2-component value, and advances the write row. ...
An interface for simplifying ("flattening") scene graphs by eliminating unneeded nodes and collapsing...
This collects together the pieces of data that are accumulated for each node while walking the scene ...
Defines a series of triangle strips.
Definition: geomTristrips.h:25
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition: pStatTimer.h:34
static bool is_whitespace(wchar_t character, const TextProperties &properties)
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:99
int get_num_rows() const
Returns the number of rows of text after it has all been wordwrapped and assembled.
int get_num_geoms() const
Returns the number of geoms in the node.
Definition: geomNode.I:46
Type get_color_type() const
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.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
void set_properties(const TextProperties &properties)
Specifies the default TextProperties that are applied to the text in the absence of any nested proper...
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
Definition: pandaNode.cxx:2925
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
An encapsulation of a font; i.e.
Definition: textFont.h:36
virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer)
Applies whatever attributes are specified in the AccumulatedAttribs object (and by the attrib_types b...
Definition: textNode.cxx:558
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
const wstring & get_wtext() const
Returns the text associated with the TextEncoder, as a wide-character string.
Definition: textEncoder.I:530
A lightweight class that represents a single element that may be timed and/or counted via stats...
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
static bool has_exact_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font exactly as named, false otherwise.
bool get_card_decal() const
Returns the card_decal flag.
Definition: textNode.I:527
LVecBase4 get_frame_actual() const
Returns the actual dimensions of the frame around the text.
Definition: textNode.I:377
bool set_wtext(const wstring &wtext)
Accepts a new text string and associated properties structure, and precomputes the wordwrapping layou...
static PN_stdfloat calc_width(wchar_t character, const TextProperties &properties)
Returns the width of a single character, according to its associated font.
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:451
int get_draw_order() const
Returns the drawing order set with set_draw_order().
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
A container for geometry primitives.
Definition: geom.h:58
This class is not normally used directly by user code, but is used by the TextNode to lay out a block...
Definition: textAssembler.h:46
virtual int get_unsafe_to_apply_attribs() const
Returns the union of all attributes from SceneGraphReducer::AttribTypes that may not safely be applie...
Definition: textNode.cxx:536
void add_data3(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z)
Sets the write row to a particular 3-component value, and advances the write row. ...
This is a four-component vector distance.
Definition: lvector4.h:91
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
LVecBase4 get_card_actual() const
Returns the actual dimensions of the card around the text.
Definition: textNode.I:578
const LVector2 & get_lr() const
Returns the lower-right corner of the assembled text, in 2-d text coordinates.
Applies a scale to colors in the scene graph and on vertices.
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:53
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
void set_max_rows(int max_rows)
If max_rows is greater than zero, no more than max_rows will be accepted.
Definition: textAssembler.I:54
void apply_attribs(PandaNode *node, int attrib_types=~(TT_clip_plane|TT_cull_face|TT_apply_texture_color))
Walks the scene graph, accumulating attribs of the indicated types, applying them to the vertices...
PandaNode * get_internal_geom() const
Returns the actual node that is used internally to render the text, if the TextNode is parented withi...
Definition: textNode.cxx:515
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
int flatten_strong()
The strongest possible flattening.
Definition: nodePath.cxx:6337
This is a two-component vector offset.
Definition: lvector2.h:91
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
A thread; that is, a lightweight process.
Definition: thread.h:51
Defines a series of line strips.
The primary interface to this module.
Definition: textNode.h:52
This defines the set of visual properties that may be assigned to the individual characters of the te...
const string & get_bin() const
Returns the drawing bin set with set_bin(), or empty string if no bin has been set.
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:30
void set_usage_hint(Geom::UsageHint usage_hint)
Specifies the UsageHint that will be applied to generated geometry.
Definition: textAssembler.I:27
virtual bool is_renderable() const
Returns true if there is some value to visiting this particular node during the cull traversal for an...
Definition: textNode.cxx:708
bool has_exact_character(wchar_t character) const
Returns true if the named character exists in the font exactly as named, false otherwise.
Definition: textNode.cxx:219
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
Definition: textNode.cxx:760
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
wstring get_wordwrapped_wtext() const
Returns a wstring that represents the contents of the text, with newlines inserted according to the w...
int flatten_medium()
A more thorough flattening than flatten_light(), this first applies all the transforms, colors, and texture matrices from the nodes onto the vertices, and then removes unneeded grouping nodes–nodes that have exactly one child, for instance, but have no special properties in themselves.
Definition: nodePath.cxx:6304
bool around(const GeometricBoundingVolume **first, const GeometricBoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
PN_stdfloat calc_width(wchar_t character) const
Returns the width of a single character of the font, or 0.0 if the character is not known...
Definition: textNode.cxx:191
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:165
bool has_bin() const
Returns true if an explicit drawing bin has been set via set_bin(), false otherwise.
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling...
Definition: cullTraverser.h:48
bool is_whitespace(wchar_t character) const
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
Definition: textNode.cxx:273
A node that holds Geom objects, renderable pieces of geometry.
Definition: geomNode.h:37
void set_dynamic_merge(bool dynamic_merge)
Sets the dynamic_merge flag.
Definition: textAssembler.I:77
string get_text() const
Returns the current text, as encoded via the current encoding system.
Definition: textEncoder.I:167
virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices, int pipeline_stage, Thread *current_thread) const
Called when needed to recompute the node&#39;s _internal_bound object.
Definition: textNode.cxx:721
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
Geom::UsageHint get_usage_hint() const
Returns the UsageHint that will be applied to generated geometry.
Definition: textNode.I:679