Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/text/textNode.cxx

Go to the documentation of this file.
00001 // Filename: textNode.cxx
00002 // Created by:  drose (13Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "textNode.h"
00020 #include "textGlyph.h"
00021 #include "stringDecoder.h"
00022 #include "config_text.h"
00023 #include "fontPool.h"
00024 #include "default_font.h"
00025 #include "dynamicTextFont.h"
00026 #include "unicodeLatinMap.h"
00027 
00028 #include "compose_matrix.h"
00029 #include "geom.h"
00030 #include "geomTristrip.h"
00031 #include "geomLinestrip.h"
00032 #include "geomPoint.h"
00033 #include "geomNode.h"
00034 #include "notify.h"
00035 #include "transformState.h"
00036 #include "colorAttrib.h"
00037 #include "colorScaleAttrib.h"
00038 #include "cullBinAttrib.h"
00039 #include "textureAttrib.h"
00040 #include "transparencyAttrib.h"
00041 #include "sceneGraphReducer.h"
00042 #include "indent.h"
00043 #include "cullTraverser.h"
00044 #include "cullTraverserData.h"
00045 #include "geometricBoundingVolume.h"
00046 #include "accumulatedAttribs.h"
00047 #include "renderState.h"
00048 #include "cullFaceAttrib.h"
00049 #include "dcast.h"
00050 
00051 #include <stdio.h>
00052 #include <ctype.h>
00053 
00054 TypeHandle TextNode::_type_handle;
00055 
00056 PT(TextFont) TextNode::_default_font;
00057 bool TextNode::_loaded_default_font = false;
00058 
00059 // This is the factor by which CT_small scales the character down.
00060 static const float small_accent_scale = 0.6f;
00061 
00062 // This is the factor by which CT_tiny scales the character down.
00063 static const float tiny_accent_scale = 0.4f;
00064 
00065 // This is the factor by which CT_squash scales the character in X and Y.
00066 static const float squash_accent_scale_x = 0.8f;
00067 static const float squash_accent_scale_y = 0.5f;
00068 
00069 // This is the factor by which CT_small_squash scales the character in X and Y.
00070 static const float small_squash_accent_scale_x = 0.6f;
00071 static const float small_squash_accent_scale_y = 0.3f;
00072 
00073 // This is the factor by which the advance is reduced for the first
00074 // character of a two-character ligature.
00075 static const float ligature_advance_scale = 0.6f;
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: TextNode::Constructor
00079 //       Access: Published
00080 //  Description:
00081 ////////////////////////////////////////////////////////////////////
00082 TextNode::
00083 TextNode(const string &name) : PandaNode(name) {
00084   _slant = 0.0f;
00085   
00086   _flags = 0;
00087   _align = A_left;
00088   _wordwrap_width = 1.0f;
00089 
00090   if (text_small_caps) {
00091     _flags |= F_small_caps;
00092   }
00093   _small_caps_scale = text_small_caps_scale;
00094 
00095   _text_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00096   _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00097   _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00098   _shadow_color.set(1.0f, 1.0f, 1.0f, 1.0f);
00099 
00100   _frame_width = 1.0f;
00101 
00102   _frame_ul.set(0.0f, 0.0f);
00103   _frame_lr.set(0.0f, 0.0f);
00104   _card_ul.set(0.0f, 0.0f);
00105   _card_lr.set(0.0f, 0.0f);
00106   _shadow_offset.set(0.0f, 0.0f);
00107 
00108   _draw_order = 1;
00109 
00110   _transform = LMatrix4f::ident_mat();
00111   _coordinate_system = CS_default;
00112 
00113   _ul2d.set(0.0f, 0.0f);
00114   _lr2d.set(0.0f, 0.0f);
00115   _ul3d.set(0.0f, 0.0f, 0.0f);
00116   _lr3d.set(0.0f, 0.0f, 0.0f);
00117   _num_rows = 0;
00118 }
00119 
00120 ////////////////////////////////////////////////////////////////////
00121 //     Function: TextNode::Destructor
00122 //       Access: Published
00123 //  Description:
00124 ////////////////////////////////////////////////////////////////////
00125 TextNode::
00126 ~TextNode() {
00127 }
00128 
00129 ////////////////////////////////////////////////////////////////////
00130 //     Function: TextNode::wordwrap_to
00131 //       Access: Published
00132 //  Description: Inserts newlines into the given text at the
00133 //               appropriate places in order to make each line be the
00134 //               longest possible line that is not longer than
00135 //               wordwrap_width (and does not break any words, if
00136 //               possible).  Returns the new string.
00137 ////////////////////////////////////////////////////////////////////
00138 string TextNode::
00139 wordwrap_to(const string &text, float wordwrap_width,
00140             bool preserve_trailing_whitespace) const {
00141   nassertr(_font != (TextFont *)NULL, text);
00142   wstring decoded = decode_text(text);
00143   wstring wrapped = 
00144     _font->wordwrap_to(decoded, wordwrap_width, preserve_trailing_whitespace);
00145   return encode_wtext(wrapped);
00146 }
00147 
00148 
00149 ////////////////////////////////////////////////////////////////////
00150 //     Function: TextNode::write
00151 //       Access: Published, Virtual
00152 //  Description:
00153 ////////////////////////////////////////////////////////////////////
00154 void TextNode::
00155 write(ostream &out, int indent_level) const {
00156   indent(out, indent_level)
00157     << "TextNode " << get_name() << "\n";
00158   if (_font != (TextFont *)NULL) {
00159     indent(out, indent_level + 2)
00160       << "with font " << _font->get_name() << "\n";
00161   }
00162   if (has_text_color()) {
00163     indent(out, indent_level + 2)
00164       << "text color is " << _text_color << "\n";
00165   } else {
00166     indent(out, indent_level + 2)
00167       << "text color is unchanged from source\n";
00168   }
00169   indent(out, indent_level + 2)
00170     << "alignment is ";
00171   switch (_align) {
00172   case A_left:
00173     out << "A_left\n";
00174     break;
00175 
00176   case A_right:
00177     out << "A_right\n";
00178     break;
00179 
00180   case A_center:
00181     out << "A_center\n";
00182     break;
00183   }
00184 
00185   if (has_wordwrap()) {
00186     indent(out, indent_level + 2)
00187       << "Word-wrapping at " << _wordwrap_width << " units.\n";
00188   }
00189 
00190   if (has_frame()) {
00191     indent(out, indent_level + 2)
00192       << "frame of color " << _frame_color << " at "
00193       << get_frame_as_set() << " line width " << _frame_width << "\n";
00194     if (get_frame_corners()) {
00195       indent(out, indent_level + 2)
00196         << "frame corners are enabled\n";
00197     }
00198     if (is_frame_as_margin()) {
00199       indent(out, indent_level + 2)
00200         << "frame coordinates are specified as margin; actual frame is:\n"
00201         << get_frame_actual() << "\n";
00202     } else {
00203       indent(out, indent_level + 2)
00204         << "frame coordinates are actual\n";
00205     }
00206   }
00207   if (has_card()) {
00208     indent(out, indent_level + 2)
00209       << "card of color " << _card_color << " at "
00210       << get_card_as_set() << "\n";
00211     if (is_card_as_margin()) {
00212       indent(out, indent_level + 2)
00213         << "card coordinates are specified as margin; actual card is:\n"
00214         << get_card_actual() << "\n";
00215     } else {
00216       indent(out, indent_level + 2)
00217         << "card coordinates are actual\n";
00218     }
00219   }
00220   if (has_shadow()) {
00221     indent(out, indent_level + 2)
00222       << "shadow of color " << _shadow_color << " at "
00223       << _shadow_offset << "\n";
00224   }
00225   if (has_bin()) {
00226     indent(out, indent_level + 2)
00227       << "bin is " << _bin << "\n";
00228   }
00229   indent(out, indent_level + 2)
00230     << "draw order is " << _draw_order << ", "
00231     << _draw_order + 1 << ", " << _draw_order + 2 << "\n";
00232 
00233   LVecBase3f scale, hpr, trans;
00234   if (decompose_matrix(_transform, scale, hpr, trans, _coordinate_system)) {
00235   indent(out, indent_level + 2)
00236     << "transform is:\n"
00237     << "  scale: " << scale << "\n"
00238     << "    hpr: " << hpr << "\n"
00239     << "  trans: " << hpr << "\n";
00240   } else {
00241     indent(out, indent_level + 2)
00242       << "transform is:\n" << _transform;
00243   }
00244   indent(out, indent_level + 2)
00245     << "in coordinate system " << _coordinate_system << "\n";
00246 
00247   indent(out, indent_level + 2)
00248     << "\ntext is " << get_text() << "\n";
00249 }
00250 
00251 ////////////////////////////////////////////////////////////////////
00252 //     Function: TextNode::generate
00253 //       Access: Published
00254 //  Description: Generates the text, according to the parameters
00255 //               indicated within the TextNode, and returns a Node
00256 //               that may be parented within the tree to represent it.
00257 ////////////////////////////////////////////////////////////////////
00258 PT(PandaNode) TextNode::
00259 generate() {
00260   if (text_cat.is_debug()) {
00261     text_cat.debug()
00262       << "Rebuilding " << *this << " with '" << get_text() << "'\n";
00263   }
00264 
00265   // The strategy here will be to assemble together a bunch of
00266   // letters, instanced from the letter hierarchy of font_def, into
00267   // our own little hierarchy.
00268 
00269   // There will be one root over the whole text block, that
00270   // contains the transform passed in.  Under this root there will be
00271   // another node for each row, that moves the row into the right place
00272   // horizontally and vertically, and for each row, there is another
00273   // node for each character.
00274 
00275   _ul2d.set(0.0f, 0.0f);
00276   _lr2d.set(0.0f, 0.0f);
00277   _ul3d.set(0.0f, 0.0f, 0.0f);
00278   _lr3d.set(0.0f, 0.0f, 0.0f);
00279   _num_rows = 0;
00280 
00281   // Now build a new sub-tree for all the text components.
00282   PT(PandaNode) root = new PandaNode(get_text());
00283 
00284   if (!has_text()) {
00285     return root;
00286   }
00287 
00288   TextFont *font = get_font();
00289   if (font == (TextFont *)NULL) {
00290     font = get_default_font();
00291   }
00292 
00293   if (font == (TextFont *)NULL) {
00294     return root;
00295   }
00296 
00297   // Compute the overall text transform matrix.  We build the text in
00298   // a Z-up coordinate system and then convert it to whatever the user
00299   // asked for.
00300   LMatrix4f mat =
00301     LMatrix4f::convert_mat(CS_zup_right, _coordinate_system) *
00302     _transform;
00303 
00304   root->set_transform(TransformState::make_mat(mat));
00305 
00306   wstring wtext = get_wtext();
00307   if (has_wordwrap()) {
00308     wtext = font->wordwrap_to(wtext, _wordwrap_width, false);
00309   }
00310 
00311   // Assemble the text.
00312   LVector2f ul, lr;
00313   int num_rows = 0;
00314   PT(PandaNode) text_root = 
00315     assemble_text(wtext.begin(), wtext.end(), font, 
00316                   ul, lr, num_rows);
00317 
00318   // Parent the text in.  We create an intermediate node so we can
00319   // choose to reinstance the text_root as the shadow, below.
00320   PT(PandaNode) text = new PandaNode("text");
00321   root->add_child(text, _draw_order + 2);
00322   text->add_child(text_root);
00323 
00324   if (has_text_color()) {
00325     text->set_attrib(ColorAttrib::make_flat(_text_color));
00326     if (_text_color[3] != 1.0) {
00327       text->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00328     }
00329   }
00330 
00331   if (has_bin()) {
00332     text->set_attrib(CullBinAttrib::make(_bin, _draw_order + 2));
00333   }
00334 
00335   // Save the bounding-box information about the text in a form
00336   // friendly to the user.
00337   _num_rows = num_rows;
00338   _ul2d = ul;
00339   _lr2d = lr;
00340   _ul3d.set(ul[0], 0.0f, ul[1]);
00341   _lr3d.set(lr[0], 0.0f, lr[1]);
00342 
00343   _ul3d = _ul3d * _transform;
00344   _lr3d = _lr3d * _transform;
00345 
00346   // Incidentally, that means we don't need to measure the text now.
00347   _flags &= ~F_needs_measure;
00348 
00349 
00350   // Now deal with all the decorations.
00351 
00352   if (has_shadow()) {
00353     // Make a shadow by instancing the text behind itself.
00354 
00355     // For now, the depth offset is 0.0 because we don't expect to see
00356     // text with shadows in the 3-d world that aren't decals.  Maybe
00357     // this will need to be addressed in the future.
00358 
00359     LMatrix4f offset =
00360       LMatrix4f::translate_mat(_shadow_offset[0], 0.0f, -_shadow_offset[1]);
00361     PT(PandaNode) shadow = new PandaNode("shadow");
00362     root->add_child(shadow, _draw_order + 1);
00363     shadow->add_child(text_root);
00364     shadow->set_transform(TransformState::make_mat(offset));
00365     shadow->set_attrib(ColorAttrib::make_flat(_shadow_color));
00366 
00367     if (_shadow_color[3] != 1.0f) {
00368       shadow->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00369     }
00370 
00371     if (has_bin()) {
00372       shadow->set_attrib(CullBinAttrib::make(_bin, _draw_order + 1));
00373     }
00374   }
00375 
00376   if (has_frame()) {
00377     PT(PandaNode) frame_root = make_frame();
00378     root->add_child(frame_root, _draw_order + 1);
00379     frame_root->set_attrib(ColorAttrib::make_flat(_frame_color));
00380     if (_frame_color[3] != 1.0f) {
00381       frame_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00382     }
00383 
00384     if (has_bin()) {
00385       frame_root->set_attrib(CullBinAttrib::make(_bin, _draw_order + 1));
00386     }
00387   }
00388 
00389   if (has_card()) {
00390     PT(PandaNode) card_root;
00391     if (has_card_border())
00392       card_root = make_card_with_border();
00393     else
00394       card_root = make_card();
00395     root->add_child(card_root, _draw_order);
00396     card_root->set_attrib(ColorAttrib::make_flat(_card_color));
00397     if (_card_color[3] != 1.0f) {
00398       card_root->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));
00399     }
00400     if (has_card_texture()) {
00401       card_root->set_attrib(TextureAttrib::make(_card_texture));
00402     }
00403 
00404     if (has_bin()) {
00405       card_root->set_attrib(CullBinAttrib::make(_bin, _draw_order));
00406     }
00407   }
00408 
00409   // Now flatten our hierarchy to get rid of the transforms we put in,
00410   // applying them to the vertices.
00411 
00412   if (text_flatten) {
00413     SceneGraphReducer gr;
00414     gr.apply_attribs(root);
00415     gr.flatten(root, true);
00416   }
00417 
00418   return root;
00419 }
00420 
00421 ////////////////////////////////////////////////////////////////////
00422 //     Function: TextNode::get_unsafe_to_apply_attribs
00423 //       Access: Public, Virtual
00424 //  Description: Returns the union of all attributes from
00425 //               SceneGraphReducer::AttribTypes that may not safely be
00426 //               applied to the vertices of this node.  If this is
00427 //               nonzero, these attributes must be dropped at this
00428 //               node as a state change.
00429 //
00430 //               This is a generalization of safe_to_transform().
00431 ////////////////////////////////////////////////////////////////////
00432 int TextNode::
00433 get_unsafe_to_apply_attribs() const {
00434   // We have no way to apply these kinds of attributes to our
00435   // TextNode, so insist they get dropped into the PandaNode's basic
00436   // state.
00437   return 
00438     SceneGraphReducer::TT_tex_matrix | 
00439     SceneGraphReducer::TT_other;
00440 }
00441 
00442 ////////////////////////////////////////////////////////////////////
00443 //     Function: TextNode::apply_attribs_to_vertices
00444 //       Access: Public, Virtual
00445 //  Description: Applies whatever attributes are specified in the
00446 //               AccumulatedAttribs object (and by the attrib_types
00447 //               bitmask) to the vertices on this node, if
00448 //               appropriate.  If this node uses geom arrays like a
00449 //               GeomNode, the supplied GeomTransformer may be used to
00450 //               unify shared arrays across multiple different nodes.
00451 //
00452 //               This is a generalization of xform().
00453 ////////////////////////////////////////////////////////////////////
00454 void TextNode::
00455 apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
00456                           GeomTransformer &transformer) {
00457   if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
00458     const LMatrix4f &mat = attribs._transform->get_mat();
00459     _transform *= mat;
00460 
00461     if ((_flags & F_needs_measure) == 0) {
00462       // If we already have a measure, transform it too.  We don't
00463       // need to invalidate the 2-d parts, since that's not affected
00464       // by the transform anyway.
00465       _ul3d = _ul3d * mat;
00466       _lr3d = _lr3d * mat;
00467     }
00468   }
00469   if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
00470     if (attribs._color != (const RenderAttrib *)NULL) {
00471       const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
00472       if (ca->get_color_type() == ColorAttrib::T_flat) {
00473         const Colorf &c = ca->get_color();
00474         _text_color = c;
00475         _frame_color = c;
00476         _card_color = c;
00477         _shadow_color = c;
00478         _flags |= F_has_text_color;
00479       }
00480     }
00481   }
00482   if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
00483     if (attribs._color_scale != (const RenderAttrib *)NULL) {
00484       const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
00485       const LVecBase4f &s = csa->get_scale();
00486       if (s != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)) {
00487         _text_color[0] *= s[0];
00488         _text_color[1] *= s[1];
00489         _text_color[2] *= s[2];
00490         _text_color[3] *= s[3];
00491         _frame_color[0] *= s[0];
00492         _frame_color[1] *= s[1];
00493         _frame_color[2] *= s[2];
00494         _frame_color[3] *= s[3];
00495         _card_color[0] *= s[0];
00496         _card_color[1] *= s[1];
00497         _card_color[2] *= s[2];
00498         _card_color[3] *= s[3];
00499         _shadow_color[0] *= s[0];
00500         _shadow_color[1] *= s[1];
00501         _shadow_color[2] *= s[2];
00502         _shadow_color[3] *= s[3];
00503       }
00504     }
00505   }
00506 
00507   // Now propagate the attributes down to our already-generated
00508   // geometry, if we have any.
00509   if ((_flags & F_needs_rebuild) == 0 && 
00510       _internal_geom != (PandaNode *)NULL) {
00511     SceneGraphReducer gr;
00512     gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer);
00513   }
00514 }
00515 
00516 ////////////////////////////////////////////////////////////////////
00517 //     Function: TextNode::calc_tight_bounds
00518 //       Access: Public, Virtual
00519 //  Description: This is used to support
00520 //               NodePath::calc_tight_bounds().  It is not intended to
00521 //               be called directly, and it has nothing to do with the
00522 //               normal Panda bounding-volume computation.
00523 //
00524 //               If the node contains any geometry, this updates
00525 //               min_point and max_point to enclose its bounding box.
00526 //               found_any is to be set true if the node has any
00527 //               geometry at all, or left alone if it has none.  This
00528 //               method may be called over several nodes, so it may
00529 //               enter with min_point, max_point, and found_any
00530 //               already set.
00531 ////////////////////////////////////////////////////////////////////
00532 CPT(TransformState) TextNode::
00533 calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
00534                   const TransformState *transform) const {
00535   CPT(TransformState) next_transform = 
00536     PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform);
00537 
00538   check_rebuild();
00539 
00540   if (_internal_geom != (PandaNode *)NULL) {
00541     _internal_geom->calc_tight_bounds(min_point, max_point, 
00542                                       found_any, next_transform);
00543   }
00544 
00545   return next_transform;
00546 }
00547 
00548 ////////////////////////////////////////////////////////////////////
00549 //     Function: TextNode::has_cull_callback
00550 //       Access: Protected, Virtual
00551 //  Description: Should be overridden by derived classes to return
00552 //               true if cull_callback() has been defined.  Otherwise,
00553 //               returns false to indicate cull_callback() does not
00554 //               need to be called for this node during the cull
00555 //               traversal.
00556 ////////////////////////////////////////////////////////////////////
00557 bool TextNode::
00558 has_cull_callback() const {
00559   return true;
00560 }
00561 
00562 ////////////////////////////////////////////////////////////////////
00563 //     Function: TextNode::cull_callback
00564 //       Access: Protected, Virtual
00565 //  Description: If has_cull_callback() returns true, this function
00566 //               will be called during the cull traversal to perform
00567 //               any additional operations that should be performed at
00568 //               cull time.  This may include additional manipulation
00569 //               of render state or additional visible/invisible
00570 //               decisions, or any other arbitrary operation.
00571 //
00572 //               By the time this function is called, the node has
00573 //               already passed the bounding-volume test for the
00574 //               viewing frustum, and the node's transform and state
00575 //               have already been applied to the indicated
00576 //               CullTraverserData object.
00577 //
00578 //               The return value is true if this node should be
00579 //               visible, or false if it should be culled.
00580 ////////////////////////////////////////////////////////////////////
00581 bool TextNode::
00582 cull_callback(CullTraverser *trav, CullTraverserData &data) {
00583   check_rebuild();
00584   if (_internal_geom != (PandaNode *)NULL) {
00585     // Render the text with this node.
00586     CullTraverserData next_data(data, _internal_geom);
00587     trav->traverse(next_data);
00588   }
00589 
00590   // Now continue to render everything else below this node.
00591   return true;
00592 }
00593 
00594 ////////////////////////////////////////////////////////////////////
00595 //     Function: TextNode::recompute_internal_bound
00596 //       Access: Protected, Virtual
00597 //  Description: Called when needed to recompute the node's
00598 //               _internal_bound object.  Nodes that contain anything
00599 //               of substance should redefine this to do the right
00600 //               thing.
00601 ////////////////////////////////////////////////////////////////////
00602 BoundingVolume *TextNode::
00603 recompute_internal_bound() {
00604   // First, get ourselves a fresh, empty bounding volume.
00605   BoundingVolume *bound = PandaNode::recompute_internal_bound();
00606   nassertr(bound != (BoundingVolume *)NULL, bound);
00607 
00608   GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
00609 
00610   // Now enclose the bounding box around the text.  We can do this
00611   // without actually generating the text, if we have at least
00612   // measured it.
00613   check_measure();
00614 
00615   LPoint3f vertices[8];
00616   vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
00617   vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
00618   vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
00619   vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
00620   vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
00621   vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
00622   vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
00623   vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
00624 
00625   gbv->around(vertices, vertices + 8);
00626 
00627   return bound;
00628 }
00629 
00630 ////////////////////////////////////////////////////////////////////
00631 //     Function: TextNode::do_rebuild
00632 //       Access: Private
00633 //  Description: Removes any existing children of the TextNode, and
00634 //               adds the newly generated text instead.
00635 ////////////////////////////////////////////////////////////////////
00636 void TextNode::
00637 do_rebuild() {
00638   _flags &= ~(F_needs_rebuild | F_needs_measure);
00639   _internal_geom = generate();
00640 }
00641 
00642 
00643 ////////////////////////////////////////////////////////////////////
00644 //     Function: TextNode::do_measure
00645 //       Access: Private
00646 //  Description: Can be called in lieu of do_rebuild() to measure the
00647 //               text and set up the bounding boxes properly without
00648 //               actually assembling it.
00649 ////////////////////////////////////////////////////////////////////
00650 void TextNode::
00651 do_measure() {
00652   _flags &= ~F_needs_measure;
00653 
00654   _ul2d.set(0.0f, 0.0f);
00655   _lr2d.set(0.0f, 0.0f);
00656   _ul3d.set(0.0f, 0.0f, 0.0f);
00657   _lr3d.set(0.0f, 0.0f, 0.0f);
00658   _num_rows = 0;
00659 
00660   if (!has_text()) {
00661     return;
00662   }
00663 
00664   TextFont *font = get_font();
00665   if (font == (TextFont *)NULL) {
00666     font = get_default_font();
00667   }
00668 
00669   if (font == (TextFont *)NULL) {
00670     return;
00671   }
00672 
00673   wstring wtext = get_wtext();
00674   if (has_wordwrap()) {
00675     wtext = font->wordwrap_to(wtext, _wordwrap_width, false);
00676   }
00677 
00678   LVector2f ul, lr;
00679   int num_rows = 0;
00680   measure_text(wtext.begin(), wtext.end(), font,
00681                ul, lr, num_rows);
00682 
00683   _num_rows = num_rows;
00684   _ul2d = ul;
00685   _lr2d = lr;
00686   _ul3d.set(ul[0], 0.0f, ul[1]);
00687   _lr3d.set(lr[0], 0.0f, lr[1]);
00688 
00689   _ul3d = _ul3d * _transform;
00690   _lr3d = _lr3d * _transform;
00691 }
00692   
00693   
00694 #ifndef CPPPARSER  // interrogate has a bit of trouble with wstring.
00695 
00696 ////////////////////////////////////////////////////////////////////
00697 //     Function: TextNode::assemble_row
00698 //       Access: Private
00699 //  Description: Assembles the letters in the source string, up until
00700 //               the first newline or the end of the string into a
00701 //               single row (which is parented to 'dest'), and returns
00702 //               the length of the row.  The source pointer is moved
00703 //               to the terminating character.
00704 ////////////////////////////////////////////////////////////////////
00705 float TextNode::
00706 assemble_row(wstring::iterator &si, const wstring::iterator &send, 
00707              TextFont *font, GeomNode *dest, const LMatrix4f &mat) {
00708   float xpos = 0.0f;
00709   while (si != send && (*si) != '\n') {
00710     wchar_t character = *si;
00711 
00712     if (character == ' ') {
00713       // A space is a special case.
00714       xpos += font->get_space_advance();
00715 
00716     } else {
00717       // A printable character.
00718       bool got_glyph;
00719       const TextGlyph *glyph;
00720       const TextGlyph *second_glyph;
00721       UnicodeLatinMap::AccentType accent_type;
00722       int additional_flags;
00723       float glyph_scale;
00724       float advance_scale;
00725       get_character_glyphs(character, font, 
00726                            got_glyph, glyph, second_glyph, accent_type,
00727                            additional_flags, glyph_scale, advance_scale);
00728 
00729       if (!got_glyph) {
00730         text_cat.warning()
00731           << "No definition in " << font->get_name() 
00732           << " for character " << character;
00733         if (character < 128 && isprint((unsigned int)character)) {
00734           text_cat.warning(false)
00735             << " ('" << (char)character << "')";
00736         }
00737         text_cat.warning(false)
00738           << "\n";
00739       }
00740 
00741       // Build up a temporary array of the Geoms that go into this
00742       // character.  Normally, there is only one Geom per character,
00743       // but it may involve multiple Geoms if we need to add cheesy
00744       // accents or ligatures.
00745       static const int max_geoms = 10;
00746       Geom *geom_array[max_geoms];
00747       int num_geoms = 0;
00748       int gi;
00749 
00750       float advance = 0.0f;
00751 
00752       if (glyph != (TextGlyph *)NULL) {
00753         PT(Geom) char_geom = glyph->get_geom();
00754         if (char_geom != (Geom *)NULL) {
00755           dest->add_geom(char_geom, glyph->get_state());
00756           geom_array[num_geoms++] = char_geom;
00757         }
00758         advance = glyph->get_advance() * advance_scale;
00759       }
00760       if (second_glyph != (TextGlyph *)NULL) {
00761         PT(Geom) second_char_geom = second_glyph->get_geom();
00762         if (second_char_geom != (Geom *)NULL) {
00763           second_char_geom->transform_vertices(LMatrix4f::translate_mat(advance, 0.0f, 0.0f));
00764           dest->add_geom(second_char_geom, second_glyph->get_state());
00765           geom_array[num_geoms++] = second_char_geom;
00766         }
00767         advance += second_glyph->get_advance();
00768       }
00769 
00770       // Now compute the matrix that will transform the glyph (or
00771       // glyphs) into position.
00772       LMatrix4f glyph_xform = LMatrix4f::scale_mat(glyph_scale);
00773 
00774       if (accent_type != UnicodeLatinMap::AT_none || additional_flags != 0) {
00775         // If we have some special handling to perform, do so now.
00776         // This will probably require the bounding volume of the
00777         // glyph, so go get that.
00778         LPoint3f min_vert, max_vert;
00779         bool found_any = false;
00780         for (gi = 0; gi < num_geoms; gi++) {
00781           geom_array[gi]->calc_tight_bounds(min_vert, max_vert, found_any);
00782         }
00783 
00784         if (found_any) {
00785           LPoint3f centroid = (min_vert + max_vert) / 2.0f;
00786           tack_on_accent(accent_type, min_vert, max_vert, centroid, 
00787                          font, dest, geom_array, num_geoms);
00788     
00789           if ((additional_flags & UnicodeLatinMap::AF_turned) != 0) {
00790             // Invert the character.  Should we also invert the accent
00791             // mark, so that an accent that would have been above the
00792             // glyph will now be below it?  That's what we do here,
00793             // which is probably the right thing to do for n-tilde,
00794             // but not for most of the rest of the accent marks.  For
00795             // now we'll assume there are no characters with accent
00796             // marks that also have the turned flag.
00797 
00798             // We rotate the character around its centroid, which may
00799             // not always be the right point, but it's the best we've
00800             // got and it's probably pretty close.
00801             LMatrix4f rotate =
00802               LMatrix4f::translate_mat(-centroid) *
00803               LMatrix4f::rotate_mat_normaxis(180.0f, LVecBase3f(0.0f, -1.0f, 0.0f)) *
00804               LMatrix4f::translate_mat(centroid);
00805             glyph_xform *= rotate;
00806           }
00807         }
00808       }
00809 
00810       glyph_xform(3, 0) += xpos;
00811       LMatrix4f net_xform = glyph_xform * mat;
00812 
00813       // Finally, transform all the Geoms for this character into
00814       // place.  Again, normally there is only one Geom per character;
00815       // there will only be multiple Geoms if we have added accents or
00816       // ligatures.
00817       for (gi = 0; gi < num_geoms; gi++) {
00818         geom_array[gi]->transform_vertices(net_xform);
00819       }
00820       
00821       xpos += advance * glyph_scale;
00822     }
00823     ++si;
00824   }
00825 
00826   return xpos;
00827 }
00828 
00829 ////////////////////////////////////////////////////////////////////
00830 //     Function: TextNode::assemble_text
00831 //       Access: Private
00832 //  Description: Constructs a hierarchy of nodes that contain the
00833 //               geometry representing the indicated source text, and
00834 //               returns it.  Also sets the ul, lr corners.
00835 ////////////////////////////////////////////////////////////////////
00836 PT(PandaNode) TextNode::
00837 assemble_text(wstring::iterator si, const wstring::iterator &send,
00838               TextFont *font, LVector2f &ul, LVector2f &lr, int &num_rows) {
00839   float line_height = font->get_line_height();
00840 
00841   ul.set(0.0f, 0.8f * line_height);
00842   lr.set(0.0f, 0.0f);
00843 
00844   // Make a geom node to hold our formatted text geometry.
00845   PT(GeomNode) root_node = new GeomNode("text");
00846 
00847   float posy = 0.0f;
00848   while (si != send) {
00849     // First, just measure the row, so we know how wide it is.
00850     // (Centered or right-justified text will require us to know this
00851     // up front.)
00852     wstring::iterator tsi = si;
00853     float row_width = measure_row(tsi, send, font);
00854 
00855     LMatrix4f mat = LMatrix4f::ident_mat();
00856     if (_align == A_left) {
00857       mat.set_row(3, LVector3f(0.0f, 0.0f, posy));
00858       lr[0] = max(lr[0], row_width);
00859 
00860     } else if (_align == A_right) {
00861       mat.set_row(3, LVector3f(-row_width, 0.0f, posy));
00862       ul[0] = min(ul[0], -row_width);
00863 
00864     } else {
00865       float half_row_width=0.5f*row_width;
00866       mat.set_row(3, LVector3f(-half_row_width, 0.0f, posy));
00867       lr[0] = max(lr[0], half_row_width);
00868       ul[0] = min(ul[0], -half_row_width);
00869     }
00870 
00871     // Also apply whatever slant the user has asked for to the entire
00872     // row.  This is an X shear.
00873     if (_slant != 0.0f) {
00874       LMatrix4f shear(1.0f, 0.0f, 0.0f, 0.0f,
00875                       0.0f, 1.0f, 0.0f, 0.0f,
00876                       _slant, 0.0f, 1.0f, 0.0f,
00877                       0.0f, 0.0f, 0.0f, 1.0f);
00878       mat = shear * mat;
00879     }
00880 
00881     // Now that we've computed the row's transform matrix, generate
00882     // the actual geoms for the row.
00883     assemble_row(si, send, font, root_node, mat);
00884     if (si != send) {
00885       // Skip past the newline.
00886       ++si;
00887     }
00888 
00889     posy -= line_height;
00890     num_rows++;
00891   }
00892 
00893   lr[1] = posy + 0.8f * line_height;
00894 
00895   return root_node.p();
00896 }
00897 
00898 ////////////////////////////////////////////////////////////////////
00899 //     Function: TextNode::measure_row
00900 //       Access: Private
00901 //  Description: Returns the length of the row in units, as it would
00902 //               be if it were assembled, without actually assembling
00903 //               it.
00904 ////////////////////////////////////////////////////////////////////
00905 float TextNode::
00906 measure_row(wstring::iterator &si, const wstring::iterator &send,
00907             TextFont *font) {
00908   float xpos = 0.0f;
00909   while (si != send && *si != '\n') {
00910     wchar_t character = *si;
00911 
00912     if (character == ' ') {
00913       // A space is a special case.
00914       xpos += font->get_space_advance();
00915 
00916     } else {
00917       // A printable character.
00918       bool got_glyph;
00919       const TextGlyph *glyph;
00920       const TextGlyph *second_glyph;
00921       UnicodeLatinMap::AccentType accent_type;
00922       int additional_flags;
00923       float glyph_scale;
00924       float advance_scale;
00925       get_character_glyphs(character, font, 
00926                            got_glyph, glyph, second_glyph, accent_type,
00927                            additional_flags, glyph_scale, advance_scale);
00928 
00929       float advance = 0.0f;
00930 
00931       if (glyph != (TextGlyph *)NULL) {
00932         advance = glyph->get_advance() * advance_scale;
00933       }
00934       if (second_glyph != (TextGlyph *)NULL) {
00935         advance += second_glyph->get_advance();
00936       }
00937 
00938       xpos += advance * glyph_scale;
00939     }
00940     ++si;
00941   }
00942 
00943   return xpos;
00944 }
00945 
00946 ////////////////////////////////////////////////////////////////////
00947 //     Function: TextNode::measure_text
00948 //       Access: Private
00949 //  Description: Sets the ul, lr corners to fit the text, without
00950 //               actually assembling it.
00951 ////////////////////////////////////////////////////////////////////
00952 void TextNode::
00953 measure_text(wstring::iterator si, const wstring::iterator &send,
00954              TextFont *font, LVector2f &ul, LVector2f &lr, int &num_rows) {
00955   float line_height = font->get_line_height();
00956 
00957   ul.set(0.0f, 0.8f * line_height);
00958   lr.set(0.0f, 0.0f);
00959 
00960   float posy = 0.0f;
00961   while (si != send) {
00962     float row_width = measure_row(si, send, font);
00963     if (si != send) {
00964       // Skip past the newline.
00965       ++si;
00966     }
00967 
00968     if (_align == A_left) {
00969       lr[0] = max(lr[0], row_width);
00970 
00971     } else if (_align == A_right) {
00972       ul[0] = min(ul[0], -row_width);
00973 
00974     } else {
00975       float half_row_width=0.5f*row_width;
00976 
00977       lr[0] = max(lr[0], half_row_width);
00978       ul[0] = min(ul[0], -half_row_width);
00979     }
00980 
00981     posy -= line_height;
00982     num_rows++;
00983   }
00984 
00985   lr[1] = posy + 0.8f * line_height;
00986 }
00987 #endif  // CPPPARSER
00988 
00989 ////////////////////////////////////////////////////////////////////
00990 //     Function: TextNode::get_character_glyphs
00991 //       Access: Private
00992 //  Description: Looks up the glyph(s) from the font for the
00993 //               appropriate character.  If the desired glyph isn't
00994 //               available (especially in the case of an accented
00995 //               letter), tries to find a suitable replacement.
00996 //               Normally, only one glyph is returned per character,
00997 //               but in the case we have to simulate a missing
00998 //               ligature in the font, two glyphs might be returned.
00999 //
01000 //               All parameters except the first two are output
01001 //               parameters.  got_glyph is set true if the glyph (or
01002 //               an acceptable substitute) is successfully found,
01003 //               false otherwise; but even if it is false, glyph might
01004 //               still be non-NULL, indicating a stand-in glyph for a
01005 //               missing character.
01006 ////////////////////////////////////////////////////////////////////
01007 void TextNode::
01008 get_character_glyphs(int character, TextFont *font,
01009                      bool &got_glyph, const TextGlyph *&glyph,
01010                      const TextGlyph *&second_glyph,
01011                      UnicodeLatinMap::AccentType &accent_type,
01012                      int &additional_flags,
01013                      float &glyph_scale, float &advance_scale) {
01014   got_glyph = false;
01015   glyph = NULL;
01016   second_glyph = NULL;
01017   accent_type = UnicodeLatinMap::AT_none;
01018   additional_flags = 0;
01019   glyph_scale = 1.0f;
01020   advance_scale = 1.0f;
01021 
01022   // Maybe we should remap the character to something else--e.g. a
01023   // small capital.
01024   const UnicodeLatinMap::Entry *map_entry = 
01025     UnicodeLatinMap::look_up(character);
01026   if (map_entry != NULL) {
01027     if (get_small_caps() && map_entry->_toupper_character != character) {
01028       character = map_entry->_toupper_character;
01029       map_entry = UnicodeLatinMap::look_up(character);
01030       glyph_scale = get_small_caps_scale();
01031     }
01032   }
01033   
01034   got_glyph = font->get_glyph(character, glyph);
01035   if (!got_glyph && map_entry != NULL && map_entry->_ascii_equiv != 0) {
01036     // If we couldn't find the Unicode glyph, try the ASCII
01037     // equivalent (without the accent marks).
01038     got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
01039     
01040     if (!got_glyph && map_entry->_toupper_character != character) {
01041       // If we still couldn't find it, try the uppercase
01042       // equivalent.
01043       character = map_entry->_toupper_character;
01044       map_entry = UnicodeLatinMap::look_up(character);
01045       if (map_entry != NULL) {
01046         got_glyph = font->get_glyph(map_entry->_ascii_equiv, glyph);
01047       }
01048     }
01049     
01050     if (got_glyph) {
01051       accent_type = map_entry->_accent_type;
01052       additional_flags = map_entry->_additional_flags;
01053       
01054       bool got_second_glyph = false;
01055       if (map_entry->_ascii_additional != 0) {
01056         // There's another character, too--probably a ligature.
01057         got_second_glyph = 
01058           font->get_glyph(map_entry->_ascii_additional, second_glyph);
01059       }
01060       
01061       if ((additional_flags & UnicodeLatinMap::AF_ligature) != 0 &&
01062           got_second_glyph) {
01063         // If we have two letters that are supposed to be in a
01064         // ligature, just jam them together.
01065         additional_flags &= ~UnicodeLatinMap::AF_ligature;
01066         advance_scale = ligature_advance_scale;
01067       }
01068       
01069       if ((additional_flags & UnicodeLatinMap::AF_smallcap) != 0) {
01070         additional_flags &= ~UnicodeLatinMap::AF_smallcap;
01071         glyph_scale = get_small_caps_scale();
01072       }
01073     }
01074   }
01075 }
01076   
01077   
01078 ////////////////////////////////////////////////////////////////////
01079 //     Function: TextNode::tack_on_accent
01080 //       Access: Private
01081 //  Description: This is a cheesy attempt to tack on an accent to an
01082 //               ASCII letter for which we don't have the appropriate
01083 //               already-accented glyph in the font.
01084 ////////////////////////////////////////////////////////////////////
01085 void TextNode::
01086 tack_on_accent(UnicodeLatinMap::AccentType accent_type,
01087                const LPoint3f &min_vert, const LPoint3f &max_vert,
01088                const LPoint3f &centroid,
01089                TextFont *font, GeomNode *dest, 
01090                Geom *geom_array[], int &num_geoms) {
01091   switch (accent_type) {
01092   case UnicodeLatinMap::AT_grave:
01093     // We use the slash as the grave and acute accents.  ASCII does
01094     // have a grave accent character, but a lot of fonts put the
01095     // reverse apostrophe there instead.  And some fonts (particularly
01096     // fonts from mf) don't even do backslash.
01097     tack_on_accent('/', CP_above, CT_small_squash_mirror_y, min_vert, max_vert, centroid,
01098                    font, dest, geom_array, num_geoms);
01099     break;
01100 
01101   case UnicodeLatinMap::AT_acute:
01102     tack_on_accent('/', CP_above, CT_small_squash, min_vert, max_vert, centroid,
01103                    font, dest, geom_array, num_geoms);
01104     break;
01105 
01106   case UnicodeLatinMap::AT_breve:
01107     tack_on_accent(')', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
01108                    font, dest, geom_array, num_geoms);
01109     break;
01110 
01111   case UnicodeLatinMap::AT_inverted_breve:
01112     tack_on_accent('(', CP_above, CT_tiny_rotate_270, min_vert, max_vert, centroid,
01113                    font, dest, geom_array, num_geoms);
01114     break;
01115 
01116   case UnicodeLatinMap::AT_circumflex:
01117     tack_on_accent('^', CP_above, CT_none, min_vert, max_vert, centroid,
01118                    font, dest, geom_array, num_geoms) ||
01119       tack_on_accent('v', CP_above, CT_squash_mirror_y, min_vert, max_vert, centroid,
01120                      font, dest, geom_array, num_geoms);
01121     break;
01122 
01123   case UnicodeLatinMap::AT_circumflex_below:
01124     tack_on_accent('^', CP_below, CT_none, min_vert, max_vert, centroid,
01125                    font, dest, geom_array, num_geoms) ||
01126       tack_on_accent('v', CP_below, CT_squash_mirror_y, min_vert, max_vert, centroid,
01127                      font, dest, geom_array, num_geoms);
01128     break;
01129 
01130   case UnicodeLatinMap::AT_caron:
01131     tack_on_accent('^', CP_above, CT_mirror_y, min_vert, max_vert, centroid,
01132                    font, dest, geom_array, num_geoms) ||
01133       tack_on_accent('v', CP_above, CT_squash, min_vert, max_vert, centroid,
01134                      font, dest, geom_array, num_geoms);
01135 
01136     break;
01137 
01138   case UnicodeLatinMap::AT_tilde:
01139     tack_on_accent('~', CP_above, CT_none, min_vert, max_vert, centroid,
01140                    font, dest, geom_array, num_geoms) ||
01141       tack_on_accent('s', CP_above, CT_squash_mirror_diag, min_vert, max_vert, centroid,
01142                      font, dest, geom_array, num_geoms);
01143     break;
01144 
01145   case UnicodeLatinMap::AT_tilde_below:
01146     tack_on_accent('~', CP_below, CT_none, min_vert, max_vert, centroid,
01147                    font, dest, geom_array, num_geoms) ||
01148       tack_on_accent('s', CP_below, CT_squash_mirror_diag, min_vert, max_vert, centroid,
01149                      font, dest, geom_array, num_geoms);
01150     break;
01151 
01152   case UnicodeLatinMap::AT_diaeresis:
01153     tack_on_accent(':', CP_above, CT_small_rotate_270, min_vert, max_vert, centroid,
01154                    font, dest, geom_array, num_geoms);
01155     break;
01156 
01157   case UnicodeLatinMap::AT_diaeresis_below:
01158     tack_on_accent(':', CP_below, CT_small_rotate_270, min_vert, max_vert, centroid,
01159                    font, dest, geom_array, num_geoms);
01160     break;
01161 
01162   case UnicodeLatinMap::AT_dot_above:
01163     tack_on_accent('.', CP_above, CT_none, min_vert, max_vert, centroid,
01164                    font, dest, geom_array, num_geoms);
01165     break;
01166 
01167   case UnicodeLatinMap::AT_dot_below:
01168     tack_on_accent('.', CP_below, CT_none, min_vert, max_vert, centroid,
01169                    font, dest, geom_array, num_geoms);
01170     break;
01171 
01172   case UnicodeLatinMap::AT_macron:
01173     tack_on_accent('-', CP_above, CT_none, min_vert, max_vert, centroid,
01174                    font, dest, geom_array, num_geoms);
01175     break;
01176 
01177   case UnicodeLatinMap::AT_line_below:
01178     tack_on_accent('-', CP_below, CT_none, min_vert, max_vert, centroid,
01179                    font, dest, geom_array, num_geoms);
01180     break;
01181 
01182   case UnicodeLatinMap::AT_ring_above:
01183     tack_on_accent('o', CP_top, CT_tiny, min_vert, max_vert, centroid,
01184                    font, dest, geom_array, num_geoms);
01185     break;
01186 
01187   case UnicodeLatinMap::AT_ring_below:
01188     tack_on_accent('o', CP_bottom, CT_tiny, min_vert, max_vert, centroid,
01189                    font, dest, geom_array, num_geoms);
01190     break;
01191 
01192   case UnicodeLatinMap::AT_cedilla:
01193     tack_on_accent('c', CP_bottom, CT_tiny_mirror_x, min_vert, max_vert, centroid,
01194                    font, dest, geom_array, num_geoms);
01195     /*
01196     tack_on_accent(',', CP_bottom, CT_none, min_vert, max_vert, centroid,
01197                    font, dest, geom_array, num_geoms);
01198     */
01199     break;
01200 
01201   case UnicodeLatinMap::AT_comma_below:
01202     tack_on_accent(',', CP_below, CT_none, min_vert, max_vert, centroid,
01203                    font, dest, geom_array, num_geoms);
01204     break;
01205 
01206   case UnicodeLatinMap::AT_ogonek:
01207     tack_on_accent(',', CP_bottom, CT_mirror_x, min_vert, max_vert, centroid,
01208                    font, dest, geom_array, num_geoms);
01209     break;
01210 
01211   case UnicodeLatinMap::AT_stroke:
01212     tack_on_accent('/', CP_within, CT_none, min_vert, max_vert, centroid,
01213                    font, dest, geom_array, num_geoms);
01214     break;
01215 
01216   default:
01217     // There are lots of other crazy kinds of accents.  Forget 'em.
01218     break;
01219   }    
01220 }
01221 
01222 ////////////////////////////////////////////////////////////////////
01223 //     Function: TextNode::tack_on_accent
01224 //       Access: Private
01225 //  Description: Generates a cheesy accent mark above (or below, etc.)
01226 //               the character.  Returns true if successful, or false
01227 //               if the named accent character doesn't exist in the
01228 //               font.
01229 ////////////////////////////////////////////////////////////////////
01230 bool TextNode::
01231 tack_on_accent(char accent_mark, TextNode::CheesyPlacement placement,
01232                TextNode::CheesyTransform transform,
01233                const LPoint3f &min_vert, const LPoint3f &max_vert,
01234                const LPoint3f &centroid,
01235                TextFont *font, GeomNode *dest, 
01236                Geom *geom_array[], int &num_geoms) {
01237   
01238   const TextGlyph *accent_glyph;
01239   if (font->get_glyph(accent_mark, accent_glyph)) {
01240     PT(Geom) accent_geom = accent_glyph->get_geom();
01241     if (accent_geom != (Geom *)NULL) {
01242       LPoint3f min_accent, max_accent;
01243       bool found_any = false;
01244       accent_geom->calc_tight_bounds(min_accent, max_accent, found_any);
01245       if (found_any) {
01246         float t, u;
01247         LMatrix4f accent_mat;
01248 
01249         // This gets set to true if the glyph gets mirrored and needs
01250         // to have backface culling disabled.
01251         bool mirrored = false;
01252 
01253         switch (transform) {
01254         case CT_none:
01255           accent_mat = LMatrix4f::ident_mat();
01256           break;
01257 
01258         case CT_mirror_x:
01259           accent_mat = LMatrix4f::scale_mat(-1.0f, 1.0f, 1.0f);
01260           t = min_accent[0];
01261           min_accent[0] = -max_accent[0];
01262           max_accent[0] = -t;
01263           mirrored = true;
01264           break;
01265 
01266         case CT_mirror_y:
01267           accent_mat = LMatrix4f::scale_mat(1.0f, 1.0f, -1.0f);
01268           t = min_accent[2];
01269           min_accent[2] = -max_accent[2];
01270           max_accent[2] = -t;
01271           mirrored = true;
01272           break;
01273 
01274         case CT_rotate_90:
01275           accent_mat =
01276             LMatrix4f::rotate_mat_normaxis(90.0f, LVecBase3f(0.0f, -1.0f, 0.0f));
01277           // rotate min, max
01278           t = min_accent[0];
01279           u = max_accent[0];
01280           max_accent[0] = -min_accent[2];
01281           min_accent[0] = -max_accent[2];
01282           max_accent[2] = u;
01283           min_accent[2] = t;
01284           break;
01285 
01286         case CT_rotate_180:
01287           accent_mat = LMatrix4f::scale_mat(-1.0f, -1.0f, 1.0f);
01288           
01289           t = min_accent[0];
01290           min_accent[0] = -max_accent[0];
01291           max_accent[0] = -t;
01292           t = min_accent[2];
01293           min_accent[2] = -max_accent[2];
01294           max_accent[2] = -t;
01295           break;
01296 
01297         case CT_rotate_270:
01298           accent_mat =
01299             LMatrix4f::rotate_mat_normaxis(270.0f, LVecBase3f(0.0f, -1.0f, 0.0f));
01300           // rotate min, max
01301           t = min_accent[0];
01302           u = max_accent[0];
01303           min_accent[0] = min_accent[2];
01304           max_accent[0] = max_accent[2];
01305           min_accent[2] = -u;
01306           max_accent[2] = -t;
01307           break;
01308 
01309         case CT_squash:
01310           accent_mat = LMatrix4f::scale_mat(squash_accent_scale_x, 1.0f, squash_accent_scale_y);
01311           min_accent[0] *= squash_accent_scale_x;
01312           max_accent[0] *= squash_accent_scale_x;
01313           min_accent[2] *= squash_accent_scale_y;
01314           max_accent[2] *= squash_accent_scale_y;
01315           break;
01316 
01317         case CT_squash_mirror_y:
01318           accent_mat = LMatrix4f::scale_mat(squash_accent_scale_x, 1.0f, -squash_accent_scale_y);
01319           min_accent[0] *= squash_accent_scale_x;
01320           max_accent[0] *= squash_accent_scale_x;
01321           t = min_accent[2];
01322           min_accent[2] = -max_accent[2] * squash_accent_scale_y;
01323           max_accent[2] = -t * squash_accent_scale_y;
01324           mirrored = true;
01325           break;
01326 
01327         case CT_squash_mirror_diag:
01328           accent_mat =
01329             LMatrix4f::rotate_mat_normaxis(270.0f, LVecBase3f(0.0f, -1.0f, 0.0f)) *
01330             LMatrix4f::scale_mat(-squash_accent_scale_x, 1.0f, squash_accent_scale_y);
01331           
01332           // rotate min, max
01333           t = min_accent[0];
01334           u = max_accent[0];
01335           min_accent[0] = min_accent[2] * -squash_accent_scale_x;
01336           max_accent[0] = max_accent[2] * -squash_accent_scale_x;
01337           min_accent[2] = -u * squash_accent_scale_y;
01338           max_accent[2] = -t * squash_accent_scale_y;
01339           mirrored = true;
01340           break;
01341 
01342         case CT_small_squash:
01343           accent_mat = LMatrix4f::scale_mat(small_squash_accent_scale_x, 1.0f, small_squash_accent_scale_y);
01344           min_accent[0] *= small_squash_accent_scale_x;
01345           max_accent[0] *= small_squash_accent_scale_x;
01346           min_accent[2] *= small_squash_accent_scale_y;
01347           max_accent[2] *= small_squash_accent_scale_y;
01348           break;
01349 
01350         case CT_small_squash_mirror_y:
01351           accent_mat = LMatrix4f::scale_mat(small_squash_accent_scale_x, 1.0f, -small_squash_accent_scale_y);
01352           min_accent[0] *= small_squash_accent_scale_x;
01353           max_accent[0] *= small_squash_accent_scale_x;
01354           t = min_accent[2];
01355           min_accent[2] = -max_accent[2] * small_squash_accent_scale_y;
01356           max_accent[2] = -t * small_squash_accent_scale_y;
01357           mirrored = true;
01358           break;
01359 
01360         case CT_small:
01361           accent_mat = LMatrix4f::scale_mat(small_accent_scale);
01362           min_accent *= small_accent_scale;
01363           max_accent *= small_accent_scale;
01364           break;
01365 
01366         case CT_small_rotate_270:
01367           accent_mat =
01368             LMatrix4f::rotate_mat_normaxis(270.0f, LVecBase3f(0.0f, -1.0f, 0.0f)) *
01369             LMatrix4f::scale_mat(small_accent_scale);
01370 
01371           // rotate min, max
01372           t = min_accent[0];
01373           u = max_accent[0];
01374           min_accent[0] = min_accent[2] * small_accent_scale;
01375           max_accent[0] = max_accent[2] * small_accent_scale;
01376           min_accent[2] = -u * small_accent_scale;
01377           max_accent[2] = -t * small_accent_scale;
01378           break;
01379 
01380         case CT_tiny:
01381           accent_mat = LMatrix4f::scale_mat(tiny_accent_scale);
01382           min_accent *= tiny_accent_scale;
01383           max_accent *= tiny_accent_scale;
01384           break;
01385 
01386         case CT_tiny_mirror_x:
01387           accent_mat = LMatrix4f::scale_mat(-tiny_accent_scale, 1.0f, tiny_accent_scale);
01388           
01389           t = min_accent[0];
01390           min_accent[0] = -max_accent[0] * tiny_accent_scale;
01391           max_accent[0] = -t * tiny_accent_scale;
01392           min_accent[2] *= tiny_accent_scale;
01393           max_accent[2] *= tiny_accent_scale;
01394           mirrored = true;
01395           break;
01396 
01397         case CT_tiny_rotate_270:
01398           accent_mat =
01399             LMatrix4f::rotate_mat_normaxis(270.0f, LVecBase3f(0.0f, -1.0f, 0.0f)) *
01400             LMatrix4f::scale_mat(tiny_accent_scale);
01401 
01402           // rotate min, max
01403           t = min_accent[0];
01404           u = max_accent[0];
01405           min_accent[0] = min_accent[2] * tiny_accent_scale;
01406           max_accent[0] = max_accent[2] * tiny_accent_scale;
01407           min_accent[2] = -u * tiny_accent_scale;
01408           max_accent[2] = -t * tiny_accent_scale;
01409           break;
01410         }
01411 
01412         LPoint3f accent_centroid = (min_accent + max_accent) / 2.0f;
01413         float accent_height = max_accent[2] - min_accent[2];
01414         LVector3f trans;
01415         switch (placement) {
01416         case CP_above:
01417           // A little above the character.
01418           trans.set(centroid[0] - accent_centroid[0], 0.0f,
01419                     max_vert[2] - accent_centroid[2] + accent_height * 0.5);
01420           break;
01421 
01422         case CP_below:
01423           // A little below the character.
01424           trans.set(centroid[0] - accent_centroid[0], 0.0f,
01425                     min_vert[2] - accent_centroid[2] - accent_height * 0.5);
01426           break;
01427 
01428         case CP_top:
01429           // Touching the top of the character.
01430           trans.set(centroid[0] - accent_centroid[0], 0.0f,
01431                     max_vert[2] - accent_centroid[2]);
01432           break;
01433 
01434         case CP_bottom:
01435           // Touching the bottom of the character.
01436           trans.set(centroid[0] - accent_centroid[0], 0.0f,
01437                     min_vert[2] - accent_centroid[2]);
01438           break;
01439 
01440         case CP_within:
01441           // Centered within the character.
01442           trans.set(centroid[0] - accent_centroid[0], 0.0f,
01443                     centroid[2] - accent_centroid[2]);
01444           break;
01445         }
01446 
01447         accent_mat.set_row(3, trans);
01448         accent_geom->transform_vertices(accent_mat);
01449 
01450         if (mirrored) {
01451           // Once someone asks for this pointer, we hold its reference
01452           // count and never free it.
01453           static CPT(RenderState) disable_backface;
01454           if (disable_backface == (const RenderState *)NULL) {
01455             disable_backface = RenderState::make
01456               (CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
01457           }
01458             
01459           CPT(RenderState) state = 
01460             accent_glyph->get_state()->compose(disable_backface);
01461           dest->add_geom(accent_geom, state);
01462         } else {
01463           dest->add_geom(accent_geom, accent_glyph->get_state());
01464         }
01465         geom_array[num_geoms++] = accent_geom;
01466 
01467         return true;
01468       }
01469     }
01470   }
01471 
01472   return false;
01473 }
01474 
01475 ////////////////////////////////////////////////////////////////////
01476 //     Function: TextNode::make_frame
01477 //       Access: Private
01478 //  Description: Creates a frame around the text.
01479 ////////////////////////////////////////////////////////////////////
01480 PT(PandaNode) TextNode::
01481 make_frame() {
01482   PT(GeomNode) frame_geode = new GeomNode("frame");
01483 
01484   LVector4f dimensions = get_frame_actual();
01485   float left = dimensions[0];
01486   float right = dimensions[1];
01487   float bottom = dimensions[2];
01488   float top = dimensions[3];
01489 
01490   GeomLinestrip *geoset = new GeomLinestrip;
01491   PTA_int lengths=PTA_int::empty_array(0);
01492   PTA_Vertexf verts;
01493   lengths.push_back(5);
01494   verts.push_back(Vertexf(left, 0.0f, top));
01495   verts.push_back(Vertexf(left, 0.0f, bottom));
01496   verts.push_back(Vertexf(right, 0.0f, bottom));
01497   verts.push_back(Vertexf(right, 0.0f, top));
01498   verts.push_back(Vertexf(left, 0.0f, top));
01499 
01500   geoset->set_num_prims(1);
01501   geoset->set_lengths(lengths);
01502 
01503   geoset->set_coords(verts);
01504   geoset->set_width(_frame_width);
01505   frame_geode->add_geom(geoset);
01506 
01507   if (get_frame_corners()) {
01508     GeomPoint *geoset = new GeomPoint;
01509 
01510     geoset->set_num_prims(4);
01511     geoset->set_coords(verts);
01512     geoset->set_size(_frame_width);
01513     frame_geode->add_geom(geoset);
01514   }
01515 
01516   return frame_geode.p();
01517 }
01518 
01519 ////////////////////////////////////////////////////////////////////
01520 //     Function: TextNode::make_card
01521 //       Access: Private
01522 //  Description: Creates a card behind the text.
01523 ////////////////////////////////////////////////////////////////////
01524 PT(PandaNode) TextNode::
01525 make_card() {
01526   PT(GeomNode) card_geode = new GeomNode("card");
01527 
01528   LVector4f dimensions = get_card_actual();
01529   float left = dimensions[0];
01530   float right = dimensions[1];
01531   float bottom = dimensions[2];
01532   float top = dimensions[3];
01533 
01534   GeomTristrip *geoset = new GeomTristrip;
01535   PTA_int lengths=PTA_int::empty_array(0);
01536   lengths.push_back(4);
01537 
01538   PTA_Vertexf verts;
01539   verts.push_back(Vertexf::rfu(left, 0.02f, top));
01540   verts.push_back(Vertexf::rfu(left, 0.02f, bottom));
01541   verts.push_back(Vertexf::rfu(right, 0.02f, top));
01542   verts.push_back(Vertexf::rfu(right, 0.02f, bottom));
01543 
01544   geoset->set_num_prims(1);
01545   geoset->set_lengths(lengths);
01546 
01547   geoset->set_coords(verts);
01548 
01549   if (has_card_texture()) {
01550     PTA_TexCoordf uvs;
01551     uvs.push_back(TexCoordf(0.0f, 1.0f));
01552     uvs.push_back(TexCoordf(0.0f, 0.0f));
01553     uvs.push_back(TexCoordf(1.0f, 1.0f));
01554     uvs.push_back(TexCoordf(1.0f, 0.0f));
01555 
01556     geoset->set_texcoords(uvs, G_PER_VERTEX);
01557   }
01558 
01559   card_geode->add_geom(geoset);
01560 
01561   return card_geode.p();
01562 }
01563 
01564 
01565 ////////////////////////////////////////////////////////////////////
01566 //     Function: TextNode::make_card_with_border
01567 //       Access: Private
01568 //  Description: Creates a card behind the text with a specified border
01569 //               for button edge or what have you.
01570 ////////////////////////////////////////////////////////////////////
01571 PT(PandaNode) TextNode::
01572 make_card_with_border() {
01573   PT(GeomNode) card_geode = new GeomNode("card");
01574 
01575   LVector4f dimensions = get_card_actual();
01576   float left = dimensions[0];
01577   float right = dimensions[1];
01578   float bottom = dimensions[2];
01579   float top = dimensions[3];
01580 
01581   // we now create three tri-strips instead of one
01582   // with vertices arranged as follows:
01583   //
01584   //  1 3            5 7  - one
01585   //  2 4            6 8  /  \ two
01586   //  9 11          13 15 \  /
01587   // 10 12          14 16 - three
01588   //
01589   GeomTristrip *geoset = new GeomTristrip;
01590   PTA_int lengths;
01591   lengths.push_back(8);
01592   lengths.push_back(8);
01593   lengths.push_back(8);
01594 
01595   PTA_Vertexf verts;
01596   // verts 1,2,3,4
01597   verts.push_back(Vertexf::rfu(left, 0.02f, top));
01598   verts.push_back(Vertexf::rfu(left, 0.02f, top - _card_border_size));
01599   verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02f, top));
01600   verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02f,
01601                                top - _card_border_size));
01602   // verts 5,6,7,8
01603   verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02f, top));
01604   verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02f,
01605                                top - _card_border_size));
01606   verts.push_back(Vertexf::rfu(right, 0.02f, top));
01607   verts.push_back(Vertexf::rfu(right, 0.02f, top - _card_border_size));
01608   // verts 9,10,11,12
01609   verts.push_back(Vertexf::rfu(left, 0.02f, bottom + _card_border_size));
01610   verts.push_back(Vertexf::rfu(left, 0.02f, bottom));
01611   verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02f,
01612                                bottom + _card_border_size));
01613   verts.push_back(Vertexf::rfu(left + _card_border_size, 0.02f, bottom));
01614   // verts 13,14,15,16
01615   verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02f,
01616                                bottom + _card_border_size));
01617   verts.push_back(Vertexf::rfu(right - _card_border_size, 0.02f, bottom));
01618   verts.push_back(Vertexf::rfu(right, 0.02f, bottom + _card_border_size));
01619   verts.push_back(Vertexf::rfu(right, 0.02f, bottom));
01620 
01621   PTA_ushort indices;
01622   // tristrip #1
01623   indices.push_back(0);
01624   indices.push_back(1);
01625   indices.push_back(2);
01626   indices.push_back(3);
01627   indices.push_back(4);
01628   indices.push_back(5);
01629   indices.push_back(6);
01630   indices.push_back(7);
01631   // tristrip #2
01632   indices.push_back(1);
01633   indices.push_back(8);
01634   indices.push_back(3);
01635   indices.push_back(10);
01636   indices.push_back(5);
01637   indices.push_back(12);
01638   indices.push_back(7);
01639   indices.push_back(14);
01640   // tristrip #3
01641   indices.push_back(8);
01642   indices.push_back(9);
01643   indices.push_back(10);
01644   indices.push_back(11);
01645   indices.push_back(12);
01646   indices.push_back(13);
01647   indices.push_back(14);
01648   indices.push_back(15);
01649 
01650   geoset->set_num_prims(3);
01651   geoset->set_lengths(lengths);
01652 
01653   geoset->set_coords(verts,indices);
01654 
01655   if (has_card_texture()) {
01656     PTA_TexCoordf uvs;
01657     uvs.push_back(TexCoordf(0.0f, 1.0f)); //1
01658     uvs.push_back(TexCoordf(0.0f, 1.0f - _card_border_uv_portion)); //2
01659     uvs.push_back(TexCoordf(0.0f + _card_border_uv_portion, 1.0f)); //3
01660     uvs.push_back(TexCoordf(0.0f + _card_border_uv_portion,
01661       1.0f - _card_border_uv_portion)); //4
01662     uvs.push_back(TexCoordf( 1.0f -_card_border_uv_portion, 1.0f)); //5
01663     uvs.push_back(TexCoordf( 1.0f -_card_border_uv_portion,
01664       1.0f - _card_border_uv_portion)); //6
01665     uvs.push_back(TexCoordf(1.0f, 1.0f)); //7
01666     uvs.push_back(TexCoordf(1.0f, 1.0f - _card_border_uv_portion)); //8
01667 
01668     uvs.push_back(TexCoordf(0.0f, _card_border_uv_portion)); //9
01669     uvs.push_back(TexCoordf(0.0f, 0.0f)); //10
01670     uvs.push_back(TexCoordf(_card_border_uv_portion, _card_border_uv_portion)); //11
01671     uvs.push_back(TexCoordf(_card_border_uv_portion, 0.0f)); //12
01672 
01673     uvs.push_back(TexCoordf(1.0f - _card_border_uv_portion, _card_border_uv_portion));//13
01674     uvs.push_back(TexCoordf(1.0f - _card_border_uv_portion, 0.0f));//14
01675     uvs.push_back(TexCoordf(1.0f, _card_border_uv_portion));//15
01676     uvs.push_back(TexCoordf(1.0f, 0.0f));//16
01677 
01678     // we can use same ref's as before (same order)
01679     geoset->set_texcoords(uvs, G_PER_VERTEX, indices);
01680 
01681   }
01682 
01683   card_geode->add_geom(geoset);
01684 
01685   return card_geode.p();
01686 }
01687 
01688 ////////////////////////////////////////////////////////////////////
01689 //     Function: TextNode::load_default_font
01690 //       Access: Private, Static
01691 //  Description: This functin is called once (or never), the first
01692 //               time someone attempts to render a TextNode using the
01693 //               default font.  It should attempt to load the default
01694 //               font, using the compiled-in version if it is
01695 //               available, or whatever system file may be named in
01696 //               Configrc.
01697 ////////////////////////////////////////////////////////////////////
01698 void TextNode::
01699 load_default_font() {
01700   _loaded_default_font = true;
01701 
01702   if (!text_default_font.empty()) {
01703     // First, attempt to load the user-specified filename.
01704     _default_font = FontPool::load_font(text_default_font);
01705     if (_default_font->is_valid()) {
01706       return;
01707     }
01708   }
01709 
01710   // Then, attempt to load the compiled-in font, if we have one.
01711 #if defined(HAVE_FREETYPE) && defined(COMPILE_IN_DEFAULT_FONT)
01712   _default_font = new DynamicTextFont((const char *)default_font_data, 
01713                                       default_font_size, 0);
01714   if (_default_font->is_valid()) {
01715     return;
01716   }
01717 #endif
01718 
01719   // Finally, fall back to a hardcoded font file, which we hope is on
01720   // the model path.  (Use text_default_font, above, if you don't want
01721   // to use this file and would prefer to specify a different font
01722   // file instead.)
01723   _default_font = FontPool::load_font("cmss12");
01724 }

Generated on Fri May 2 00:44:21 2003 for Panda by doxygen1.3