00001 // Filename: eggBinMaker.h 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 #ifndef EGGBINMAKER_H 00020 #define EGGBINMAKER_H 00021 00022 /////////////////////////////////////////////////////////////////// 00023 // 00024 // EggBinMaker 00025 // 00026 // This is a handy class for collecting related nodes together. Its 00027 // purpose is to make it easier to process egg files for converting to 00028 // another scene graph format. Egg is very general and allows nodes 00029 // to be parented willy-nilly anywhere you like, while many other 00030 // scene graph formats have requirements that certain kinds of nodes 00031 // be grouped together. 00032 // 00033 // Although EggBinMaker can be used to group any kinds of nodes 00034 // together, one of the most common examples is grouping polygons into 00035 // polysets. Egg allows individual polygons to be parented directly 00036 // to any group node, while most scene graph formats prefer to have 00037 // polygons with similar attributes grouped into some kind of a 00038 // polyset node. Therefore, the following usage discussion will use 00039 // grouping polygons into polysets as an example. 00040 // 00041 // EggBinMaker is actually an abstract class; it cannot be used 00042 // directly. To use it, you must create a subclass and redefine some 00043 // or all of its virtual functions to specify the precise behavior you 00044 // require. 00045 // 00046 // You must define at least the following function: 00047 // 00048 // virtual int get_bin_number(const EggNode *node); 00049 // 00050 // This function identifies the kinds of nodes in the graph, for 00051 // instance EggPolygons, that are to be put into bins. It will be 00052 // called once for each node encountered, and it should return 00053 // nonzero if the node is to be binned, and zero otherwise. To 00054 // group polygons into polysets, this function might look like: 00055 // 00056 // virtual int get_bin_number(const EggNode *node) { 00057 // if (node->is_of_type(EggPolygon::get_class_type())) { 00058 // return 1; 00059 // } else { 00060 // return 0; 00061 // } 00062 // } 00063 // 00064 // 00065 // This function may also return the bin number that a given node 00066 // should be dropped into. The bin number is completely arbitrary, 00067 // and it just serves to differentiate different bins. 00068 // 00069 // By default, all sibling nodes will be dropped into the same bin; 00070 // you can redefine this to sort nodes further into categories. 00071 // For instance, if you wanted to put textured polygons into a 00072 // different polyset than untextured polygons, you might define 00073 // this function as follows: 00074 // 00075 // virtual int get_bin_number(const EggNode *node) { 00076 // if (node->is_of_type(EggPolygon::get_class_type())) { 00077 // EggPolygon *poly = DCAST(EggPolygon, node); 00078 // return (poly->has_texture()) ? 1 : 2; 00079 // } else { 00080 // return 0; 00081 // } 00082 // } 00083 // 00084 // Of course, unrelated nodes--nodes that belong to different 00085 // parents--will never be placed into the same bin together, 00086 // regardless of the bin number. 00087 // 00088 // It is important to note that it is not necessarily true that 00089 // there is only one bin for each bin number. If you redefine 00090 // sorts_less(), below, you provide a finer-grained control that 00091 // may create multiple bins for a given bin number. 00092 // 00093 // This function may be called several times for a given node, and 00094 // it should return the same number each time. 00095 // 00096 // 00097 // You may also redefine any or all of the following functions: 00098 // 00099 // virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b); 00100 // 00101 // Sometimes a simple bin number alone is not enough. For 00102 // instance, suppose you needed to group together not just all 00103 // textured polygons, but all polygons that shared a particular 00104 // texture map. Two polygons that are each textured with a 00105 // different texture map should go into different polysets. To do 00106 // this with bin numbers, you'd have to know ahead of time all the 00107 // texture maps that are in use, and assign a unique number to each 00108 // one. 00109 // 00110 // sorts_less() can make this unnecessary. It's a finer-grained 00111 // sorting than by bin numbers. Once two nodes have been grouped 00112 // together into the same bin number, sorts_less is called on them. 00113 // If it returns true, then node a should be placed into an earlier 00114 // bin than node b, even though they share the same bin number. If 00115 // sorts_less(a, b) and sorts_less(b, a) both return false, then 00116 // nodes a and b are placed into the same bin. 00117 // 00118 // To continue the example, and sort polygons into different bins 00119 // based on the texture map: 00120 // 00121 // virtual bool sorts_less(int bin_number, 00122 // const EggNode *a, const EggNode *b) { 00123 // if (bin_number == 2) { 00124 // // bin 2, textured geometry 00125 // return (a->get_texture() < b->get_texture()); 00126 // } else { 00127 // // bin 1, untextured geometry 00128 // return false; 00129 // } 00130 // } 00131 // 00132 // The actual comparison can be arbitrary, as long as it is 00133 // consistent. Its only purpose is to assign some ordering among 00134 // bins. In the example, for instance, the comparison is based on 00135 // the pointer to the texture maps--it doesn't matter which comes 00136 // before the other, as long as it's consistent. 00137 // 00138 // In particular, it should never be true that sorts_less(a, b) and 00139 // sorts_less(b, a) both return true--that is a clear 00140 // contradiction. 00141 // 00142 // Of course, if you're using sorts_less() anyway, you could put 00143 // *all* of the logic for binning into this function; there's no 00144 // need to use both get_bin_number() and sorts_less(), necessarily. 00145 // In the current example, here's another version of sorts_less() 00146 // that accomplishes the same thing as the combined effects of the 00147 // above get_bin_number() and sorts_less() working together: 00148 // 00149 // virtual bool sorts_less(int bin_number, 00150 // const EggNode *a, const EggNode *b) { 00151 // if (a->has_texture() != b->has_texture()) { 00152 // return ((int)a->has_texture() < (int)b->has_texture()); 00153 // } 00154 // if (a->has_texture()) { 00155 // return (a->get_texture() < b->get_texture()); 00156 // } 00157 // return false; 00158 // } 00159 // 00160 // 00161 // virtual bool collapse_group(const EggGroup *group, int bin_number); 00162 // 00163 // After all the nodes have been assigned to bins and the 00164 // individual bins (polysets) have been created, it might turn out 00165 // that some groups have had all their children placed into the 00166 // same bin. In this case, the group node is now redundant, since 00167 // it contains just the one child, the new EggBin (polyset) node. 00168 // It might be advantageous to remove the group and collapse its 00169 // properties into the new node. 00170 // 00171 // In this case (and this case only), collapse_group() will be 00172 // called, given the node and the bin number. If it returns true, 00173 // the node will indeed be collapsed into its bin; otherwise, they 00174 // will be left separate. 00175 // 00176 // The point is that there might be some attributes in the group 00177 // node (for instance, a matrix transform) that cannot be 00178 // represented in a polyset node in the new scene graph format, so 00179 // there may be some cases in which the group cannot be safely 00180 // collapsed. Since the egg library cannot know about which such 00181 // cases cause problems, it leaves it up to you. The default 00182 // behavior is never to collapse nodes. 00183 // 00184 // 00185 // virtual string get_bin_name(int bin_number); 00186 // 00187 // This function is called as each new bin is created, to 00188 // optionally define a name for the new node. If it returns the 00189 // empty string, the node name will be empty, unless it was 00190 // collapsed with its parent group, in which case it will inherit 00191 // its former parent's name. 00192 // 00193 // 00194 // 00195 // Once you have subclassed EggBinMaker and defined the functions as 00196 // you require, you use it by simply calling make_bins() one or more 00197 // times, passing it the pointer to the root of the scene graph or of 00198 // some subgraph. It will traverse the subgraph and create a series 00199 // of EggBin objects, as required, moving all the binned geometry 00200 // under the EggBin objects. The return value is the number of 00201 // EggBins created. Each EggBin stores its bin number, which may be 00202 // retrieved via get_bin_number(). 00203 // 00204 /////////////////////////////////////////////////////////////////// 00205 00206 00207 #include <pandabase.h> 00208 00209 #include "eggObject.h" 00210 00211 #include <pointerTo.h> 00212 #include <notify.h> 00213 00214 #include "pset.h" 00215 #include "pmap.h" 00216 00217 class EggNode; 00218 class EggGroup; 00219 class EggGroupNode; 00220 class EggBin; 00221 class EggBinMaker; 00222 00223 //////////////////////////////////////////////////////////////////// 00224 // Class : EggBinMakerCompareNodes 00225 // Description : This is just an STL function object, used to sort 00226 // nodes within EggBinMaker. It's part of the private 00227 // interface; ignore it. 00228 //////////////////////////////////////////////////////////////////// 00229 class EXPCL_PANDAEGG EggBinMakerCompareNodes { 00230 public: 00231 EggBinMakerCompareNodes() { 00232 // We need to have a default constructor to compile, but it should 00233 // never be called. 00234 nassertv(false); 00235 } 00236 EggBinMakerCompareNodes(EggBinMaker *ebm) : _ebm(ebm) { } 00237 bool operator ()(const EggNode *a, const EggNode *b) const; 00238 00239 EggBinMaker *_ebm; 00240 }; 00241 00242 00243 //////////////////////////////////////////////////////////////////// 00244 // Class : EggBinMaker 00245 // Description : This is a handy class for collecting related nodes 00246 // together. It is an abstract class; to use it you 00247 // must subclass off of it. See the somewhat lengthy 00248 // comment above. 00249 //////////////////////////////////////////////////////////////////// 00250 class EXPCL_PANDAEGG EggBinMaker : public EggObject { 00251 public: 00252 EggBinMaker(); 00253 ~EggBinMaker(); 00254 00255 int make_bins(EggGroupNode *root_group); 00256 00257 virtual int 00258 get_bin_number(const EggNode *node)=0; 00259 00260 virtual bool 00261 sorts_less(int bin_number, const EggNode *a, const EggNode *b); 00262 00263 virtual bool 00264 collapse_group(const EggGroup *group, int bin_number); 00265 00266 virtual string 00267 get_bin_name(int bin_number); 00268 00269 private: 00270 // The logic is two-pass. First, we make a scene graph traversal 00271 // and store all the pointers into the GroupNodes/SortedNodes 00272 // structure, which groups nodes by their parent group, and then 00273 // sorted into bin order. 00274 typedef pmultiset<PT(EggNode), EggBinMakerCompareNodes> SortedNodes; 00275 typedef pmap<EggGroupNode *, SortedNodes> GroupNodes; 00276 00277 // Then we walk through that list and create a Bins/Nodes structure 00278 // for each group, which separates out the nodes into the individual 00279 // bins. 00280 typedef pvector< PT(EggNode) > Nodes; 00281 typedef pvector<Nodes> Bins; 00282 00283 void collect_nodes(EggGroupNode *group); 00284 int get_bins_for_group(GroupNodes::const_iterator gi); 00285 void make_bins_for_group(EggGroupNode *group, const Bins &bins); 00286 void setup_bin(EggBin *bin, const Nodes &nodes); 00287 00288 GroupNodes _group_nodes; 00289 00290 00291 public: 00292 00293 static TypeHandle get_class_type() { 00294 return _type_handle; 00295 } 00296 static void init_type() { 00297 EggObject::init_type(); 00298 register_type(_type_handle, "EggBinMaker", 00299 EggObject::get_class_type()); 00300 } 00301 virtual TypeHandle get_type() const { 00302 return get_class_type(); 00303 } 00304 virtual TypeHandle force_init_type() {init_type(); return get_class_type();} 00305 00306 private: 00307 static TypeHandle _type_handle; 00308 00309 }; 00310 00311 #endif 00312 00313