From 9c3a3c9c22babd34f54ef6adc4a5df5ea6ccd5f7 Mon Sep 17 00:00:00 2001 From: David Rose Date: Sat, 16 Mar 2002 00:24:10 +0000 Subject: [PATCH] pgraph flatten --- direct/src/ffi/FFIRename.py | 2 + direct/src/showbase/qpShowBase.py | 1 + panda/src/egg2pg/qpeggLoader.cxx | 4 - panda/src/egg2pg/qpload_egg_file.cxx | 13 +- panda/src/pgraph/Sources.pp | 6 + panda/src/pgraph/billboardEffect.cxx | 12 +- panda/src/pgraph/billboardEffect.h | 2 +- panda/src/pgraph/config_pgraph.cxx | 4 + panda/src/pgraph/config_pgraph.h | 1 + panda/src/pgraph/cullFaceAttrib.h | 2 +- panda/src/pgraph/decalEffect.cxx | 12 + panda/src/pgraph/decalEffect.h | 1 + panda/src/pgraph/pandaNode.I | 78 +++ panda/src/pgraph/pandaNode.cxx | 637 ++++++++++++++++-------- panda/src/pgraph/pandaNode.h | 36 +- panda/src/pgraph/pgraph_composite2.cxx | 2 + panda/src/pgraph/qpgeomNode.I | 3 + panda/src/pgraph/qpgeomNode.cxx | 68 +++ panda/src/pgraph/qpgeomNode.h | 4 + panda/src/pgraph/qpnodePath.I | 5 + panda/src/pgraph/qpnodePath.cxx | 27 +- panda/src/pgraph/qpnodePath.h | 4 +- panda/src/pgraph/renderEffect.cxx | 5 +- panda/src/pgraph/transparencyAttrib.cxx | 6 + panda/src/pgui/qppgItem.cxx | 7 +- panda/src/putil/cycleDataReader.I | 50 +- panda/src/putil/cycleDataReader.h | 3 +- panda/src/putil/cycleDataWriter.I | 71 ++- panda/src/putil/cycleDataWriter.h | 5 +- panda/src/testbed/pview.cxx | 3 + panda/src/text/qptextNode.cxx | 7 +- 31 files changed, 801 insertions(+), 280 deletions(-) diff --git a/direct/src/ffi/FFIRename.py b/direct/src/ffi/FFIRename.py index aec8caa110..8c58426c40 100644 --- a/direct/src/ffi/FFIRename.py +++ b/direct/src/ffi/FFIRename.py @@ -118,6 +118,7 @@ pgraphClassRenameDictionary = { 'PGEntry' : 'SpPGEntry', 'PGWaitBar' : 'SpPGWaitBar', 'PartBundleNode' : 'SpPartBundleNode', + 'SceneGraphReducer' : 'SpSceneGraphReducer', 'SequenceNode' : 'SpSequenceNode', 'TextNode' : 'SpTextNode', 'Trackball' : 'SpTrackball', @@ -150,6 +151,7 @@ pgraphClassRenameDictionary = { 'QpPGEntry' : 'PGEntry', 'QpPGWaitBar' : 'PGWaitBar', 'QpPartBundleNode' : 'PartBundleNode', + 'QpSceneGraphReducer' : 'SceneGraphReducer', 'QpSequenceNode' : 'SequenceNode', 'QpTextNode' : 'TextNode', 'QpTrackball' : 'Trackball', diff --git a/direct/src/showbase/qpShowBase.py b/direct/src/showbase/qpShowBase.py index 78d6c282fd..fd53a63859 100644 --- a/direct/src/showbase/qpShowBase.py +++ b/direct/src/showbase/qpShowBase.py @@ -216,6 +216,7 @@ class ShowBase: rendering 3-d geometry. """ self.render = NodePath('render') + self.render.setTwoSided(0) def setupRender2d(self): """setupRender2d(self) diff --git a/panda/src/egg2pg/qpeggLoader.cxx b/panda/src/egg2pg/qpeggLoader.cxx index 0cc431bb6f..3bfd64f586 100644 --- a/panda/src/egg2pg/qpeggLoader.cxx +++ b/panda/src/egg2pg/qpeggLoader.cxx @@ -1008,7 +1008,6 @@ setup_bucket(BuilderBucket &bucket, PandaNode *parent, break; default: - bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_none)); break; } @@ -1050,9 +1049,6 @@ setup_bucket(BuilderBucket &bucket, PandaNode *parent, // The primitive is marked with backface culling disabled--we want // to see both sides. bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none)); - - } else { - bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise)); } } diff --git a/panda/src/egg2pg/qpload_egg_file.cxx b/panda/src/egg2pg/qpload_egg_file.cxx index f49a98acd1..5d8a013c9a 100644 --- a/panda/src/egg2pg/qpload_egg_file.cxx +++ b/panda/src/egg2pg/qpload_egg_file.cxx @@ -19,11 +19,8 @@ #include "qpload_egg_file.h" #include "qpeggLoader.h" #include "config_egg2pg.h" - -/* -#include "sceneGraphReducer.h" +#include "qpsceneGraphReducer.h" #include "renderRelation.h" -*/ static PT(PandaNode) load_from_loader(qpEggLoader &loader) { @@ -37,13 +34,11 @@ load_from_loader(qpEggLoader &loader) { return NULL; } - /* - if (loader._root != (NamedNode *)NULL && egg_flatten) { - SceneGraphReducer gr(RenderRelation::get_class_type()); + if (loader._root != (PandaNode *)NULL && egg_flatten) { + qpSceneGraphReducer gr; int num_reduced = gr.flatten(loader._root, egg_flatten_siblings); - egg2pg_cat.info() << "Flattened " << num_reduced << " arcs.\n"; + egg2pg_cat.info() << "Flattened " << num_reduced << " nodes.\n"; } - */ return loader._root; } diff --git a/panda/src/pgraph/Sources.pp b/panda/src/pgraph/Sources.pp index c56cae2f8f..eb07fb15c6 100644 --- a/panda/src/pgraph/Sources.pp +++ b/panda/src/pgraph/Sources.pp @@ -35,6 +35,7 @@ qpfog.I qpfog.h \ fogAttrib.I fogAttrib.h \ qpgeomNode.I qpgeomNode.h \ + qpgeomTransformer.I qpgeomTransformer.h \ qplensNode.I qplensNode.h \ qplodNode.I qplodNode.h \ materialAttrib.I materialAttrib.h \ @@ -47,6 +48,7 @@ renderEffects.I renderEffects.h \ renderModeAttrib.I renderModeAttrib.h \ renderState.I renderState.h \ + qpsceneGraphReducer.I qpsceneGraphReducer.h \ selectiveChildNode.I selectiveChildNode.h \ qpsequenceNode.I qpsequenceNode.h \ texMatrixAttrib.I texMatrixAttrib.h \ @@ -86,6 +88,7 @@ qpfog.cxx \ fogAttrib.cxx \ qpgeomNode.cxx \ + qpgeomTransformer.cxx \ qplensNode.cxx \ qplodNode.cxx \ materialAttrib.cxx \ @@ -98,6 +101,7 @@ renderEffects.cxx \ renderModeAttrib.cxx \ renderState.cxx \ + qpsceneGraphReducer.cxx \ selectiveChildNode.cxx \ qpsequenceNode.cxx \ texMatrixAttrib.cxx \ @@ -139,6 +143,7 @@ qpfog.I qpfog.h \ fogAttrib.I fogAttrib.h \ qpgeomNode.I qpgeomNode.h \ + qpgeomTransformer.I qpgeomTransformer.h \ qplensNode.I qplensNode.h \ qplodNode.I qplodNode.h \ materialAttrib.I materialAttrib.h \ @@ -151,6 +156,7 @@ renderEffects.I renderEffects.h \ renderModeAttrib.I renderModeAttrib.h \ renderState.I renderState.h \ + qpsceneGraphReducer.I qpsceneGraphReducer.h \ selectiveChildNode.I selectiveChildNode.h \ qpsequenceNode.I qpsequenceNode.h \ texMatrixAttrib.I texMatrixAttrib.h \ diff --git a/panda/src/pgraph/billboardEffect.cxx b/panda/src/pgraph/billboardEffect.cxx index d6d0a8639b..099044f50b 100644 --- a/panda/src/pgraph/billboardEffect.cxx +++ b/panda/src/pgraph/billboardEffect.cxx @@ -47,15 +47,15 @@ make(const LVector3f &up_vector, bool eye_relative, } //////////////////////////////////////////////////////////////////// -// Function: BillboardEffect::safe_to_combine +// Function: BillboardEffect::safe_to_transform // Access: Public, Virtual -// Description: Returns true if this kind of effect can safely be -// combined with sibling nodes that share the exact same -// effect, or false if this is not a good idea. +// Description: Returns true if it is generally safe to transform +// this particular kind of RenderEffect by calling the +// xform() method, false otherwise. //////////////////////////////////////////////////////////////////// bool BillboardEffect:: -safe_to_combine() const { - return true; +safe_to_transform() const { + return false; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/billboardEffect.h b/panda/src/pgraph/billboardEffect.h index 144cbe6f01..0ffa3e96bc 100644 --- a/panda/src/pgraph/billboardEffect.h +++ b/panda/src/pgraph/billboardEffect.h @@ -55,7 +55,7 @@ PUBLISHED: INLINE const LPoint3f &get_look_at_point() const; public: - virtual bool safe_to_combine() const; + virtual bool safe_to_transform() const; virtual void output(ostream &out) const; CPT(TransformState) do_billboard(const TransformState *net_transform, diff --git a/panda/src/pgraph/config_pgraph.cxx b/panda/src/pgraph/config_pgraph.cxx index 84629e981d..d15c9ed5e9 100644 --- a/panda/src/pgraph/config_pgraph.cxx +++ b/panda/src/pgraph/config_pgraph.cxx @@ -70,6 +70,10 @@ ConfigureFn(config_pgraph) { // helps make culling errors obvious. const bool qpfake_view_frustum_cull = config_pgraph.GetBool("fake-view-frustum-cull", false); +// Set this true to make ambiguous path warning messages generate an +// assertion failure instead of just a warning (which can then be +// trapped with assert-abort). +const bool unambiguous_graph = config_pgraph.GetBool("unambiguous-graph", false); //////////////////////////////////////////////////////////////////// // Function: init_libpgraph diff --git a/panda/src/pgraph/config_pgraph.h b/panda/src/pgraph/config_pgraph.h index 4f235538b9..255b938ae0 100644 --- a/panda/src/pgraph/config_pgraph.h +++ b/panda/src/pgraph/config_pgraph.h @@ -27,6 +27,7 @@ ConfigureDecl(config_pgraph, EXPCL_PANDA, EXPTP_PANDA); NotifyCategoryDecl(pgraph, EXPCL_PANDA, EXPTP_PANDA); extern const bool qpfake_view_frustum_cull; +extern const bool unambiguous_graph; extern EXPCL_PANDA void init_libpgraph(); diff --git a/panda/src/pgraph/cullFaceAttrib.h b/panda/src/pgraph/cullFaceAttrib.h index 2693e46da7..7ecd10bfcc 100644 --- a/panda/src/pgraph/cullFaceAttrib.h +++ b/panda/src/pgraph/cullFaceAttrib.h @@ -38,7 +38,7 @@ PUBLISHED: }; private: - INLINE CullFaceAttrib(Mode mode = M_cull_none); + INLINE CullFaceAttrib(Mode mode = M_cull_clockwise); PUBLISHED: static CPT(RenderAttrib) make(Mode mode); diff --git a/panda/src/pgraph/decalEffect.cxx b/panda/src/pgraph/decalEffect.cxx index 6579ea12a0..03f324d213 100644 --- a/panda/src/pgraph/decalEffect.cxx +++ b/panda/src/pgraph/decalEffect.cxx @@ -35,6 +35,18 @@ make() { return return_new(effect); } +//////////////////////////////////////////////////////////////////// +// Function: DecalEffect::safe_to_combine +// Access: Public, Virtual +// Description: Returns true if this kind of effect can safely be +// combined with sibling nodes that share the exact same +// effect, or false if this is not a good idea. +//////////////////////////////////////////////////////////////////// +bool DecalEffect:: +safe_to_combine() const { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: DecalEffect::compare_to_impl // Access: Protected, Virtual diff --git a/panda/src/pgraph/decalEffect.h b/panda/src/pgraph/decalEffect.h index a7e5de3ff9..148b005601 100644 --- a/panda/src/pgraph/decalEffect.h +++ b/panda/src/pgraph/decalEffect.h @@ -37,6 +37,7 @@ PUBLISHED: static CPT(RenderEffect) make(); protected: + virtual bool safe_to_combine() const; virtual int compare_to_impl(const RenderEffect *other) const; virtual RenderEffect *make_default_impl() const; diff --git a/panda/src/pgraph/pandaNode.I b/panda/src/pgraph/pandaNode.I index 2e515166e8..b8d2b58d43 100644 --- a/panda/src/pgraph/pandaNode.I +++ b/panda/src/pgraph/pandaNode.I @@ -52,6 +52,16 @@ get_child() const { return _child; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::DownConnection::set_child +// Access: Public +// Description: This is only called by PandaNode::replace_child(). +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode::DownConnection:: +set_child(PandaNode *child) { + _child = child; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::DownConnection::get_sort // Access: Public @@ -151,6 +161,16 @@ Children(const PandaNode::Children ©) : { } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::Children::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode::Children:: +operator = (const PandaNode::Children ©) { + _cdata = copy._cdata; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::Children::get_num_children // Access: Public @@ -172,6 +192,48 @@ get_child(int n) const { return _cdata->_down[n].get_child(); } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ChildrenCopy::Copy Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE PandaNode::ChildrenCopy:: +ChildrenCopy(const PandaNode::ChildrenCopy ©) : + _list(copy._list) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ChildrenCopy::Copy Assignment Operator +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE void PandaNode::ChildrenCopy:: +operator = (const PandaNode::ChildrenCopy ©) { + _list = copy._list; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ChildrenCopy::get_num_children +// Access: Public +// Description: Returns the number of children of the node. +//////////////////////////////////////////////////////////////////// +INLINE int PandaNode::ChildrenCopy:: +get_num_children() const { + return _list.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ChildrenCopy::get_child +// Access: Public +// Description: Returns the nth child of the node. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode *PandaNode::ChildrenCopy:: +get_child(int n) const { + nassertr(n >= 0 && n < (int)_list.size(), NULL); + return _list[n]; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::get_num_parents // Access: Published @@ -715,3 +777,19 @@ get_children() const { return Children(cdata); } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::get_children_copy +// Access: Public +// Description: Returns an object that can be used to walk through +// the list of children of the node. Unlike +// get_children(), this function actually returns an +// object that protects you from self-modifying loops, +// because it makes and returns a copy of the complete +// children list. +//////////////////////////////////////////////////////////////////// +INLINE PandaNode::ChildrenCopy PandaNode:: +get_children_copy() const { + CDReader cdata(_cycler); + return ChildrenCopy(cdata); +} + diff --git a/panda/src/pgraph/pandaNode.cxx b/panda/src/pgraph/pandaNode.cxx index b8469b5e96..48c90eecb4 100644 --- a/panda/src/pgraph/pandaNode.cxx +++ b/panda/src/pgraph/pandaNode.cxx @@ -27,6 +27,79 @@ TypeHandle PandaNode::_type_handle; +// +// We go through some considerable effort in this class to ensure that +// NodePaths are kept consistent as we attach and detach nodes. We +// must enforce the following rules: +// +// 1) Each NodePath (i.e. chain of NodePathComponents) represents a +// complete unbroken chain from a PandaNode to the root of the graph. +// +// 2) Each NodePathComponent chain is unique. There are no two +// different NodePathComponents that reference the same path to the +// root. +// +// 3) If a PandaNode with no parents is attached to a new parent, all +// NodePaths that previously indicated this node as the root of graph +// must now be updated to include the complete chain to the new root. +// +// 4) If a PandaNode with other parents is attached to a new parent, +// any previously existing NodePaths are not affected. +// +// 5) If a PandaNode is disconnected from its parent, and it has no +// other parents, all NodePaths that previously passed through this +// node to the old parent must now be updated to indicate this node is +// now the root. +// +// 6) If a PandaNode is disconnected from its parent, and it has at +// least one other parent, all NodePaths that previously passed +// through this node to the old parent must now be updated to pass +// through one of the other parents instead. +// +// Rules (5) and (6) can especially complicate things because they +// introduce the possibility that two formerly distinct NodePaths are +// now equivalent, which violates rule (2). For example, if A is the +// top of the graph with children B and C, and D is instanced to both +// B and C, and E is a child of D, there might be two different +// NodePaths to D: A/B/D/E and A/C/D/E. If you then break the +// connection between D and E, both NodePaths must now become just the +// singleton E. +// +// Unfortunately, we cannot simply remove one of the extra +// NodePathComponents, because there may be any number of NodePath +// objects that reference them. Instead, we define the concept of +// "collapsed" NodePathComponents, which means one NodePathComponent +// can be "collapsed" into a different one so that any attempt to +// reference the first actually retrieves the second, rather like a +// symbolic link in the file system. When the NodePath traverses its +// component chain, it will pass right over the collapsed component in +// favor of the one it has been collapsed into. +// + + +// +// There are two different interfaces here for making and breaking +// parent-child connections: the fundamental PandaNode interface, via +// add_child() and remove_child() (and related functions), and the +// NodePath support interface, via attach(), detach(), and reparent(). +// They both do essentially the same thing, but with slightly +// different inputs. Both are responsible for keeping all NodePaths +// up-to-date according to the above rules. +// +// The NodePath support interface functions are strictly called from +// within the NodePath class, and are used to implement +// NodePath::reparent_to() and NodePath::remove_node(), etc. The +// fundamental interface, on the other hand, is designed to be called +// directly by the user. +// +// The fundamental interface has a slightly lower overhead because it +// does not need to create a NodePathComponent chain where one does +// not already exist; however, the NodePath support interface is more +// useful when the NodePath already does exist, because it ensures +// that the particular NodePath calling it is kept appropriately +// up-to-date. +// + //////////////////////////////////////////////////////////////////// // Function: PandaNode::CData::make_copy @@ -260,6 +333,20 @@ fillin_down_list(PandaNode::Down &down_list, } } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::ChildrenCopy::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +PandaNode::ChildrenCopy:: +ChildrenCopy(const PandaNode::CDReader &cdata) { + Children cr(cdata); + int num_children = cr.get_num_children(); + for (int i = 0; i < num_children; i++) { + _list.push_back(cr.get_child(i)); + } +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::Constructor // Access: Published @@ -396,6 +483,18 @@ safe_to_combine() const { return true; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::preserve_name +// Access: Public, Virtual +// Description: Returns true if the node's name has extrinsic meaning +// and must be preserved across a flatten operation, +// false otherwise. +//////////////////////////////////////////////////////////////////// +bool PandaNode:: +preserve_name() const { + return false; +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::xform // Access: Public, Virtual @@ -577,20 +676,8 @@ add_child(PandaNode *child_node, int sort) { cdata->_down.insert(DownConnection(child_node, sort)); cdata_child->_up.insert(UpConnection(this)); - - // We also have to adjust any qpNodePathComponents the child might - // have that reference the child as a top node. Any other - // components we can leave alone, because we are making a new - // instance of the child. - Paths::iterator pi; - for (pi = cdata_child->_paths.begin(); - pi != cdata_child->_paths.end(); - ++pi) { - if ((*pi)->is_top_node()) { - (*pi)->set_next(get_generic_component()); - } - } - child_node->fix_path_lengths(cdata_child); + + new_connection(this, child_node); // Mark the bounding volumes stale. force_bound_stale(); @@ -616,35 +703,8 @@ remove_child(int n) { cdata->_down.erase(cdata->_down.begin() + n); int num_erased = cdata_child->_up.erase(UpConnection(this)); nassertv(num_erased == 1); - - // Now sever any qpNodePathComponents on the child that reference - // this node. If we have multiple of these, we have to collapse - // them together. - qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL; - Paths::iterator pi; - pi = cdata_child->_paths.begin(); - while (pi != cdata_child->_paths.end()) { - Paths::iterator pnext = pi; - ++pnext; - if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) { - if (collapsed == (qpNodePathComponent *)NULL) { - (*pi)->set_top_node(); - collapsed = (*pi); - } else { - // This is a different component that used to reference a - // different instance, but now it's all just the same topnode. - // We have to collapse this and the previous one together. - // However, there might be some qpNodePaths out there that - // still keep a pointer to this one, so we can't remove it - // altogether. - (*pi)->collapse_with(collapsed); - cdata_child->_paths.erase(pi); - } - } - pi = pnext; - } - - child_node->fix_path_lengths(cdata_child); + + sever_connection(this, child_node); // Mark the bounding volumes stale. force_bound_stale(); @@ -659,68 +719,115 @@ remove_child(int n) { // Access: Published // Description: Removes the indicated child from the node. Returns // true if the child was removed, false if it was not -// already a child of the node. +// already a child of the node. This will also +// successfully remove the child if it had been stashed. //////////////////////////////////////////////////////////////////// bool PandaNode:: remove_child(PandaNode *child_node) { - // Ensure the child_node is not deleted while we do this. - PT(PandaNode) keep_child = child_node; - - CDWriter cdata(_cycler); - CDWriter cdata_child(child_node->_cycler); - - // First, look for and remove this node from the child's parent - // list. - int num_erased = cdata_child->_up.erase(UpConnection(this)); - if (num_erased == 0) { - // No such node; it wasn't our child to begin with. + // First, look for the parent in the child's up list, to ensure the + // child is known. + int parent_index = child_node->find_parent(this); + if (parent_index < 0) { + // Nope, no relation. return false; } - - // Now sever any qpNodePathComponents on the child that reference - // this node. If we have multiple of these, we have to collapse - // them together (see above). - qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL; - Paths::iterator pi; - pi = cdata_child->_paths.begin(); - while (pi != cdata_child->_paths.end()) { - Paths::iterator pnext = pi; - ++pnext; - if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) { - if (collapsed == (qpNodePathComponent *)NULL) { - (*pi)->set_top_node(); - collapsed = (*pi); - } else { - (*pi)->collapse_with(collapsed); - cdata_child->_paths.erase(pi); - } - } - pi = pnext; + + int child_index = find_child(child_node); + if (child_index >= 0) { + // The child exists; remove it. + remove_child(child_index); + return true; + } + + int stashed_index = find_stashed(child_node); + if (stashed_index >= 0) { + // The child has been stashed; remove it. + remove_stashed(stashed_index); + return true; + } + + // Never heard of this child. This shouldn't be possible, because + // the parent was in the child's up list, above. Must be some + // internal error. + nassertr(false, false); + return false; +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::replace_child +// Access: Published +// Description: Searches for the orig_child node in the node's list +// of children, and replaces it with the new_child +// instead. Returns true if the replacement is made, or +// false if the node is not a child. +//////////////////////////////////////////////////////////////////// +bool PandaNode:: +replace_child(PandaNode *orig_child, PandaNode *new_child) { + // First, look for the parent in the child's up list, to ensure the + // child is known. + int parent_index = orig_child->find_parent(this); + if (parent_index < 0) { + // Nope, no relation. + return false; + } + + if (orig_child == new_child) { + // Trivial no-op. + return true; } - child_node->fix_path_lengths(cdata_child); - - // Now, look for and remove the child node from our down list. - Down::iterator di; - bool found = false; - for (di = cdata->_down.begin(); di != cdata->_down.end() && !found; ++di) { - if ((*di).get_child() == child_node) { - cdata->_down.erase(di); - found = true; + // Don't let orig_child be destructed yet. + PT(PandaNode) keep_orig_child = orig_child; + + int child_index = find_child(orig_child); + if (child_index >= 0) { + // The child exists; replace it. + CDWriter cdata(_cycler); + DownConnection &down = cdata->_down[child_index]; + nassertr(down.get_child() == orig_child, false); + down.set_child(new_child); + + } else { + int stashed_index = find_stashed(orig_child); + if (stashed_index >= 0) { + // The child has been stashed; remove it. + CDWriter cdata(_cycler); + DownConnection &down = cdata->_stashed[stashed_index]; + nassertr(down.get_child() == orig_child, false); + down.set_child(new_child); + + } else { + // Never heard of this child. This shouldn't be possible, because + // the parent was in the child's up list, above. Must be some + // internal error. + nassertr(false, false); + return false; } } + + // Now adjust the bookkeeping on both children. + + CDWriter cdata_orig_child(orig_child->_cycler); + CDWriter cdata_new_child(new_child->_cycler); - nassertr(found, false); + cdata_new_child->_up.insert(UpConnection(this)); + int num_erased = cdata_orig_child->_up.erase(UpConnection(this)); + nassertr(num_erased == 1, false); + + sever_connection(this, orig_child); + orig_child->parents_changed(); + + new_connection(this, new_child); + new_child->parents_changed(); // Mark the bounding volumes stale. force_bound_stale(); - - // Call callback hooks. children_changed(); - child_node->parents_changed(); + return true; } + //////////////////////////////////////////////////////////////////// // Function: PandaNode::stash_child // Access: Published @@ -745,20 +852,8 @@ stash_child(int child_index) { cdata->_stashed.insert(DownConnection(child_node, sort)); cdata_child->_up.insert(UpConnection(this)); - - // We also have to adjust any qpNodePathComponents the child might - // have that reference the child as a top node. Any other - // components we can leave alone, because we are making a new - // instance of the child. - Paths::iterator pi; - for (pi = cdata_child->_paths.begin(); - pi != cdata_child->_paths.end(); - ++pi) { - if ((*pi)->is_top_node()) { - (*pi)->set_next(get_generic_component()); - } - } - child_node->fix_path_lengths(cdata_child); + + new_connection(this, child_node); // Mark the bounding volumes stale. force_bound_stale(); @@ -792,20 +887,8 @@ unstash_child(int stashed_index) { cdata->_down.insert(DownConnection(child_node, sort)); cdata_child->_up.insert(UpConnection(this)); - - // We also have to adjust any qpNodePathComponents the child might - // have that reference the child as a top node. Any other - // components we can leave alone, because we are making a new - // instance of the child. - Paths::iterator pi; - for (pi = cdata_child->_paths.begin(); - pi != cdata_child->_paths.end(); - ++pi) { - if ((*pi)->is_top_node()) { - (*pi)->set_next(get_generic_component()); - } - } - child_node->fix_path_lengths(cdata_child); + + new_connection(this, child_node); // Mark the bounding volumes stale. force_bound_stale(); @@ -837,12 +920,40 @@ find_stashed(PandaNode *node) const { return -1; } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::add_stashed +// Access: Published +// Description: Adds a new child to the node, directly as a stashed +// child. The child is not added in the normal sense, +// but will be revealed if unstash_child() is called on +// it later. +// +// If the same child is added to a node more than once, +// the previous instance is first removed. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +add_stashed(PandaNode *child_node, int sort) { + // Ensure the child_node is not deleted while we do this. + PT(PandaNode) keep_child = child_node; + remove_child(child_node); + + CDWriter cdata(_cycler); + CDWriter cdata_child(child_node->_cycler); + + cdata->_stashed.insert(DownConnection(child_node, sort)); + cdata_child->_up.insert(UpConnection(this)); + + new_connection(this, child_node); + + // Call callback hooks. + children_changed(); + child_node->parents_changed(); +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::remove_stashed // Access: Published -// Description: Removes the nth stashed child from the node. This is -// the only way to remove a child from the node that has -// previously been stashed, without unstashing it first. +// Description: Removes the nth stashed child from the node. //////////////////////////////////////////////////////////////////// void PandaNode:: remove_stashed(int n) { @@ -855,35 +966,8 @@ remove_stashed(int n) { cdata->_stashed.erase(cdata->_stashed.begin() + n); int num_erased = cdata_child->_up.erase(UpConnection(this)); nassertv(num_erased == 1); - - // Now sever any qpNodePathComponents on the child that reference - // this node. If we have multiple of these, we have to collapse - // them together. - qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL; - Paths::iterator pi; - pi = cdata_child->_paths.begin(); - while (pi != cdata_child->_paths.end()) { - Paths::iterator pnext = pi; - ++pnext; - if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) { - if (collapsed == (qpNodePathComponent *)NULL) { - (*pi)->set_top_node(); - collapsed = (*pi); - } else { - // This is a different component that used to reference a - // different instance, but now it's all just the same topnode. - // We have to collapse this and the previous one together. - // However, there might be some qpNodePaths out there that - // still keep a pointer to this one, so we can't remove it - // altogether. - (*pi)->collapse_with(collapsed); - cdata_child->_paths.erase(pi); - } - } - pi = pnext; - } - - child_node->fix_path_lengths(cdata_child); + + sever_connection(this, child_node); // Mark the bounding volumes stale. force_bound_stale(); @@ -907,29 +991,8 @@ remove_all_children() { PT(PandaNode) child_node = (*di).get_child(); CDWriter cdata_child(child_node->_cycler); cdata_child->_up.erase(UpConnection(this)); - - // Now sever any qpNodePathComponents on the child that - // reference this node. If we have multiple of these, we have - // to collapse them together (see above). - qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL; - Paths::iterator pi; - pi = cdata_child->_paths.begin(); - while (pi != cdata_child->_paths.end()) { - Paths::iterator pnext = pi; - ++pnext; - if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) { - if (collapsed == (qpNodePathComponent *)NULL) { - (*pi)->set_top_node(); - collapsed = (*pi); - } else { - (*pi)->collapse_with(collapsed); - cdata_child->_paths.erase(pi); - } - } - pi = pnext; - } - - child_node->fix_path_lengths(cdata_child); + + sever_connection(this, child_node); child_node->parents_changed(); } cdata->_down.clear(); @@ -938,29 +1001,8 @@ remove_all_children() { PT(PandaNode) child_node = (*di).get_child(); CDWriter cdata_child(child_node->_cycler); cdata_child->_up.erase(UpConnection(this)); - - // Now sever any qpNodePathComponents on the child that - // reference this node. If we have multiple of these, we have - // to collapse them together (see above). - qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL; - Paths::iterator pi; - pi = cdata_child->_paths.begin(); - while (pi != cdata_child->_paths.end()) { - Paths::iterator pnext = pi; - ++pnext; - if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) { - if (collapsed == (qpNodePathComponent *)NULL) { - (*pi)->set_top_node(); - collapsed = (*pi); - } else { - (*pi)->collapse_with(collapsed); - cdata_child->_paths.erase(pi); - } - } - pi = pnext; - } - - child_node->fix_path_lengths(cdata_child); + + sever_connection(this, child_node); child_node->parents_changed(); } cdata->_stashed.clear(); @@ -970,6 +1012,69 @@ remove_all_children() { children_changed(); } +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::steal_children +// Access: Published +// Description: Moves all the children from the other node onto this +// node. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +steal_children(PandaNode *other) { + if (other == this) { + // Trivial. + return; + } + + // We do this through the high-level interface for convenience. + // This could begin to be a problem if we have a node with hundreds + // of children to copy; this could break down the ov_set.insert() + // method, which is an O(n^2) operation. If this happens, we should + // rewrite this to do a simpler add_child() operation that involves + // push_back() instead of insert(), and then sort the down list at + // the end. + + int num_children = other->get_num_children(); + for (int i = 0; i < num_children; i++) { + PandaNode *child_node = other->get_child(i); + int sort = other->get_child_sort(i); + add_child(child_node, sort); + } + int num_stashed = other->get_num_stashed(); + for (int i = 0; i < num_stashed; i++) { + PandaNode *child_node = other->get_stashed(i); + int sort = other->get_stashed_sort(i); + add_stashed(child_node, sort); + } + + other->remove_all_children(); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::copy_children +// Access: Published +// Description: Makes another instance of all the children of the +// other node, copying them to this node. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +copy_children(PandaNode *other) { + if (other == this) { + // Trivial. + return; + } + int num_children = other->get_num_children(); + for (int i = 0; i < num_children; i++) { + PandaNode *child_node = other->get_child(i); + int sort = other->get_child_sort(i); + add_child(child_node, sort); + } + int num_stashed = other->get_num_stashed(); + for (int i = 0; i < num_stashed; i++) { + PandaNode *child_node = other->get_stashed(i); + int sort = other->get_stashed_sort(i); + add_stashed(child_node, sort); + } +} + //////////////////////////////////////////////////////////////////// // Function: PandaNode::output // Access: Published, Virtual @@ -1191,6 +1296,7 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) { CDReader from_cdata(from->_cycler); Down::const_iterator di; for (di = from_cdata->_down.begin(); di != from_cdata->_down.end(); ++di) { + int sort = (*di).get_sort(); PandaNode *source_child = (*di).get_child(); PT(PandaNode) dest_child; @@ -1207,7 +1313,7 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) { inst_map[source_child] = dest_child; } - add_child(dest_child); + add_child(dest_child, sort); } } @@ -1230,7 +1336,7 @@ attach(qpNodePathComponent *parent, PandaNode *child_node, int sort) { if (child == (qpNodePathComponent *)NULL) { // The child was not already attached to the parent, so get a new // component. - child = get_top_component(child_node); + child = get_top_component(child_node, true); } reparent(parent, child, sort); @@ -1403,9 +1509,14 @@ get_component(qpNodePathComponent *parent, PandaNode *child_node) { // this for a node that has parents, unless you are // about to create a new instance (and immediately // reconnect the qpNodePathComponent elsewhere). +// +// If force is true, this will always return something, +// even if it needs to create a new top component; +// otherwise, if force is false, it will return NULL if +// there is not already a top component available. //////////////////////////////////////////////////////////////////// PT(qpNodePathComponent) PandaNode:: -get_top_component(PandaNode *child_node) { +get_top_component(PandaNode *child_node, bool force) { { CDReader cdata_child(child_node->_cycler); @@ -1422,6 +1533,12 @@ get_top_component(PandaNode *child_node) { } } + if (!force) { + // If we don't care to force the point, return NULL to indicate + // there's not already a top component. + return NULL; + } + // We don't already have such a qpNodePathComponent; create and // return a new one. PT(qpNodePathComponent) child = @@ -1445,17 +1562,28 @@ PT(qpNodePathComponent) PandaNode:: get_generic_component() { int num_parents = get_num_parents(); if (num_parents == 0) { - return get_top_component(this); + // No parents; no ambiguity. This is the root. + return get_top_component(this, true); + } + + PT(qpNodePathComponent) result; + if (num_parents == 1) { + // Only one parent; no ambiguity. + PT(qpNodePathComponent) parent = get_parent(0)->get_generic_component(); + result = get_component(parent, this); } else { - if (num_parents != 1) { - pgraph_cat.warning() - << *this << " has " << num_parents - << " parents; choosing arbitrary path to root.\n"; - } + // Oops, multiple parents; the NodePath is ambiguous. + pgraph_cat.warning() + << *this << " has " << num_parents + << " parents; choosing arbitrary path to root.\n"; + PT(qpNodePathComponent) parent = get_parent(0)->get_generic_component(); - return get_component(parent, this); + result = get_component(parent, this); + nassertr(!unambiguous_graph, result); } + + return result; } //////////////////////////////////////////////////////////////////// @@ -1480,7 +1608,106 @@ delete_component(qpNodePathComponent *component) { _cycler.release_write_stage(i, cdata); } } - nassertv(max_num_erased == 1); + + // This may legitimately be zero if we are deleting a collapsed NodePath. + // nassertv(max_num_erased == 1); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::sever_connection +// Access: Private, Static +// Description: This is called internally when a parent-child +// connection is broken to update the NodePathComponents +// that reflected this connection. +// +// It severs any NodePathComponents on the child node +// that reference the indicated parent node. If the +// child node has additional parents, chooses one of +// them arbitrarily instead. Collapses together +// instances below this node that used to differentiate +// on some instance above the old parent. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +sever_connection(PandaNode *parent_node, PandaNode *child_node) { + CDWriter cdata_child(child_node->_cycler); + qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL; + + Paths::iterator pi; + pi = cdata_child->_paths.begin(); + while (pi != cdata_child->_paths.end()) { + Paths::iterator pnext = pi; + ++pnext; + if (!(*pi)->is_top_node() && + (*pi)->get_next()->get_node() == parent_node) { + if (collapsed == (qpNodePathComponent *)NULL) { + + // This is the first component we've found that references + // this node. Should we sever it or reattach it? + if (child_node->get_num_parents() == 0) { + // If the node no longer has any parents, all of its paths will be + // severed here. Collapse them all with the existing top + // component if there is one. + collapsed = get_top_component(child_node, false); + + } else { + // If the node still has one parent, all of its paths that + // referenced the old parent will be combined with the remaining + // parent. If there are multiple parents, choose one arbitrarily + // to combine with. + collapsed = child_node->get_generic_component(); + } + + if (collapsed == (qpNodePathComponent *)NULL) { + // Sever the component here; there's nothing to attach it + // to. + (*pi)->set_top_node(); + collapsed = (*pi); + + } else { + // Collapse the new component with the pre-existing + // component. + (*pi)->collapse_with(collapsed); + cdata_child->_paths.erase(pi); + } + + } else { + // This is the second (or later) component we've found that + // references this node. We should collapse it with the first + // one. + (*pi)->collapse_with(collapsed); + cdata_child->_paths.erase(pi); + } + } + pi = pnext; + } + child_node->fix_path_lengths(cdata_child); +} + +//////////////////////////////////////////////////////////////////// +// Function: PandaNode::new_connection +// Access: Private, Static +// Description: This is called internally when a parent-child +// connection is establshed to update the +// NodePathComponents that might be involved. +// +// It adjusts any NodePathComponents the child has that +// reference the child as a top node. Any other +// components we can leave alone, because we are making +// a new instance of the child. +//////////////////////////////////////////////////////////////////// +void PandaNode:: +new_connection(PandaNode *parent_node, PandaNode *child_node) { + CDWriter cdata_child(child_node->_cycler); + + Paths::iterator pi; + for (pi = cdata_child->_paths.begin(); + pi != cdata_child->_paths.end(); + ++pi) { + if ((*pi)->is_top_node()) { + (*pi)->set_next(parent_node->get_generic_component()); + } + } + child_node->fix_path_lengths(cdata_child); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/pandaNode.h b/panda/src/pgraph/pandaNode.h index 0389dc7ba5..dea5a1ab0e 100644 --- a/panda/src/pgraph/pandaNode.h +++ b/panda/src/pgraph/pandaNode.h @@ -36,6 +36,7 @@ #include "luse.h" #include "ordered_vector.h" #include "pointerTo.h" +#include "pointerToArray.h" #include "notify.h" class qpNodePathComponent; @@ -67,6 +68,7 @@ public: virtual bool safe_to_flatten() const; virtual bool safe_to_transform() const; virtual bool safe_to_combine() const; + virtual bool preserve_name() const; virtual void xform(const LMatrix4f &mat); virtual PandaNode *combine_with(PandaNode *other); @@ -89,18 +91,24 @@ PUBLISHED: void add_child(PandaNode *child_node, int sort = 0); void remove_child(int n); bool remove_child(PandaNode *child_node); + bool replace_child(PandaNode *orig_child, PandaNode *new_child); INLINE bool stash_child(PandaNode *child_node); void stash_child(int child_index); INLINE bool unstash_child(PandaNode *child_node); void unstash_child(int stashed_index); + INLINE int get_num_stashed() const; INLINE PandaNode *get_stashed(int n) const; INLINE int get_stashed_sort(int n) const; int find_stashed(PandaNode *node) const; + + void add_stashed(PandaNode *child_node, int sort = 0); void remove_stashed(int n); void remove_all_children(); + void steal_children(PandaNode *other); + void copy_children(PandaNode *other); INLINE void set_attrib(const RenderAttrib *attrib, int override = 0); INLINE const RenderAttrib *get_attrib(TypeHandle type) const; @@ -173,6 +181,8 @@ protected: BoundedObject _internal_bound; private: + class CData; + // parent-child manipulation for qpNodePath support. Don't try to // call these directly. static PT(qpNodePathComponent) attach(qpNodePathComponent *parent, @@ -182,10 +192,12 @@ private: qpNodePathComponent *child, int sort); static PT(qpNodePathComponent) get_component(qpNodePathComponent *parent, PandaNode *child); - static PT(qpNodePathComponent) get_top_component(PandaNode *child); + static PT(qpNodePathComponent) get_top_component(PandaNode *child, + bool force); PT(qpNodePathComponent) get_generic_component(); void delete_component(qpNodePathComponent *component); - class CData; + static void sever_connection(PandaNode *parent_node, PandaNode *child_node); + static void new_connection(PandaNode *parent_node, PandaNode *child_node); void fix_path_lengths(const CData *cdata); void r_list_descendants(ostream &out, int indent_level) const; @@ -195,6 +207,7 @@ private: INLINE DownConnection(PandaNode *child, int sort); INLINE bool operator < (const DownConnection &other) const; INLINE PandaNode *get_child() const; + INLINE void set_child(PandaNode *child); INLINE int get_sort() const; private: @@ -273,6 +286,7 @@ public: public: INLINE Children(const CDReader &cdata); INLINE Children(const Children ©); + INLINE void operator = (const Children ©); INLINE int get_num_children() const; INLINE PandaNode *get_child(int n) const; @@ -283,6 +297,24 @@ public: INLINE Children get_children() const; + // This interface *does* protect you from self-modifying loops, by + // copying the list of children. + class EXPCL_PANDA ChildrenCopy { + public: + ChildrenCopy(const CDReader &cdata); + INLINE ChildrenCopy(const ChildrenCopy ©); + INLINE void operator = (const ChildrenCopy ©); + + INLINE int get_num_children() const; + INLINE PandaNode *get_child(int n) const; + + private: + typedef PTA(PT(PandaNode)) List; + List _list; + }; + + INLINE ChildrenCopy get_children_copy() const; + public: static void register_with_read_factory(); virtual void write_datagram(BamWriter *manager, Datagram &dg); diff --git a/panda/src/pgraph/pgraph_composite2.cxx b/panda/src/pgraph/pgraph_composite2.cxx index 313741f3b7..42e0298b2e 100644 --- a/panda/src/pgraph/pgraph_composite2.cxx +++ b/panda/src/pgraph/pgraph_composite2.cxx @@ -4,6 +4,7 @@ #include "qpfog.cxx" #include "fogAttrib.cxx" #include "qpgeomNode.cxx" +#include "qpgeomTransformer.cxx" #include "qplensNode.cxx" #include "qplodNode.cxx" #include "materialAttrib.cxx" @@ -16,6 +17,7 @@ #include "renderEffects.cxx" #include "renderModeAttrib.cxx" #include "renderState.cxx" +#include "qpsceneGraphReducer.cxx" #include "selectiveChildNode.cxx" #include "qpsequenceNode.cxx" #include "test_pgraph.cxx" diff --git a/panda/src/pgraph/qpgeomNode.I b/panda/src/pgraph/qpgeomNode.I index 49db002992..5807b4f96d 100644 --- a/panda/src/pgraph/qpgeomNode.I +++ b/panda/src/pgraph/qpgeomNode.I @@ -112,6 +112,7 @@ add_geom(Geom *geom, const RenderState *state) { CDWriter cdata(_cycler); cdata->_geoms.push_back(GeomEntry(geom, state)); + mark_bound_stale(); return cdata->_geoms.size() - 1; } @@ -126,6 +127,7 @@ remove_geom(int n) { nassertv(n >= 0 && n < (int)cdata->_geoms.size()); cdata->_geoms.erase(cdata->_geoms.begin() + n); + mark_bound_stale(); } //////////////////////////////////////////////////////////////////// @@ -137,4 +139,5 @@ INLINE void qpGeomNode:: remove_all_geoms() { CDWriter cdata(_cycler); cdata->_geoms.clear(); + mark_bound_stale(); } diff --git a/panda/src/pgraph/qpgeomNode.cxx b/panda/src/pgraph/qpgeomNode.cxx index 71637f76ae..4f6616f7d7 100644 --- a/panda/src/pgraph/qpgeomNode.cxx +++ b/panda/src/pgraph/qpgeomNode.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "qpgeomNode.h" +#include "qpgeomTransformer.h" #include "bamReader.h" #include "bamWriter.h" #include "datagram.h" @@ -153,6 +154,73 @@ make_copy() const { return new qpGeomNode(*this); } +//////////////////////////////////////////////////////////////////// +// Function: qpGeomNode::xform +// Access: Public, Virtual +// Description: Transforms the contents of this node by the indicated +// matrix, if it means anything to do so. For most +// kinds of nodes, this does nothing. +// +// For a GeomNode, this does the right thing, but it is +// better to use a GeomTransformer instead, since it +// will share the new arrays properly between different +// GeomNodes. +//////////////////////////////////////////////////////////////////// +void qpGeomNode:: +xform(const LMatrix4f &mat) { + qpGeomTransformer transformer; + transformer.transform_vertices(this, mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomNode::combine_with +// Access: Public, Virtual +// Description: Collapses this node with the other node, if possible, +// and returns a pointer to the combined node, or NULL +// if the two nodes cannot safely be combined. +// +// The return value may be this, other, or a new node +// altogether. +// +// This function is called from GraphReducer::flatten(), +// and need not deal with children; its job is just to +// decide whether to collapse the two nodes and what the +// collapsed node should look like. +//////////////////////////////////////////////////////////////////// +PandaNode *qpGeomNode:: +combine_with(PandaNode *other) { + if (is_exact_type(get_class_type()) && + other->is_exact_type(get_class_type())) { + // Two GeomNodes can combine by moving Geoms from one to the other. + qpGeomNode *gother = DCAST(qpGeomNode, other); + add_geoms_from(gother); + return this; + } + + return PandaNode::combine_with(other); +} + +//////////////////////////////////////////////////////////////////// +// Function: qpGeomNode::add_geoms_from +// Access: Published +// Description: Copies the Geoms (and their associated RenderStates) +// from the indicated GeomNode into this one. +//////////////////////////////////////////////////////////////////// +void qpGeomNode:: +add_geoms_from(const qpGeomNode *other) { + CDReader cdata_other(other->_cycler); + CDWriter cdata(_cycler); + + Geoms::const_iterator gi; + for (gi = cdata_other->_geoms.begin(); + gi != cdata_other->_geoms.end(); + ++gi) { + const GeomEntry &entry = (*gi); + cdata->_geoms.push_back(entry); + } + mark_bound_stale(); +} + //////////////////////////////////////////////////////////////////// // Function: qpGeomNode::write_geoms // Access: Published diff --git a/panda/src/pgraph/qpgeomNode.h b/panda/src/pgraph/qpgeomNode.h index b60c1a6752..497797e4b4 100644 --- a/panda/src/pgraph/qpgeomNode.h +++ b/panda/src/pgraph/qpgeomNode.h @@ -44,6 +44,8 @@ protected: public: virtual ~qpGeomNode(); virtual PandaNode *make_copy() const; + virtual void xform(const LMatrix4f &mat); + virtual PandaNode *combine_with(PandaNode *other); PUBLISHED: INLINE int get_num_geoms() const; @@ -52,6 +54,7 @@ PUBLISHED: INLINE void set_geom_state(int n, const RenderState *state); INLINE int add_geom(Geom *geom, const RenderState *state = RenderState::make_empty()); + void add_geoms_from(const qpGeomNode *other); INLINE void remove_geom(int n); INLINE void remove_all_geoms(); @@ -118,6 +121,7 @@ private: static TypeHandle _type_handle; friend class PandaNode::Children; + friend class qpGeomTransformer; }; #include "qpgeomNode.I" diff --git a/panda/src/pgraph/qpnodePath.I b/panda/src/pgraph/qpnodePath.I index 69513406f2..26b07881bb 100644 --- a/panda/src/pgraph/qpnodePath.I +++ b/panda/src/pgraph/qpnodePath.I @@ -159,6 +159,7 @@ fail() { //////////////////////////////////////////////////////////////////// INLINE bool qpNodePath:: is_empty() const { + uncollapse_head(); return (_head == (qpNodePathComponent *)NULL); } @@ -272,6 +273,7 @@ attach_new_node(const string &name, int sort) const { //////////////////////////////////////////////////////////////////// INLINE void qpNodePath:: output(ostream &out) const { + uncollapse_head(); if (_head == (qpNodePathComponent *)NULL) { out << "(empty)"; } else { @@ -1113,6 +1115,9 @@ operator < (const qpNodePath &other) const { //////////////////////////////////////////////////////////////////// INLINE int qpNodePath:: compare_to(const qpNodePath &other) const { + uncollapse_head(); + other.uncollapse_head(); + // Nowadays, the NodePathComponents at the head are pointerwise // equivalent if and only iff the NodePaths are equivalent. So we // only have to compare pointers. diff --git a/panda/src/pgraph/qpnodePath.cxx b/panda/src/pgraph/qpnodePath.cxx index ee84012a93..6a88f0b14f 100644 --- a/panda/src/pgraph/qpnodePath.cxx +++ b/panda/src/pgraph/qpnodePath.cxx @@ -38,6 +38,7 @@ #include "plist.h" #include "boundingSphere.h" #include "qpgeomNode.h" +#include "qpsceneGraphReducer.h" // stack seems to overflow on Intel C++ at 7000. If we need more than // 7000, need to increase stack size. @@ -2403,10 +2404,8 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) { int qpNodePath:: flatten_light() { nassertr(!is_empty(), 0); - /* - SceneGraphReducer gr(_graph_type); - gr.apply_transitions(arc()); - */ + qpSceneGraphReducer gr; + gr.apply_attribs(node()); return 0; } @@ -2436,17 +2435,13 @@ flatten_light() { // The return value is the number of arcs removed. //////////////////////////////////////////////////////////////////// int qpNodePath:: -flatten_medium(int max_children) { +flatten_medium() { nassertr(!is_empty(), 0); - /* - SceneGraphReducer gr(_graph_type); - gr.set_max_children(max_children); - gr.apply_transitions(arc()); + qpSceneGraphReducer gr; + gr.apply_attribs(node()); int num_removed = gr.flatten(node(), false); return num_removed; - */ - return 0; } //////////////////////////////////////////////////////////////////// @@ -2467,17 +2462,13 @@ flatten_medium(int max_children) { // because of less-effective culling. //////////////////////////////////////////////////////////////////// int qpNodePath:: -flatten_strong(int max_children) { +flatten_strong() { nassertr(!is_empty(), 0); - /* - SceneGraphReducer gr(_graph_type); - gr.set_max_children(max_children); - gr.apply_transitions(arc()); + qpSceneGraphReducer gr; + gr.apply_attribs(node()); int num_removed = gr.flatten(node(), true); return num_removed; - */ - return 0; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/qpnodePath.h b/panda/src/pgraph/qpnodePath.h index 395459b96f..86ade6772b 100644 --- a/panda/src/pgraph/qpnodePath.h +++ b/panda/src/pgraph/qpnodePath.h @@ -477,8 +477,8 @@ PUBLISHED: bool calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point); int flatten_light(); - int flatten_medium(int max_children = 1); - int flatten_strong(int max_children = 1); + int flatten_medium(); + int flatten_strong(); bool write_bam_file(const string &filename) const; diff --git a/panda/src/pgraph/renderEffect.cxx b/panda/src/pgraph/renderEffect.cxx index 5fca33d1e1..5c103d24cf 100644 --- a/panda/src/pgraph/renderEffect.cxx +++ b/panda/src/pgraph/renderEffect.cxx @@ -72,8 +72,7 @@ RenderEffect:: // Access: Public, Virtual // Description: Returns true if it is generally safe to transform // this particular kind of RenderEffect by calling the -// xform() method, false otherwise. For instance, it's -// usually a bad idea to attempt to xform a Character. +// xform() method, false otherwise. //////////////////////////////////////////////////////////////////// bool RenderEffect:: safe_to_transform() const { @@ -89,7 +88,7 @@ safe_to_transform() const { //////////////////////////////////////////////////////////////////// bool RenderEffect:: safe_to_combine() const { - return false; + return true; } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/pgraph/transparencyAttrib.cxx b/panda/src/pgraph/transparencyAttrib.cxx index 350f47d77d..5a935462a2 100644 --- a/panda/src/pgraph/transparencyAttrib.cxx +++ b/panda/src/pgraph/transparencyAttrib.cxx @@ -66,21 +66,27 @@ output(ostream &out) const { switch (get_mode()) { case M_none: out << "none"; + break; case M_alpha: out << "alpha"; + break; case M_alpha_sorted: out << "alpha sorted"; + break; case M_multisample: out << "multisample"; + break; case M_multisample_mask: out << "multisample mask"; + break; case M_binary: out << "binary"; + break; } } diff --git a/panda/src/pgui/qppgItem.cxx b/panda/src/pgui/qppgItem.cxx index 3e4497b9f4..377276ad50 100644 --- a/panda/src/pgui/qppgItem.cxx +++ b/panda/src/pgui/qppgItem.cxx @@ -23,6 +23,7 @@ #include "config_pgui.h" #include "pandaNode.h" +#include "qpsceneGraphReducer.h" #include "throw_event.h" #include "string_utils.h" #include "qpnodePath.h" @@ -122,11 +123,9 @@ xform(const LMatrix4f &mat) { // Apply the matrix to the previous transform. node->set_transform(node->get_transform()->compose(TransformState::make_mat(mat))); - /* // Now flatten the transform into the subgraph. - SceneGraphReducer gr; - gr.apply_transitions(arc); - */ + qpSceneGraphReducer gr; + gr.apply_attribs(node); } // Transform the frame style too. diff --git a/panda/src/putil/cycleDataReader.I b/panda/src/putil/cycleDataReader.I index ef01c970f7..a4de5484e9 100644 --- a/panda/src/putil/cycleDataReader.I +++ b/panda/src/putil/cycleDataReader.I @@ -28,9 +28,9 @@ template INLINE CycleDataReader:: CycleDataReader(const PipelineCycler &cycler) : - _cycler(cycler) + _cycler(&cycler) { - _pointer = _cycler.read(); + _pointer = _cycler->read(); _write_pointer = (CycleDataType *)NULL; } @@ -47,10 +47,29 @@ CycleDataReader(const CycleDataReader ©) : _write_pointer(copy._write_pointer) { nassertv(_pointer != (const CycleDataType *)NULL); - // We cannot copy-construct a CycleDataReader that has elevated - // itself to a write pointer. + // We cannot copy a CycleDataReader that has elevated itself to a + // write pointer. nassertv(_write_pointer == (const CycleDataType *)NULL); - _cycler.increment_read(_pointer); + _cycler->increment_read(_pointer); +} + +//////////////////////////////////////////////////////////////////// +// Function: CycleDataReader::Copy Assignment (full) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void CycleDataReader:: +operator = (const CycleDataReader ©) { + _cycler = copy._cycler; + _pointer = copy._pointer; + _write_pointer = copy._write_pointer; + + nassertv(_pointer != (const CycleDataType *)NULL); + // We cannot copy a CycleDataReader that has elevated itself to a + // write pointer. + nassertv(_write_pointer == (const CycleDataType *)NULL); + _cycler->increment_read(_pointer); } //////////////////////////////////////////////////////////////////// @@ -65,9 +84,9 @@ INLINE CycleDataReader:: // If the _write_pointer is non-NULL, then someone called // elevate_to_write() at some point, and we now actually hold a // write pointer, not a read pointer. - ((PipelineCycler &)_cycler).release_write(_write_pointer); + ((PipelineCycler *)_cycler)->release_write(_write_pointer); } else if (_pointer != (CycleDataType *)NULL) { - _cycler.release_read(_pointer); + _cycler->release_read(_pointer); } } @@ -80,7 +99,7 @@ INLINE CycleDataReader:: template INLINE const CycleDataType *CycleDataReader:: operator -> () const { - nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat()); + nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat()); return _pointer; } @@ -93,7 +112,7 @@ operator -> () const { template INLINE CycleDataReader:: operator const CycleDataType * () const { - nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat()); + nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat()); return _pointer; } @@ -113,7 +132,7 @@ take_pointer() { const CycleDataType *pointer = _pointer; _pointer = (CycleDataType *)NULL; _write_pointer = (CycleDataType *)NULL; - nassertr(pointer != (const CycleDataType *)NULL, _cycler.cheat()); + nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat()); return pointer; } @@ -162,6 +181,17 @@ CycleDataReader(const CycleDataReader ©) : { } +//////////////////////////////////////////////////////////////////// +// Function: CycleDataReader::Copy Assignment (trivial) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void CycleDataReader:: +operator = (const CycleDataReader ©) { + _pointer = copy._pointer; +} + //////////////////////////////////////////////////////////////////// // Function: CycleDataReader::Destructor (trivial) // Access: Public diff --git a/panda/src/putil/cycleDataReader.h b/panda/src/putil/cycleDataReader.h index c2ec6ea1f8..7b2446370e 100644 --- a/panda/src/putil/cycleDataReader.h +++ b/panda/src/putil/cycleDataReader.h @@ -41,6 +41,7 @@ class CycleDataReader { public: INLINE CycleDataReader(const PipelineCycler &cycler); INLINE CycleDataReader(const CycleDataReader ©); + INLINE void operator = (const CycleDataReader ©); INLINE ~CycleDataReader(); @@ -53,7 +54,7 @@ public: private: #ifdef DO_PIPELINING // This is the data stored for a real pipelining implementation. - const PipelineCycler &_cycler; + const PipelineCycler *_cycler; const CycleDataType *_pointer; CycleDataType *_write_pointer; #else // !DO_PIPELINING diff --git a/panda/src/putil/cycleDataWriter.I b/panda/src/putil/cycleDataWriter.I index 36aba39a6c..37773d7e2c 100644 --- a/panda/src/putil/cycleDataWriter.I +++ b/panda/src/putil/cycleDataWriter.I @@ -28,9 +28,39 @@ template INLINE CycleDataWriter:: CycleDataWriter(PipelineCycler &cycler) : - _cycler(cycler) + _cycler(&cycler) { - _pointer = _cycler.write(); + _pointer = _cycler->write(); +} + +//////////////////////////////////////////////////////////////////// +// Function: CycleDataWriter::Copy Constructor (full) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE CycleDataWriter:: +CycleDataWriter(const CycleDataWriter ©) : + _cycler(copy._cycler), + _pointer(copy._pointer) +{ + nassertv(_pointer != (CycleDataType *)NULL); + _cycler->increment_write(_pointer); +} + +//////////////////////////////////////////////////////////////////// +// Function: CycleDataWriter::Copy Assigment (full) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void CycleDataWriter:: +operator = (const CycleDataWriter ©) { + _cycler = copy._cycler; + _pointer = copy._pointer; + + nassertv(_pointer != (CycleDataType *)NULL); + _cycler->increment_write(_pointer); } //////////////////////////////////////////////////////////////////// @@ -46,7 +76,7 @@ template INLINE CycleDataWriter:: CycleDataWriter(PipelineCycler &cycler, CycleDataWriter &take_from) : - _cycler(cycler), + _cycler(&cycler), _pointer(take_from._pointer) { take_from._pointer = (CycleDataType *)NULL; @@ -64,9 +94,9 @@ template INLINE CycleDataWriter:: CycleDataWriter(PipelineCycler &cycler, CycleDataReader &take_from) : - _cycler(cycler) + _cycler(&cycler) { - _pointer = _cycler.elevate_read(take_from.take_pointer()); + _pointer = _cycler->elevate_read(take_from.take_pointer()); } //////////////////////////////////////////////////////////////////// @@ -78,7 +108,7 @@ template INLINE CycleDataWriter:: ~CycleDataWriter() { if (_pointer != (CycleDataType *)NULL) { - _cycler.release_write(_pointer); + _cycler->release_write(_pointer); } } @@ -91,7 +121,7 @@ INLINE CycleDataWriter:: template INLINE CycleDataType *CycleDataWriter:: operator -> () { - nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat()); + nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat()); return _pointer; } @@ -104,7 +134,7 @@ operator -> () { template INLINE const CycleDataType *CycleDataWriter:: operator -> () const { - nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat()); + nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat()); return _pointer; } @@ -117,7 +147,7 @@ operator -> () const { template INLINE CycleDataWriter:: operator CycleDataType * () { - nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat()); + nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat()); return _pointer; } @@ -135,6 +165,29 @@ CycleDataWriter(PipelineCycler &cycler) { _pointer = cycler.write(); } +//////////////////////////////////////////////////////////////////// +// Function: CycleDataWriter::Copy Constructor (trivial) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE CycleDataWriter:: +CycleDataWriter(const CycleDataWriter ©) : + _pointer(copy._pointer) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: CycleDataWriter::Copy Assigment (trivial) +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +template +INLINE void CycleDataWriter:: +operator = (const CycleDataWriter ©) { + _pointer = copy._pointer; +} + //////////////////////////////////////////////////////////////////// // Function: CycleDataWriter::Constructor (trivial) // Access: Public diff --git a/panda/src/putil/cycleDataWriter.h b/panda/src/putil/cycleDataWriter.h index 469dc5ac18..d750b74c98 100644 --- a/panda/src/putil/cycleDataWriter.h +++ b/panda/src/putil/cycleDataWriter.h @@ -40,6 +40,9 @@ template class CycleDataWriter { public: INLINE CycleDataWriter(PipelineCycler &cycler); + INLINE CycleDataWriter(const CycleDataWriter ©); + INLINE void operator = (const CycleDataWriter ©); + INLINE CycleDataWriter(PipelineCycler &cycler, CycleDataWriter &take_from); INLINE CycleDataWriter(PipelineCycler &cycler, CycleDataReader &take_from); @@ -53,7 +56,7 @@ public: private: #ifdef DO_PIPELINING // This is the data stored for a real pipelining implementation. - PipelineCycler &_cycler; + PipelineCycler *_cycler; CycleDataType *_pointer; #else // !DO_PIPELINING // This is all we need for the trivial, do-nothing implementation. diff --git a/panda/src/testbed/pview.cxx b/panda/src/testbed/pview.cxx index e5e787e22c..3467071138 100644 --- a/panda/src/testbed/pview.cxx +++ b/panda/src/testbed/pview.cxx @@ -378,6 +378,9 @@ main(int argc, char *argv[]) { render.attach_new_node(camera); camera->set_scene(render); + // This is maybe here temporarily, and maybe not. + render.set_two_sided(0); + // Set up a data graph for tracking user input. PT(PandaNode) data_root = new PandaNode("data_root"); PandaNode *mouse = setup_mouse(data_root, window); diff --git a/panda/src/text/qptextNode.cxx b/panda/src/text/qptextNode.cxx index bbe4f217e4..edee94a51b 100644 --- a/panda/src/text/qptextNode.cxx +++ b/panda/src/text/qptextNode.cxx @@ -33,6 +33,7 @@ #include "cullBinAttrib.h" #include "textureAttrib.h" #include "transparencyAttrib.h" +#include "qpsceneGraphReducer.h" #include "indent.h" #include @@ -362,11 +363,9 @@ generate() { // applying them to the vertices. if (text_flatten) { - /* **** - SceneGraphReducer gr(RenderRelation::get_class_type()); - gr.apply_transitions(root_arc); + qpSceneGraphReducer gr; + gr.apply_attribs(root); gr.flatten(root, true); - */ } return root;