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