Panda3D

eggBinMaker.h

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 
 All Classes Functions Variables Enumerations