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