From 2fa7929b45f4e91044143dd5946031671ede6ef3 Mon Sep 17 00:00:00 2001 From: David Rose Date: Fri, 31 Jan 2003 17:45:42 +0000 Subject: [PATCH] dynamic TextFont; scene graph improvements to support this --- panda/src/pgraph/Sources.pp | 3 + panda/src/pgraph/accumulatedAttribs.I | 58 +++++ panda/src/pgraph/accumulatedAttribs.cxx | 190 ++++++++++++++ panda/src/pgraph/accumulatedAttribs.h | 59 +++++ panda/src/pgraph/geomNode.cxx | 111 ++++++++ panda/src/pgraph/geomNode.h | 7 + panda/src/pgraph/nodePath.cxx | 56 +---- panda/src/pgraph/nodePath.h | 3 - panda/src/pgraph/pandaNode.cxx | 85 +++++++ panda/src/pgraph/pandaNode.h | 13 +- panda/src/pgraph/pgraph_composite1.cxx | 1 + panda/src/pgraph/sceneGraphReducer.I | 67 +++-- panda/src/pgraph/sceneGraphReducer.cxx | 322 ++---------------------- panda/src/pgraph/sceneGraphReducer.h | 59 ++--- panda/src/pgui/pgItem.cxx | 1 - panda/src/text/textNode.I | 283 +++++++++++---------- panda/src/text/textNode.cxx | 242 ++++++++++++++++-- panda/src/text/textNode.h | 56 +++-- 18 files changed, 1004 insertions(+), 612 deletions(-) create mode 100644 panda/src/pgraph/accumulatedAttribs.I create mode 100644 panda/src/pgraph/accumulatedAttribs.cxx create mode 100644 panda/src/pgraph/accumulatedAttribs.h diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index 21c6dae47d..9234b777fd 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -8,6 +8,7 @@ #define TARGET pgraph #define SOURCES \ + accumulatedAttribs.I accumulatedAttribs.h \ alphaTestAttrib.I alphaTestAttrib.h \ ambientLight.I ambientLight.h \ bamFile.I bamFile.h \ @@ -91,6 +92,7 @@ #define COMBINED_SOURCES $[TARGET]_composite1.cxx $[TARGET]_composite2.cxx #define INCLUDED_SOURCES \ + accumulatedAttribs.cxx \ alphaTestAttrib.cxx \ ambientLight.cxx \ bamFile.cxx \ @@ -173,6 +175,7 @@ workingNodePath.cxx #define INSTALL_HEADERS \ + accumulatedAttribs.I accumulatedAttribs.h \ alphaTestAttrib.I alphaTestAttrib.h \ ambientLight.I ambientLight.h \ bamFile.I bamFile.h \ diff --git a/panda/src/pgraph/accumulatedAttribs.I b/panda/src/pgraph/accumulatedAttribs.I new file mode 100644 index 0000000000..bd5098f8e5 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// 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 ©) : + _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 ©) { + _transform = copy._transform; + _color = copy._color; + _color_scale = copy._color_scale; + _tex_matrix = copy._tex_matrix; + _other = copy._other; +} diff --git a/panda/src/pgraph/accumulatedAttribs.cxx b/panda/src/pgraph/accumulatedAttribs.cxx new file mode 100644 index 0000000000..28fc4aa2da --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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(); + } +} diff --git a/panda/src/pgraph/accumulatedAttribs.h b/panda/src/pgraph/accumulatedAttribs.h new file mode 100644 index 0000000000..6b7c811334 --- /dev/null +++ b/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 +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + +#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 ©); + INLINE void operator = (const AccumulatedAttribs ©); + + 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 + + diff --git a/panda/src/pgraph/geomNode.cxx b/panda/src/pgraph/geomNode.cxx index ed36335b46..86efafba7e 100644 --- a/panda/src/pgraph/geomNode.cxx +++ b/panda/src/pgraph/geomNode.cxx @@ -18,6 +18,8 @@ #include "geomNode.h" #include "geomTransformer.h" +#include "sceneGraphReducer.h" +#include "accumulatedAttribs.h" #include "bamReader.h" #include "bamWriter.h" #include "datagram.h" @@ -154,6 +156,62 @@ make_copy() const { 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 // Access: Public, Virtual @@ -200,6 +258,59 @@ combine_with(PandaNode *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 // Access: Published diff --git a/panda/src/pgraph/geomNode.h b/panda/src/pgraph/geomNode.h index 5654b313e1..54b3d58e98 100644 --- a/panda/src/pgraph/geomNode.h +++ b/panda/src/pgraph/geomNode.h @@ -44,8 +44,15 @@ protected: public: virtual ~GeomNode(); 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 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: INLINE int get_num_geoms() const; diff --git a/panda/src/pgraph/nodePath.cxx b/panda/src/pgraph/nodePath.cxx index 2819c5c7e2..b1397d05b6 100644 --- a/panda/src/pgraph/nodePath.cxx +++ b/panda/src/pgraph/nodePath.cxx @@ -2788,8 +2788,8 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) { nassertr_always(!is_empty(), 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; } @@ -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 // Access: Private diff --git a/panda/src/pgraph/nodePath.h b/panda/src/pgraph/nodePath.h index d53bebb2ab..40b834ae55 100644 --- a/panda/src/pgraph/nodePath.h +++ b/panda/src/pgraph/nodePath.h @@ -542,9 +542,6 @@ private: void r_adjust_all_priorities(PandaNode *node, int adjustment); 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 Textures; Texture *r_find_texture(PandaNode *node, const RenderState *state, diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index 19234b864f..ad551f8b76 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -23,6 +23,8 @@ #include "bamWriter.h" #include "indent.h" #include "geometricBoundingVolume.h" +#include "sceneGraphReducer.h" +#include "accumulatedAttribs.h" TypeHandle PandaNode::_type_handle; @@ -501,6 +503,18 @@ safe_to_combine() const { 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 // Access: Public, Virtual @@ -513,6 +527,42 @@ preserve_name() const { 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 // Access: Public, Virtual @@ -568,6 +618,41 @@ combine_with(PandaNode *other) { 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 // Access: Public, Virtual diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 10a8762040..eaa983681a 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -45,6 +45,8 @@ class CullTraverser; class CullTraverserData; class Light; class FactoryParams; +class AccumulatedAttribs; +class GeomTransformer; //////////////////////////////////////////////////////////////////// // Class : PandaNode @@ -71,10 +73,19 @@ public: virtual bool safe_to_transform() const; virtual bool safe_to_modify_transform() const; virtual bool safe_to_combine() const; + virtual bool safe_to_flatten_below() 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 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 cull_callback(CullTraverser *trav, CullTraverserData &data); virtual bool has_selective_visibility() const; diff --git a/panda/src/pgraph/pgraph_composite1.cxx b/panda/src/pgraph/pgraph_composite1.cxx index 5ea507592d..c9d0af3ae8 100644 --- a/panda/src/pgraph/pgraph_composite1.cxx +++ b/panda/src/pgraph/pgraph_composite1.cxx @@ -1,3 +1,4 @@ +#include "accumulatedAttribs.cxx" #include "ambientLight.cxx" #include "bamFile.cxx" #include "billboardEffect.cxx" diff --git a/panda/src/pgraph/sceneGraphReducer.I b/panda/src/pgraph/sceneGraphReducer.I index 78801e3bc6..07d8ef7c1e 100644 --- a/panda/src/pgraph/sceneGraphReducer.I +++ b/panda/src/pgraph/sceneGraphReducer.I @@ -18,39 +18,58 @@ //////////////////////////////////////////////////////////////////// -// Function: SceneGraphReducer::AccumulatedAttribs::Constructor -// Access: Public +// Function: SceneGraphReducer::Constructor +// Access: Published // Description: //////////////////////////////////////////////////////////////////// -INLINE SceneGraphReducer::AccumulatedAttribs:: -AccumulatedAttribs() { +INLINE SceneGraphReducer:: +SceneGraphReducer() { } //////////////////////////////////////////////////////////////////// -// Function: SceneGraphReducer::AccumulatedAttribs::Copy Constructor -// Access: Public +// Function: SceneGraphReducer::Destructor +// Access: Published // Description: //////////////////////////////////////////////////////////////////// -INLINE SceneGraphReducer::AccumulatedAttribs:: -AccumulatedAttribs(const SceneGraphReducer::AccumulatedAttribs ©) : - _transform(copy._transform), - _color(copy._color), - _color_scale(copy._color_scale), - _tex_matrix(copy._tex_matrix), - _other(copy._other) -{ +INLINE SceneGraphReducer:: +~SceneGraphReducer() { +} + + +//////////////////////////////////////////////////////////////////// +// 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::AccumulatedAttribs::Copy Assignment -// Access: Public -// Description: +// 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 ©) { - _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); } diff --git a/panda/src/pgraph/sceneGraphReducer.cxx b/panda/src/pgraph/sceneGraphReducer.cxx index 33f9fc7242..4c2abd85f3 100644 --- a/panda/src/pgraph/sceneGraphReducer.cxx +++ b/panda/src/pgraph/sceneGraphReducer.cxx @@ -21,6 +21,7 @@ #include "colorAttrib.h" #include "texMatrixAttrib.h" #include "colorScaleAttrib.h" +#include "accumulatedAttribs.h" #include "geomNode.h" #include "pointerTo.h" @@ -28,292 +29,15 @@ #include "indent.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 -// Access: Public +// Access: Published // Description: Simplifies the graph by removing unnecessary nodes // and nodes. // // In general, a node (and its parent node) is a // 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 // 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(). //////////////////////////////////////////////////////////////////// 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()) { pgraph_cat.debug() << "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); } - trans.collect(node, attrib_types); + AccumulatedAttribs next_attribs(attribs); + next_attribs.collect(node, attrib_types); if (pgraph_cat.is_debug()) { pgraph_cat.debug() << "Got attribs from " << *node << "\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 @@ -405,6 +130,7 @@ r_apply_attribs(PandaNode *node, int attrib_types, } apply_types |= TT_transform; } + apply_types |= node->get_unsafe_to_apply_attribs(); // Also, check the children of this node. If any of them indicates // 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, - 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. - 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? bool resist_copy = false; @@ -473,14 +199,14 @@ r_apply_attribs(PandaNode *node, int attrib_types, if (resist_copy) { // If any of our children should have been copied but weren't, we // 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. nassertv(num_children == node->get_num_children()); for (i = 0; i < num_children; 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 -// Access: Protected, Virtual +// Access: Protected // Description: Decides whether or not the indicated child node is a // 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:: 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 -// Access: Protected, Virtual +// Access: Protected // Description: Decides whether or not the indicated sibling nodes // should be collapsed into a single node or not. // 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 -// Access: Protected, Virtual +// Access: Protected // Description: Collapses together the indicated parent node and // child node and leaves the result attached to the // grandparent. The return value is true if the node is // successfully collapsed, false if we chickened out. -// -// This function may be extended in a user class to -// handle special kinds of nodes. //////////////////////////////////////////////////////////////////// bool SceneGraphReducer:: 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 -// Access: Protected, Virtual +// Access: Protected // Description: Performs the work of collapsing two sibling nodes // together into a single node, leaving the resulting // node attached to the parent. @@ -779,7 +500,7 @@ do_flatten_siblings(PandaNode *parent_node, PandaNode *child1, //////////////////////////////////////////////////////////////////// // Function: SceneGraphReducer::collapse_nodes -// Access: Protected, Virtual +// Access: Protected // Description: Collapses the two nodes into a single node, if // possible. The 'siblings' flag is true if the two // 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 // a new node altogether, or it may be NULL to indicate // 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:: collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) { @@ -799,7 +517,7 @@ collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) { //////////////////////////////////////////////////////////////////// // Function: SceneGraphReducer::choose_name -// Access: Protected, Virtual +// Access: Protected // Description: Chooses a suitable name for the collapsed node, based // on the names of the two sources nodes. //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/sceneGraphReducer.h b/panda/src/pgraph/sceneGraphReducer.h index 489df47448..89cb6ca4e6 100644 --- a/panda/src/pgraph/sceneGraphReducer.h +++ b/panda/src/pgraph/sceneGraphReducer.h @@ -23,11 +23,14 @@ #include "transformState.h" #include "renderAttrib.h" #include "renderState.h" +#include "accumulatedAttribs.h" #include "geomTransformer.h" #include "typedObject.h" #include "pointerTo.h" +class PandaNode; + /////////////////////////////////////////////////////////////////// // Class : SceneGraphReducer // Description : An interface for simplifying ("flattening") scene @@ -41,8 +44,8 @@ //////////////////////////////////////////////////////////////////// class EXPCL_PANDA SceneGraphReducer { PUBLISHED: - SceneGraphReducer(); - virtual ~SceneGraphReducer(); + INLINE SceneGraphReducer(); + INLINE ~SceneGraphReducer(); enum AttribTypes { TT_transform = 0x001, @@ -52,53 +55,35 @@ PUBLISHED: 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); protected: - class AccumulatedAttribs { - public: - INLINE AccumulatedAttribs(); - INLINE AccumulatedAttribs(const AccumulatedAttribs ©); - INLINE void operator = (const AccumulatedAttribs ©); - - 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, bool combine_siblings); 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: GeomTransformer _transformer; diff --git a/panda/src/pgui/pgItem.cxx b/panda/src/pgui/pgItem.cxx index bcda896641..aa93aab324 100644 --- a/panda/src/pgui/pgItem.cxx +++ b/panda/src/pgui/pgItem.cxx @@ -779,7 +779,6 @@ TextNode *PGItem:: get_text_node() { if (_text_node == (TextNode *)NULL) { _text_node = new TextNode("pguiText"); - _text_node->freeze(); _text_node->set_text_color(0.0f, 0.0f, 0.0f, 1.0f); // The default TextNode is aligned to the left, for the diff --git a/panda/src/text/textNode.I b/panda/src/text/textNode.I index 4f45a624d5..1709d3e57f 100644 --- a/panda/src/text/textNode.I +++ b/panda/src/text/textNode.I @@ -20,70 +20,25 @@ //////////////////////////////////////////////////////////////////// // Function: TextNode::freeze // 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:: 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 // 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:: 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) { if (_font != font) { _font = font; - rebuild(true); + invalidate_with_measure(); } } @@ -193,7 +148,7 @@ INLINE void TextNode:: set_slant(float slant) { if (_slant != slant) { _slant = slant; - rebuild(true); + invalidate_with_measure(); } } @@ -216,7 +171,7 @@ INLINE void TextNode:: set_align(TextNode::Alignment align_type) { if (_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) { _flags |= F_has_wordwrap; _wordwrap_width = wordwrap; - rebuild(true); + invalidate_with_measure(); } } @@ -255,7 +210,7 @@ INLINE void TextNode:: clear_wordwrap() { if (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) { _text_color = text_color; _flags |= F_has_text_color; - rebuild(false); + invalidate_no_measure(); } } @@ -313,7 +268,7 @@ INLINE void TextNode:: clear_text_color() { if (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) { if (_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; _card_border_size = size; _card_border_uv_portion = uv_portion; - rebuild(false); + invalidate_no_measure(); } } @@ -394,7 +349,7 @@ INLINE void TextNode:: clear_card_border() { if (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) { if (_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) { _flags |= F_has_card_texture; _card_texture = card_texture; - rebuild(false); + invalidate_no_measure(); } } } @@ -489,7 +444,7 @@ clear_card_texture() { if (has_card_texture()) { _flags &= ~F_has_card_texture; _card_texture = NULL; - rebuild(false); + invalidate_no_measure(); } } @@ -532,7 +487,7 @@ INLINE void TextNode:: set_shadow_color(const Colorf &shadow_color) { if (_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); _frame_ul.set(left, top); _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; _frame_ul.set(left, top); _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:: clear_frame() { _flags &= ~F_has_frame; - rebuild(false); + invalidate_no_measure(); } //////////////////////////////////////////////////////////////////// @@ -650,10 +605,11 @@ INLINE LVecBase4f TextNode:: get_frame_actual() const { nassertr(has_frame(), LVecBase4f(0.0, 0.0, 0.0, 0.0)); 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 { 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); _card_ul.set(left, top); _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; _card_ul.set(left, top); _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:: clear_card() { _flags &= ~F_has_card; - rebuild(false); + invalidate_no_measure(); } //////////////////////////////////////////////////////////////////// @@ -816,13 +772,15 @@ get_card_as_set() const { INLINE LVecBase4f TextNode:: get_card_actual() const { 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()) { - 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 { return get_card_as_set(); } @@ -857,7 +815,7 @@ INLINE void TextNode:: set_shadow(float xoffset, float yoffset) { _flags |= F_has_shadow; _shadow_offset.set(xoffset, yoffset); - rebuild(false); + invalidate_no_measure(); } //////////////////////////////////////////////////////////////////// @@ -869,7 +827,7 @@ set_shadow(float xoffset, float yoffset) { INLINE void TextNode:: clear_shadow() { _flags &= ~F_has_shadow; - rebuild(false); + invalidate_no_measure(); } //////////////////////////////////////////////////////////////////// @@ -911,7 +869,7 @@ get_shadow() const { INLINE void TextNode:: set_bin(const string &bin) { _bin = bin; - rebuild(false); + invalidate_no_measure(); } //////////////////////////////////////////////////////////////////// @@ -967,7 +925,7 @@ get_bin() const { INLINE int TextNode:: set_draw_order(int draw_order) { _draw_order = draw_order; - rebuild(false); + invalidate_no_measure(); return _draw_order + 3; } @@ -990,7 +948,7 @@ get_draw_order() const { INLINE void TextNode:: set_transform(const LMatrix4f &transform) { _transform = transform; - rebuild(true); + invalidate_with_measure(); } //////////////////////////////////////////////////////////////////// @@ -1012,7 +970,7 @@ get_transform() const { INLINE void TextNode:: set_coordinate_system(CoordinateSystem coordinate_system) { _coordinate_system = coordinate_system; - rebuild(true); + invalidate_with_measure(); } //////////////////////////////////////////////////////////////////// @@ -1033,9 +991,11 @@ get_coordinate_system() const { //////////////////////////////////////////////////////////////////// INLINE void TextNode:: 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(); _wtext = wstring(); _flags |= (F_got_text | F_got_wtext); - rebuild(true); + invalidate_with_measure(); } //////////////////////////////////////////////////////////////////// @@ -1089,7 +1049,7 @@ INLINE void TextNode:: append_text(const string &text) { _text = get_text() + text; _flags = (_flags | F_got_text) & ~F_got_wtext; - rebuild(true); + invalidate_with_measure(); } //////////////////////////////////////////////////////////////////// @@ -1103,7 +1063,7 @@ INLINE void TextNode:: append_char(int character) { _wtext = get_wtext() + wstring(1, (wchar_t)character); _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)); } -//////////////////////////////////////////////////////////////////// -// 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 // Access: Published @@ -1210,6 +1128,7 @@ measure() { //////////////////////////////////////////////////////////////////// INLINE float TextNode:: get_left() const { + check_measure(); return _ul2d[0]; } @@ -1222,6 +1141,7 @@ get_left() const { //////////////////////////////////////////////////////////////////// INLINE float TextNode:: get_right() const { + check_measure(); return _lr2d[0]; } @@ -1234,6 +1154,7 @@ get_right() const { //////////////////////////////////////////////////////////////////// INLINE float TextNode:: get_bottom() const { + check_measure(); return _lr2d[1]; } @@ -1246,6 +1167,7 @@ get_bottom() const { //////////////////////////////////////////////////////////////////// INLINE float TextNode:: get_top() const { + check_measure(); return _ul2d[1]; } @@ -1257,7 +1179,8 @@ get_top() const { //////////////////////////////////////////////////////////////////// INLINE float TextNode:: 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:: 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:: get_upper_left_3d() const { + check_measure(); return _ul3d; } @@ -1292,6 +1217,7 @@ get_upper_left_3d() const { //////////////////////////////////////////////////////////////////// INLINE LPoint3f TextNode:: get_lower_right_3d() const { + check_measure(); return _lr3d; } @@ -1304,9 +1230,23 @@ get_lower_right_3d() const { //////////////////////////////////////////////////////////////////// INLINE int TextNode:: get_num_rows() const { + check_measure(); 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 // Access: Public @@ -1317,9 +1257,11 @@ get_num_rows() const { //////////////////////////////////////////////////////////////////// INLINE void TextNode:: 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) { _wtext = get_wtext() + wtext; _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); 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(); + } +} diff --git a/panda/src/text/textNode.cxx b/panda/src/text/textNode.cxx index 38c483aa03..c2cf6bc10b 100644 --- a/panda/src/text/textNode.cxx +++ b/panda/src/text/textNode.cxx @@ -30,11 +30,17 @@ #include "notify.h" #include "transformState.h" #include "colorAttrib.h" +#include "colorScaleAttrib.h" #include "cullBinAttrib.h" #include "textureAttrib.h" #include "transparencyAttrib.h" #include "sceneGraphReducer.h" #include "indent.h" +#include "cullTraverser.h" +#include "cullTraverserData.h" +#include "geometricBoundingVolume.h" +#include "accumulatedAttribs.h" +#include "dcast.h" #include #include @@ -83,9 +89,6 @@ TextNode(const string &name) : PandaNode(name) { _ul3d.set(0.0f, 0.0f, 0.0f); _lr3d.set(0.0f, 0.0f, 0.0f); _num_rows = 0; - - _freeze_level = 0; - _needs_rebuild = false; } //////////////////////////////////////////////////////////////////// @@ -302,6 +305,9 @@ generate() { _ul3d = _ul3d * _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. @@ -462,15 +468,212 @@ decode_text(const string &text) const { } //////////////////////////////////////////////////////////////////// -// Function: TextNode::xform +// Function: TextNode::get_unsafe_to_apply_attribs // 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:: -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:: 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:: do_measure() { + _flags &= ~F_needs_measure; + _ul2d.set(0.0f, 0.0f); _lr2d.set(0.0f, 0.0f); _ul3d.set(0.0f, 0.0f, 0.0f); diff --git a/panda/src/text/textNode.h b/panda/src/text/textNode.h index edc3940bda..665e997f05 100644 --- a/panda/src/text/textNode.h +++ b/panda/src/text/textNode.h @@ -38,22 +38,19 @@ class StringDecoder; // represent the indicated text. // // 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 -// 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 { PUBLISHED: @@ -71,9 +68,8 @@ PUBLISHED: E_utf8, E_unicode }; - + INLINE int freeze(); - INLINE int get_freeze_level() const; INLINE int thaw(); INLINE void set_font(TextFont *font); @@ -188,9 +184,6 @@ PUBLISHED: 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 // was last built (and is currently visible). INLINE float get_left() const; @@ -206,6 +199,7 @@ PUBLISHED: INLINE int get_num_rows() const; PT(PandaNode) generate(); + INLINE void update(); public: // Direct support for wide-character strings. @@ -222,12 +216,29 @@ public: wstring decode_text(const string &text) const; // 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: wstring decode_text_impl(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_measure(); @@ -246,6 +257,7 @@ private: PT(PandaNode) make_card_with_border(); PT(TextFont) _font; + PT(PandaNode) _internal_geom; Encoding _encoding; float _slant; @@ -271,6 +283,8 @@ private: F_expand_amp = 0x0800, F_got_text = 0x1000, F_got_wtext = 0x2000, + F_needs_rebuild = 0x4000, + F_needs_measure = 0x8000, }; int _flags; @@ -296,8 +310,6 @@ private: LPoint2f _ul2d, _lr2d; LPoint3f _ul3d, _lr3d; int _num_rows; - int _freeze_level; - bool _needs_rebuild; public: static Encoding _default_encoding;