00001 // Filename: eggBinMaker.cxx 00002 // Created by: drose (21Jan99) 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 "eggBinMaker.h" 00020 #include "eggGroupNode.h" 00021 #include "eggGroup.h" 00022 #include "eggBin.h" 00023 00024 #include "dcast.h" 00025 #include "notify.h" 00026 00027 TypeHandle EggBinMaker::_type_handle; 00028 00029 00030 //////////////////////////////////////////////////////////////////// 00031 // Function: EggBinMakerCompareNodes::Function operator 00032 // Access: Public 00033 // Description: Called by the SortedNodes set to put nodes into bin 00034 // order. Returns true if the first node falls into an 00035 // earlier bin than the second node, false otherwise. 00036 //////////////////////////////////////////////////////////////////// 00037 bool EggBinMakerCompareNodes:: 00038 operator ()(const EggNode *a, const EggNode *b) const { 00039 int bin_number_a = _ebm->get_bin_number(a); 00040 int bin_number_b = _ebm->get_bin_number(b); 00041 00042 if (bin_number_a != bin_number_b) { 00043 // If the two nodes return different bin numbers, then they 00044 // sort based on those numbers. 00045 return bin_number_a < bin_number_b; 00046 } 00047 00048 // The two nodes fell into the same bin number, so fall back on the 00049 // comparison function to see if they should be differentiated. 00050 return _ebm->sorts_less(bin_number_a, a, b); 00051 } 00052 00053 //////////////////////////////////////////////////////////////////// 00054 // Function: EggBinMaker::Constructor 00055 // Access: Public 00056 // Description: 00057 //////////////////////////////////////////////////////////////////// 00058 EggBinMaker:: 00059 EggBinMaker() { 00060 } 00061 00062 //////////////////////////////////////////////////////////////////// 00063 // Function: EggBinMaker::Destructor 00064 // Access: Public 00065 // Description: 00066 //////////////////////////////////////////////////////////////////// 00067 EggBinMaker:: 00068 ~EggBinMaker() { 00069 } 00070 00071 00072 //////////////////////////////////////////////////////////////////// 00073 // Function: EggBinMaker::make_bins 00074 // Access: Public 00075 // Description: The main entry point to EggBinMaker. Walks the egg 00076 // scene graph beginning at the indicated root node, and 00077 // moves all binnable nodes into EggBin objects. 00078 // Returns the number of EggBins created. 00079 //////////////////////////////////////////////////////////////////// 00080 int EggBinMaker:: 00081 make_bins(EggGroupNode *root_group) { 00082 _group_nodes.clear(); 00083 00084 collect_nodes(root_group); 00085 00086 int num_bins = 0; 00087 GroupNodes::const_iterator gi; 00088 for (gi = _group_nodes.begin(); gi != _group_nodes.end(); ++gi) { 00089 num_bins += get_bins_for_group(gi); 00090 } 00091 00092 return num_bins; 00093 } 00094 00095 //////////////////////////////////////////////////////////////////// 00096 // Function: EggBinMaker::sorts_less 00097 // Access: Public, Virtual 00098 // Description: May be overridden in derived classes to create 00099 // additional bins within a particular bin number, based 00100 // on some arbitrary property of nodes. This function 00101 // establishes an arbitrary but fixed ordering between 00102 // nodes; if two nodes do not sort to the same position, 00103 // different bins are created for each one (with the 00104 // same bin number on each bin). 00105 //////////////////////////////////////////////////////////////////// 00106 bool EggBinMaker:: 00107 sorts_less(int, const EggNode *, const EggNode *) { 00108 return false; 00109 } 00110 00111 //////////////////////////////////////////////////////////////////// 00112 // Function: EggBinMaker::collapse_group 00113 // Access: Public, Virtual 00114 // Description: May be overridden in derived classes to specify 00115 // whether a particular group node, apparently 00116 // redundant, may be safely collapsed out. 00117 //////////////////////////////////////////////////////////////////// 00118 bool EggBinMaker:: 00119 collapse_group(const EggGroup *, int) { 00120 return false; 00121 } 00122 00123 //////////////////////////////////////////////////////////////////// 00124 // Function: EggBinMaker::get_bin_name 00125 // Access: Public, Virtual 00126 // Description: May be overridden in derived classes to define a name 00127 // for each new bin, based on its bin number. 00128 //////////////////////////////////////////////////////////////////// 00129 string EggBinMaker:: 00130 get_bin_name(int) { 00131 return string(); 00132 } 00133 00134 //////////////////////////////////////////////////////////////////// 00135 // Function: EggBinMaker::collect_nodes 00136 // Access: Private 00137 // Description: Walks the egg scene graph, identifying nodes to be 00138 // binned and moving them from the scene graph into the 00139 // internal bin structure. 00140 //////////////////////////////////////////////////////////////////// 00141 void EggBinMaker:: 00142 collect_nodes(EggGroupNode *group) { 00143 // We have to play games with this next iterator, because we might 00144 // be destructively operating on the child list as we traverse it. 00145 EggGroupNode::iterator i, next; 00146 00147 bool first_in_group = true; 00148 GroupNodes::iterator gni = _group_nodes.end(); 00149 00150 i = group->begin(); 00151 next = i; 00152 while (i != group->end()) { 00153 EggNode *node = (*i); 00154 ++next; 00155 00156 if (get_bin_number(node) != 0) { 00157 // Ok, here's a node to be binned. Add it to the appropriate 00158 // bin. 00159 if (first_in_group) { 00160 // If this is the first time this group has been encountered, 00161 // we need to create a new entry in _group_nodes for it. 00162 00163 pair<GroupNodes::iterator, bool> result; 00164 result = _group_nodes.insert 00165 (GroupNodes::value_type 00166 (group, SortedNodes(EggBinMakerCompareNodes(this)))); 00167 00168 nassertv(result.second); 00169 gni = result.first; 00170 first_in_group = false; 00171 } 00172 00173 // Add this node to the set of all nodes being binned for the 00174 // group. This also puts the nodes into bin order. 00175 nassertv(gni != _group_nodes.end()); 00176 (*gni).second.insert(node); 00177 00178 // And remove it from the scene graph. 00179 group->erase(i); 00180 00181 } else if (node->is_of_type(EggGroupNode::get_class_type())) { 00182 // Here's a normal group node, not to be binned. Traverse. 00183 collect_nodes(DCAST(EggGroupNode, node)); 00184 } 00185 00186 i = next; 00187 } 00188 } 00189 00190 00191 //////////////////////////////////////////////////////////////////// 00192 // Function: EggBinMaker::get_bins_for_group 00193 // Access: Private 00194 // Description: Breaks the set of nodes for a given group up into 00195 // individual bins. 00196 //////////////////////////////////////////////////////////////////// 00197 int EggBinMaker:: 00198 get_bins_for_group(GroupNodes::const_iterator gi) { 00199 EggGroupNode *group = (*gi).first; 00200 const SortedNodes &nodes = (*gi).second; 00201 00202 // It shouldn't be possible for this to be empty. 00203 nassertr(!nodes.empty(), 0); 00204 00205 Bins bins; 00206 EggBinMakerCompareNodes cn(this); 00207 SortedNodes::const_iterator sni, last; 00208 sni = nodes.begin(); 00209 last = sni; 00210 00211 bins.push_back(Nodes()); 00212 bins.back().push_back(*sni); 00213 ++sni; 00214 while (sni != nodes.end()) { 00215 if (cn(*last, *sni)) { 00216 // Begin a new bin. 00217 bins.push_back(Nodes()); 00218 } 00219 bins.back().push_back(*sni); 00220 00221 last = sni; 00222 ++sni; 00223 } 00224 00225 make_bins_for_group(group, bins); 00226 return bins.size(); 00227 } 00228 00229 //////////////////////////////////////////////////////////////////// 00230 // Function: EggBinMaker::make_bins_for_group 00231 // Access: Private 00232 // Description: Creates the EggBin nodes indicated by the internal 00233 // bin structure for each group. 00234 //////////////////////////////////////////////////////////////////// 00235 void EggBinMaker:: 00236 make_bins_for_group(EggGroupNode *group, const Bins &bins) { 00237 // We shouldn't be able to get here if we have no bins! 00238 nassertv(!bins.empty()); 00239 00240 // If the group will have only one bin, and no other children, and 00241 // the group is not the root node (and it is not some funny 00242 // group-like node like a <Table>), maybe we should collapse the 00243 // group and its bin together. 00244 00245 bool collapse = false; 00246 00247 if (group->empty() && 00248 bins.size() == 1 && 00249 group->get_parent() != NULL && 00250 group->is_of_type(EggGroup::get_class_type())) { 00251 const Nodes &nodes = bins.front(); 00252 nassertv(!nodes.empty()); 00253 int bin_number = get_bin_number(nodes.front()); 00254 collapse = collapse_group(DCAST(EggGroup, group), bin_number); 00255 } 00256 00257 if (collapse) { 00258 EggBin *bin = new EggBin(*DCAST(EggGroup, group)); 00259 setup_bin(bin, bins.front()); 00260 00261 EggGroupNode *parent = group->get_parent(); 00262 parent->remove_child(group); 00263 parent->add_child(bin); 00264 00265 } else { 00266 Bins::const_iterator bi; 00267 for (bi = bins.begin(); bi != bins.end(); ++bi) { 00268 EggBin *bin = new EggBin; 00269 setup_bin(bin, *bi); 00270 00271 group->add_child(bin); 00272 } 00273 } 00274 } 00275 00276 00277 //////////////////////////////////////////////////////////////////// 00278 // Function: EggBinMaker::setup_bin 00279 // Access: Private 00280 // Description: Sets up a recently-created EggBin structure with all 00281 // of its children. 00282 //////////////////////////////////////////////////////////////////// 00283 void EggBinMaker:: 00284 setup_bin(EggBin *bin, const Nodes &nodes) { 00285 nassertv(!nodes.empty()); 00286 int bin_number = get_bin_number(nodes.front()); 00287 bin->set_bin_number(bin_number); 00288 00289 string bin_name = get_bin_name(bin_number); 00290 if (!bin_name.empty()) { 00291 bin->set_name(bin_name); 00292 } 00293 00294 Nodes::const_iterator ni; 00295 for (ni = nodes.begin(); ni != nodes.end(); ++ni) { 00296 bin->add_child(*ni); 00297 } 00298 } 00299