56PStatCollector TextNode::_text_generate_pcollector(
"*:Generate Text");
62TextNode(
const string &name) :
PandaNode(name) {
67 _usage_hint = GeomEnums::UH_static;
70 _flatten_flags |= FF_strong;
72 if (text_dynamic_merge) {
73 _flatten_flags |= FF_dynamic_merge;
76 if (text_small_caps) {
80 _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
81 _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
85 _frame_ul.set(0.0f, 0.0f);
86 _frame_lr.set(0.0f, 0.0f);
87 _card_ul.set(0.0f, 0.0f);
88 _card_lr.set(0.0f, 0.0f);
90 _transform = LMatrix4::ident_mat();
91 _coordinate_system = CS_default;
93 _ul3d.set(0.0f, 0.0f, 0.0f);
94 _lr3d.set(0.0f, 0.0f, 0.0f);
102TextNode(
const string &name,
const TextProperties ©) :
103 PandaNode(name), TextProperties(copy)
107 _usage_hint = GeomEnums::UH_static;
109 _frame_color.set(1.0f, 1.0f, 1.0f, 1.0f);
110 _card_color.set(1.0f, 1.0f, 1.0f, 1.0f);
114 _frame_ul.set(0.0f, 0.0f);
115 _frame_lr.set(0.0f, 0.0f);
116 _card_ul.set(0.0f, 0.0f);
117 _card_lr.set(0.0f, 0.0f);
119 _transform = LMatrix4::ident_mat();
120 _coordinate_system = CS_default;
122 _ul3d.set(0.0f, 0.0f, 0.0f);
123 _lr3d.set(0.0f, 0.0f, 0.0f);
134 _card_texture(copy._card_texture),
135 _frame_color(copy._frame_color),
136 _card_color(copy._card_color),
138 _max_rows(copy._max_rows),
140 _frame_width(copy._frame_width),
141 _card_border_size(copy._card_border_size),
142 _card_border_uv_portion(copy._card_border_uv_portion),
143 _frame_ul(copy._frame_ul),
144 _frame_lr(copy._frame_lr),
145 _card_ul(copy._card_ul),
146 _card_lr(copy._card_lr),
147 _transform(copy._transform),
148 _coordinate_system(copy._coordinate_system),
152 invalidate_with_measure();
162 return new TextNode(*
this);
179 if (font ==
nullptr) {
201 if (font ==
nullptr) {
220 if (font ==
nullptr) {
244 if (font ==
nullptr) {
259 if (font ==
nullptr) {
263 PN_stdfloat width = 0.0f;
265 std::wstring::const_iterator si;
266 for (si = line.begin(); si != line.end(); ++si) {
277output(std::ostream &out)
const {
278 PandaNode::output(out);
280 PT(
PandaNode) internal_geom = do_get_internal_geom();
282 if (internal_geom !=
nullptr) {
283 geom_count = count_geoms(internal_geom);
286 out <<
" (" << geom_count <<
" geoms)";
293write(std::ostream &out,
int indent_level)
const {
294 PandaNode::write(out, indent_level);
295 MutexHolder holder(_lock);
296 TextProperties::write(out, indent_level + 2);
297 indent(out, indent_level + 2)
298 <<
"transform is: " << *TransformState::make_mat(_transform) <<
"\n";
299 indent(out, indent_level + 2)
300 <<
"in coordinate system " << _coordinate_system <<
"\n";
301 indent(out, indent_level + 2)
302 <<
"text is " <<
get_text() <<
"\n";
314get_internal_geom()
const {
318 <<
"TextNode::get_internal_geom() called.\n";
319 return do_get_internal_geom();
327 MutexHolder holder(_lock);
328 invalidate_with_measure();
343 SceneGraphReducer::TT_tex_matrix |
344 SceneGraphReducer::TT_other;
360 if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
361 const LMatrix4 &mat = attribs._transform->get_mat();
364 if ((_flags & F_needs_measure) == 0) {
372 if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
373 if (attribs._color !=
nullptr) {
378 TextProperties::set_shadow_color(c);
381 invalidate_no_measure();
385 if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
386 if (attribs._color_scale !=
nullptr) {
389 if (s != LVecBase4(1.0f, 1.0f, 1.0f, 1.0f)) {
390 LVecBase4 tc = get_text_color();
391 tc.componentwise_mult(s);
394 LVecBase4 sc = get_shadow_color();
395 sc.componentwise_mult(s);
396 TextProperties::set_shadow_color(sc);
398 _frame_color.componentwise_mult(s);
399 _card_color.componentwise_mult(s);
401 invalidate_no_measure();
408 if ((_flags & F_needs_rebuild) == 0 &&
409 _internal_geom !=
nullptr) {
411 gr.
apply_attribs(_internal_geom, attribs, attrib_types, transformer);
433 PT(
PandaNode) geom = do_get_internal_geom();
434 if (geom !=
nullptr) {
435 geom->calc_tight_bounds(min_point, max_point,
436 found_any, next_transform, current_thread);
439 return next_transform;
463 PT(PandaNode) internal_geom = do_get_internal_geom();
464 if (internal_geom !=
nullptr) {
466 CullTraverserData next_data(data, internal_geom);
492 int &internal_vertices,
494 Thread *current_thread)
const {
507 vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
508 vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
509 vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
510 vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
511 vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
512 vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
513 vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
514 vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
517 gbv->
around(vertices, vertices + 8);
519 internal_bounds = bound;
520 internal_vertices = 0;
531 PT(PandaNode) child = do_get_internal_geom();
532 if (child !=
nullptr) {
534 child->r_prepare_scene(gsg, child_state, transformer, current_thread);
547 _flags &= ~(F_needs_rebuild | F_needs_measure);
548 _internal_geom = do_generate();
569 nassertr(_lock.debug_is_locked(),
nullptr);
571 PStatTimer timer(_text_generate_pcollector);
572 if (text_cat.is_debug()) {
574 <<
"Rebuilding " << get_type() <<
" " << get_name()
575 <<
" with '" <<
get_text() <<
"'\n";
587 _ul3d.set(0.0f, 0.0f, 0.0f);
588 _lr3d.set(0.0f, 0.0f, 0.0f);
592 size_t newline = name.find(
'\n');
593 if (newline != string::npos) {
594 name = name.substr(0, newline);
596 PT(PandaNode) root =
new PandaNode(name);
602 TextFont *font = get_font();
603 if (font ==
nullptr) {
610 LMatrix4::convert_mat(CS_zup_right, _coordinate_system) *
614 root->set_transform(transform);
619 TextAssembler assembler(
this);
620 assembler.set_properties(*
this);
621 assembler.set_max_rows(_max_rows);
622 assembler.set_usage_hint(_usage_hint);
623 assembler.set_dynamic_merge((_flatten_flags & FF_dynamic_merge) != 0);
624 bool all_set = assembler.set_wtext(wtext);
627 _flags &= ~F_has_overflow;
630 _flags |= F_has_overflow;
633 PT(PandaNode) text_root = assembler.assemble_text();
634 _text_ul = assembler.get_ul();
635 _text_lr = assembler.get_lr();
636 _num_rows = assembler.get_num_rows();
637 _wordwrapped_wtext = assembler.get_wordwrapped_wtext();
640 PT(PandaNode) text =
new PandaNode(
"text");
641 root->add_child(text, get_draw_order() + 2);
642 text->add_child(text_root);
646 const LVector2 &ul = assembler.get_ul();
647 const LVector2 &lr = assembler.get_lr();
648 _ul3d.set(ul[0], 0.0f, ul[1]);
649 _lr3d.set(lr[0], 0.0f, lr[1]);
651 _ul3d = _ul3d * _transform;
652 _lr3d = _lr3d * _transform;
655 _flags &= ~F_needs_measure;
660 NodePath root_np(root);
661 if (_flatten_flags & FF_strong) {
662 root_np.flatten_strong();
663 }
else if (_flatten_flags & FF_medium) {
664 root_np.flatten_medium();
665 }
else if (_flatten_flags & FF_light) {
666 root_np.flatten_light();
671 if (_flags & F_has_card) {
672 PT(PandaNode) card_root;
673 if (_flags & F_has_card_border) {
674 card_root = make_card_with_border();
676 card_root = make_card();
678 card_root->set_transform(transform);
680 if (_card_color[3] != 1.0f) {
683 if (_flags & F_has_card_texture) {
692 SceneGraphReducer gr;
697 card_root->add_child(root);
700 if (_flags & F_card_decal) {
705 if (_flags & F_has_frame) {
706 PT(PandaNode) frame_root = make_frame();
707 frame_root->set_transform(transform);
708 root->add_child(frame_root, get_draw_order() + 1);
710 if (_frame_color[3] != 1.0f) {
718 SceneGraphReducer gr;
730do_get_internal_geom()
const {
731 MutexHolder holder(_lock);
733 return _internal_geom;
741 nassertr(_lock.debug_is_locked(),
nullptr);
742 nassertr((_flags & F_needs_measure) == 0,
nullptr);
744 PT(GeomNode) frame_node =
new GeomNode(
"frame");
746 PN_stdfloat left = _frame_ul[0];
747 PN_stdfloat right = _frame_lr[0];
748 PN_stdfloat bottom = _frame_lr[1];
749 PN_stdfloat top = _frame_ul[1];
751 if (_flags & F_frame_as_margin) {
752 left = _text_ul[0] - left;
753 right = _text_lr[0] + right;
754 bottom = _text_lr[1] - bottom;
755 top = _text_ul[1] + top;
761 PT(GeomVertexData) vdata =
new GeomVertexData
763 vdata->unclean_set_num_rows(4);
764 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
766 vertex.set_data3(left, 0.0f, top);
767 vertex.set_data3(left, 0.0f, bottom);
768 vertex.set_data3(right, 0.0f, bottom);
769 vertex.set_data3(right, 0.0f, top);
771 PT(GeomLinestrips) frame =
new GeomLinestrips(_usage_hint);
772 frame->add_consecutive_vertices(0, 4);
773 frame->add_vertex(0);
774 frame->close_primitive();
776 PT(Geom) geom =
new Geom(vdata);
777 geom->add_primitive(frame);
778 frame_node->add_geom(geom, state);
780 if (_flags & F_frame_corners) {
781 PT(GeomPoints) corners =
new GeomPoints(_usage_hint);
782 corners->add_consecutive_vertices(0, 4);
783 PT(Geom) geom2 =
new Geom(vdata);
784 geom2->add_primitive(corners);
785 frame_node->add_geom(geom2, state);
796 nassertr(_lock.debug_is_locked(),
nullptr);
797 nassertr((_flags & F_needs_measure) == 0,
nullptr);
799 PT(GeomNode) card_node =
new GeomNode(
"card");
801 PN_stdfloat left = _card_ul[0];
802 PN_stdfloat right = _card_lr[0];
803 PN_stdfloat bottom = _card_lr[1];
804 PN_stdfloat top = _card_ul[1];
806 if (_flags & F_card_as_margin) {
807 left = _text_ul[0] - left;
808 right = _text_lr[0] + right;
809 bottom = _text_lr[1] - bottom;
810 top = _text_ul[1] + top;
813 PT(GeomVertexData) vdata =
new GeomVertexData
815 vdata->unclean_set_num_rows(4);
816 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
817 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
819 vertex.set_data3(left, 0.0f, top);
820 vertex.set_data3(left, 0.0f, bottom);
821 vertex.set_data3(right, 0.0f, top);
822 vertex.set_data3(right, 0.0f, bottom);
824 texcoord.set_data2(0.0f, 1.0f);
825 texcoord.set_data2(0.0f, 0.0f);
826 texcoord.set_data2(1.0f, 1.0f);
827 texcoord.set_data2(1.0f, 0.0f);
829 PT(GeomTristrips) card =
new GeomTristrips(_usage_hint);
830 card->add_consecutive_vertices(0, 4);
831 card->close_primitive();
833 PT(Geom) geom =
new Geom(vdata);
834 geom->add_primitive(card);
836 card_node->add_geom(geom);
847make_card_with_border() {
848 nassertr(_lock.debug_is_locked(),
nullptr);
849 nassertr((_flags & F_needs_measure) == 0,
nullptr);
851 PT(GeomNode) card_node =
new GeomNode(
"card");
853 PN_stdfloat left = _card_ul[0];
854 PN_stdfloat right = _card_lr[0];
855 PN_stdfloat bottom = _card_lr[1];
856 PN_stdfloat top = _card_ul[1];
858 if (_flags & F_card_as_margin) {
859 left = _text_ul[0] - left;
860 right = _text_lr[0] + right;
861 bottom = _text_lr[1] - bottom;
862 top = _text_ul[1] + top;
871 PT(GeomVertexData) vdata =
new GeomVertexData
873 vdata->unclean_set_num_rows(16);
874 GeomVertexWriter vertex(vdata, InternalName::get_vertex());
875 GeomVertexWriter texcoord(vdata, InternalName::get_texcoord());
878 vertex.set_data3(left, 0.02, top);
879 vertex.set_data3(left, 0.02, top - _card_border_size);
880 vertex.set_data3(left + _card_border_size, 0.02, top);
881 vertex.set_data3(left + _card_border_size, 0.02,
882 top - _card_border_size);
884 vertex.set_data3(right - _card_border_size, 0.02, top);
885 vertex.set_data3(right - _card_border_size, 0.02,
886 top - _card_border_size);
887 vertex.set_data3(right, 0.02, top);
888 vertex.set_data3(right, 0.02, top - _card_border_size);
890 vertex.set_data3(left, 0.02, bottom + _card_border_size);
891 vertex.set_data3(left, 0.02, bottom);
892 vertex.set_data3(left + _card_border_size, 0.02,
893 bottom + _card_border_size);
894 vertex.set_data3(left + _card_border_size, 0.02, bottom);
896 vertex.set_data3(right - _card_border_size, 0.02,
897 bottom + _card_border_size);
898 vertex.set_data3(right - _card_border_size, 0.02, bottom);
899 vertex.set_data3(right, 0.02, bottom + _card_border_size);
900 vertex.set_data3(right, 0.02, bottom);
902 texcoord.set_data2(0.0f, 1.0f);
903 texcoord.set_data2(0.0f, 1.0f - _card_border_uv_portion);
904 texcoord.set_data2(0.0f + _card_border_uv_portion, 1.0f);
905 texcoord.set_data2(0.0f + _card_border_uv_portion,
906 1.0f - _card_border_uv_portion);
907 texcoord.set_data2(1.0f -_card_border_uv_portion, 1.0f);
908 texcoord.set_data2(1.0f -_card_border_uv_portion,
909 1.0f - _card_border_uv_portion);
910 texcoord.set_data2(1.0f, 1.0f);
911 texcoord.set_data2(1.0f, 1.0f - _card_border_uv_portion);
913 texcoord.set_data2(0.0f, _card_border_uv_portion);
914 texcoord.set_data2(0.0f, 0.0f);
915 texcoord.set_data2(_card_border_uv_portion, _card_border_uv_portion);
916 texcoord.set_data2(_card_border_uv_portion, 0.0f);
918 texcoord.set_data2(1.0f - _card_border_uv_portion, _card_border_uv_portion);
919 texcoord.set_data2(1.0f - _card_border_uv_portion, 0.0f);
920 texcoord.set_data2(1.0f, _card_border_uv_portion);
921 texcoord.set_data2(1.0f, 0.0f);
923 PT(GeomTristrips) card =
new GeomTristrips(_usage_hint);
924 card->reserve_num_vertices(24);
927 card->add_consecutive_vertices(0, 8);
928 card->close_primitive();
934 card->add_vertex(10);
936 card->add_vertex(12);
938 card->add_vertex(14);
939 card->close_primitive();
942 card->add_consecutive_vertices(8, 8);
943 card->close_primitive();
945 PT(Geom) geom =
new Geom(vdata);
946 geom->add_primitive(card);
948 card_node->add_geom(geom);
962 GeomNode *geom_node = DCAST(GeomNode, node);
967 for (
size_t i = 0; i < children.get_num_children(); ++i) {
968 num_geoms += count_geoms(children.get_child(i));
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is used by the SceneGraphReducer to maintain and accumulate the set of attributes we have ...
This defines a bounding sphere, consisting of a center and a radius.
This is an abstract class for any volume in any sense which can be said to define the locality of ref...
Indicates what color should be applied to renderable geometry.
get_color
If the type is T_flat or T_off, this returns the color that will be applied to geometry.
get_color_type
Returns the type of color specified by this ColorAttrib.
static ConstPointerTo< RenderAttrib > make_flat(const LColor &color)
Constructs a new ColorAttrib object that indicates geometry should be rendered in the indicated color...
Applies a scale to colors in the scene graph and on vertices.
get_scale
Returns the scale to be applied to colors.
static ConstPointerTo< RenderAttrib > make(const std::string &bin_name, int draw_order)
Constructs a new CullBinAttrib assigning geometry into the named bin.
This collects together the pieces of data that are accumulated for each node while walking the scene ...
This object performs a depth-first traversal of the scene graph, with optional view-frustum culling,...
void traverse(const NodePath &root)
Begins the traversal from the indicated node.
static ConstPointerTo< RenderEffect > make()
Constructs a new DecalEffect object.
get_num_geoms
Returns the number of geoms in the node.
This is another abstract class, for a general class of bounding volumes that actually enclose points ...
bool around(const GeometricBoundingVolume **first, const GeometricBoundingVolume **last)
Resets the volume to enclose only the volumes indicated.
This is a base class for the GraphicsStateGuardian class, which is itself a base class for the variou...
bool debug_is_locked() const
Returns true if the current thread has locked the Mutex, false otherwise.
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
A lightweight class that represents a single element that may be timed and/or counted via stats.
A basic node of the scene graph or data graph.
virtual bool is_geom_node() const
A simple downcast check.
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
virtual ConstPointerTo< TransformState > calc_tight_bounds(LPoint3 &min_point, LPoint3 &max_point, bool &found_any, const TransformState *transform, Thread *current_thread=Thread::get_current_thread()) const
This is used to support NodePath::calc_tight_bounds().
get_children
Returns an object that can be used to walk through the list of children of the node.
static ConstPointerTo< RenderAttrib > make(Mode mode, PN_stdfloat thickness=1.0f, bool perspective=false, const LColor &wireframe_color=LColor::zero())
Constructs a new RenderModeAttrib object that specifies whether to draw polygons in the normal,...
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
static ConstPointerTo< RenderState > make(const RenderAttrib *attrib, int override=0)
Returns a RenderState with one attribute set.
ConstPointerTo< RenderState > compose(const RenderState *other) const
Returns a new RenderState object that represents the composition of this state with the other state.
An interface for simplifying ("flattening") scene graphs by eliminating unneeded nodes and collapsing...
void apply_attribs(PandaNode *node, int attrib_types=~(TT_clip_plane|TT_cull_face|TT_apply_texture_color))
Walks the scene graph, accumulating attribs of the indicated types, applying them to the vertices,...
static bool is_whitespace(wchar_t character, const TextProperties &properties)
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
static PN_stdfloat calc_width(wchar_t character, const TextProperties &properties)
Returns the width of a single character, according to its associated font.
static bool has_exact_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font exactly as named, false otherwise.
static bool has_character(wchar_t character, const TextProperties &properties)
Returns true if the named character exists in the font or can be synthesized by Panda,...
This class can be used to convert text between multiple representations, e.g.
get_text
Returns the current text, as encoded via the current encoding system.
const std::wstring & get_wtext() const
Returns the text associated with the TextEncoder, as a wide-character string.
An encapsulation of a font; i.e.
The primary interface to this module.
PN_stdfloat calc_width(wchar_t character) const
Returns the width of a single character of the font, or 0.0 if the character is not known.
bool has_exact_character(wchar_t character) const
Returns true if the named character exists in the font exactly as named, false otherwise.
virtual void r_prepare_scene(GraphicsStateGuardianBase *gsg, const RenderState *node_state, GeomTransformer &transformer, Thread *current_thread)
The recursive implementation of prepare_scene().
virtual void text_changed() final
Given that we have just read an ampersand from the StringDecoder, and that we have expand_amp in effe...
virtual int get_unsafe_to_apply_attribs() const
Returns the union of all attributes from SceneGraphReducer::AttribTypes that may not safely be applie...
virtual bool is_renderable() const
Returns true if there is some value to visiting this particular node during the cull traversal for an...
virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types, GeomTransformer &transformer)
Applies whatever attributes are specified in the AccumulatedAttribs object (and by the attrib_types b...
virtual void compute_internal_bounds(CPT(BoundingVolume) &internal_bounds, int &internal_vertices, int pipeline_stage, Thread *current_thread) const
Called when needed to recompute the node's _internal_bound object.
bool is_whitespace(wchar_t character) const
Returns true if the indicated character represents whitespace in the font, or false if anything visib...
bool has_character(wchar_t character) const
Returns true if the named character exists in the font or can be synthesized by Panda,...
virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data)
This function will be called during the cull traversal to perform any additional operations that shou...
This defines the set of visual properties that may be assigned to the individual characters of the te...
set_text_color
Sets the color of the text.
set_small_caps
Sets the small_caps flag.
static ConstPointerTo< RenderAttrib > make()
Constructs a new TextureAttrib object that does nothing.
A thread; that is, a lightweight process.
static ConstPointerTo< RenderAttrib > make(Mode mode)
Constructs a new TransparencyAttrib object.
TypeHandle is the identifier used to differentiate C++ class types.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.