Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/pgraph/sceneGraphReducer.cxx

Go to the documentation of this file.
00001 // Filename: SceneGraphReducer.cxx
00002 // Created by:  drose (14Mar02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "sceneGraphReducer.h"
00020 #include "config_pgraph.h"
00021 #include "colorAttrib.h"
00022 #include "texMatrixAttrib.h"
00023 #include "colorScaleAttrib.h"
00024 #include "accumulatedAttribs.h"
00025 
00026 #include "geomNode.h"
00027 #include "pointerTo.h"
00028 #include "geom.h"
00029 #include "indent.h"
00030 #include "plist.h"
00031 
00032 ////////////////////////////////////////////////////////////////////
00033 //     Function: SceneGraphReducer::flatten
00034 //       Access: Published
00035 //  Description: Simplifies the graph by removing unnecessary nodes
00036 //               and nodes.
00037 //
00038 //               In general, a node (and its parent node) is a
00039 //               candidate for removal if the node has no siblings and
00040 //               the node has no special properties.
00041 //
00042 //               If combine_siblings is true, sibling nodes may also
00043 //               be collapsed into a single node.  This will further
00044 //               reduce scene graph complexity, sometimes
00045 //               substantially, at the cost of reduced spatial
00046 //               separation.
00047 //
00048 //               Returns the number of nodes removed from the graph.
00049 ////////////////////////////////////////////////////////////////////
00050 int SceneGraphReducer::
00051 flatten(PandaNode *root, bool combine_siblings) {
00052   int num_total_nodes = 0;
00053   int num_pass_nodes;
00054 
00055   do {
00056     num_pass_nodes = 0;
00057 
00058     // Get a copy of the children list, so we don't have to worry
00059     // about self-modifications.
00060     PandaNode::ChildrenCopy cr = root->get_children_copy();
00061 
00062     // Now visit each of the children in turn.
00063     int num_children = cr.get_num_children();
00064     for (int i = 0; i < num_children; i++) {
00065       PandaNode *child_node = cr.get_child(i);
00066       num_pass_nodes += r_flatten(root, child_node, combine_siblings);
00067     }
00068 
00069     if (combine_siblings && root->get_num_children() >= 2) {
00070       num_pass_nodes += flatten_siblings(root);
00071     }
00072 
00073     num_total_nodes += num_pass_nodes;
00074 
00075     // If combine_siblings is true, we should repeat the above until
00076     // we don't get any more benefit from flattening, because each
00077     // pass could convert cousins into siblings, which may get
00078     // flattened next pass.  If combine_siblings is not true, the
00079     // first pass will be fully effective, and there's no point in
00080     // trying again.
00081   } while (combine_siblings && num_pass_nodes != 0);
00082 
00083   return num_total_nodes;
00084 }
00085 
00086 ////////////////////////////////////////////////////////////////////
00087 //     Function: SceneGraphReducer::r_apply_attribs
00088 //       Access: Protected
00089 //  Description: The recursive implementation of apply_attribs().
00090 ////////////////////////////////////////////////////////////////////
00091 void SceneGraphReducer::
00092 r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
00093                 int attrib_types, GeomTransformer &transformer) {
00094   if (pgraph_cat.is_debug()) {
00095     pgraph_cat.debug()
00096       << "r_apply_attribs(" << *node << "), node's attribs are:\n";
00097     node->get_transform()->write(pgraph_cat.debug(false), 2);
00098     node->get_state()->write(pgraph_cat.debug(false), 2);
00099     node->get_effects()->write(pgraph_cat.debug(false), 2);
00100   }
00101 
00102   AccumulatedAttribs next_attribs(attribs);
00103   next_attribs.collect(node, attrib_types);
00104 
00105   if (pgraph_cat.is_debug()) {
00106     pgraph_cat.debug()
00107       << "Got attribs from " << *node << "\n"
00108       << "Accumulated attribs are:\n";
00109     next_attribs.write(pgraph_cat.debug(false), attrib_types, 2);
00110   }
00111 
00112   // Check to see if we can't propagate any of these attribs past
00113   // this node for some reason.
00114   int apply_types = 0;
00115 
00116   const RenderEffects *effects = node->get_effects();
00117   if (!effects->safe_to_transform()) {
00118     if (pgraph_cat.is_debug()) {
00119       pgraph_cat.debug()
00120         << "Node " << *node
00121         << " contains a non-transformable effect; leaving transform here.\n";
00122     }
00123     apply_types |= TT_transform;
00124   }
00125   if (!node->safe_to_transform()) {
00126     if (pgraph_cat.is_debug()) {
00127       pgraph_cat.debug()
00128         << "Cannot safely transform nodes of type " << node->get_type()
00129         << "; leaving a transform here but carrying on otherwise.\n";
00130     }
00131     apply_types |= TT_transform;
00132   }
00133   apply_types |= node->get_unsafe_to_apply_attribs();
00134 
00135   // Also, check the children of this node.  If any of them indicates
00136   // it is not safe to modify its transform, we must drop our
00137   // transform here.
00138   int num_children = node->get_num_children();
00139   int i;
00140   if ((apply_types & TT_transform) == 0) {
00141     bool children_transform_friendly = true;
00142     for (i = 0; i < num_children && children_transform_friendly; i++) {
00143       PandaNode *child_node = node->get_child(i);
00144       children_transform_friendly = child_node->safe_to_modify_transform();
00145     }
00146 
00147     if (!children_transform_friendly) {
00148       if (pgraph_cat.is_debug()) {
00149         pgraph_cat.debug()
00150           << "Node " << *node
00151           << " has a child that cannot modify its transform; leaving transform here.\n";
00152       }
00153       apply_types |= TT_transform;
00154     }
00155   }
00156 
00157   // Directly store whatever attributes we must,
00158   next_attribs.apply_to_node(node, attrib_types & apply_types);
00159 
00160   // And apply the rest to the vertices.
00161   node->apply_attribs_to_vertices(next_attribs, attrib_types, transformer);
00162 
00163   // Do we need to copy any children to flatten instances?
00164   bool resist_copy = false;
00165   for (i = 0; i < num_children; i++) {
00166     PandaNode *child_node = node->get_child(i);
00167 
00168     if (child_node->get_num_parents() > 1) {
00169       if (!child_node->safe_to_flatten()) {
00170         if (pgraph_cat.is_debug()) {
00171           pgraph_cat.debug()
00172             << "Cannot duplicate nodes of type " << child_node->get_type()
00173             << ".\n";
00174           resist_copy = true;
00175         }
00176 
00177       } else {
00178         PT(PandaNode) new_node = child_node->make_copy();
00179         if (new_node->get_type() != child_node->get_type()) {
00180           pgraph_cat.error()
00181             << "Don't know how to copy nodes of type "
00182             << child_node->get_type() << "\n";
00183           resist_copy = true;
00184 
00185         } else {
00186           if (pgraph_cat.is_debug()) {
00187             pgraph_cat.debug()
00188               << "Duplicated " << *child_node << "\n";
00189           }
00190           
00191           new_node->copy_children(child_node);
00192           node->replace_child(child_node, new_node);
00193           child_node = new_node;
00194         }
00195       }
00196     }
00197   }
00198 
00199   if (resist_copy) {
00200     // If any of our children should have been copied but weren't, we
00201     // need to drop the state here before continuing.
00202     next_attribs.apply_to_node(node, attrib_types);
00203   }
00204 
00205   // Now it's safe to traverse through all of our children.
00206   nassertv(num_children == node->get_num_children());
00207   for (i = 0; i < num_children; i++) {
00208     PandaNode *child_node = node->get_child(i);
00209     r_apply_attribs(child_node, next_attribs, attrib_types, transformer);
00210   }
00211 }
00212 
00213 
00214 ////////////////////////////////////////////////////////////////////
00215 //     Function: SceneGraphReducer::r_flatten
00216 //       Access: Protected
00217 //  Description: The recursive implementation of flatten().
00218 ////////////////////////////////////////////////////////////////////
00219 int SceneGraphReducer::
00220 r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
00221           bool combine_siblings) {
00222   int num_nodes = 0;
00223 
00224   // First, recurse on each of the children.
00225   {
00226     PandaNode::ChildrenCopy cr = parent_node->get_children_copy();
00227     int num_children = cr.get_num_children();
00228     for (int i = 0; i < num_children; i++) {
00229       PandaNode *child_node = cr.get_child(i);
00230       num_nodes += r_flatten(parent_node, child_node, combine_siblings);
00231     }
00232   }
00233 
00234   // Now that the above loop has removed some children, the child list
00235   // saved above is no longer accurate, so hereafter we must ask the
00236   // node for its real child list.
00237   if (combine_siblings && parent_node->get_num_children() >= 2) {
00238     if (parent_node->safe_to_combine()) {
00239       num_nodes += flatten_siblings(parent_node);
00240     }
00241   }
00242 
00243   if (parent_node->get_num_children() == 1) {
00244     // If we now have exactly one child, consider flattening the node
00245     // out.
00246     PT(PandaNode) child_node = parent_node->get_child(0);
00247     int child_sort = parent_node->get_child_sort(0);
00248 
00249     if (consider_child(grandparent_node, parent_node, child_node)) {
00250       // Ok, do it.
00251       parent_node->remove_child(0);
00252 
00253       if (do_flatten_child(grandparent_node, parent_node, child_node)) {
00254         // Done!
00255         num_nodes++;
00256       } else {
00257         // Chicken out.
00258         parent_node->add_child(child_node, child_sort);
00259       }
00260     }
00261   }
00262 
00263   return num_nodes;
00264 }
00265 
00266 class SortByState {
00267 public:
00268   INLINE bool
00269   operator () (const PandaNode *node1, const PandaNode *node2) const;
00270 };
00271 
00272 INLINE bool SortByState::
00273 operator () (const PandaNode *node1, const PandaNode *node2) const {
00274   if (node1->get_transform() != node2->get_transform()) {
00275     return node1->get_transform() < node2->get_transform();
00276   }
00277   if (node1->get_state() != node2->get_state()) {
00278     return node1->get_state() < node2->get_state();
00279   }
00280   if (node1->get_effects() != node2->get_effects()) {
00281     return node1->get_effects() < node2->get_effects();
00282   }
00283   if (node1->get_draw_mask() != node2->get_draw_mask()) {
00284     return node1->get_draw_mask() < node2->get_draw_mask();
00285   }
00286 
00287   return 0;
00288 }
00289 
00290 
00291 ////////////////////////////////////////////////////////////////////
00292 //     Function: SceneGraphReducer::flatten_siblings
00293 //       Access: Protected
00294 //  Description: Attempts to collapse together any pairs of siblings
00295 //               of the indicated node that share the same properties.
00296 ////////////////////////////////////////////////////////////////////
00297 int SceneGraphReducer::
00298 flatten_siblings(PandaNode *parent_node) {
00299   int num_nodes = 0;
00300 
00301   // First, collect the children into groups of nodes with common
00302   // properties.
00303   typedef plist< PT(PandaNode) > NodeList;
00304   typedef pmap<PandaNode *, NodeList, SortByState> Collected;
00305   Collected collected;
00306 
00307   {
00308     // Protect this within a local scope, so the Children member will
00309     // destruct and free the read pointer before we try to write to
00310     // these nodes.
00311     PandaNode::Children cr = parent_node->get_children();
00312     int num_children = cr.get_num_children();
00313     for (int i = 0; i < num_children; i++) {
00314       PandaNode *child_node = cr.get_child(i);
00315       if (child_node->safe_to_combine()) {
00316         collected[child_node].push_back(child_node);
00317       }
00318     }
00319   }
00320 
00321   // Now visit each of those groups and try to collapse them together.
00322   // A O(n^2) operation, but presumably the number of nodes in each
00323   // group is small.  And if each node in the group can collapse with
00324   // any other node, it becomes a O(n) operation.
00325   Collected::iterator ci;
00326   for (ci = collected.begin(); ci != collected.end(); ++ci) {
00327     const RenderEffects *effects = (*ci).first->get_effects();
00328     if (effects->safe_to_combine()) {
00329       NodeList &nodes = (*ci).second;
00330 
00331       NodeList::iterator ai1;
00332       ai1 = nodes.begin();
00333       while (ai1 != nodes.end()) {
00334         NodeList::iterator ai1_hold = ai1;
00335         PandaNode *child1 = (*ai1);
00336         ++ai1;
00337         NodeList::iterator ai2 = ai1;
00338         while (ai2 != nodes.end()) {
00339           NodeList::iterator ai2_hold = ai2;
00340           PandaNode *child2 = (*ai2);
00341           ++ai2;
00342 
00343           if (consider_siblings(parent_node, child1, child2)) {
00344             PT(PandaNode) new_node = 
00345               do_flatten_siblings(parent_node, child1, child2);
00346             if (new_node != (PandaNode *)NULL) {
00347               // We successfully collapsed a node.
00348               nodes.erase(ai2_hold);
00349               nodes.erase(ai1_hold);
00350               nodes.push_back(new_node);
00351               ai1 = nodes.begin();
00352               ai2 = nodes.end();
00353               num_nodes++;
00354             }
00355           }
00356         }
00357       }
00358     }
00359   }
00360 
00361   return num_nodes;
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: SceneGraphReducer::consider_child
00366 //       Access: Protected
00367 //  Description: Decides whether or not the indicated child node is a
00368 //               suitable candidate for removal.  Returns true if the
00369 //               node may be removed, false if it should be kept.
00370 ////////////////////////////////////////////////////////////////////
00371 bool SceneGraphReducer::
00372 consider_child(PandaNode *grandparent_node, PandaNode *parent_node, 
00373                PandaNode *child_node) {
00374   if (!parent_node->safe_to_combine() || !child_node->safe_to_combine()) {
00375     // One or both nodes cannot be safely combined with another node;
00376     // do nothing.
00377     return false;
00378   }
00379 
00380   if (parent_node->get_transform() != child_node->get_transform() ||
00381       parent_node->get_state() != child_node->get_state() ||
00382       parent_node->get_effects() != child_node->get_effects() ||
00383       parent_node->get_draw_mask() != child_node->get_draw_mask()) {
00384     // The two nodes have a different state; too bad.
00385     return false;
00386   }
00387 
00388   if (!parent_node->get_effects()->safe_to_combine()) {
00389     // The effects don't want to be combined.
00390     return false;
00391   }
00392 
00393   return true;
00394 }
00395 
00396 ////////////////////////////////////////////////////////////////////
00397 //     Function: SceneGraphReducer:c:onsider_siblings
00398 //       Access: Protected
00399 //  Description: Decides whether or not the indicated sibling nodes
00400 //               should be collapsed into a single node or not.
00401 //               Returns true if the nodes may be collapsed, false if
00402 //               they should be kept distinct.
00403 ////////////////////////////////////////////////////////////////////
00404 bool SceneGraphReducer::
00405 consider_siblings(PandaNode *parent_node, PandaNode *child1,
00406                   PandaNode *child2) {
00407   // We don't have to worry about the states being different betweeen
00408   // child1 and child2, since the SortByState object already
00409   // guaranteed we only consider children that have the same state.
00410   return true;
00411 }
00412 
00413 ////////////////////////////////////////////////////////////////////
00414 //     Function: SceneGraphReducer::do_flatten_child
00415 //       Access: Protected
00416 //  Description: Collapses together the indicated parent node and
00417 //               child node and leaves the result attached to the
00418 //               grandparent.  The return value is true if the node is
00419 //               successfully collapsed, false if we chickened out.
00420 ////////////////////////////////////////////////////////////////////
00421 bool SceneGraphReducer::
00422 do_flatten_child(PandaNode *grandparent_node, PandaNode *parent_node, 
00423                  PandaNode *child_node) {
00424   if (pgraph_cat.is_debug()) {
00425     pgraph_cat.debug()
00426       << "Collapsing " << *parent_node << " and " << *child_node << "\n";
00427   }
00428 
00429   PT(PandaNode) new_parent = collapse_nodes(parent_node, child_node, false);
00430   if (new_parent == (PandaNode *)NULL) {
00431     if (pgraph_cat.is_debug()) {
00432       pgraph_cat.debug()
00433         << "Decided not to collapse " << *parent_node 
00434         << " and " << *child_node << "\n";
00435     }
00436     return false;
00437   }
00438 
00439   choose_name(new_parent, parent_node, child_node);
00440 
00441   if (new_parent != child_node) {
00442     new_parent->steal_children(child_node);
00443   }
00444 
00445   if (new_parent != parent_node) {
00446     grandparent_node->replace_child(parent_node, new_parent);
00447   }
00448 
00449   return true;
00450 }
00451 
00452 ////////////////////////////////////////////////////////////////////
00453 //     Function: SceneGraphReducer::do_flatten_siblings
00454 //       Access: Protected
00455 //  Description: Performs the work of collapsing two sibling nodes
00456 //               together into a single node, leaving the resulting
00457 //               node attached to the parent.
00458 //
00459 //               Returns a pointer to a PandaNode the reflects the
00460 //               combined node (which may be either of the source nodes,
00461 //               or a new node altogether) if the siblings are
00462 //               successfully collapsed, or NULL if we chickened out.
00463 ////////////////////////////////////////////////////////////////////
00464 PandaNode *SceneGraphReducer::
00465 do_flatten_siblings(PandaNode *parent_node, PandaNode *child1,
00466                     PandaNode *child2) {
00467   if (pgraph_cat.is_debug()) {
00468     pgraph_cat.debug()
00469       << "Collapsing " << *child1 << " and " << *child2 << "\n";
00470   }
00471 
00472   PT(PandaNode) new_child = collapse_nodes(child2, child1, true);
00473   if (new_child == (PandaNode *)NULL) {
00474     if (pgraph_cat.is_debug()) {
00475       pgraph_cat.debug()
00476         << "Decided not to collapse " << *child1 << " and " << *child2 << "\n";
00477     }
00478     return NULL;
00479   }
00480 
00481   choose_name(new_child, child2, child1);
00482 
00483   if (new_child == child1) {
00484     new_child->steal_children(child2);
00485     parent_node->remove_child(child2);
00486 
00487   } else if (new_child == child2) {
00488     new_child->steal_children(child1);
00489     parent_node->remove_child(child1);
00490 
00491   } else {
00492     new_child->steal_children(child1);
00493     new_child->steal_children(child2);
00494     parent_node->remove_child(child2);
00495     parent_node->replace_child(child1, new_child);
00496   }
00497 
00498   return new_child;
00499 }
00500 
00501 ////////////////////////////////////////////////////////////////////
00502 //     Function: SceneGraphReducer::collapse_nodes
00503 //       Access: Protected
00504 //  Description: Collapses the two nodes into a single node, if
00505 //               possible.  The 'siblings' flag is true if the two
00506 //               nodes are siblings nodes; otherwise, node1 is a
00507 //               parent of node2.  The return value is the resulting
00508 //               node, which may be either one of the source nodes, or
00509 //               a new node altogether, or it may be NULL to indicate
00510 //               that the collapse operation could not take place.
00511 ////////////////////////////////////////////////////////////////////
00512 PT(PandaNode) SceneGraphReducer::
00513 collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) {
00514   return node2->combine_with(node1);
00515 }
00516 
00517 
00518 ////////////////////////////////////////////////////////////////////
00519 //     Function: SceneGraphReducer::choose_name
00520 //       Access: Protected
00521 //  Description: Chooses a suitable name for the collapsed node, based
00522 //               on the names of the two sources nodes.
00523 ////////////////////////////////////////////////////////////////////
00524 void SceneGraphReducer::
00525 choose_name(PandaNode *preserve, PandaNode *source1, PandaNode *source2) {
00526   string name;
00527   bool got_name = false;
00528 
00529   name = source1->get_name();
00530   got_name = !name.empty() || source1->preserve_name();
00531 
00532   if (source2->preserve_name() || !got_name) {
00533     name = source2->get_name();
00534     got_name = !name.empty() || source2->preserve_name();
00535   }
00536 
00537   if (got_name) {
00538     preserve->set_name(name);
00539   }
00540 }

Generated on Fri May 2 00:42:20 2003 for Panda by doxygen1.3