Panda3D

eggBinMaker.h
Go to the documentation of this file.
1 /**
2 * PANDA 3D SOFTWARE
3 * Copyright (c) Carnegie Mellon University. All rights reserved.
4 *
5 * All use of this software is subject to the terms of the revised BSD
6 * license. You should have received a copy of this license along
7 * with this source code in a file named "LICENSE."
8 *
9 * @file eggBinMaker.h
10 * @author drose
11 * @date 1999-01-21
12 */
13
14 #ifndef EGGBINMAKER_H
15 #define EGGBINMAKER_H
16
17 /*
18 * EggBinMaker This is a handy class for collecting related nodes together.
19 * Its purpose is to make it easier to process egg files for converting to
20 * another scene graph format. Egg is very general and allows nodes to be
21 * parented willy-nilly anywhere you like, while many other scene graph
22 * formats have requirements that certain kinds of nodes be grouped together.
23 * Although EggBinMaker can be used to group any kinds of nodes together, one
24 * of the most common examples is grouping polygons into polysets. Egg allows
25 * individual polygons to be parented directly to any group node, while most
26 * scene graph formats prefer to have polygons with similar attributes grouped
27 * into some kind of a polyset node. Therefore, the following usage
28 * discussion will use grouping polygons into polysets as an example.
29 * EggBinMaker is actually an abstract class; it cannot be used directly. To
30 * use it, you must create a subclass and redefine some or all of its virtual
31 * functions to specify the precise behavior you require. You must define at
32 * least the following function: virtual int get_bin_number(const EggNode
33 * *node); This function identifies the kinds of nodes in the graph, for
34 * instance EggPolygons, that are to be put into bins. It will be called once
35 * for each node encountered, and it should return nonzero if the node is to
36 * be binned, and zero otherwise. To group polygons into polysets, this
37 * function might look like: virtual int get_bin_number(const EggNode *node) {
38 * if (node->is_of_type(EggPolygon::get_class_type())) { return 1; } else {
39 * return 0; } } This function may also return the bin number that a given
40 * node should be dropped into. The bin number is completely arbitrary, and
41 * it just serves to differentiate different bins. By default, all sibling
42 * nodes will be dropped into the same bin; you can redefine this to sort
43 * nodes further into categories. For instance, if you wanted to put textured
44 * polygons into a different polyset than untextured polygons, you might
45 * define this function as follows: virtual int get_bin_number(const EggNode
46 * *node) { if (node->is_of_type(EggPolygon::get_class_type())) { EggPolygon
47 * *poly = DCAST(EggPolygon, node); return (poly->has_texture()) ? 1 : 2; }
48 * else { return 0; } } Of course, unrelated nodes--nodes that belong to
49 * different parents--will never be placed into the same bin together,
50 * regardless of the bin number. It is important to note that it is not
51 * necessarily true that there is only one bin for each bin number. If you
52 * redefine sorts_less(), below, you provide a finer-grained control that may
53 * create multiple bins for a given bin number. This function may be called
54 * several times for a given node, and it should return the same number each
55 * time. You may also redefine any or all of the following functions: virtual
56 * void prepare_node(EggNode *node); This method is called, once, on each node
57 * in the egg hierarchy as it is visited the first time. It allows the
58 * subclass a chance to analyze the node or do any other initial processing.
59 * This is a fine opportunity to tag an EggUserData onto the node, for
60 * instance. virtual bool sorts_less(int bin_number, const EggNode *a, const
61 * EggNode *b); Sometimes a simple bin number alone is not enough. For
62 * instance, suppose you needed to group together not just all textured
63 * polygons, but all polygons that shared a particular texture map. Two
64 * polygons that are each textured with a different texture map should go into
65 * different polysets. To do this with bin numbers, you'd have to know ahead
66 * of time all the texture maps that are in use, and assign a unique number to
67 * each one. sorts_less() can make this unnecessary. It's a finer-grained
68 * sorting than by bin numbers. Once two nodes have been grouped together
69 * into the same bin number, sorts_less is called on them. If it returns
70 * true, then node a should be placed into an earlier bin than node b, even
71 * though they share the same bin number. If sorts_less(a, b) and
72 * sorts_less(b, a) both return false, then nodes a and b are placed into the
73 * same bin. To continue the example, and sort polygons into different bins
74 * based on the texture map: virtual bool sorts_less(int bin_number, const
75 * EggNode *a, const EggNode *b) { if (bin_number == 2) { bin 2, textured
76 * geometry return (a->get_texture() < b->get_texture()); } else { bin 1,
77 * untextured geometry return false; } } The actual comparison can be
78 * arbitrary, as long as it is consistent. Its only purpose is to assign some
79 * ordering among bins. In the example, for instance, the comparison is based
80 * on the pointer to the texture maps--it doesn't matter which comes before
81 * the other, as long as it's consistent. In particular, it should never be
82 * true that sorts_less(a, b) and sorts_less(b, a) both return true--that is a
83 * clear contradiction. Of course, if you're using sorts_less() anyway, you
84 * could put *all* of the logic for binning into this function; there's no
85 * need to use both get_bin_number() and sorts_less(), necessarily. In the
86 * current example, here's another version of sorts_less() that accomplishes
87 * the same thing as the combined effects of the above get_bin_number() and
88 * sorts_less() working together: virtual bool sorts_less(int bin_number,
89 * const EggNode *a, const EggNode *b) { if (a->has_texture() !=
90 * b->has_texture()) { return ((int)a->has_texture() < (int)b->has_texture());
91 * } if (a->has_texture()) { return (a->get_texture() < b->get_texture()); }
92 * return false; } virtual bool collapse_group(const EggGroup *group, int
93 * bin_number); After all the nodes have been assigned to bins and the
94 * individual bins (polysets) have been created, it might turn out that some
95 * groups have had all their children placed into the same bin. In this case,
96 * the group node is now redundant, since it contains just the one child, the
97 * new EggBin (polyset) node. It might be advantageous to remove the group
98 * and collapse its properties into the new node. In this case (and this case
99 * only), collapse_group() will be called, given the node and the bin number.
100 * If it returns true, the node will indeed be collapsed into its bin;
101 * otherwise, they will be left separate. The point is that there might be
102 * some attributes in the group node (for instance, a matrix transform) that
103 * cannot be represented in a polyset node in the new scene graph format, so
104 * there may be some cases in which the group cannot be safely collapsed.
105 * Since the egg library cannot know about which such cases cause problems, it
106 * leaves it up to you. The default behavior is never to collapse nodes.
107 * virtual string get_bin_name(int bin_number, EggNode *child); This function
108 * is called as each new bin is created, to optionally define a name for the
109 * new node. If it returns the empty string, the node name will be empty,
110 * unless it was collapsed with its parent group, in which case it will
111 * inherit its former parent's name. Once you have subclassed EggBinMaker and
112 * defined the functions as you require, you use it by simply calling
113 * make_bins() one or more times, passing it the pointer to the root of the
114 * scene graph or of some subgraph. It will traverse the subgraph and create
115 * a series of EggBin objects, as required, moving all the binned geometry
116 * under the EggBin objects. The return value is the number of EggBins
117 * created. Each EggBin stores its bin number, which may be retrieved via
118 * get_bin_number().
119 */
120
121
122 #include "pandabase.h"
123
124 #include "eggObject.h"
125
126 #include "pointerTo.h"
127 #include "pnotify.h"
128
129 #include "pset.h"
130 #include "pmap.h"
131
132 class EggNode;
133 class EggGroup;
134 class EggGroupNode;
135 class EggBin;
136 class EggBinMaker;
137
138 /**
139 * This is just an STL function object, used to sort nodes within EggBinMaker.
140 * It's part of the private interface; ignore it.
141 */
142 class EXPCL_PANDA_EGG EggBinMakerCompareNodes {
143 public:
144 EggBinMakerCompareNodes() {
145 // We need to have a default constructor to compile, but it should never
146 // be called.
147 nassertv(false);
148 }
149 EggBinMakerCompareNodes(EggBinMaker *ebm) : _ebm(ebm) { }
150 bool operator ()(const EggNode *a, const EggNode *b) const;
151
152 EggBinMaker *_ebm;
153 };
154
155
156 /**
157 * This is a handy class for collecting related nodes together. It is an
158 * abstract class; to use it you must subclass off of it. See the somewhat
159 * lengthy comment above.
160 */
161 class EXPCL_PANDA_EGG EggBinMaker : public EggObject {
162 PUBLISHED:
163 EggBinMaker();
164 ~EggBinMaker();
165
166 int make_bins(EggGroupNode *root_group);
167
168 virtual void
169 prepare_node(EggNode *node);
170
171 virtual int
172 get_bin_number(const EggNode *node)=0;
173
174 virtual bool
175 sorts_less(int bin_number, const EggNode *a, const EggNode *b);
176
177 virtual bool
178 collapse_group(const EggGroup *group, int bin_number);
179
180 virtual std::string
181 get_bin_name(int bin_number, const EggNode *child);
182
183 virtual PT(EggBin)
184 make_bin(int bin_number, const EggNode *child, EggGroup *collapse_from);
185
186 private:
187 // The logic is two-pass. First, we make a scene graph traversal and store
188 // all the pointers into the GroupNodesSortedNodes structure, which groups
189 // nodes by their parent group, and then sorted into bin order.
190 typedef pmultiset<PT(EggNode), EggBinMakerCompareNodes> SortedNodes;
191 typedef pmap<EggGroupNode *, SortedNodes> GroupNodes;
192
193 // Then we walk through that list and create a BinsNodes structure for each
194 // group, which separates out the nodes into the individual bins.
195 typedef pvector< PT(EggNode) > Nodes;
196 typedef pvector<Nodes> Bins;
197
198 void collect_nodes(EggGroupNode *group);
199 int get_bins_for_group(GroupNodes::const_iterator gi);
200 void make_bins_for_group(EggGroupNode *group, const Bins &bins);
201 void setup_bin(EggBin *bin, const Nodes &nodes);
202
203 GroupNodes _group_nodes;
204
205
206 public:
207
208 static TypeHandle get_class_type() {
209 return _type_handle;
210 }
211 static void init_type() {
212 EggObject::init_type();
213 register_type(_type_handle, "EggBinMaker",
214 EggObject::get_class_type());
215 }
216 virtual TypeHandle get_type() const {
217 return get_class_type();
218 }
219 virtual TypeHandle force_init_type() {init_type(); return get_class_type();}
220
221 private:
222 static TypeHandle _type_handle;
223
224 };
225
226 #endif
EggBinMakerCompareNodes
This is just an STL function object, used to sort nodes within EggBinMaker.
Definition: eggBinMaker.h:142
EggBinMaker
This is a handy class for collecting related nodes together.
Definition: eggBinMaker.h:161
EggBin
A type of group node that holds related subnodes.
Definition: eggBin.h:26
EggGroupNode
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
EggGroup
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:36
EggObject
The highest-level base class in the egg directory.
Definition: eggObject.h:29
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
pmap< EggGroupNode *, SortedNodes >
pmultiset
This is our own Panda specialization on the default STL multiset.
Definition: pset.h:108
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
eggObject.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pandabase.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pmap.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnotify.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pointerTo.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pset.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
register_type
void register_type(TypeHandle &type_handle, const std::string &name)
This inline function is just a convenient way to call TypeRegistry::register_type(),...
Definition: register_type.I:22