Browse Source

dynamic TextFont; scene graph improvements to support this

David Rose 23 years ago
parent
commit
2fa7929b45

+ 3 - 0
panda/src/pgraph/Sources.pp

@@ -8,6 +8,7 @@
   #define TARGET pgraph
   #define TARGET pgraph
   
   
   #define SOURCES \
   #define SOURCES \
+    accumulatedAttribs.I accumulatedAttribs.h \
     alphaTestAttrib.I alphaTestAttrib.h \  
     alphaTestAttrib.I alphaTestAttrib.h \  
     ambientLight.I ambientLight.h \
     ambientLight.I ambientLight.h \
     bamFile.I bamFile.h \
     bamFile.I bamFile.h \
@@ -91,6 +92,7 @@
 
 
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
   #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx
   #define INCLUDED_SOURCES \
   #define INCLUDED_SOURCES \
+    accumulatedAttribs.cxx \
     alphaTestAttrib.cxx \  
     alphaTestAttrib.cxx \  
     ambientLight.cxx \
     ambientLight.cxx \
     bamFile.cxx \
     bamFile.cxx \
@@ -173,6 +175,7 @@
     workingNodePath.cxx
     workingNodePath.cxx
 
 
   #define INSTALL_HEADERS \
   #define INSTALL_HEADERS \
+    accumulatedAttribs.I accumulatedAttribs.h \
     alphaTestAttrib.I alphaTestAttrib.h \  
     alphaTestAttrib.I alphaTestAttrib.h \  
     ambientLight.I ambientLight.h \
     ambientLight.I ambientLight.h \
     bamFile.I bamFile.h \
     bamFile.I bamFile.h \

+ 58 - 0
panda/src/pgraph/accumulatedAttribs.I

@@ -0,0 +1,58 @@
+// Filename: accumulatedAttribs.I
+// Created by:  drose (30Jan03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: AccumulatedAttribs::Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE AccumulatedAttribs::
+AccumulatedAttribs() {
+  _transform = TransformState::make_identity();
+  _other = RenderState::make_empty();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AccumulatedAttribs::Copy Constructor
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE AccumulatedAttribs::
+AccumulatedAttribs(const AccumulatedAttribs &copy) :
+  _transform(copy._transform),
+  _color(copy._color),
+  _color_scale(copy._color_scale),
+  _tex_matrix(copy._tex_matrix),
+  _other(copy._other)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AccumulatedAttribs::Copy Assignment
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void AccumulatedAttribs::
+operator = (const AccumulatedAttribs &copy) {
+  _transform = copy._transform;
+  _color = copy._color;
+  _color_scale = copy._color_scale;
+  _tex_matrix = copy._tex_matrix;
+  _other = copy._other;
+}

+ 190 - 0
panda/src/pgraph/accumulatedAttribs.cxx

@@ -0,0 +1,190 @@
+// Filename: accumulatedAttribs.cxx
+// Created by:  drose (30Jan03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "accumulatedAttribs.h"
+#include "sceneGraphReducer.h"
+#include "geomTransformer.h"
+#include "pandaNode.h"
+#include "colorAttrib.h"
+#include "colorScaleAttrib.h"
+#include "texMatrixAttrib.h"
+#include "config_pgraph.h"
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: AccumulatedAttribs::write
+//       Access: Public
+//  Description:
+////////////////////////////////////////////////////////////////////
+void AccumulatedAttribs::
+write(ostream &out, int attrib_types, int indent_level) const {
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    _transform->write(out, indent_level);
+  }
+  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+    if (_color == (const RenderAttrib *)NULL) {
+      indent(out, indent_level) << "no color\n";
+    } else {
+      _color->write(out, indent_level);
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+    if (_color_scale == (const RenderAttrib *)NULL) {
+      indent(out, indent_level) << "no color scale\n";
+    } else {
+      _color_scale->write(out, indent_level);
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_tex_matrix) != 0) {
+    if (_tex_matrix == (const RenderAttrib *)NULL) {
+      indent(out, indent_level) << "no tex matrix\n";
+    } else {
+      _tex_matrix->write(out, indent_level);
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_other) != 0) {
+    _other->write(out, indent_level);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AccumulatedAttribs::collect
+//       Access: Public
+//  Description: Collects the state and transform from the indicated
+//               node and adds it to the accumulator, removing it from
+//               the node.
+////////////////////////////////////////////////////////////////////
+void AccumulatedAttribs::
+collect(PandaNode *node, int attrib_types) {
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    // Collect the node's transform.
+    nassertv(_transform != (TransformState *)NULL);
+    _transform = _transform->compose(node->get_transform());
+    node->set_transform(TransformState::make_identity());
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+    const RenderAttrib *node_attrib = 
+      node->get_attrib(ColorAttrib::get_class_type());
+    if (node_attrib != (const RenderAttrib *)NULL) {
+      // The node has a color attribute; apply it.
+      if (_color == (const RenderAttrib *)NULL) {
+        _color = node_attrib;
+      } else {
+        _color = _color->compose(node_attrib);
+      }
+      node->clear_attrib(ColorAttrib::get_class_type());
+    }
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+    const RenderAttrib *node_attrib = 
+      node->get_attrib(ColorScaleAttrib::get_class_type());
+    if (node_attrib != (const RenderAttrib *)NULL) {
+      // The node has a color scale attribute; apply it.
+      if (_color_scale == (const RenderAttrib *)NULL) {
+        _color_scale = node_attrib;
+      } else {
+        _color_scale = _color_scale->compose(node_attrib);
+      }
+      node->clear_attrib(ColorScaleAttrib::get_class_type());
+    }
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_tex_matrix) != 0) {
+    const RenderAttrib *node_attrib = 
+      node->get_attrib(TexMatrixAttrib::get_class_type());
+    if (node_attrib != (const RenderAttrib *)NULL) {
+      // The node has a tex matrix attribute; apply it.
+      if (_tex_matrix == (const RenderAttrib *)NULL) {
+        _tex_matrix = node_attrib;
+      } else {
+        _tex_matrix = _tex_matrix->compose(node_attrib);
+      }
+      node->clear_attrib(TexMatrixAttrib::get_class_type());
+    }
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    // Collect everything else.
+    nassertv(_other != (RenderState *)NULL);
+    _other = _other->compose(node->get_state());
+    node->set_state(RenderState::make_empty());
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AccumulatedAttribs::apply_to_node
+//       Access: Public
+//  Description: Stores the indicated attributes in the node's
+//               transform and state information; does not attempt to
+//               apply the properties to the vertices.  Clears the
+//               attributes from the accumulator for future
+//               traversals.
+////////////////////////////////////////////////////////////////////
+void AccumulatedAttribs::
+apply_to_node(PandaNode *node, int attrib_types) {
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    node->set_transform(_transform->compose(node->get_transform()));
+    _transform = TransformState::make_identity();
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+    if (_color != (RenderAttrib *)NULL) {
+      const RenderAttrib *node_attrib =
+        node->get_attrib(ColorAttrib::get_class_type());
+      if (node_attrib != (RenderAttrib *)NULL) {
+        node->set_attrib(_color->compose(node_attrib));
+      } else {
+        node->set_attrib(_color);
+      }
+      _color = (RenderAttrib *)NULL;
+    }
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+    if (_color_scale != (RenderAttrib *)NULL) {
+      const RenderAttrib *node_attrib =
+        node->get_attrib(ColorScaleAttrib::get_class_type());
+      if (node_attrib != (RenderAttrib *)NULL) {
+        node->set_attrib(_color_scale->compose(node_attrib));
+      } else {
+        node->set_attrib(_color_scale);
+      }
+      _color_scale = (RenderAttrib *)NULL;
+    }
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_tex_matrix) != 0) {
+    if (_tex_matrix != (RenderAttrib *)NULL) {
+      const RenderAttrib *node_attrib =
+        node->get_attrib(TexMatrixAttrib::get_class_type());
+      if (node_attrib != (RenderAttrib *)NULL) {
+        node->set_attrib(_tex_matrix->compose(node_attrib));
+      } else {
+        node->set_attrib(_tex_matrix);
+      }
+      _tex_matrix = (RenderAttrib *)NULL;
+    }
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_other) != 0) {
+    node->set_state(_other->compose(node->get_state()));
+    _other = RenderState::make_empty();
+  }
+}

+ 59 - 0
panda/src/pgraph/accumulatedAttribs.h

@@ -0,0 +1,59 @@
+// Filename: accumulatedAttribs.h
+// Created by:  drose (30Jan03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef ACCUMULATEDATTRIBS_H
+#define ACCUMULATEDATTRIBS_H
+
+#include "pandabase.h"
+#include "transformState.h"
+#include "renderAttrib.h"
+#include "renderState.h"
+#include "pointerTo.h"
+
+class PandaNode;
+
+///////////////////////////////////////////////////////////////////
+//       Class : AccumulatedAttribs
+// Description : This class is used by the SceneGraphReducer to
+//               maintain and accumulate the set of attributes we have
+//               encountered on each node that might eventually be
+//               applied to the vertices at the leaves.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA AccumulatedAttribs {
+public:
+  INLINE AccumulatedAttribs();
+  INLINE AccumulatedAttribs(const AccumulatedAttribs &copy);
+  INLINE void operator = (const AccumulatedAttribs &copy);
+  
+  void write(ostream &out, int attrib_types, int indent_level) const;
+  
+  void collect(PandaNode *node, int attrib_types);
+  void apply_to_node(PandaNode *node, int attrib_types);
+
+  CPT(TransformState) _transform;
+  CPT(RenderAttrib) _color;
+  CPT(RenderAttrib) _color_scale;
+  CPT(RenderAttrib) _tex_matrix;
+  CPT(RenderState) _other;
+};
+
+#include "accumulatedAttribs.I"
+
+#endif
+
+

+ 111 - 0
panda/src/pgraph/geomNode.cxx

@@ -18,6 +18,8 @@
 
 
 #include "geomNode.h"
 #include "geomNode.h"
 #include "geomTransformer.h"
 #include "geomTransformer.h"
+#include "sceneGraphReducer.h"
+#include "accumulatedAttribs.h"
 #include "bamReader.h"
 #include "bamReader.h"
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "datagram.h"
 #include "datagram.h"
@@ -154,6 +156,62 @@ make_copy() const {
   return new GeomNode(*this);
   return new GeomNode(*this);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::apply_attribs_to_vertices
+//       Access: Public, Virtual
+//  Description: Applies whatever attributes are specified in the
+//               AccumulatedAttribs object (and by the attrib_types
+//               bitmask) to the vertices on this node, if
+//               appropriate.  If this node uses geom arrays like a
+//               GeomNode, the supplied GeomTransformer may be used to
+//               unify shared arrays across multiple different nodes.
+//
+//               This is a generalization of xform().
+////////////////////////////////////////////////////////////////////
+void GeomNode::
+apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
+                          GeomTransformer &transformer) {
+  if (pgraph_cat.is_debug()) {
+    pgraph_cat.debug()
+      << "Transforming geometry.\n";
+  }
+
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    if (!attribs._transform->is_identity()) {
+      transformer.transform_vertices(this, attribs._transform->get_mat());
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+    if (attribs._color != (const RenderAttrib *)NULL) {
+      const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
+      if (ca->get_color_type() == ColorAttrib::T_flat) {
+        transformer.set_color(this, ca->get_color());
+      }
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+    if (attribs._color_scale != (const RenderAttrib *)NULL) {
+      const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
+      if (csa->get_scale() != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)) {
+        transformer.transform_colors(this, csa->get_scale());
+      }
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_tex_matrix) != 0) {
+    if (attribs._tex_matrix != (const RenderAttrib *)NULL) {
+      const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, attribs._tex_matrix);
+      if (tma->get_mat() != LMatrix4f::ident_mat()) {
+        transformer.transform_texcoords(this, tma->get_mat());
+      }
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_other) != 0) {
+    if (!attribs._other->is_empty()) {
+      transformer.apply_state(this, attribs._other);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::xform
 //     Function: GeomNode::xform
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -200,6 +258,59 @@ combine_with(PandaNode *other) {
   return PandaNode::combine_with(other);
   return PandaNode::combine_with(other);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: GeomNode::calc_tight_bounds
+//       Access: Public, Virtual
+//  Description: This is used to support
+//               NodePath::calc_tight_bounds().  It is not intended to
+//               be called directly, and it has nothing to do with the
+//               normal Panda bounding-volume computation.
+//
+//               If the node contains any geometry, this updates
+//               min_point and max_point to enclose its bounding box.
+//               found_any is to be set true if the node has any
+//               geometry at all, or left alone if it has none.  This
+//               method may be called over several nodes, so it may
+//               enter with min_point, max_point, and found_any
+//               already set.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) GeomNode::
+calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
+                  const TransformState *transform) const {
+  CPT(TransformState) next_transform = 
+    PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform);
+
+  const LMatrix4f &mat = next_transform->get_mat();
+  int num_geoms = get_num_geoms();
+  for (int i = 0; i < num_geoms; i++) {
+    Geom *geom = get_geom(i);
+    Geom::VertexIterator vi = geom->make_vertex_iterator();
+    int num_prims = geom->get_num_prims();
+    
+    for (int p = 0; p < num_prims; p++) {
+      int length = geom->get_length(p);
+      for (int v = 0; v < length; v++) {
+        Vertexf vertex = geom->get_next_vertex(vi) * mat;
+        
+        if (found_any) {
+          min_point.set(min(min_point[0], vertex[0]),
+                        min(min_point[1], vertex[1]),
+                        min(min_point[2], vertex[2]));
+          max_point.set(max(max_point[0], vertex[0]),
+                        max(max_point[1], vertex[1]),
+                        max(max_point[2], vertex[2]));
+        } else {
+          min_point = vertex;
+          max_point = vertex;
+          found_any = true;
+        }
+      }
+    }
+  }
+
+  return next_transform;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: GeomNode::add_geoms_from
 //     Function: GeomNode::add_geoms_from
 //       Access: Published
 //       Access: Published

+ 7 - 0
panda/src/pgraph/geomNode.h

@@ -44,8 +44,15 @@ protected:
 public:
 public:
   virtual ~GeomNode();
   virtual ~GeomNode();
   virtual PandaNode *make_copy() const;
   virtual PandaNode *make_copy() const;
+  virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs,
+                                         int attrib_types,
+                                         GeomTransformer &transformer);
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
   virtual PandaNode *combine_with(PandaNode *other); 
   virtual PandaNode *combine_with(PandaNode *other); 
+  virtual CPT(TransformState)
+    calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
+                      bool &found_any,
+                      const TransformState *transform) const;
 
 
 PUBLISHED:
 PUBLISHED:
   INLINE int get_num_geoms() const;
   INLINE int get_num_geoms() const;

+ 2 - 54
panda/src/pgraph/nodePath.cxx

@@ -2788,8 +2788,8 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) {
   nassertr_always(!is_empty(), false);
   nassertr_always(!is_empty(), false);
 
 
   bool found_any = false;
   bool found_any = false;
-  r_calc_tight_bounds(node(), min_point, max_point, found_any, 
-                      TransformState::make_identity());
+  node()->calc_tight_bounds(min_point, max_point, found_any, 
+                            TransformState::make_identity());
 
 
   return found_any;
   return found_any;
 }
 }
@@ -3190,58 +3190,6 @@ r_force_recompute_bounds(PandaNode *node) {
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: NodePath::r_calc_tight_bounds
-//       Access: Private
-//  Description: 
-////////////////////////////////////////////////////////////////////
-void NodePath::
-r_calc_tight_bounds(PandaNode *node, LPoint3f &min_point, LPoint3f &max_point,
-                    bool &found_any, const TransformState *transform) {
-  CPT(TransformState) next_transform = 
-    transform->compose(node->get_transform());
-  if (node->is_geom_node()) {
-    GeomNode *gnode;
-    DCAST_INTO_V(gnode, node);
-
-    const LMatrix4f &mat = next_transform->get_mat();
-    int num_geoms = gnode->get_num_geoms();
-    for (int i = 0; i < num_geoms; i++) {
-      Geom *geom = gnode->get_geom(i);
-      Geom::VertexIterator vi = geom->make_vertex_iterator();
-      int num_prims = geom->get_num_prims();
-
-      for (int p = 0; p < num_prims; p++) {
-        int length = geom->get_length(p);
-        for (int v = 0; v < length; v++) {
-          Vertexf vertex = geom->get_next_vertex(vi) * mat;
-          
-          if (found_any) {
-            min_point.set(min(min_point[0], vertex[0]),
-                          min(min_point[1], vertex[1]),
-                          min(min_point[2], vertex[2]));
-            max_point.set(max(max_point[0], vertex[0]),
-                          max(max_point[1], vertex[1]),
-                          max(max_point[2], vertex[2]));
-          } else {
-            min_point = vertex;
-            max_point = vertex;
-            found_any = true;
-          }
-        }
-      }
-    }
-  }
-
-  // Now consider children.
-  PandaNode::Children cr = node->get_children();
-  int num_children = cr.get_num_children();
-  for (int i = 0; i < num_children; i++) {
-    r_calc_tight_bounds(cr.get_child(i), min_point, max_point,
-                        found_any, next_transform);
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: NodePath::r_find_texture
 //     Function: NodePath::r_find_texture
 //       Access: Private
 //       Access: Private

+ 0 - 3
panda/src/pgraph/nodePath.h

@@ -542,9 +542,6 @@ private:
   void r_adjust_all_priorities(PandaNode *node, int adjustment);
   void r_adjust_all_priorities(PandaNode *node, int adjustment);
 
 
   void r_force_recompute_bounds(PandaNode *node);
   void r_force_recompute_bounds(PandaNode *node);
-  void r_calc_tight_bounds(PandaNode *node,
-                           LPoint3f &min_point, LPoint3f &max_point,
-                           bool &found_any, const TransformState *transform);
 
 
   typedef pset<Texture *> Textures;
   typedef pset<Texture *> Textures;
   Texture *r_find_texture(PandaNode *node, const RenderState *state,
   Texture *r_find_texture(PandaNode *node, const RenderState *state,

+ 85 - 0
panda/src/pgraph/pandaNode.cxx

@@ -23,6 +23,8 @@
 #include "bamWriter.h"
 #include "bamWriter.h"
 #include "indent.h"
 #include "indent.h"
 #include "geometricBoundingVolume.h"
 #include "geometricBoundingVolume.h"
+#include "sceneGraphReducer.h"
+#include "accumulatedAttribs.h"
 
 
 
 
 TypeHandle PandaNode::_type_handle;
 TypeHandle PandaNode::_type_handle;
@@ -501,6 +503,18 @@ safe_to_combine() const {
   return true;
   return true;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::safe_to_flatten_below
+//       Access: Public, Virtual
+//  Description: Returns true if a flatten operation may safely
+//               continue past this node, or false if it should drop
+//               all attributes here and stop.
+////////////////////////////////////////////////////////////////////
+bool PandaNode::
+safe_to_flatten_below() const {
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::preserve_name
 //     Function: PandaNode::preserve_name
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -513,6 +527,42 @@ preserve_name() const {
   return false;
   return false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::get_unsafe_to_apply_attribs
+//       Access: Public, Virtual
+//  Description: Returns the union of all attributes from
+//               SceneGraphReducer::AttribTypes that may not safely be
+//               applied to the vertices of this node.  If this is
+//               nonzero, these attributes must be dropped at this
+//               node as a state change.
+//
+//               This is a generalization of safe_to_transform().
+////////////////////////////////////////////////////////////////////
+int PandaNode::
+get_unsafe_to_apply_attribs() const {
+  return 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::apply_attribs_to_vertices
+//       Access: Public, Virtual
+//  Description: Applies whatever attributes are specified in the
+//               AccumulatedAttribs object (and by the attrib_types
+//               bitmask) to the vertices on this node, if
+//               appropriate.  If this node uses geom arrays like a
+//               GeomNode, the supplied GeomTransformer may be used to
+//               unify shared arrays across multiple different nodes.
+//
+//               This is a generalization of xform().
+////////////////////////////////////////////////////////////////////
+void PandaNode::
+apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
+                          GeomTransformer &transformer) {
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    xform(attribs._transform->get_mat());
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::xform
 //     Function: PandaNode::xform
 //       Access: Public, Virtual
 //       Access: Public, Virtual
@@ -568,6 +618,41 @@ combine_with(PandaNode *other) {
   return (PandaNode *)NULL;
   return (PandaNode *)NULL;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: PandaNode::calc_tight_bounds
+//       Access: Public, Virtual
+//  Description: This is used to support
+//               NodePath::calc_tight_bounds().  It is not intended to
+//               be called directly, and it has nothing to do with the
+//               normal Panda bounding-volume computation.
+//
+//               If the node contains any geometry, this updates
+//               min_point and max_point to enclose its bounding box.
+//               found_any is to be set true if the node has any
+//               geometry at all, or left alone if it has none.  This
+//               method may be called over several nodes, so it may
+//               enter with min_point, max_point, and found_any
+//               already set.
+//
+//               This function is recursive, and the return value is
+//               the transform after it has been modified by this
+//               node's transform.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) PandaNode::
+calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
+                  const TransformState *transform) const {
+  CPT(TransformState) next_transform = transform->compose(get_transform());
+
+  Children cr = get_children();
+  int num_children = cr.get_num_children();
+  for (int i = 0; i < num_children; i++) {
+    cr.get_child(i)->calc_tight_bounds(min_point, max_point,
+                                       found_any, next_transform);
+  }
+
+  return next_transform;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: PandaNode::has_cull_callback
 //     Function: PandaNode::has_cull_callback
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 12 - 1
panda/src/pgraph/pandaNode.h

@@ -45,6 +45,8 @@ class CullTraverser;
 class CullTraverserData;
 class CullTraverserData;
 class Light;
 class Light;
 class FactoryParams;
 class FactoryParams;
+class AccumulatedAttribs;
+class GeomTransformer;
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : PandaNode
 //       Class : PandaNode
@@ -71,10 +73,19 @@ public:
   virtual bool safe_to_transform() const;
   virtual bool safe_to_transform() const;
   virtual bool safe_to_modify_transform() const;
   virtual bool safe_to_modify_transform() const;
   virtual bool safe_to_combine() const;
   virtual bool safe_to_combine() const;
+  virtual bool safe_to_flatten_below() const;
   virtual bool preserve_name() const;
   virtual bool preserve_name() const;
+  virtual int get_unsafe_to_apply_attribs() const;
+  virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs,
+                                         int attrib_types,
+                                         GeomTransformer &transformer);
   virtual void xform(const LMatrix4f &mat);
   virtual void xform(const LMatrix4f &mat);
   virtual PandaNode *combine_with(PandaNode *other); 
   virtual PandaNode *combine_with(PandaNode *other); 
-
+  virtual CPT(TransformState)
+    calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
+                      bool &found_any,
+                      const TransformState *transform) const;
+  
   virtual bool has_cull_callback() const;
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
   virtual bool has_selective_visibility() const;
   virtual bool has_selective_visibility() const;

+ 1 - 0
panda/src/pgraph/pgraph_composite1.cxx

@@ -1,3 +1,4 @@
+#include "accumulatedAttribs.cxx"
 #include "ambientLight.cxx"
 #include "ambientLight.cxx"
 #include "bamFile.cxx"
 #include "bamFile.cxx"
 #include "billboardEffect.cxx"
 #include "billboardEffect.cxx"

+ 43 - 24
panda/src/pgraph/sceneGraphReducer.I

@@ -18,39 +18,58 @@
 
 
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::Constructor
-//       Access: Public
+//     Function: SceneGraphReducer::Constructor
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE SceneGraphReducer::AccumulatedAttribs::
-AccumulatedAttribs() {
+INLINE SceneGraphReducer::
+SceneGraphReducer() {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::Copy Constructor
-//       Access: Public
+//     Function: SceneGraphReducer::Destructor
+//       Access: Published
 //  Description:
 //  Description:
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE SceneGraphReducer::AccumulatedAttribs::
-AccumulatedAttribs(const SceneGraphReducer::AccumulatedAttribs &copy) :
-  _transform(copy._transform),
-  _color(copy._color),
-  _color_scale(copy._color_scale),
-  _tex_matrix(copy._tex_matrix),
-  _other(copy._other)
-{
+INLINE SceneGraphReducer::
+~SceneGraphReducer() {
 }
 }
 
 
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::Copy Assignment
-//       Access: Public
-//  Description:
+//     Function: SceneGraphReducer::apply_attribs
+//       Access: Published
+//  Description: Walks the scene graph, accumulating attribs of
+//               the indicated types, applying them to the vertices,
+//               and removing them from the scene graph.  This has a
+//               performance optimization benefit in itself, but is
+//               especially useful to pave the way for a call to
+//               flatten() and greatly improve the effectiveness of
+//               the flattening operation.
+//
+//               Multiply instanced geometry is duplicated before the
+//               attribs are applied.
+//
+//               Of course, this operation does make certain dynamic
+//               operations impossible.
+////////////////////////////////////////////////////////////////////
+INLINE void SceneGraphReducer::
+apply_attribs(PandaNode *node, int attrib_types) {
+  AccumulatedAttribs attribs;
+  r_apply_attribs(node, attribs, attrib_types, _transformer);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: SceneGraphReducer::apply_attribs
+//       Access: Published
+//  Description: This flavor of apply_attribs() can be called
+//               recursively from within another flatten process
+//               (e.g. from PandaNode::apply_attribs_to_vertices()).
+//               The parameters were presumably received from a parent
+//               SceneGraphReducer object.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-INLINE void SceneGraphReducer::AccumulatedAttribs::
-operator = (const SceneGraphReducer::AccumulatedAttribs &copy) {
-  _transform = copy._transform;
-  _color = copy._color;
-  _color_scale = copy._color_scale;
-  _tex_matrix = copy._tex_matrix;
-  _other = copy._other;
+INLINE void SceneGraphReducer::
+apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
+              int attrib_types, GeomTransformer &transformer) {
+  r_apply_attribs(node, attribs, attrib_types, transformer);
 }
 }

+ 20 - 302
panda/src/pgraph/sceneGraphReducer.cxx

@@ -21,6 +21,7 @@
 #include "colorAttrib.h"
 #include "colorAttrib.h"
 #include "texMatrixAttrib.h"
 #include "texMatrixAttrib.h"
 #include "colorScaleAttrib.h"
 #include "colorScaleAttrib.h"
+#include "accumulatedAttribs.h"
 
 
 #include "geomNode.h"
 #include "geomNode.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
@@ -28,292 +29,15 @@
 #include "indent.h"
 #include "indent.h"
 #include "plist.h"
 #include "plist.h"
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::write
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-void SceneGraphReducer::AccumulatedAttribs::
-write(ostream &out, int attrib_types, int indent_level) const {
-  if ((attrib_types & TT_transform) != 0) {
-    _transform->write(out, indent_level);
-  }
-  if ((attrib_types & TT_color) != 0) {
-    if (_color == (const RenderAttrib *)NULL) {
-      indent(out, indent_level) << "no color\n";
-    } else {
-      _color->write(out, indent_level);
-    }
-  }
-  if ((attrib_types & TT_color_scale) != 0) {
-    if (_color_scale == (const RenderAttrib *)NULL) {
-      indent(out, indent_level) << "no color scale\n";
-    } else {
-      _color_scale->write(out, indent_level);
-    }
-  }
-  if ((attrib_types & TT_tex_matrix) != 0) {
-    if (_tex_matrix == (const RenderAttrib *)NULL) {
-      indent(out, indent_level) << "no tex matrix\n";
-    } else {
-      _tex_matrix->write(out, indent_level);
-    }
-  }
-  if ((attrib_types & TT_other) != 0) {
-    _other->write(out, indent_level);
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::collect
-//       Access: Public
-//  Description: Collects the state and transform from the indicated
-//               node and adds it to the accumulator, removing it from
-//               the node.
-////////////////////////////////////////////////////////////////////
-void SceneGraphReducer::AccumulatedAttribs::
-collect(PandaNode *node, int attrib_types) {
-  if ((attrib_types & TT_transform) != 0) {
-    // Collect the node's transform.
-    nassertv(_transform != (TransformState *)NULL);
-    _transform = _transform->compose(node->get_transform());
-    node->set_transform(TransformState::make_identity());
-  }
-
-  if ((attrib_types & TT_color) != 0) {
-    const RenderAttrib *node_attrib = 
-      node->get_attrib(ColorAttrib::get_class_type());
-    if (node_attrib != (const RenderAttrib *)NULL) {
-      // The node has a color attribute; apply it.
-      if (_color == (const RenderAttrib *)NULL) {
-        _color = node_attrib;
-      } else {
-        _color = _color->compose(node_attrib);
-      }
-      node->clear_attrib(ColorAttrib::get_class_type());
-    }
-  }
-
-  if ((attrib_types & TT_color_scale) != 0) {
-    const RenderAttrib *node_attrib = 
-      node->get_attrib(ColorScaleAttrib::get_class_type());
-    if (node_attrib != (const RenderAttrib *)NULL) {
-      // The node has a color scale attribute; apply it.
-      if (_color_scale == (const RenderAttrib *)NULL) {
-        _color_scale = node_attrib;
-      } else {
-        _color_scale = _color_scale->compose(node_attrib);
-      }
-      node->clear_attrib(ColorScaleAttrib::get_class_type());
-    }
-  }
-
-  if ((attrib_types & TT_tex_matrix) != 0) {
-    const RenderAttrib *node_attrib = 
-      node->get_attrib(TexMatrixAttrib::get_class_type());
-    if (node_attrib != (const RenderAttrib *)NULL) {
-      // The node has a tex matrix attribute; apply it.
-      if (_tex_matrix == (const RenderAttrib *)NULL) {
-        _tex_matrix = node_attrib;
-      } else {
-        _tex_matrix = _tex_matrix->compose(node_attrib);
-      }
-      node->clear_attrib(TexMatrixAttrib::get_class_type());
-    }
-  }
-
-  if ((attrib_types & TT_transform) != 0) {
-    // Collect everything else.
-    nassertv(_other != (RenderState *)NULL);
-    _other = _other->compose(node->get_state());
-    node->set_state(RenderState::make_empty());
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::apply_to_node
-//       Access: Public
-//  Description: Stores the indicated attributes in the node's
-//               transform and state information; does not attempt to
-//               apply the properties to the vertices.  Clears the
-//               attributes from the accumulator for future
-//               traversals.
-////////////////////////////////////////////////////////////////////
-void SceneGraphReducer::AccumulatedAttribs::
-apply_to_node(PandaNode *node, int attrib_types) {
-  if ((attrib_types & TT_transform) != 0) {
-    node->set_transform(_transform->compose(node->get_transform()));
-    _transform = TransformState::make_identity();
-  }
-
-  if ((attrib_types & TT_color) != 0) {
-    if (_color != (RenderAttrib *)NULL) {
-      const RenderAttrib *node_attrib =
-        node->get_attrib(ColorAttrib::get_class_type());
-      if (node_attrib != (RenderAttrib *)NULL) {
-        node->set_attrib(_color->compose(node_attrib));
-      } else {
-        node->set_attrib(_color);
-      }
-      _color = (RenderAttrib *)NULL;
-    }
-  }
-
-  if ((attrib_types & TT_color_scale) != 0) {
-    if (_color_scale != (RenderAttrib *)NULL) {
-      const RenderAttrib *node_attrib =
-        node->get_attrib(ColorScaleAttrib::get_class_type());
-      if (node_attrib != (RenderAttrib *)NULL) {
-        node->set_attrib(_color_scale->compose(node_attrib));
-      } else {
-        node->set_attrib(_color_scale);
-      }
-      _color_scale = (RenderAttrib *)NULL;
-    }
-  }
-
-  if ((attrib_types & TT_tex_matrix) != 0) {
-    if (_tex_matrix != (RenderAttrib *)NULL) {
-      const RenderAttrib *node_attrib =
-        node->get_attrib(TexMatrixAttrib::get_class_type());
-      if (node_attrib != (RenderAttrib *)NULL) {
-        node->set_attrib(_tex_matrix->compose(node_attrib));
-      } else {
-        node->set_attrib(_tex_matrix);
-      }
-      _tex_matrix = (RenderAttrib *)NULL;
-    }
-  }
-
-  if ((attrib_types & TT_other) != 0) {
-    node->set_state(_other->compose(node->get_state()));
-    _other = RenderState::make_empty();
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::AccumulatedAttribs::apply_to_vertices
-//       Access: Public
-//  Description: Applies the indicated attributes to the node so that
-//               they do not need to be stored as separate attributes
-//               any more.
-////////////////////////////////////////////////////////////////////
-void SceneGraphReducer::AccumulatedAttribs::
-apply_to_vertices(PandaNode *node, int attrib_types, 
-                  GeomTransformer &transformer) {
-  if (node->is_geom_node()) {
-    if (pgraph_cat.is_debug()) {
-      pgraph_cat.debug()
-        << "Transforming geometry.\n";
-    }
-
-    // We treat GeomNodes as a special case, since we can apply more
-    // than just a transform matrix and so we can share vertex arrays
-    // across different GeomNodes.
-    GeomNode *gnode = DCAST(GeomNode, node);
-    if ((attrib_types & TT_transform) != 0) {
-      if (!_transform->is_identity()) {
-        transformer.transform_vertices(gnode, _transform->get_mat());
-      }
-    }
-    if ((attrib_types & TT_color) != 0) {
-      if (_color != (const RenderAttrib *)NULL) {
-        const ColorAttrib *ca = DCAST(ColorAttrib, _color);
-        if (ca->get_color_type() == ColorAttrib::T_flat) {
-          transformer.set_color(gnode, ca->get_color());
-        }
-      }
-    }
-    if ((attrib_types & TT_color_scale) != 0) {
-      if (_color_scale != (const RenderAttrib *)NULL) {
-        const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, _color_scale);
-        if (csa->get_scale() != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)) {
-          transformer.transform_colors(gnode, csa->get_scale());
-        }
-      }
-    }
-    if ((attrib_types & TT_tex_matrix) != 0) {
-      if (_tex_matrix != (const RenderAttrib *)NULL) {
-        const TexMatrixAttrib *tma = DCAST(TexMatrixAttrib, _tex_matrix);
-        if (tma->get_mat() != LMatrix4f::ident_mat()) {
-          transformer.transform_texcoords(gnode, tma->get_mat());
-        }
-      }
-    }
-    if ((attrib_types & TT_other) != 0) {
-      if (!_other->is_empty()) {
-        transformer.apply_state(gnode, _other);
-      }
-    }
-
-  } else {
-    // This handles any kind of node other than a GeomNode.
-    if ((attrib_types & TT_transform) != 0) {
-      node->xform(_transform->get_mat());
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::Constructor
-//       Access: Public
-//  Description:
-////////////////////////////////////////////////////////////////////
-SceneGraphReducer::
-SceneGraphReducer() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::Destructor
-//       Access: Public, Virtual
-//  Description:
-////////////////////////////////////////////////////////////////////
-SceneGraphReducer::
-~SceneGraphReducer() {
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: SceneGraphReducer::apply_attribs
-//       Access: Public
-//  Description: Walks the scene graph, accumulating attribs of
-//               the indicated types, applying them to the vertices,
-//               and removing them from the scene graph.  This has a
-//               performance optimization benefit in itself, but is
-//               especially useful to pave the way for a call to
-//               flatten() and greatly improve the effectiveness of
-//               the flattening operation.
-//
-//               Multiply instanced geometry is duplicated before the
-//               attribs are applied.
-//
-//               Of course, this operation does make certain dynamic
-//               operations impossible.
-////////////////////////////////////////////////////////////////////
-void SceneGraphReducer::
-apply_attribs(PandaNode *node, int attrib_types) {
-  AccumulatedAttribs trans;
-
-  trans._transform = TransformState::make_identity();
-  trans._color = (ColorAttrib *)NULL;
-  trans._color_scale = (ColorScaleAttrib *)NULL;
-  trans._tex_matrix = (TexMatrixAttrib *)NULL;
-  trans._other = RenderState::make_empty();
-
-  r_apply_attribs(node, attrib_types, trans);
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::flatten
 //     Function: SceneGraphReducer::flatten
-//       Access: Public
+//       Access: Published
 //  Description: Simplifies the graph by removing unnecessary nodes
 //  Description: Simplifies the graph by removing unnecessary nodes
 //               and nodes.
 //               and nodes.
 //
 //
 //               In general, a node (and its parent node) is a
 //               In general, a node (and its parent node) is a
 //               candidate for removal if the node has no siblings and
 //               candidate for removal if the node has no siblings and
-//               the node and node have no special properties.  The
-//               definition of what, precisely, is a 'special
-//               property' may be extended by subclassing from this
-//               type and redefining consider_node() appropriately.
+//               the node has no special properties.
 //
 //
 //               If combine_siblings is true, sibling nodes may also
 //               If combine_siblings is true, sibling nodes may also
 //               be collapsed into a single node.  This will further
 //               be collapsed into a single node.  This will further
@@ -365,8 +89,8 @@ flatten(PandaNode *root, bool combine_siblings) {
 //  Description: The recursive implementation of apply_attribs().
 //  Description: The recursive implementation of apply_attribs().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void SceneGraphReducer::
 void SceneGraphReducer::
-r_apply_attribs(PandaNode *node, int attrib_types,
-                SceneGraphReducer::AccumulatedAttribs trans) {
+r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
+                int attrib_types, GeomTransformer &transformer) {
   if (pgraph_cat.is_debug()) {
   if (pgraph_cat.is_debug()) {
     pgraph_cat.debug()
     pgraph_cat.debug()
       << "r_apply_attribs(" << *node << "), node's attribs are:\n";
       << "r_apply_attribs(" << *node << "), node's attribs are:\n";
@@ -375,13 +99,14 @@ r_apply_attribs(PandaNode *node, int attrib_types,
     node->get_effects()->write(pgraph_cat.debug(false), 2);
     node->get_effects()->write(pgraph_cat.debug(false), 2);
   }
   }
 
 
-  trans.collect(node, attrib_types);
+  AccumulatedAttribs next_attribs(attribs);
+  next_attribs.collect(node, attrib_types);
 
 
   if (pgraph_cat.is_debug()) {
   if (pgraph_cat.is_debug()) {
     pgraph_cat.debug()
     pgraph_cat.debug()
       << "Got attribs from " << *node << "\n"
       << "Got attribs from " << *node << "\n"
       << "Accumulated attribs are:\n";
       << "Accumulated attribs are:\n";
-    trans.write(pgraph_cat.debug(false), attrib_types, 2);
+    next_attribs.write(pgraph_cat.debug(false), attrib_types, 2);
   }
   }
 
 
   // Check to see if we can't propagate any of these attribs past
   // Check to see if we can't propagate any of these attribs past
@@ -405,6 +130,7 @@ r_apply_attribs(PandaNode *node, int attrib_types,
     }
     }
     apply_types |= TT_transform;
     apply_types |= TT_transform;
   }
   }
+  apply_types |= node->get_unsafe_to_apply_attribs();
 
 
   // Also, check the children of this node.  If any of them indicates
   // Also, check the children of this node.  If any of them indicates
   // it is not safe to modify its transform, we must drop our
   // it is not safe to modify its transform, we must drop our
@@ -429,10 +155,10 @@ r_apply_attribs(PandaNode *node, int attrib_types,
   }
   }
 
 
   // Directly store whatever attributes we must,
   // Directly store whatever attributes we must,
-  trans.apply_to_node(node, attrib_types & apply_types);
+  next_attribs.apply_to_node(node, attrib_types & apply_types);
 
 
   // And apply the rest to the vertices.
   // And apply the rest to the vertices.
-  trans.apply_to_vertices(node, attrib_types, _transformer);
+  node->apply_attribs_to_vertices(next_attribs, attrib_types, transformer);
 
 
   // Do we need to copy any children to flatten instances?
   // Do we need to copy any children to flatten instances?
   bool resist_copy = false;
   bool resist_copy = false;
@@ -473,14 +199,14 @@ r_apply_attribs(PandaNode *node, int attrib_types,
   if (resist_copy) {
   if (resist_copy) {
     // If any of our children should have been copied but weren't, we
     // If any of our children should have been copied but weren't, we
     // need to drop the state here before continuing.
     // need to drop the state here before continuing.
-    trans.apply_to_node(node, attrib_types);
+    next_attribs.apply_to_node(node, attrib_types);
   }
   }
 
 
   // Now it's safe to traverse through all of our children.
   // Now it's safe to traverse through all of our children.
   nassertv(num_children == node->get_num_children());
   nassertv(num_children == node->get_num_children());
   for (i = 0; i < num_children; i++) {
   for (i = 0; i < num_children; i++) {
     PandaNode *child_node = node->get_child(i);
     PandaNode *child_node = node->get_child(i);
-    r_apply_attribs(child_node, attrib_types, trans);
+    r_apply_attribs(child_node, next_attribs, attrib_types, transformer);
   }
   }
 }
 }
 
 
@@ -637,12 +363,10 @@ flatten_siblings(PandaNode *parent_node) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::consider_child
 //     Function: SceneGraphReducer::consider_child
-//       Access: Protected, Virtual
+//       Access: Protected
 //  Description: Decides whether or not the indicated child node is a
 //  Description: Decides whether or not the indicated child node is a
 //               suitable candidate for removal.  Returns true if the
 //               suitable candidate for removal.  Returns true if the
-//               node may be removed, false if it should be kept.  This
-//               function may be extended in a user class to protect
-//               special kinds of nodes from deletion.
+//               node may be removed, false if it should be kept.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool SceneGraphReducer::
 bool SceneGraphReducer::
 consider_child(PandaNode *grandparent_node, PandaNode *parent_node, 
 consider_child(PandaNode *grandparent_node, PandaNode *parent_node, 
@@ -671,7 +395,7 @@ consider_child(PandaNode *grandparent_node, PandaNode *parent_node,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer:c:onsider_siblings
 //     Function: SceneGraphReducer:c:onsider_siblings
-//       Access: Protected, Virtual
+//       Access: Protected
 //  Description: Decides whether or not the indicated sibling nodes
 //  Description: Decides whether or not the indicated sibling nodes
 //               should be collapsed into a single node or not.
 //               should be collapsed into a single node or not.
 //               Returns true if the nodes may be collapsed, false if
 //               Returns true if the nodes may be collapsed, false if
@@ -688,14 +412,11 @@ consider_siblings(PandaNode *parent_node, PandaNode *child1,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::do_flatten_child
 //     Function: SceneGraphReducer::do_flatten_child
-//       Access: Protected, Virtual
+//       Access: Protected
 //  Description: Collapses together the indicated parent node and
 //  Description: Collapses together the indicated parent node and
 //               child node and leaves the result attached to the
 //               child node and leaves the result attached to the
 //               grandparent.  The return value is true if the node is
 //               grandparent.  The return value is true if the node is
 //               successfully collapsed, false if we chickened out.
 //               successfully collapsed, false if we chickened out.
-//
-//               This function may be extended in a user class to
-//               handle special kinds of nodes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 bool SceneGraphReducer::
 bool SceneGraphReducer::
 do_flatten_child(PandaNode *grandparent_node, PandaNode *parent_node, 
 do_flatten_child(PandaNode *grandparent_node, PandaNode *parent_node, 
@@ -730,7 +451,7 @@ do_flatten_child(PandaNode *grandparent_node, PandaNode *parent_node,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::do_flatten_siblings
 //     Function: SceneGraphReducer::do_flatten_siblings
-//       Access: Protected, Virtual
+//       Access: Protected
 //  Description: Performs the work of collapsing two sibling nodes
 //  Description: Performs the work of collapsing two sibling nodes
 //               together into a single node, leaving the resulting
 //               together into a single node, leaving the resulting
 //               node attached to the parent.
 //               node attached to the parent.
@@ -779,7 +500,7 @@ do_flatten_siblings(PandaNode *parent_node, PandaNode *child1,
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::collapse_nodes
 //     Function: SceneGraphReducer::collapse_nodes
-//       Access: Protected, Virtual
+//       Access: Protected
 //  Description: Collapses the two nodes into a single node, if
 //  Description: Collapses the two nodes into a single node, if
 //               possible.  The 'siblings' flag is true if the two
 //               possible.  The 'siblings' flag is true if the two
 //               nodes are siblings nodes; otherwise, node1 is a
 //               nodes are siblings nodes; otherwise, node1 is a
@@ -787,9 +508,6 @@ do_flatten_siblings(PandaNode *parent_node, PandaNode *child1,
 //               node, which may be either one of the source nodes, or
 //               node, which may be either one of the source nodes, or
 //               a new node altogether, or it may be NULL to indicate
 //               a new node altogether, or it may be NULL to indicate
 //               that the collapse operation could not take place.
 //               that the collapse operation could not take place.
-//
-//               This function may be extended in a user class to
-//               handle combining special kinds of nodes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(PandaNode) SceneGraphReducer::
 PT(PandaNode) SceneGraphReducer::
 collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) {
 collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) {
@@ -799,7 +517,7 @@ collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) {
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: SceneGraphReducer::choose_name
 //     Function: SceneGraphReducer::choose_name
-//       Access: Protected, Virtual
+//       Access: Protected
 //  Description: Chooses a suitable name for the collapsed node, based
 //  Description: Chooses a suitable name for the collapsed node, based
 //               on the names of the two sources nodes.
 //               on the names of the two sources nodes.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////

+ 22 - 37
panda/src/pgraph/sceneGraphReducer.h

@@ -23,11 +23,14 @@
 #include "transformState.h"
 #include "transformState.h"
 #include "renderAttrib.h"
 #include "renderAttrib.h"
 #include "renderState.h"
 #include "renderState.h"
+#include "accumulatedAttribs.h"
 #include "geomTransformer.h"
 #include "geomTransformer.h"
 
 
 #include "typedObject.h"
 #include "typedObject.h"
 #include "pointerTo.h"
 #include "pointerTo.h"
 
 
+class PandaNode;
+
 ///////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////
 //       Class : SceneGraphReducer
 //       Class : SceneGraphReducer
 // Description : An interface for simplifying ("flattening") scene
 // Description : An interface for simplifying ("flattening") scene
@@ -41,8 +44,8 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA SceneGraphReducer {
 class EXPCL_PANDA SceneGraphReducer {
 PUBLISHED:
 PUBLISHED:
-  SceneGraphReducer();
-  virtual ~SceneGraphReducer();
+  INLINE SceneGraphReducer();
+  INLINE ~SceneGraphReducer();
 
 
   enum AttribTypes {
   enum AttribTypes {
     TT_transform       = 0x001,
     TT_transform       = 0x001,
@@ -52,53 +55,35 @@ PUBLISHED:
     TT_other           = 0x010,
     TT_other           = 0x010,
   };
   };
 
 
-  void apply_attribs(PandaNode *node, int attrib_types = ~0);
+  INLINE void apply_attribs(PandaNode *node, int attrib_types = ~0);
+  INLINE void apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
+                            int attrib_types, GeomTransformer &transformer);
 
 
   int flatten(PandaNode *root, bool combine_siblings);
   int flatten(PandaNode *root, bool combine_siblings);
 
 
 protected:
 protected:
-  class AccumulatedAttribs {
-  public:
-    INLINE AccumulatedAttribs();
-    INLINE AccumulatedAttribs(const AccumulatedAttribs &copy);
-    INLINE void operator = (const AccumulatedAttribs &copy);
-
-    void write(ostream &out, int attrib_types, int indent_level) const;
-
-    void collect(PandaNode *node, int attrib_types);
-    void apply_to_node(PandaNode *node, int attrib_types);
-    void apply_to_vertices(PandaNode *node, int attrib_types,
-                           GeomTransformer &transfomer);
-
-    CPT(TransformState) _transform;
-    CPT(RenderAttrib) _color;
-    CPT(RenderAttrib) _color_scale;
-    CPT(RenderAttrib) _tex_matrix;
-    CPT(RenderState) _other;
-  };
-
-  void r_apply_attribs(PandaNode *node, int attrib_types,
-                       AccumulatedAttribs trans);
+  void r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
+                       int attrib_types, GeomTransformer &transformer);
 
 
   int r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
   int r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
                 bool combine_siblings);
                 bool combine_siblings);
   int flatten_siblings(PandaNode *parent_node);
   int flatten_siblings(PandaNode *parent_node);
 
 
-  virtual bool consider_child(PandaNode *grandparent_node,
-                              PandaNode *parent_node, PandaNode *child_node);
-  virtual bool consider_siblings(PandaNode *parent_node, PandaNode *child1,
-                                 PandaNode *child2);
+  bool consider_child(PandaNode *grandparent_node,
+                      PandaNode *parent_node, PandaNode *child_node);
+  bool consider_siblings(PandaNode *parent_node, PandaNode *child1,
+                         PandaNode *child2);
 
 
-  virtual bool do_flatten_child(PandaNode *grandparent_node, 
-                                PandaNode *parent_node, PandaNode *child_node);
+  bool do_flatten_child(PandaNode *grandparent_node, 
+                        PandaNode *parent_node, PandaNode *child_node);
 
 
-  virtual PandaNode *do_flatten_siblings(PandaNode *parent_node, 
-                                         PandaNode *child1, PandaNode *child2);
+  PandaNode *do_flatten_siblings(PandaNode *parent_node, 
+                                 PandaNode *child1, PandaNode *child2);
 
 
-  virtual PT(PandaNode) collapse_nodes(PandaNode *node1, PandaNode *node2, 
-                                       bool siblings);
-  virtual void choose_name(PandaNode *preserve, PandaNode *source1, 
-                           PandaNode *source2);
+  PT(PandaNode) collapse_nodes(PandaNode *node1, PandaNode *node2, 
+                               bool siblings);
+  void choose_name(PandaNode *preserve, PandaNode *source1, 
+                   PandaNode *source2);
 
 
 private:
 private:
   GeomTransformer _transformer;
   GeomTransformer _transformer;

+ 0 - 1
panda/src/pgui/pgItem.cxx

@@ -779,7 +779,6 @@ TextNode *PGItem::
 get_text_node() {
 get_text_node() {
   if (_text_node == (TextNode *)NULL) {
   if (_text_node == (TextNode *)NULL) {
     _text_node = new TextNode("pguiText");
     _text_node = new TextNode("pguiText");
-    _text_node->freeze();
     _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
     _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f);
 
 
     // The default TextNode is aligned to the left, for the
     // The default TextNode is aligned to the left, for the

+ 141 - 142
panda/src/text/textNode.I

@@ -20,70 +20,25 @@
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextNode::freeze
 //     Function: TextNode::freeze
 //       Access: Published
 //       Access: Published
-//  Description: Freezes the TextNode in its current state, so that
-//               updates will not immediately be displayed.  A series
-//               of state changes may then be applied in succession,
-//               which will not force the TextNode to be recomputed.
-//               When thaw() is later called, the TextNode will update
-//               itself exactly once to reflect all the state changes
-//               that were made.
-//
-//               freeze() and thaw() can nest.  Strictly speaking,
-//               each call to freeze() increments the current freeze
-//               level, while each call to thaw() decrements it.  The
-//               TextNode will only be updated when the current freeze
-//               level is zero.
-//
-//               The return value of freeze() is the freeze level
-//               *before* the freeze took place.  This number should
-//               match the return value of the matching thaw().
+//  Description: This method is deprecated and no longer does
+//               anything.  It is included for historical purposes
+//               only and will shortly be removed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int TextNode::
 INLINE int TextNode::
 freeze() {
 freeze() {
-  if (text_cat.is_debug()) {
-    text_cat.debug()
-      << "Freezing " << this->get_name() << ", level = "
-      << _freeze_level << "\n";
-  }
-  return _freeze_level++;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: TextNode::get_freeze_level
-//       Access: Published
-//  Description: Returns the current freeze level.  The TextNode will
-//               not be updated visually unless this number is zero.
-//               See freeze().
-////////////////////////////////////////////////////////////////////
-INLINE int TextNode::
-get_freeze_level() const {
-  return _freeze_level;
+  return 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextNode::thaw
 //     Function: TextNode::thaw
 //       Access: Published
 //       Access: Published
-//  Description: Allows changes made since the last freeze() to be
-//               visible.  Strictly speaking, this actually decrements
-//               the freeze level, and updates the TextNode if the
-//               level reaches zero.  The return value is the new
-//               freeze level after adjusting.  See freeze().
+//  Description: This method is deprecated and no longer does
+//               anything.  It is included for historical purposes
+//               only and will shortly be removed.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int TextNode::
 INLINE int TextNode::
 thaw() {
 thaw() {
-  if (text_cat.is_debug()) {
-    text_cat.debug()
-      << "Thawing " << this->get_name() << ", level = "
-      << _freeze_level-1 << "\n";
-  }
-  nassertr(_freeze_level > 0, _freeze_level);
-  _freeze_level--;
-
-  if (_freeze_level == 0 && _needs_rebuild) {
-    do_rebuild();
-  }
-
-  return _freeze_level;
+  return 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -95,7 +50,7 @@ INLINE void TextNode::
 set_font(TextFont *font) {
 set_font(TextFont *font) {
   if (_font != font) {
   if (_font != font) {
     _font = font;
     _font = font;
-    rebuild(true);
+    invalidate_with_measure();
   }
   }
 }
 }
 
 
@@ -193,7 +148,7 @@ INLINE void TextNode::
 set_slant(float slant) {
 set_slant(float slant) {
   if (_slant != slant) {
   if (_slant != slant) {
     _slant = slant;
     _slant = slant;
-    rebuild(true);
+    invalidate_with_measure();
   }
   }
 }
 }
 
 
@@ -216,7 +171,7 @@ INLINE void TextNode::
 set_align(TextNode::Alignment align_type) {
 set_align(TextNode::Alignment align_type) {
   if (_align != align_type) {
   if (_align != align_type) {
     _align = align_type;
     _align = align_type;
-    rebuild(true);
+    invalidate_with_measure();
   }
   }
 }
 }
 
 
@@ -241,7 +196,7 @@ set_wordwrap(float wordwrap) {
   if (!has_wordwrap() || _wordwrap_width != wordwrap) {
   if (!has_wordwrap() || _wordwrap_width != wordwrap) {
     _flags |= F_has_wordwrap;
     _flags |= F_has_wordwrap;
     _wordwrap_width = wordwrap;
     _wordwrap_width = wordwrap;
-    rebuild(true);
+    invalidate_with_measure();
   }
   }
 }
 }
 
 
@@ -255,7 +210,7 @@ INLINE void TextNode::
 clear_wordwrap() {
 clear_wordwrap() {
   if (has_wordwrap()) {
   if (has_wordwrap()) {
     _flags &= ~F_has_wordwrap;
     _flags &= ~F_has_wordwrap;
-    rebuild(true);
+    invalidate_with_measure();
   }
   }
 }
 }
 
 
@@ -299,7 +254,7 @@ set_text_color(const Colorf &text_color) {
   if (!has_text_color() || _text_color != text_color) {
   if (!has_text_color() || _text_color != text_color) {
     _text_color = text_color;
     _text_color = text_color;
     _flags |= F_has_text_color;
     _flags |= F_has_text_color;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -313,7 +268,7 @@ INLINE void TextNode::
 clear_text_color() {
 clear_text_color() {
   if (has_text_color()) {
   if (has_text_color()) {
     _flags &= ~F_has_text_color;
     _flags &= ~F_has_text_color;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -356,7 +311,7 @@ INLINE void TextNode::
 set_frame_color(const Colorf &frame_color) {
 set_frame_color(const Colorf &frame_color) {
   if (_frame_color != frame_color) {
   if (_frame_color != frame_color) {
     _frame_color = frame_color;
     _frame_color = frame_color;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -381,7 +336,7 @@ set_card_border(float size, float uv_portion) {
     _flags |= F_has_card_border;
     _flags |= F_has_card_border;
     _card_border_size = size;
     _card_border_size = size;
     _card_border_uv_portion = uv_portion;
     _card_border_uv_portion = uv_portion;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -394,7 +349,7 @@ INLINE void TextNode::
 clear_card_border() {
 clear_card_border() {
   if (has_card_border()) {
   if (has_card_border()) {
     _flags &= ~F_has_card_border;
     _flags &= ~F_has_card_border;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -447,7 +402,7 @@ INLINE void TextNode::
 set_card_color(const Colorf &card_color) {
 set_card_color(const Colorf &card_color) {
   if (_card_color != card_color) {
   if (_card_color != card_color) {
     _card_color = card_color;
     _card_color = card_color;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -474,7 +429,7 @@ set_card_texture(Texture *card_texture) {
     if (!has_card_texture() || _card_texture != card_texture) {
     if (!has_card_texture() || _card_texture != card_texture) {
       _flags |= F_has_card_texture;
       _flags |= F_has_card_texture;
       _card_texture = card_texture;
       _card_texture = card_texture;
-      rebuild(false);
+      invalidate_no_measure();
     }
     }
   }
   }
 }
 }
@@ -489,7 +444,7 @@ clear_card_texture() {
   if (has_card_texture()) {
   if (has_card_texture()) {
     _flags &= ~F_has_card_texture;
     _flags &= ~F_has_card_texture;
     _card_texture = NULL;
     _card_texture = NULL;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -532,7 +487,7 @@ INLINE void TextNode::
 set_shadow_color(const Colorf &shadow_color) {
 set_shadow_color(const Colorf &shadow_color) {
   if (_shadow_color != shadow_color) {
   if (_shadow_color != shadow_color) {
     _shadow_color = shadow_color;
     _shadow_color = shadow_color;
-    rebuild(false);
+    invalidate_no_measure();
   }
   }
 }
 }
 
 
@@ -560,7 +515,7 @@ set_frame_as_margin(float left, float right, float bottom, float top) {
   _flags |= (F_has_frame | F_frame_as_margin);
   _flags |= (F_has_frame | F_frame_as_margin);
   _frame_ul.set(left, top);
   _frame_ul.set(left, top);
   _frame_lr.set(right, bottom);
   _frame_lr.set(right, bottom);
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -579,7 +534,7 @@ set_frame_actual(float left, float right, float bottom, float top) {
   _flags &= ~F_frame_as_margin;
   _flags &= ~F_frame_as_margin;
   _frame_ul.set(left, top);
   _frame_ul.set(left, top);
   _frame_lr.set(right, bottom);
   _frame_lr.set(right, bottom);
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -591,7 +546,7 @@ set_frame_actual(float left, float right, float bottom, float top) {
 INLINE void TextNode::
 INLINE void TextNode::
 clear_frame() {
 clear_frame() {
   _flags &= ~F_has_frame;
   _flags &= ~F_has_frame;
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -650,10 +605,11 @@ INLINE LVecBase4f TextNode::
 get_frame_actual() const {
 get_frame_actual() const {
   nassertr(has_frame(), LVecBase4f(0.0, 0.0, 0.0, 0.0));
   nassertr(has_frame(), LVecBase4f(0.0, 0.0, 0.0, 0.0));
   if (is_frame_as_margin()) {
   if (is_frame_as_margin()) {
-    return LVecBase4f(get_left() - _frame_ul[0],
-                      get_right() + _frame_lr[0],
-                      get_bottom() - _frame_lr[1],
-                      get_top() + _frame_ul[1]);
+    check_measure();
+    return LVecBase4f(_ul2d[0] - _frame_ul[0],
+                      _lr2d[0] + _frame_lr[0],
+                      _lr2d[1] - _frame_lr[1],
+                      _ul2d[1] + _frame_ul[1]);
   } else {
   } else {
     return get_frame_as_set();
     return get_frame_as_set();
   }
   }
@@ -723,7 +679,7 @@ set_card_as_margin(float left, float right, float bottom, float top) {
   _flags |= (F_has_card | F_card_as_margin);
   _flags |= (F_has_card | F_card_as_margin);
   _card_ul.set(left, top);
   _card_ul.set(left, top);
   _card_lr.set(right, bottom);
   _card_lr.set(right, bottom);
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -742,7 +698,7 @@ set_card_actual(float left, float right, float bottom, float top) {
   _flags &= ~F_card_as_margin;
   _flags &= ~F_card_as_margin;
   _card_ul.set(left, top);
   _card_ul.set(left, top);
   _card_lr.set(right, bottom);
   _card_lr.set(right, bottom);
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -754,7 +710,7 @@ set_card_actual(float left, float right, float bottom, float top) {
 INLINE void TextNode::
 INLINE void TextNode::
 clear_card() {
 clear_card() {
   _flags &= ~F_has_card;
   _flags &= ~F_has_card;
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -816,13 +772,15 @@ get_card_as_set() const {
 INLINE LVecBase4f TextNode::
 INLINE LVecBase4f TextNode::
 get_card_actual() const {
 get_card_actual() const {
   if (!has_card()) {
   if (!has_card()) {
-    return LVecBase4f(get_left(), get_right(), get_bottom(), get_top());
+    check_measure();
+    return LVecBase4f(_ul2d[0], _lr2d[0], _lr2d[1], _ul2d[1]);
 
 
   } else if (is_card_as_margin()) {
   } else if (is_card_as_margin()) {
-    return LVecBase4f(get_left() - _card_ul[0],
-                      get_right() + _card_lr[0],
-                      get_bottom() - _card_lr[1],
-                      get_top() + _card_ul[1]);
+    check_measure();
+    return LVecBase4f(_ul2d[0] - _card_ul[0],
+                      _lr2d[0] + _card_lr[0],
+                      _lr2d[1] - _card_lr[1],
+                      _ul2d[1] + _card_ul[1]);
   } else {
   } else {
     return get_card_as_set();
     return get_card_as_set();
   }
   }
@@ -857,7 +815,7 @@ INLINE void TextNode::
 set_shadow(float xoffset, float yoffset) {
 set_shadow(float xoffset, float yoffset) {
   _flags |= F_has_shadow;
   _flags |= F_has_shadow;
   _shadow_offset.set(xoffset, yoffset);
   _shadow_offset.set(xoffset, yoffset);
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -869,7 +827,7 @@ set_shadow(float xoffset, float yoffset) {
 INLINE void TextNode::
 INLINE void TextNode::
 clear_shadow() {
 clear_shadow() {
   _flags &= ~F_has_shadow;
   _flags &= ~F_has_shadow;
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -911,7 +869,7 @@ get_shadow() const {
 INLINE void TextNode::
 INLINE void TextNode::
 set_bin(const string &bin) {
 set_bin(const string &bin) {
   _bin = bin;
   _bin = bin;
-  rebuild(false);
+  invalidate_no_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -967,7 +925,7 @@ get_bin() const {
 INLINE int TextNode::
 INLINE int TextNode::
 set_draw_order(int draw_order) {
 set_draw_order(int draw_order) {
   _draw_order = draw_order;
   _draw_order = draw_order;
-  rebuild(false);
+  invalidate_no_measure();
   return _draw_order + 3;
   return _draw_order + 3;
 }
 }
 
 
@@ -990,7 +948,7 @@ get_draw_order() const {
 INLINE void TextNode::
 INLINE void TextNode::
 set_transform(const LMatrix4f &transform) {
 set_transform(const LMatrix4f &transform) {
   _transform = transform;
   _transform = transform;
-  rebuild(true);
+  invalidate_with_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1012,7 +970,7 @@ get_transform() const {
 INLINE void TextNode::
 INLINE void TextNode::
 set_coordinate_system(CoordinateSystem coordinate_system) {
 set_coordinate_system(CoordinateSystem coordinate_system) {
   _coordinate_system = coordinate_system;
   _coordinate_system = coordinate_system;
-  rebuild(true);
+  invalidate_with_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1033,9 +991,11 @@ get_coordinate_system() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void TextNode::
 INLINE void TextNode::
 set_text(const string &text) {
 set_text(const string &text) {
-  _text = text;
-  _flags = (_flags | F_got_text) & ~F_got_wtext;
-  rebuild(true);
+  if (!has_text() || _text != text) {
+    _text = text;
+    _flags = (_flags | F_got_text) & ~F_got_wtext;
+    invalidate_with_measure();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1048,7 +1008,7 @@ clear_text() {
   _text = string();
   _text = string();
   _wtext = wstring();
   _wtext = wstring();
   _flags |= (F_got_text | F_got_wtext);
   _flags |= (F_got_text | F_got_wtext);
-  rebuild(true);
+  invalidate_with_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1089,7 +1049,7 @@ INLINE void TextNode::
 append_text(const string &text) {
 append_text(const string &text) {
   _text = get_text() + text;
   _text = get_text() + text;
   _flags = (_flags | F_got_text) & ~F_got_wtext;
   _flags = (_flags | F_got_text) & ~F_got_wtext;
-  rebuild(true);
+  invalidate_with_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1103,7 +1063,7 @@ INLINE void TextNode::
 append_char(int character) {
 append_char(int character) {
   _wtext = get_wtext() + wstring(1, (wchar_t)character);
   _wtext = get_wtext() + wstring(1, (wchar_t)character);
   _flags = (_flags | F_got_wtext) & ~F_got_text;
   _flags = (_flags | F_got_wtext) & ~F_got_text;
-  rebuild(true);
+  invalidate_with_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1159,48 +1119,6 @@ calc_width(const string &line) const {
   return _font->calc_width(decode_text(line));
   return _font->calc_width(decode_text(line));
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: TextNode::rebuild
-//       Access: Published
-//  Description: Updates the TextNode, if it is not frozen, or marks
-//               the TextNode as requiring an update if it is.  If the
-//               text is currently frozen, nothing will be done until
-//               it is thawed, unless needs_measure is true, in which
-//               case the text will be re-measured even if it is
-//               currently frozen.
-//
-//               Normally, this function is called automatically
-//               whenever any of the parameters changes.  It should
-//               not need to be called explicitly unless something
-//               goes wrong.
-////////////////////////////////////////////////////////////////////
-INLINE void TextNode::
-rebuild(bool needs_measure) {
-  if (_freeze_level <= 0) {
-    do_rebuild();
-  } else {
-    _needs_rebuild = true;
-
-    if (needs_measure) {
-      measure();
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: TextNode::measure
-//       Access: Published
-//  Description: Measures the extent of the text as it will be placed,
-//               without actually placing it.  Normally, this function
-//               is called automatically whenever any of the
-//               parameters changes.  It should not need to be called
-//               explicitly unless something goes wrong.
-////////////////////////////////////////////////////////////////////
-INLINE void TextNode::
-measure() {
-  do_measure();
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextNode::get_left
 //     Function: TextNode::get_left
 //       Access: Published
 //       Access: Published
@@ -1210,6 +1128,7 @@ measure() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float TextNode::
 INLINE float TextNode::
 get_left() const {
 get_left() const {
+  check_measure();
   return _ul2d[0];
   return _ul2d[0];
 }
 }
 
 
@@ -1222,6 +1141,7 @@ get_left() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float TextNode::
 INLINE float TextNode::
 get_right() const {
 get_right() const {
+  check_measure();
   return _lr2d[0];
   return _lr2d[0];
 }
 }
 
 
@@ -1234,6 +1154,7 @@ get_right() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float TextNode::
 INLINE float TextNode::
 get_bottom() const {
 get_bottom() const {
+  check_measure();
   return _lr2d[1];
   return _lr2d[1];
 }
 }
 
 
@@ -1246,6 +1167,7 @@ get_bottom() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float TextNode::
 INLINE float TextNode::
 get_top() const {
 get_top() const {
+  check_measure();
   return _ul2d[1];
   return _ul2d[1];
 }
 }
 
 
@@ -1257,7 +1179,8 @@ get_top() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float TextNode::
 INLINE float TextNode::
 get_height() const {
 get_height() const {
-  return get_top() - get_bottom();
+  check_measure();
+  return _ul2d[1] - _lr2d[1];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1268,7 +1191,8 @@ get_height() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE float TextNode::
 INLINE float TextNode::
 get_width() const {
 get_width() const {
-  return get_right() - get_left();
+  check_measure();
+  return _lr2d[0] - _ul2d[0];
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1280,6 +1204,7 @@ get_width() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE LPoint3f TextNode::
 INLINE LPoint3f TextNode::
 get_upper_left_3d() const {
 get_upper_left_3d() const {
+  check_measure();
   return _ul3d;
   return _ul3d;
 }
 }
 
 
@@ -1292,6 +1217,7 @@ get_upper_left_3d() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE LPoint3f TextNode::
 INLINE LPoint3f TextNode::
 get_lower_right_3d() const {
 get_lower_right_3d() const {
+  check_measure();
   return _lr3d;
   return _lr3d;
 }
 }
 
 
@@ -1304,9 +1230,23 @@ get_lower_right_3d() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE int TextNode::
 INLINE int TextNode::
 get_num_rows() const {
 get_num_rows() const {
+  check_measure();
   return _num_rows;
   return _num_rows;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::update
+//       Access: Published
+//  Description: Can be called after the TextNode has been fully
+//               configured, to force the node to recompute its text
+//               immediately, rather than waiting for it to be drawn.
+//               This call is optional.
+////////////////////////////////////////////////////////////////////
+INLINE void TextNode::
+update() {
+  check_rebuild();
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: TextNode::set_wtext
 //     Function: TextNode::set_wtext
 //       Access: Public
 //       Access: Public
@@ -1317,9 +1257,11 @@ get_num_rows() const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void TextNode::
 INLINE void TextNode::
 set_wtext(const wstring &wtext) {
 set_wtext(const wstring &wtext) {
-  _wtext = wtext;
-  _flags = (_flags | F_got_wtext) & ~F_got_text;
-  rebuild(true);
+  if (!has_text() || _wtext != wtext) {
+    _wtext = wtext;
+    _flags = (_flags | F_got_wtext) & ~F_got_text;
+    invalidate_with_measure();
+  }
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1347,7 +1289,7 @@ INLINE void TextNode::
 append_wtext(const wstring &wtext) {
 append_wtext(const wstring &wtext) {
   _wtext = get_wtext() + wtext;
   _wtext = get_wtext() + wtext;
   _flags = (_flags | F_got_wtext) & ~F_got_text;
   _flags = (_flags | F_got_wtext) & ~F_got_text;
-  rebuild(true);
+  invalidate_with_measure();
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -1378,3 +1320,60 @@ wordwrap_to(const wstring &wtext, float wordwrap_width,
   nassertr(_font != (TextFont *)NULL, wtext);
   nassertr(_font != (TextFont *)NULL, wtext);
   return _font->wordwrap_to(wtext, wordwrap_width, preserve_trailing_whitespace);
   return _font->wordwrap_to(wtext, wordwrap_width, preserve_trailing_whitespace);
 }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::invalidate_no_measure
+//       Access: Private
+//  Description: Called internally whenever some state on the TextNode
+//               changes, requiring the internal geometry to be
+//               recomputed, but which will not result in a change in
+//               the size or shape of the text (for instance, the text
+//               color changes).
+////////////////////////////////////////////////////////////////////
+INLINE void TextNode::
+invalidate_no_measure() {
+  _flags |= F_needs_rebuild;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::invalidate_with_measure
+//       Access: Private
+//  Description: Called internally whenever some state on the TextNode
+//               changes, requiring the internal geometry to be
+//               recomputed, and which will may result in a change in
+//               the size or shape of the text (for instance, the text
+//               scale changes).
+////////////////////////////////////////////////////////////////////
+INLINE void TextNode::
+invalidate_with_measure() {
+  _flags |= (F_needs_rebuild | F_needs_measure);
+  mark_bound_stale();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::check_rebuild
+//       Access: Private
+//  Description: Called internally to call do_rebuild() if necessary
+//               (that is, if the internal geometry has changed
+//               recently).
+////////////////////////////////////////////////////////////////////
+INLINE void TextNode::
+check_rebuild() const {
+  if ((_flags & F_needs_rebuild) != 0) {
+    ((TextNode *)this)->do_rebuild();
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::check_measure
+//       Access: Private
+//  Description: Called internally to call do_measure() if necessary;
+//               this will remeasure the text without necessarily
+//               rebuilding it.
+////////////////////////////////////////////////////////////////////
+INLINE void TextNode::
+check_measure() const {
+  if ((_flags & F_needs_measure) != 0) {
+    ((TextNode *)this)->do_measure();
+  }
+}

+ 216 - 26
panda/src/text/textNode.cxx

@@ -30,11 +30,17 @@
 #include "notify.h"
 #include "notify.h"
 #include "transformState.h"
 #include "transformState.h"
 #include "colorAttrib.h"
 #include "colorAttrib.h"
+#include "colorScaleAttrib.h"
 #include "cullBinAttrib.h"
 #include "cullBinAttrib.h"
 #include "textureAttrib.h"
 #include "textureAttrib.h"
 #include "transparencyAttrib.h"
 #include "transparencyAttrib.h"
 #include "sceneGraphReducer.h"
 #include "sceneGraphReducer.h"
 #include "indent.h"
 #include "indent.h"
+#include "cullTraverser.h"
+#include "cullTraverserData.h"
+#include "geometricBoundingVolume.h"
+#include "accumulatedAttribs.h"
+#include "dcast.h"
 
 
 #include <stdio.h>
 #include <stdio.h>
 #include <ctype.h>
 #include <ctype.h>
@@ -83,9 +89,6 @@ TextNode(const string &name) : PandaNode(name) {
   _ul3d.set(0.0f, 0.0f, 0.0f);
   _ul3d.set(0.0f, 0.0f, 0.0f);
   _lr3d.set(0.0f, 0.0f, 0.0f);
   _lr3d.set(0.0f, 0.0f, 0.0f);
   _num_rows = 0;
   _num_rows = 0;
-
-  _freeze_level = 0;
- _needs_rebuild = false;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -302,6 +305,9 @@ generate() {
   _ul3d = _ul3d * _transform;
   _ul3d = _ul3d * _transform;
   _lr3d = _lr3d * _transform;
   _lr3d = _lr3d * _transform;
 
 
+  // Incidentally, that means we don't need to measure the text now.
+  _flags &= ~F_needs_measure;
+
 
 
   // Now deal with all the decorations.
   // Now deal with all the decorations.
 
 
@@ -462,15 +468,212 @@ decode_text(const string &text) const {
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
-//     Function: TextNode::xform
+//     Function: TextNode::get_unsafe_to_apply_attribs
 //       Access: Public, Virtual
 //       Access: Public, Virtual
-//  Description: Transforms the contents of this PandaNode by the
-//               indicated matrix, if it means anything to do so.  For
-//               most kinds of PandaNodes, this does nothing.
+//  Description: Returns the union of all attributes from
+//               SceneGraphReducer::AttribTypes that may not safely be
+//               applied to the vertices of this node.  If this is
+//               nonzero, these attributes must be dropped at this
+//               node as a state change.
+//
+//               This is a generalization of safe_to_transform().
+////////////////////////////////////////////////////////////////////
+int TextNode::
+get_unsafe_to_apply_attribs() const {
+  // We have no way to apply these kinds of attributes to our
+  // TextNode, so insist they get dropped into the PandaNode's basic
+  // state.
+  return 
+    SceneGraphReducer::TT_tex_matrix | 
+    SceneGraphReducer::TT_other;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::apply_attribs_to_vertices
+//       Access: Public, Virtual
+//  Description: Applies whatever attributes are specified in the
+//               AccumulatedAttribs object (and by the attrib_types
+//               bitmask) to the vertices on this node, if
+//               appropriate.  If this node uses geom arrays like a
+//               GeomNode, the supplied GeomTransformer may be used to
+//               unify shared arrays across multiple different nodes.
+//
+//               This is a generalization of xform().
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextNode::
 void TextNode::
-xform(const LMatrix4f &mat) {
-  _transform *= mat;
+apply_attribs_to_vertices(const AccumulatedAttribs &attribs, int attrib_types,
+                          GeomTransformer &transformer) {
+  if ((attrib_types & SceneGraphReducer::TT_transform) != 0) {
+    const LMatrix4f &mat = attribs._transform->get_mat();
+    _transform *= mat;
+
+    if ((_flags & F_needs_measure) == 0) {
+      // If we already have a measure, transform it too.  We don't
+      // need to invalidate the 2-d parts, since that's not affected
+      // by the transform anyway.
+      _ul3d = _ul3d * mat;
+      _lr3d = _lr3d * mat;
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_color) != 0) {
+    if (attribs._color != (const RenderAttrib *)NULL) {
+      const ColorAttrib *ca = DCAST(ColorAttrib, attribs._color);
+      if (ca->get_color_type() == ColorAttrib::T_flat) {
+        const Colorf &c = ca->get_color();
+        _text_color = c;
+        _frame_color = c;
+        _card_color = c;
+        _shadow_color = c;
+        _flags |= F_has_text_color;
+      }
+    }
+  }
+  if ((attrib_types & SceneGraphReducer::TT_color_scale) != 0) {
+    if (attribs._color_scale != (const RenderAttrib *)NULL) {
+      const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attribs._color_scale);
+      const LVecBase4f &s = csa->get_scale();
+      if (s != LVecBase4f(1.0f, 1.0f, 1.0f, 1.0f)) {
+        _text_color[0] *= s[0];
+        _text_color[1] *= s[1];
+        _text_color[2] *= s[2];
+        _text_color[3] *= s[3];
+        _frame_color[0] *= s[0];
+        _frame_color[1] *= s[1];
+        _frame_color[2] *= s[2];
+        _frame_color[3] *= s[3];
+        _card_color[0] *= s[0];
+        _card_color[1] *= s[1];
+        _card_color[2] *= s[2];
+        _card_color[3] *= s[3];
+        _shadow_color[0] *= s[0];
+        _shadow_color[1] *= s[1];
+        _shadow_color[2] *= s[2];
+        _shadow_color[3] *= s[3];
+      }
+    }
+  }
+
+  // Now propagate the attributes down to our already-generated
+  // geometry, if we have any.
+  if ((_flags & F_needs_rebuild) == 0 && 
+      _internal_geom != (PandaNode *)NULL) {
+    SceneGraphReducer gr;
+    gr.apply_attribs(_internal_geom, attribs, attrib_types, transformer);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::calc_tight_bounds
+//       Access: Public, Virtual
+//  Description: This is used to support
+//               NodePath::calc_tight_bounds().  It is not intended to
+//               be called directly, and it has nothing to do with the
+//               normal Panda bounding-volume computation.
+//
+//               If the node contains any geometry, this updates
+//               min_point and max_point to enclose its bounding box.
+//               found_any is to be set true if the node has any
+//               geometry at all, or left alone if it has none.  This
+//               method may be called over several nodes, so it may
+//               enter with min_point, max_point, and found_any
+//               already set.
+////////////////////////////////////////////////////////////////////
+CPT(TransformState) TextNode::
+calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point, bool &found_any,
+                  const TransformState *transform) const {
+  CPT(TransformState) next_transform = 
+    PandaNode::calc_tight_bounds(min_point, max_point, found_any, transform);
+
+  check_rebuild();
+
+  if (_internal_geom != (PandaNode *)NULL) {
+    _internal_geom->calc_tight_bounds(min_point, max_point, 
+                                      found_any, next_transform);
+  }
+
+  return next_transform;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::has_cull_callback
+//       Access: Protected, Virtual
+//  Description: Should be overridden by derived classes to return
+//               true if cull_callback() has been defined.  Otherwise,
+//               returns false to indicate cull_callback() does not
+//               need to be called for this node during the cull
+//               traversal.
+////////////////////////////////////////////////////////////////////
+bool TextNode::
+has_cull_callback() const {
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::cull_callback
+//       Access: Protected, Virtual
+//  Description: If has_cull_callback() returns true, this function
+//               will be called during the cull traversal to perform
+//               any additional operations that should be performed at
+//               cull time.  This may include additional manipulation
+//               of render state or additional visible/invisible
+//               decisions, or any other arbitrary operation.
+//
+//               By the time this function is called, the node has
+//               already passed the bounding-volume test for the
+//               viewing frustum, and the node's transform and state
+//               have already been applied to the indicated
+//               CullTraverserData object.
+//
+//               The return value is true if this node should be
+//               visible, or false if it should be culled.
+////////////////////////////////////////////////////////////////////
+bool TextNode::
+cull_callback(CullTraverser *trav, CullTraverserData &data) {
+  check_rebuild();
+  if (_internal_geom != (PandaNode *)NULL) {
+    // Render the text with this node.
+    CullTraverserData next_data(data, _internal_geom);
+    trav->traverse(next_data);
+  }
+
+  // Now continue to render everything else below this node.
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TextNode::recompute_internal_bound
+//       Access: Protected, Virtual
+//  Description: Called when needed to recompute the node's
+//               _internal_bound object.  Nodes that contain anything
+//               of substance should redefine this to do the right
+//               thing.
+////////////////////////////////////////////////////////////////////
+BoundingVolume *TextNode::
+recompute_internal_bound() {
+  // First, get ourselves a fresh, empty bounding volume.
+  BoundingVolume *bound = PandaNode::recompute_internal_bound();
+  nassertr(bound != (BoundingVolume *)NULL, bound);
+
+  GeometricBoundingVolume *gbv = DCAST(GeometricBoundingVolume, bound);
+
+  // Now enclose the bounding box around the text.  We can do this
+  // without actually generating the text, if we have at least
+  // measured it.
+  check_measure();
+
+  LPoint3f vertices[8];
+  vertices[0].set(_ul3d[0], _ul3d[1], _ul3d[2]);
+  vertices[1].set(_ul3d[0], _ul3d[1], _lr3d[2]);
+  vertices[2].set(_ul3d[0], _lr3d[1], _ul3d[2]);
+  vertices[3].set(_ul3d[0], _lr3d[1], _lr3d[2]);
+  vertices[4].set(_lr3d[0], _ul3d[1], _ul3d[2]);
+  vertices[5].set(_lr3d[0], _ul3d[1], _lr3d[2]);
+  vertices[6].set(_lr3d[0], _lr3d[1], _ul3d[2]);
+  vertices[7].set(_lr3d[0], _lr3d[1], _lr3d[2]);
+
+  gbv->around(vertices, vertices + 8);
+
+  return bound;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -596,23 +799,8 @@ expand_amp_sequence(StringDecoder &decoder) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextNode::
 void TextNode::
 do_rebuild() {
 do_rebuild() {
-  _needs_rebuild = false;
-
-  remove_all_children();
-
-  PT(PandaNode) new_text = generate();
-  if (new_text != (PandaNode *)NULL) {
-    add_child(new_text);
-
-    /*
-    // And we flatten one more time, to remove the new node itself if
-    // possible (it might be an unneeded node above multiple
-    // children).  This flatten operation should be fairly
-    // lightweight; it's already pretty flat.
-    SceneGraphReducer gr(RenderRelation::get_class_type());
-    gr.flatten(this, false);
-    */
-  }
+  _flags &= ~(F_needs_rebuild | F_needs_measure);
+  _internal_geom = generate();
 }
 }
 
 
 
 
@@ -625,6 +813,8 @@ do_rebuild() {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 void TextNode::
 void TextNode::
 do_measure() {
 do_measure() {
+  _flags &= ~F_needs_measure;
+
   _ul2d.set(0.0f, 0.0f);
   _ul2d.set(0.0f, 0.0f);
   _lr2d.set(0.0f, 0.0f);
   _lr2d.set(0.0f, 0.0f);
   _ul3d.set(0.0f, 0.0f, 0.0f);
   _ul3d.set(0.0f, 0.0f, 0.0f);

+ 34 - 22
panda/src/text/textNode.h

@@ -38,22 +38,19 @@ class StringDecoder;
 //               represent the indicated text.
 //               represent the indicated text.
 //
 //
 //               The TextNode may be used in one of two ways.
 //               The TextNode may be used in one of two ways.
-//               Naively, it may be parented to the scene graph
-//               directly; used in this way, you can optionally call
-//               freeze() and thaw() between changing many parameters
-//               in the text at once, to avoid unnecessary expensive
-//               regeneration with each parameter change.  However, it
-//               will work, if slowly, even if you never call freeze()
-//               and thaw().
+//               Naively, it may simply be parented directly into the
+//               scene graph and rendered as if it were a GeomNode; in
+//               this mode, the actual polygon geometry that renders
+//               the text is not directly visible or accessible, but
+//               remains hidden within the TextNode.
 //
 //
 //               The second way TextNode may be used is as a text
 //               The second way TextNode may be used is as a text
-//               generator.  To use it in this way, call freeze() once
-//               on the TextNode when you create it, and never call
-//               thaw().  Do not parent the TextNode to the scene
-//               graph; instea, set the properties of the text and
-//               call generate() to return a node which you may parent
-//               wherever you like.  Each time you call generate() a
-//               new node is returned.
+//               generator.  To use it in this way, do not parent the
+//               TextNode to the scene graph; instead, set the
+//               properties of the text and call generate() to return
+//               an ordinary node, containing ordinary geometry, which
+//               you may use however you like.  Each time you call
+//               generate() a new node is returned.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA TextNode : public PandaNode {
 class EXPCL_PANDA TextNode : public PandaNode {
 PUBLISHED:
 PUBLISHED:
@@ -71,9 +68,8 @@ PUBLISHED:
     E_utf8,
     E_utf8,
     E_unicode
     E_unicode
   };
   };
-
+ 
   INLINE int freeze();
   INLINE int freeze();
-  INLINE int get_freeze_level() const;
   INLINE int thaw();
   INLINE int thaw();
 
 
   INLINE void set_font(TextFont *font);
   INLINE void set_font(TextFont *font);
@@ -188,9 +184,6 @@ PUBLISHED:
 
 
   virtual void write(ostream &out, int indent_level = 0) const;
   virtual void write(ostream &out, int indent_level = 0) const;
 
 
-  INLINE void rebuild(bool needs_measure);
-  INLINE void measure();
-
   // The following functions return information about the text that
   // The following functions return information about the text that
   // was last built (and is currently visible).
   // was last built (and is currently visible).
   INLINE float get_left() const;
   INLINE float get_left() const;
@@ -206,6 +199,7 @@ PUBLISHED:
   INLINE int get_num_rows() const;
   INLINE int get_num_rows() const;
 
 
   PT(PandaNode) generate();
   PT(PandaNode) generate();
+  INLINE void update();
 
 
 public:
 public:
   // Direct support for wide-character strings.
   // Direct support for wide-character strings.
@@ -222,12 +216,29 @@ public:
   wstring decode_text(const string &text) const;
   wstring decode_text(const string &text) const;
 
 
   // From parent class PandaNode
   // From parent class PandaNode
-  virtual void xform(const LMatrix4f &mat);
+  virtual int get_unsafe_to_apply_attribs() const;
+  virtual void apply_attribs_to_vertices(const AccumulatedAttribs &attribs,
+                                         int attrib_types,
+                                         GeomTransformer &transformer);
+  virtual CPT(TransformState)
+    calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point,
+                      bool &found_any,
+                      const TransformState *transform) const;
+
+  virtual bool has_cull_callback() const;
+  virtual bool cull_callback(CullTraverser *trav, CullTraverserData &data);
+
+  virtual BoundingVolume *recompute_internal_bound();
 
 
 private:
 private:
   wstring decode_text_impl(StringDecoder &decoder) const;
   wstring decode_text_impl(StringDecoder &decoder) const;
   int expand_amp_sequence(StringDecoder &decoder) const;
   int expand_amp_sequence(StringDecoder &decoder) const;
 
 
+  INLINE void invalidate_no_measure();
+  INLINE void invalidate_with_measure();
+  INLINE void check_rebuild() const;
+  INLINE void check_measure() const;
+
   void do_rebuild();
   void do_rebuild();
   void do_measure();
   void do_measure();
 
 
@@ -246,6 +257,7 @@ private:
   PT(PandaNode) make_card_with_border();
   PT(PandaNode) make_card_with_border();
 
 
   PT(TextFont) _font;
   PT(TextFont) _font;
+  PT(PandaNode) _internal_geom;
 
 
   Encoding _encoding;
   Encoding _encoding;
   float _slant;
   float _slant;
@@ -271,6 +283,8 @@ private:
     F_expand_amp       =  0x0800,
     F_expand_amp       =  0x0800,
     F_got_text         =  0x1000,
     F_got_text         =  0x1000,
     F_got_wtext        =  0x2000,
     F_got_wtext        =  0x2000,
+    F_needs_rebuild    =  0x4000,
+    F_needs_measure    =  0x8000,
   };
   };
 
 
   int _flags;
   int _flags;
@@ -296,8 +310,6 @@ private:
   LPoint2f _ul2d, _lr2d;
   LPoint2f _ul2d, _lr2d;
   LPoint3f _ul3d, _lr3d;
   LPoint3f _ul3d, _lr3d;
   int _num_rows;
   int _num_rows;
-  int _freeze_level;
-  bool _needs_rebuild;
 
 
 public:
 public:
   static Encoding _default_encoding;
   static Encoding _default_encoding;