Panda3D

textNode.cxx

00001 // Filename: textNode.cxx
00002 // Created by:  drose (13Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "textNode.h"
00016 #include "textGlyph.h"
00017 #include "stringDecoder.h"
00018 #include "config_text.h"
00019 #include "textAssembler.h"
00020 
00021 #include "compose_matrix.h"
00022 #include "geom.h"
00023 #include "geomLinestrips.h"
00024 #include "geomPoints.h"
00025 #include "geomTristrips.h"
00026 #include "geomVertexWriter.h"
00027 #include "geomNode.h"
00028 #include "pnotify.h"
00029 #include "transformState.h"
00030 #include "colorAttrib.h"
00031 #include "colorScaleAttrib.h"
00032 #include "cullBinAttrib.h"
00033 #include "textureAttrib.h"
00034 #include "transparencyAttrib.h"
00035 #include "sceneGraphReducer.h"
00036 #include "indent.h"
00037 #include "cullTraverser.h"
00038 #include "cullTraverserData.h"
00039 #include "geometricBoundingVolume.h"
00040 #include "accumulatedAttribs.h"
00041 #include "renderState.h"
00042 #include "renderModeAttrib.h"
00043 #include "decalEffect.h"
00044 #include "dcast.h"
00045 #include "bamFile.h"
00046 #include "zStream.h"
00047 #include "pStatCollector.h"
00048 #include "pStatTimer.h"
00049 #include "boundingSphere.h"
00050 
00051 #include <stdio.h>
00052 
00053 TypeHandle TextNode::_type_handle;
00054 
00055 PStatCollector TextNode::_text_generate_pcollector("*:Generate Text");
00056 
00057 ////////////////////////////////////////////////////////////////////
00058 //     Function: TextNode::Constructor
00059 //       Access: Published
00060 //  Description:
00061 ////////////////////////////////////////////////////////////////////
00062 TextNode::
00063 TextNode(const string &name) : PandaNode(name) {
00064   set_cull_callback();
00065 
00066   _flags = 0;
00067   _max_rows = 0;
00068   _usage_hint = GeomEnums::UH_static;
00069   _flatten_flags = 0;
00070   if (text_flatten) {
00071     _flatten_flags |= FF_strong;
00072   }
00073   if (text_dynamic_merge) {
00074     _flatten_flags |= FF_dynamic_merge;
00075   }
00076 
00077   if (text_small_caps) {
00078     set_small_caps(true);
00079   }
00080 
00081   _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00082   _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00083 
00084   _frame_width = 1.0f;
00085 
00086   _frame_ul.set(0.0f, 0.0f);
00087   _frame_lr.set(0.0f, 0.0f);
00088   _card_ul.set(0.0f, 0.0f);
00089   _card_lr.set(0.0f, 0.0f);
00090 
00091   _transform = LMatrix4::ident_mat();
00092   _coordinate_system = CS_default;
00093 
00094   _ul3d.set(0.0f, 0.0f, 0.0f);
00095   _lr3d.set(0.0f, 0.0f, 0.0f);
00096 }
00097 
00098 ////////////////////////////////////////////////////////////////////
00099 //     Function: TextNode::Copy Constructor
00100 //       Access: Published
00101 //  Description: It's sort of a copy constructor: it copies the
00102 //               indicated TextProperties, without copying a complete
00103 //               TextNode.
00104 ////////////////////////////////////////////////////////////////////
00105 TextNode::
00106 TextNode(const string &name, const TextProperties &copy) : 
00107   PandaNode(name), TextProperties(copy) 
00108 {
00109   _flags = 0;
00110   _max_rows = 0;
00111   _usage_hint = GeomEnums::UH_static;
00112 
00113   _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00114   _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00115 
00116   _frame_width = 1.0f;
00117 
00118   _frame_ul.set(0.0f, 0.0f);
00119   _frame_lr.set(0.0f, 0.0f);
00120   _card_ul.set(0.0f, 0.0f);
00121   _card_lr.set(0.0f, 0.0f);
00122 
00123   _transform = LMatrix4::ident_mat();
00124   _coordinate_system = CS_default;
00125 
00126   _ul3d.set(0.0f, 0.0f, 0.0f);
00127   _lr3d.set(0.0f, 0.0f, 0.0f);
00128 }
00129 
00130 ////////////////////////////////////////////////////////////////////
00131 //     Function: TextNode::Copy Constructor
00132 //       Access: Published
00133 //  Description: OK, this is a true copy constructor.
00134 ////////////////////////////////////////////////////////////////////
00135 TextNode::
00136 TextNode(const TextNode &copy) : 
00137   PandaNode(copy), 
00138   TextEncoder(copy),
00139   TextProperties(copy),
00140   _card_texture(copy._card_texture),
00141   _frame_color(copy._frame_color),
00142   _card_color(copy._card_color),
00143   _flags(copy._flags),
00144   _max_rows(copy._max_rows),
00145   _usage_hint(GeomEnums::UH_static),
00146   _frame_width(copy._frame_width),
00147   _card_border_size(copy._card_border_size),
00148   _card_border_uv_portion(copy._card_border_uv_portion),
00149   _frame_ul(copy._frame_ul),
00150   _frame_lr(copy._frame_lr),
00151   _card_ul(copy._card_ul),
00152   _card_lr(copy._card_lr),
00153   _transform(copy._transform),
00154   _coordinate_system(copy._coordinate_system),
00155   _ul3d(copy._ul3d),
00156   _lr3d(copy._lr3d)
00157 {
00158   invalidate_with_measure();
00159 }
00160 
00161 ////////////////////////////////////////////////////////////////////
00162 //     Function: TextNode::make_copy
00163 //       Access: Protected, Virtual
00164 //  Description: Returns a newly-allocated Node that is a shallow copy
00165 //               of this one.  It will be a different Node pointer,
00166 //               but its internal data may or may not be shared with
00167 //               that of the original Node.
00168 ////////////////////////////////////////////////////////////////////
00169 PandaNode *TextNode::
00170 make_copy() const {
00171   return new TextNode(*this);
00172 }
00173 
00174 ////////////////////////////////////////////////////////////////////
00175 //     Function: TextNode::Destructor
00176 //       Access: Published
00177 //  Description:
00178 ////////////////////////////////////////////////////////////////////
00179 TextNode::
00180 ~TextNode() {
00181 }
00182 
00183 ////////////////////////////////////////////////////////////////////
00184 //     Function: TextNode::calc_width
00185 //       Access: Published
00186 //  Description: Returns the width of a single character of the font,
00187 //               or 0.0 if the character is not known.  This may be a
00188 //               wide character (greater than 255).
00189 ////////////////////////////////////////////////////////////////////
00190 PN_stdfloat TextNode::
00191 calc_width(wchar_t character) const {
00192   TextFont *font = get_font();
00193   if (font == (TextFont *)NULL) {
00194     return 0.0f;
00195   }
00196 
00197   return TextAssembler::calc_width(character, *this);
00198 }
00199 
00200 ////////////////////////////////////////////////////////////////////
00201 //     Function: TextNode::has_exact_character
00202 //       Access: Published
00203 //  Description: Returns true if the named character exists in the
00204 //               font exactly as named, false otherwise.  Note that
00205 //               because Panda can assemble glyphs together
00206 //               automatically using cheesy accent marks, this is not
00207 //               a reliable indicator of whether a suitable glyph can
00208 //               be rendered for the character.  For that, use
00209 //               has_character() instead.
00210 //
00211 //               This returns true for whitespace and Unicode
00212 //               whitespace characters (if they exist in the font),
00213 //               but returns false for characters that would render
00214 //               with the "invalid glyph".  It also returns false for
00215 //               characters that would be synthesized within Panda,
00216 //               but see has_character().
00217 ////////////////////////////////////////////////////////////////////
00218 bool TextNode::
00219 has_exact_character(wchar_t character) const {
00220   TextFont *font = get_font();
00221   if (font == (TextFont *)NULL) {
00222     return false;
00223   }
00224 
00225   return TextAssembler::has_exact_character(character, *this);
00226 }
00227 
00228 ////////////////////////////////////////////////////////////////////
00229 //     Function: TextNode::has_character
00230 //       Access: Published
00231 //  Description: Returns true if the named character exists in the
00232 //               font or can be synthesized by Panda, false otherwise.
00233 //               (Panda can synthesize some accented characters by
00234 //               combining similar-looking glyphs from the font.)
00235 //
00236 //               This returns true for whitespace and Unicode
00237 //               whitespace characters (if they exist in the font),
00238 //               but returns false for characters that would render
00239 //               with the "invalid glyph".
00240 ////////////////////////////////////////////////////////////////////
00241 bool TextNode::
00242 has_character(wchar_t character) const {
00243   TextFont *font = get_font();
00244   if (font == (TextFont *)NULL) {
00245     return false;
00246   }
00247 
00248   return TextAssembler::has_character(character, *this);
00249 }
00250 
00251 ////////////////////////////////////////////////////////////////////
00252 //     Function: TextNode::is_whitespace
00253 //       Access: Published
00254 //  Description: Returns true if the indicated character represents
00255 //               whitespace in the font, or false if anything visible
00256 //               will be rendered for it.
00257 //
00258 //               This returns true for whitespace and Unicode
00259 //               whitespace characters (if they exist in the font),
00260 //               and returns false for any other characters, including
00261 //               characters that do not exist in the font (these would
00262 //               be rendered with the "invalid glyph", which is
00263 //               visible).
00264 //
00265 //               Note that this function can be reliably used to
00266 //               identify Unicode whitespace characters only if the
00267 //               font has all of the whitespace characters defined.
00268 //               It will return false for any character not in the
00269 //               font, even if it is an official Unicode whitespace
00270 //               character.
00271 ////////////////////////////////////////////////////////////////////
00272 bool TextNode::
00273 is_whitespace(wchar_t character) const {
00274   TextFont *font = get_font();
00275   if (font == (TextFont *)NULL) {
00276     return false;
00277   }
00278 
00279   return TextAssembler::is_whitespace(character, *this);
00280 }
00281 
00282 ////////////////////////////////////////////////////////////////////
00283 //     Function: TextNode::calc_width
00284 //       Access: Published
00285 //  Description: Returns the width of a line of text of arbitrary
00286 //               characters.  The line should not include the newline
00287 //               character or any embedded control characters like \1
00288 //               or \3.
00289 ////////////////////////////////////////////////////////////////////
00290 PN_stdfloat TextNode::
00291 calc_width(const wstring &line) const {
00292   PN_stdfloat width = 0.0f;
00293 
00294   wstring::const_iterator si;
00295   for (si = line.begin(); si != line.end(); ++si) {
00296     width += calc_width(*si);
00297   }
00298 
00299   return width;
00300 }
00301 
00302 ////////////////////////////////////////////////////////////////////
00303 //     Function: TextNode::output
00304 //       Access: Public, Virtual
00305 //  Description: 
00306 ////////////////////////////////////////////////////////////////////
00307 void TextNode::
00308 output(ostream &out) const {
00309   PandaNode::output(out);
00310 
00311   check_rebuild();
00312   int geom_count = 0;
00313   if (_internal_geom != (PandaNode *)NULL) {
00314     geom_count = count_geoms(_internal_geom);
00315   }
00316 
00317   out << " (" << geom_count << " geoms)";
00318 }
00319 
00320 ////////////////////////////////////////////////////////////////////
00321 //     Function: TextNode::write
00322 //       Access: Published, Virtual
00323 //  Description:
00324 ////////////////////////////////////////////////////////////////////
00325 void TextNode::
00326 write(ostream &out, int indent_level) const {
00327   PandaNode::write(out, indent_level);
00328   TextProperties::write(out, indent_level + 2);
00329   indent(out, indent_level + 2)
00330     << "transform is: " << *TransformState::make_mat(_transform) << "\n";
00331   indent(out, indent_level + 2)
00332     << "in coordinate system " << _coordinate_system << "\n";
00333   indent(out, indent_level + 2)
00334     << "text is " << get_text() << "\n";
00335 }
00336 
00337 ////////////////////////////////////////////////////////////////////
00338 //     Function: TextNode::generate
00339 //       Access: Published
00340 //  Description: Generates the text, according to the parameters
00341 //               indicated within the TextNode, and returns a Node
00342 //               that may be parented within the tree to represent it.
00343 ////////////////////////////////////////////////////////////////////
00344 PT(PandaNode) TextNode::
00345 generate() {
00346   PStatTimer timer(_text_generate_pcollector);
00347   if (text_cat.is_debug()) {
00348     text_cat.debug()
00349       << "Rebuilding " << get_type() << " " << get_name()
00350       << " with '" << get_text() << "'\n";
00351   }
00352 
00353   // The strategy here will be to assemble together a bunch of
00354   // letters, instanced from the letter hierarchy of font_def, into
00355   // our own little hierarchy.
00356 
00357   // There will be one root over the whole text block, that
00358   // contains the transform passed in.  Under this root there will be
00359   // another node for each row, that moves the row into the right place
00360   // horizontally and vertically, and for each row, there is another
00361   // node for each character.
00362 
00363   _ul3d.set(0.0f, 0.0f, 0.0f);
00364   _lr3d.set(0.0f, 0.0f, 0.0f);
00365 
00366   // Now build a new sub-tree for all the text components.
00367   string name = get_text();
00368   size_t newline = name.find('\n');
00369   if (newline != string::npos) {
00370     name = name.substr(0, newline);
00371   }
00372   PT(PandaNode) root = new PandaNode(name);
00373 
00374   if (!has_text()) {
00375     return root;
00376   }
00377 
00378   TextFont *font = get_font();
00379   if (font == (TextFont *)NULL) {
00380     return root;
00381   }
00382 
00383   // Compute the overall text transform matrix.  We build the text in
00384   // a Z-up coordinate system and then convert it to whatever the user
00385   // asked for.
00386   LMatrix4 mat =
00387     LMatrix4::convert_mat(CS_zup_right, _coordinate_system) *
00388     _transform;
00389 
00390   CPT(TransformState) transform = TransformState::make_mat(mat);
00391   root->set_transform(transform);
00392 
00393   wstring wtext = get_wtext();
00394 
00395   // Assemble the text.
00396   TextAssembler assembler(this);
00397   assembler.set_properties(*this);
00398   assembler.set_max_rows(_max_rows);
00399   assembler.set_usage_hint(_usage_hint);
00400   assembler.set_dynamic_merge((_flatten_flags & FF_dynamic_merge) != 0);
00401   bool all_set = assembler.set_wtext(wtext);
00402   if (all_set) {
00403     // No overflow.
00404     _flags &= ~F_has_overflow;
00405   } else {
00406     // Overflow.
00407     _flags |= F_has_overflow;
00408   }
00409 
00410   PT(PandaNode) text_root = assembler.assemble_text();
00411   _text_ul = assembler.get_ul();
00412   _text_lr = assembler.get_lr();
00413   _num_rows = assembler.get_num_rows();
00414   _wordwrapped_wtext = assembler.get_wordwrapped_wtext();
00415 
00416   // Parent the text in.
00417   PT(PandaNode) text = new PandaNode("text");
00418   root->add_child(text, get_draw_order() + 2);
00419   text->add_child(text_root);
00420 
00421   // Save the bounding-box information about the text in a form
00422   // friendly to the user.
00423   const LVector2 &ul = assembler.get_ul();
00424   const LVector2 &lr = assembler.get_lr();
00425   _ul3d.set(ul[0], 0.0f, ul[1]);
00426   _lr3d.set(lr[0], 0.0f, lr[1]);
00427 
00428   _ul3d = _ul3d * _transform;
00429   _lr3d = _lr3d * _transform;
00430 
00431   // Incidentally, that means we don't need to measure the text now.
00432   _flags &= ~F_needs_measure;
00433 
00434   // Now flatten our hierarchy to get rid of the transforms we put in,
00435   // applying them to the vertices.
00436 
00437   NodePath root_np(root);
00438   if (_flatten_flags & FF_strong) {
00439     root_np.flatten_strong();
00440   } else if (_flatten_flags & FF_medium) {
00441     root_np.flatten_medium();
00442   } else if (_flatten_flags & FF_light) {
00443     root_np.flatten_light();
00444   }
00445 
00446   // Now deal with the decorations.
00447 
00448   if (has_card()) {
00449     PT(PandaNode) card_root;
00450     if (has_card_border()) {
00451       card_root = make_card_with_border();
00452     } else {
00453       card_root = make_card();
00454     }
00455     card_root->set_transform(transform);
00456     card_root->set_attrib(ColorAttrib::make_flat(get_card_color()));
00457     if (get_card_color()[3] != 1.0f) {
00458       card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00459     }
00460     if (has_card_texture()) {
00461       card_root->set_attrib(TextureAttrib::make(get_card_texture()));
00462     }
00463 
00464     if (has_bin()) {
00465       card_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order()));
00466     }
00467 
00468     // We always apply attribs down to the card vertices.
00469     SceneGraphReducer gr;
00470     gr.apply_attribs(card_root);
00471 
00472     // In order to decal the text onto the card, the card must
00473     // become the parent of the text.
00474     card_root->add_child(root);
00475     root = card_root;
00476 
00477     if (get_card_decal()) {
00478       card_root->set_effect(DecalEffect::make());
00479     }
00480   }
00481 
00482   if (has_frame()) {
00483     PT(PandaNode) frame_root = make_frame();
00484     frame_root->set_transform(transform);
00485     root->add_child(frame_root, get_draw_order() + 1);
00486     frame_root->set_attrib(ColorAttrib::make_flat(get_frame_color()));
00487     if (get_frame_color()[3] != 1.0f) {
00488       frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00489     }
00490 
00491     if (has_bin()) {
00492       frame_root->set_attrib(CullBinAttrib::make(get_bin(), get_draw_order() + 1));
00493     }
00494 
00495     SceneGraphReducer gr;
00496     gr.apply_attribs(frame_root);
00497   }
00498 
00499   return root;
00500 }
00501 
00502 ////////////////////////////////////////////////////////////////////
00503 //     Function: TextNode::get_internal_geom
00504 //       Access: Published
00505 //  Description: Returns the actual node that is used internally to
00506 //               render the text, if the TextNode is parented within
00507 //               the scene graph.
00508 //
00509 //               In general, you should not call this method.  Call
00510 //               generate() instead if you want to get a handle to
00511 //               geometry that represents the text.  This method is
00512 //               provided as a debugging aid only.
00513 ////////////////////////////////////////////////////////////////////
00514 PandaNode *TextNode::
00515 get_internal_geom() const {
00516   // Output a nuisance warning to discourage the naive from calling
00517   // this method accidentally.
00518   text_cat.info()
00519     << "TextNode::get_internal_geom() called.\n";
00520   check_rebuild();
00521   return _internal_geom;
00522 }
00523 
00524 ////////////////////////////////////////////////////////////////////
00525 //     Function: TextNode::get_unsafe_to_apply_attribs
00526 //       Access: Public, Virtual
00527 //  Description: Returns the union of all attributes from
00528 //               SceneGraphReducer::AttribTypes that may not safely be
00529 //               applied to the vertices of this node.  If this is
00530 //               nonzero, these attributes must be dropped at this
00531 //               node as a state change.
00532 //
00533 //               This is a generalization of safe_to_transform().
00534 ////////////////////////////////////////////////////////////////////
00535 int TextNode::
00536 get_unsafe_to_apply_attribs() const {
00537   // We have no way to apply these kinds of attributes to our
00538   // TextNode, so insist they get dropped into the PandaNode's basic
00539   // state.
00540   return 
00541     SceneGraphReducer::TT_tex_matrix | 
00542     SceneGraphReducer::TT_other;
00543 }
00544 
00545 ////////////////////////////////////////////////////////////////////
00546 //     Function: TextNode::apply_attribs_to_vertices
00547 //       Access: Public, Virtual
00548 //  Description: Applies whatever attributes are specified in the
00549 //               AccumulatedAttribs object (and by the attrib_types
00550 //               bitmask) to the vertices on this node, if
00551 //               appropriate.  If this node uses geom arrays like a
00552 //               GeomNode, the supplied GeomTransformer may be used to
00553 //               unify shared arrays across multiple different nodes.
00554 //
00555 //               This is a generalization of xform().
00556 ////////////////////////////////////////////////////////////////////
00557 void TextNode::
00558 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
00559                           GeomTransformer &transformer) {
00560   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
00561     const LMatrix4 &mat = attribs._transform->get_mat();
00562     _transform *= mat;
00563 
00564     if ((_flags & F_needs_measure) == 0) {
00565       // If we already have a measure, transform it too.  We don't
00566       // need to invalidate the 2-d parts, since that's not affected
00567       // by the transform anyway.
00568       _ul3d = _ul3d * mat;
00569       _lr3d = _lr3d * mat;
00570     }
00571   }
00572   if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
00573     if (attribs._color != (const RenderAttrib *)NULL) {
00574       const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
00575       if (ca->get_color_type() == ColorAttrib::T_flat) {
00576         const LColor &c = ca->get_color();
00577         set_text_color(c);
00578         set_frame_color(c);
00579         set_card_color(c);
00580         set_shadow_color(c);
00581       }
00582     }
00583   }
00584   if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
00585     if (attribs._color_scale != (const RenderAttrib *)NULL) {
00586       const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
00587       const LVecBase4 &s = csa->get_scale();
00588       if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
00589         LVecBase4 tc = get_text_color();
00590         tc[0] *= s[0];
00591         tc[1] *= s[1];
00592         tc[2] *= s[2];
00593         tc[3] *= s[3];
00594         set_text_color(tc);
00595         LVecBase4 sc = get_shadow_color();
00596         sc[0] *= s[0];
00597         sc[1] *= s[1];
00598         sc[2] *= s[2];
00599         sc[3] *= s[3];
00600         set_shadow_color(sc);
00601         LVecBase4 fc = get_frame_color();
00602         fc[0] *= s[0];
00603         fc[1] *= s[1];
00604         fc[2] *= s[2];
00605         fc[3] *= s[3];
00606         set_frame_color(fc);
00607         LVecBase4 cc = get_card_color();
00608         cc[0] *= s[0];
00609         cc[1] *= s[1];
00610         cc[2] *= s[2];
00611         cc[3] *= s[3];
00612         set_card_color(cc);
00613       }
00614     }
00615   }
00616 
00617   // Now propagate the attributes down to our already-generated
00618   // geometry, if we have any.
00619   if ((_flags & F_needs_rebuild) == 0 && 
00620       _internal_geom != (PandaNode *)NULL) {
00621     SceneGraphReducer gr;
00622     gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer);
00623   }
00624 }
00625 
00626 ////////////////////////////////////////////////////////////////////
00627 //     Function: TextNode::calc_tight_bounds
00628 //       Access: Public, Virtual
00629 //  Description: This is used to support
00630 //               NodePath::calc_tight_bounds().  It is not intended to
00631 //               be called directly, and it has nothing to do with the
00632 //               normal Panda bounding-volume computation.
00633 //
00634 //               If the node contains any geometry, this updates
00635 //               min_point and max_point to enclose its bounding box.
00636 //               found_any is to be set true if the node has any
00637 //               geometry at all, or left alone if it has none.  This
00638 //               method may be called over several nodes, so it may
00639 //               enter with min_point, max_point, and found_any
00640 //               already set.
00641 ////////////////////////////////////////////////////////////////////
00642 CPT(TransformState) TextNode::
00643 calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any,
00644                   const TransformState *transform, Thread *current_thread) const {
00645   CPT(TransformState) next_transform = 
00646     PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform,
00647                                  current_thread);
00648 
00649   check_rebuild();
00650 
00651   if (_internal_geom != (PandaNode *)NULL) {
00652     _internal_geom->calc_tight_bounds(min_point, max_point, 
00653                                       found_any, next_transform, current_thread);
00654   }
00655 
00656   return next_transform;
00657 }
00658 
00659 ////////////////////////////////////////////////////////////////////
00660 //     Function: TextNode::cull_callback
00661 //       Access: Protected, Virtual
00662 //  Description: This function will be called during the cull
00663 //               traversal to perform any additional operations that
00664 //               should be performed at cull time.  This may include
00665 //               additional manipulation of render state or additional
00666 //               visible/invisible decisions, or any other arbitrary
00667 //               operation.
00668 //
00669 //               Note that this function will *not* be called unless
00670 //               set_cull_callback() is called in the constructor of
00671 //               the derived class.  It is necessary to call
00672 //               set_cull_callback() to indicated that we require
00673 //               cull_callback() to be called.
00674 //
00675 //               By the time this function is called, the node has
00676 //               already passed the bounding-volume test for the
00677 //               viewing frustum, and the node's transform and state
00678 //               have already been applied to the indicated
00679 //               CullTraverserData object.
00680 //
00681 //               The return value is true if this node should be
00682 //               visible, or false if it should be culled.
00683 ////////////////////////////////////////////////////////////////////
00684 bool TextNode::
00685 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00686   check_rebuild();
00687   if (_internal_geom != (PandaNode *)NULL) {
00688     // Render the text with this node.
00689     CullTraverserData next_data(data, _internal_geom);
00690     trav->traverse(next_data);
00691   }
00692 
00693   // Now continue to render everything else below this node.
00694   return true;
00695 }
00696 
00697 ////////////////////////////////////////////////////////////////////
00698 //     Function: TextNode::is_renderable
00699 //       Access: Public, Virtual
00700 //  Description: Returns true if there is some value to visiting this
00701 //               particular node during the cull traversal for any
00702 //               camera, false otherwise.  This will be used to
00703 //               optimize the result of get_net_draw_show_mask(), so
00704 //               that any subtrees that contain only nodes for which
00705 //               is_renderable() is false need not be visited.
00706 ////////////////////////////////////////////////////////////////////
00707 bool TextNode::
00708 is_renderable() const {
00709   return true;
00710 }
00711 
00712 ////////////////////////////////////////////////////////////////////
00713 //     Function: TextNode::compute_internal_bounds
00714 //       Access: Protected, Virtual
00715 //  Description: Called when needed to recompute the node's
00716 //               _internal_bound object.  Nodes that contain anything
00717 //               of substance should redefine this to do the right
00718 //               thing.
00719 ////////////////////////////////////////////////////////////////////
00720 void TextNode::
00721 compute_internal_bounds(CPT(BoundingVolume) &internal_bounds,
00722                         int &internal_vertices,
00723                         int pipeline_stage,
00724                         Thread *current_thread) const {
00725   // First, get ourselves a fresh, empty bounding volume.
00726   PT(BoundingVolume) bound = new BoundingSphere;
00727 
00728   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
00729 
00730   // Now enclose the bounding box around the text.  We can do this
00731   // without actually generating the text, if we have at least
00732   // measured it.
00733   check_measure();
00734 
00735   LPoint3 vertices[8];
00736   vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
00737   vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
00738   vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
00739   vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
00740   vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
00741   vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
00742   vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
00743   vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
00744 
00745   gbv->around(vertices, vertices + 8);
00746 
00747   internal_bounds = bound;
00748   internal_vertices = 0;  // TODO: estimate this better.
00749 }
00750 
00751 ////////////////////////////////////////////////////////////////////
00752 //     Function: TextNode::r_prepare_scene
00753 //       Access: Protected, Virtual
00754 //  Description: The recursive implementation of prepare_scene().
00755 //               Don't call this directly; call
00756 //               PandaNode::prepare_scene() or
00757 //               NodePath::prepare_scene() instead.
00758 ////////////////////////////////////////////////////////////////////
00759 void TextNode::
00760 r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state,
00761                 GeomTransformer &transformer, Thread *current_thread) {
00762   check_rebuild();
00763 
00764   PandaNode *child = _internal_geom;
00765   if (child != (PandaNode *)NULL) {
00766     CPT(RenderState) child_state = node_state->compose(child->get_state());
00767     child->r_prepare_scene(gsg, child_state, transformer, current_thread);
00768   }
00769   
00770   PandaNode::r_prepare_scene(gsg, node_state, transformer, current_thread);
00771 }
00772 
00773 ////////////////////////////////////////////////////////////////////
00774 //     Function: TextNode::do_rebuild
00775 //       Access: Private
00776 //  Description: Removes any existing children of the TextNode, and
00777 //               adds the newly generated text instead.
00778 ////////////////////////////////////////////////////////////////////
00779 void TextNode::
00780 do_rebuild() {
00781   _flags &= ~(F_needs_rebuild | F_needs_measure);
00782   _internal_geom = generate();
00783 }
00784 
00785 
00786 ////////////////////////////////////////////////////////////////////
00787 //     Function: TextNode::do_measure
00788 //       Access: Private
00789 //  Description: Can be called in lieu of do_rebuild() to measure the
00790 //               text and set up the bounding boxes properly without
00791 //               actually assembling it.
00792 ////////////////////////////////////////////////////////////////////
00793 void TextNode::
00794 do_measure() {
00795   // We no longer make this a special case.
00796   do_rebuild();
00797 }
00798 
00799 ////////////////////////////////////////////////////////////////////
00800 //     Function: TextNode::make_frame
00801 //       Access: Private
00802 //  Description: Creates a frame around the text.
00803 ////////////////////////////////////////////////////////////////////
00804 PT(PandaNode) TextNode::
00805 make_frame() {
00806   PT(GeomNode) frame_node = new GeomNode("frame");
00807 
00808   LVector4 dimensions = get_frame_actual();
00809   PN_stdfloat left = dimensions[0];
00810   PN_stdfloat right = dimensions[1];
00811   PN_stdfloat bottom = dimensions[2];
00812   PN_stdfloat top = dimensions[3];
00813     
00814   CPT(RenderAttrib) thick = RenderModeAttrib::make(RenderModeAttrib::M_unchanged, _frame_width);
00815   CPT(RenderState) state = RenderState::make(thick);
00816 
00817   PT(GeomVertexData) vdata = new GeomVertexData
00818     ("text", GeomVertexFormat::get_v3(), get_usage_hint());
00819   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00820   
00821   vertex.add_data3(left, 0.0f, top);
00822   vertex.add_data3(left, 0.0f, bottom);
00823   vertex.add_data3(right, 0.0f, bottom);
00824   vertex.add_data3(right, 0.0f, top);
00825   
00826   PT(GeomLinestrips) frame = new GeomLinestrips(get_usage_hint());
00827   frame->add_consecutive_vertices(0, 4);
00828   frame->add_vertex(0);
00829   frame->close_primitive();
00830   
00831   PT(Geom) geom = new Geom(vdata);
00832   geom->add_primitive(frame);
00833   frame_node->add_geom(geom, state);
00834   
00835   if (get_frame_corners()) {
00836     PT(GeomPoints) corners = new GeomPoints(get_usage_hint());
00837     corners->add_consecutive_vertices(0, 4);
00838     PT(Geom) geom2 = new Geom(vdata);
00839     geom2->add_primitive(corners);
00840     frame_node->add_geom(geom2, state);
00841   }
00842 
00843   return frame_node.p();
00844 }
00845 
00846 ////////////////////////////////////////////////////////////////////
00847 //     Function: TextNode::make_card
00848 //       Access: Private
00849 //  Description: Creates a card behind the text.
00850 ////////////////////////////////////////////////////////////////////
00851 PT(PandaNode) TextNode::
00852 make_card() {
00853   PT(GeomNode) card_node = new GeomNode("card");
00854 
00855   LVector4 dimensions = get_card_actual();
00856   PN_stdfloat left = dimensions[0];
00857   PN_stdfloat right = dimensions[1];
00858   PN_stdfloat bottom = dimensions[2];
00859   PN_stdfloat top = dimensions[3];
00860 
00861   PT(GeomVertexData) vdata = new GeomVertexData
00862     ("text", GeomVertexFormat::get_v3t2(), get_usage_hint());
00863   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00864   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00865   
00866   vertex.add_data3(left, 0.0f, top);
00867   vertex.add_data3(left, 0.0f, bottom);
00868   vertex.add_data3(right, 0.0f, top);
00869   vertex.add_data3(right, 0.0f, bottom);
00870   
00871   texcoord.add_data2(0.0f, 1.0f);
00872   texcoord.add_data2(0.0f, 0.0f);
00873   texcoord.add_data2(1.0f, 1.0f);
00874   texcoord.add_data2(1.0f, 0.0f);
00875   
00876   PT(GeomTristrips) card = new GeomTristrips(get_usage_hint());
00877   card->add_consecutive_vertices(0, 4);
00878   card->close_primitive();
00879   
00880   PT(Geom) geom = new Geom(vdata);
00881   geom->add_primitive(card);
00882   
00883   card_node->add_geom(geom);
00884 
00885   return card_node.p();
00886 }
00887 
00888 
00889 ////////////////////////////////////////////////////////////////////
00890 //     Function: TextNode::make_card_with_border
00891 //       Access: Private
00892 //  Description: Creates a card behind the text with a specified border
00893 //               for button edge or what have you.
00894 ////////////////////////////////////////////////////////////////////
00895 PT(PandaNode) TextNode::
00896 make_card_with_border() {
00897   PT(GeomNode) card_node = new GeomNode("card");
00898 
00899   LVector4 dimensions = get_card_actual();
00900   PN_stdfloat left = dimensions[0];
00901   PN_stdfloat right = dimensions[1];
00902   PN_stdfloat bottom = dimensions[2];
00903   PN_stdfloat top = dimensions[3];
00904 
00905   // we now create three tri-strips instead of one
00906   // with vertices arranged as follows:
00907   //
00908   //  1 3            5 7  - one
00909   //  2 4            6 8  /  \ two
00910   //  9 11          13 15 \  /
00911   // 10 12          14 16 - three
00912   //
00913 
00914   PT(GeomVertexData) vdata = new GeomVertexData
00915     ("text", GeomVertexFormat::get_v3t2(), get_usage_hint());
00916   GeomVertexWriter vertex(vdata, InternalName::get_vertex());
00917   GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
00918   
00919   // verts 1,2,3,4
00920   vertex.add_data3(left, 0.02, top);
00921   vertex.add_data3(left, 0.02, top - _card_border_size);
00922   vertex.add_data3(left + _card_border_size, 0.02, top);
00923   vertex.add_data3(left + _card_border_size, 0.02,
00924                     top - _card_border_size);
00925   // verts 5,6,7,8
00926   vertex.add_data3(right - _card_border_size, 0.02, top);
00927   vertex.add_data3(right - _card_border_size, 0.02,
00928                     top - _card_border_size);
00929   vertex.add_data3(right, 0.02, top);
00930   vertex.add_data3(right, 0.02, top - _card_border_size);
00931   // verts 9,10,11,12
00932   vertex.add_data3(left, 0.02, bottom + _card_border_size);
00933   vertex.add_data3(left, 0.02, bottom);
00934   vertex.add_data3(left + _card_border_size, 0.02,
00935                     bottom + _card_border_size);
00936   vertex.add_data3(left + _card_border_size, 0.02, bottom);
00937   // verts 13,14,15,16
00938   vertex.add_data3(right - _card_border_size, 0.02,
00939                     bottom + _card_border_size);
00940   vertex.add_data3(right - _card_border_size, 0.02, bottom);
00941   vertex.add_data3(right, 0.02, bottom + _card_border_size);
00942   vertex.add_data3(right, 0.02, bottom);
00943   
00944   texcoord.add_data2(0.0f, 1.0f); //1
00945   texcoord.add_data2(0.0f, 1.0f - _card_border_uv_portion); //2
00946   texcoord.add_data2(0.0f + _card_border_uv_portion, 1.0f); //3
00947   texcoord.add_data2(0.0f + _card_border_uv_portion,
00948                       1.0f - _card_border_uv_portion); //4
00949   texcoord.add_data2(1.0f -_card_border_uv_portion, 1.0f); //5
00950   texcoord.add_data2(1.0f -_card_border_uv_portion,
00951                       1.0f - _card_border_uv_portion); //6
00952   texcoord.add_data2(1.0f, 1.0f); //7
00953   texcoord.add_data2(1.0f, 1.0f - _card_border_uv_portion); //8
00954   
00955   texcoord.add_data2(0.0f, _card_border_uv_portion); //9
00956   texcoord.add_data2(0.0f, 0.0f); //10
00957   texcoord.add_data2(_card_border_uv_portion, _card_border_uv_portion); //11
00958   texcoord.add_data2(_card_border_uv_portion, 0.0f); //12
00959 
00960   texcoord.add_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);//13
00961   texcoord.add_data2(1.0f - _card_border_uv_portion, 0.0f);//14
00962   texcoord.add_data2(1.0f, _card_border_uv_portion);//15
00963   texcoord.add_data2(1.0f, 0.0f);//16
00964   
00965   PT(GeomTristrips) card = new GeomTristrips(get_usage_hint());
00966   
00967   // tristrip #1
00968   card->add_consecutive_vertices(0, 8);
00969   card->close_primitive();
00970   
00971   // tristrip #2
00972   card->add_vertex(1);
00973   card->add_vertex(8);
00974   card->add_vertex(3);
00975   card->add_vertex(10);
00976   card->add_vertex(5);
00977   card->add_vertex(12);
00978   card->add_vertex(7);
00979   card->add_vertex(14);
00980   card->close_primitive();
00981   
00982   // tristrip #3
00983   card->add_consecutive_vertices(8, 8);
00984   card->close_primitive();
00985   
00986   PT(Geom) geom = new Geom(vdata);
00987   geom->add_primitive(card);
00988   
00989   card_node->add_geom(geom);
00990   
00991   return card_node.p();
00992 }
00993 
00994 ////////////////////////////////////////////////////////////////////
00995 //     Function: TextNode::count_geoms
00996 //       Access: Private, Static
00997 //  Description: Recursively counts the number of Geoms at the
00998 //               indicated node and below.  Strictly for reporting
00999 //               this count on output.
01000 ////////////////////////////////////////////////////////////////////
01001 int TextNode::
01002 count_geoms(PandaNode *node) {
01003   int num_geoms = 0;
01004 
01005   if (node->is_geom_node()) {
01006     GeomNode *geom_node = DCAST(GeomNode, node);
01007     num_geoms += geom_node->get_num_geoms();
01008   }
01009 
01010   Children children = node->get_children();
01011   for (int i = 0; i < children.get_num_children(); ++i) {
01012     num_geoms += count_geoms(children.get_child(i));
01013   }
01014 
01015   return num_geoms;
01016 }
 All Classes Functions Variables Enumerations