Panda3D
Loading...
Searching...
No Matches
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
52using std::string;
53
54TypeHandle TextNode::_type_handle;
55
56PStatCollector TextNode::_text_generate_pcollector("*:Generate Text");
57
58/**
59 *
60 */
61TextNode::
62TextNode(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 */
101TextNode::
102TextNode(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 */
129TextNode::
130TextNode(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 */
160PandaNode *TextNode::
161make_copy() const {
162 return new TextNode(*this);
163}
164
165/**
166 *
167 */
168TextNode::
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 */
176PN_stdfloat TextNode::
177calc_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 */
199has_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 */
218has_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 */
242is_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 */
256PN_stdfloat TextNode::
257calc_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 */
276void TextNode::
277output(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 */
292void TextNode::
293write(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 */
313PT(PandaNode) TextNode::
314get_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 */
325void TextNode::
326text_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 */
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 */
357apply_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 */
426CPT(TransformState) TextNode::
427calc_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 */
460bool TextNode::
461cull_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 */
481is_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 */
491compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
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 */
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 */
544void TextNode::
545do_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 */
556void TextNode::
557do_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 */
567PT(PandaNode) TextNode::
568do_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 */
729PT(PandaNode) TextNode::
730do_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 */
739PT(PandaNode) TextNode::
740make_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 */
794PT(PandaNode) TextNode::
795make_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 */
846PT(PandaNode) TextNode::
847make_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 */
957int TextNode::
958count_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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
This defines a bounding sphere, consisting of a center and a radius.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
Indicates what color should be applied to renderable geometry.
Definition colorAttrib.h:27
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
get_color_type
Returns the type of color specified by this ColorAttrib.
Definition colorAttrib.h:46
Applies a scale to colors in the scene graph and on vertices.
get_scale
Returns the scale to be applied to colors.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
This class exists just to provide scoping for the various enumerated types used by Geom,...
Definition geomEnums.h:24
Defines a series of line strips.
A node that holds Geom objects, renderable pieces of geometry.
Definition geomNode.h:34
get_num_geoms
Returns the number of geoms in the node.
Definition geomNode.h:71
Defines a series of disconnected points.
Definition geomPoints.h:23
An object specifically designed to transform the vertices of a Geom without disturbing indexing or af...
Defines a series of triangle strips.
This defines the actual numeric vertex data stored in a Geom, in the structure defined by a particula...
static const GeomVertexFormat * get_v3t2()
Returns a standard vertex format with a 2-component texture coordinate pair and a 3-component vertex ...
static const GeomVertexFormat * get_v3()
Returns a standard vertex format with just a 3-component vertex position.
This object provides a high-level interface for quickly writing a sequence of numeric values from a v...
A container for geometry primitives.
Definition geom.h:54
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
bool around(const GeometricBoundingVolume **first, const GeometricBoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
bool debug_is_locked() const
Returns true if the current thread has locked the Mutex, false otherwise.
Definition mutexDirect.I:90
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition mutexHolder.h:25
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition nodePath.h:159
A lightweight class that represents a single element that may be timed and/or counted via stats.
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
A basic node of the scene graph or data graph.
Definition pandaNode.h:65
virtual bool is_geom_node() const
A simple downcast check.
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
get_children
Returns an object that can be used to walk through the list of children of the node.
Definition pandaNode.h:782
This is the base class for a number of render attributes (other than transform) that may be set on sc...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition renderState.h:47
An interface for simplifying ("flattening") scene graphs by eliminating unneeded nodes and collapsing...
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 class is not normally used directly by user code, but is used by the TextNode to lay out a block...
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...
static PN_stdfloat calc_width(wchar_t character, const TextProperties &properties)
Returns the width of a single character, according to its associated font.
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.
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,...
This class can be used to convert text between multiple representations, e.g.
Definition textEncoder.h:33
get_text
Returns the current text, as encoded via the current encoding system.
const std::wstring & get_wtext() const
Returns the text associated with the TextEncoder, as a wide-character string.
An encapsulation of a font; i.e.
Definition textFont.h:32
The primary interface to this module.
Definition textNode.h:48
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
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
virtual void text_changed() final
Given that we have just read an ampersand from the StringDecoder, and that we have expand_amp in effe...
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
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
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
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
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
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
This defines the set of visual properties that may be assigned to the individual characters of the te...
set_small_caps
Sets the small_caps flag.
A thread; that is, a lightweight process.
Definition thread.h:46
Indicates a coordinate-system transform on vertices.
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition indent.cxx:20
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.