Panda3D
|
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 ©) : 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 ©) : 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 }