diff --git a/panda/src/egg/eggBinMaker.cxx b/panda/src/egg/eggBinMaker.cxx index 0be90afa5f..24e745a53c 100644 --- a/panda/src/egg/eggBinMaker.cxx +++ b/panda/src/egg/eggBinMaker.cxx @@ -135,13 +135,35 @@ collapse_group(const EggGroup *, int) { // Function: EggBinMaker::get_bin_name // Access: Public, Virtual // Description: May be overridden in derived classes to define a name -// for each new bin, based on its bin number. +// for each new bin, based on its bin number, and a +// sample child. //////////////////////////////////////////////////////////////////// string EggBinMaker:: -get_bin_name(int) { +get_bin_name(int, const EggNode *) { return string(); } +//////////////////////////////////////////////////////////////////// +// Function: EggBinMaker::make_bin +// Access: Public, Virtual +// Description: May be overridden in derived classes to construct a +// new EggBin object (or some derived class, if needed), +// and preload some initial data into as required. +// +// child is an arbitrary child of the bin, and +// collapse_from is the group the bin is being collapsed +// with, if any (implying collapse_group() returned +// true), or NULL if not. +//////////////////////////////////////////////////////////////////// +PT(EggBin) EggBinMaker:: +make_bin(int, const EggNode *, EggGroup *collapse_from) { + if (collapse_from == (EggGroup *)NULL) { + return new EggBin; + } else { + return new EggBin(*collapse_from); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggBinMaker::collect_nodes // Access: Private @@ -268,8 +290,11 @@ make_bins_for_group(EggGroupNode *group, const Bins &bins) { } if (collapse) { - EggBin *bin = new EggBin(*DCAST(EggGroup, group)); - setup_bin(bin, bins.front()); + const Nodes &nodes = bins.front(); + nassertv(!nodes.empty()); + int bin_number = get_bin_number(nodes.front()); + PT(EggBin) bin = make_bin(bin_number, nodes.front(), DCAST(EggGroup, group)); + setup_bin(bin, nodes); EggGroupNode *parent = group->get_parent(); parent->remove_child(group); @@ -278,8 +303,11 @@ make_bins_for_group(EggGroupNode *group, const Bins &bins) { } else { Bins::const_iterator bi; for (bi = bins.begin(); bi != bins.end(); ++bi) { - EggBin *bin = new EggBin; - setup_bin(bin, *bi); + const Nodes &nodes = (*bi); + nassertv(!nodes.empty()); + int bin_number = get_bin_number(nodes.front()); + PT(EggBin) bin = make_bin(bin_number, nodes.front(), NULL); + setup_bin(bin, nodes); group->add_child(bin); } @@ -299,7 +327,7 @@ setup_bin(EggBin *bin, const Nodes &nodes) { int bin_number = get_bin_number(nodes.front()); bin->set_bin_number(bin_number); - string bin_name = get_bin_name(bin_number); + string bin_name = get_bin_name(bin_number, nodes.front()); if (!bin_name.empty()) { bin->set_name(bin_name); } diff --git a/panda/src/egg/eggBinMaker.h b/panda/src/egg/eggBinMaker.h index 3e81b47f88..bbf1bb4008 100644 --- a/panda/src/egg/eggBinMaker.h +++ b/panda/src/egg/eggBinMaker.h @@ -190,7 +190,7 @@ // behavior is never to collapse nodes. // // -// virtual string get_bin_name(int bin_number); +// virtual string get_bin_name(int bin_number, EggNode *child); // // This function is called as each new bin is created, to // optionally define a name for the new node. If it returns the @@ -275,7 +275,10 @@ PUBLISHED: collapse_group(const EggGroup *group, int bin_number); virtual string - get_bin_name(int bin_number); + get_bin_name(int bin_number, const EggNode *child); + + virtual PT(EggBin) + make_bin(int bin_number, const EggNode *child, EggGroup *collapse_from); private: // The logic is two-pass. First, we make a scene graph traversal diff --git a/panda/src/egg/eggPrimitive.I b/panda/src/egg/eggPrimitive.I index 966b1ca053..c04edd5b54 100644 --- a/panda/src/egg/eggPrimitive.I +++ b/panda/src/egg/eggPrimitive.I @@ -72,6 +72,25 @@ INLINE EggPrimitive:: clear(); } +//////////////////////////////////////////////////////////////////// +// Function: EggPrimitive::get_sort_name +// Access: Published +// Description: Returns the name of the primitive for the purposes of +// sorting primitives into different groups, if there is +// one. +// +// Presently, this is defined as the primitive name +// itself, unless it begins with a digit. +//////////////////////////////////////////////////////////////////// +INLINE string EggPrimitive:: +get_sort_name() const { + const string &name = get_name(); + if (!name.empty() && !isdigit(name[0])) { + return name; + } + return string(); +} + //////////////////////////////////////////////////////////////////// // Function: EggPrimitive::clear_connected_shading // Access: Published diff --git a/panda/src/egg/eggPrimitive.h b/panda/src/egg/eggPrimitive.h index 34e1c0442d..4842c6dcdf 100644 --- a/panda/src/egg/eggPrimitive.h +++ b/panda/src/egg/eggPrimitive.h @@ -87,6 +87,8 @@ PUBLISHED: virtual EggRenderMode *determine_draw_order(); virtual EggRenderMode *determine_bin(); + INLINE string get_sort_name() const; + virtual Shading get_shading() const; INLINE void clear_connected_shading(); INLINE Shading get_connected_shading() const; diff --git a/panda/src/egg2pg/characterMaker.cxx b/panda/src/egg2pg/characterMaker.cxx index edf5ea2966..eeed386a88 100644 --- a/panda/src/egg2pg/characterMaker.cxx +++ b/panda/src/egg2pg/characterMaker.cxx @@ -152,7 +152,7 @@ egg_to_index(EggNode *egg_node) const { // the character's top node. //////////////////////////////////////////////////////////////////// PandaNode *CharacterMaker:: -part_to_node(PartGroup *part) const { +part_to_node(PartGroup *part, const string &name) const { PandaNode *node = _character_node; if (part->is_of_type(CharacterJoint::get_class_type())) { @@ -166,18 +166,18 @@ part_to_node(PartGroup *part) const { // We should always return a GeomNode, so that all polysets // created at the same level will get added into the same // GeomNode. Look for a child of this node. If it doesn't have a - // child yet, add a GeomNode and it. Otherwise, if it already has - // a child, return that. - if (node->is_geom_node()) { + // child yet, add a GeomNode and return it. Otherwise, if it + // already has a child, return that. + if (node->is_geom_node() && node->get_name() == name) { return node; } for (int i = 0; i < node->get_num_children(); i++) { PandaNode *child = node->get_child(i); - if (child->is_geom_node()) { + if (child->is_geom_node() && child->get_name() == name) { return child; } } - PT(GeomNode) geom_node = new GeomNode(""); + PT(GeomNode) geom_node = new GeomNode(name); node->add_child(geom_node); return geom_node; @@ -404,7 +404,7 @@ make_qpgeometry(EggNode *egg_node) { is_dynamic = false; } - PandaNode *parent = part_to_node(egg_to_part(bin_home)); + PandaNode *parent = part_to_node(egg_to_part(bin_home), egg_bin->get_name()); LMatrix4d transform = egg_bin->get_vertex_frame() * bin_home->get_node_frame_inv(); @@ -431,7 +431,7 @@ make_qpgeometry(EggNode *egg_node) { //////////////////////////////////////////////////////////////////// void CharacterMaker:: make_static_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { - PandaNode *node = part_to_node(egg_to_part(prim_home)); + PandaNode *node = part_to_node(egg_to_part(prim_home), string()); // We need this funny transform to convert from the coordinate // space of the original vertices to that of the new joint node. @@ -450,7 +450,7 @@ make_static_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { //////////////////////////////////////////////////////////////////// void CharacterMaker:: make_dynamic_primitive(EggPrimitive *egg_primitive, EggGroupNode *prim_home) { - PandaNode *node = part_to_node(egg_to_part(prim_home)); + PandaNode *node = part_to_node(egg_to_part(prim_home), string()); LMatrix4d transform = egg_primitive->get_vertex_frame() * diff --git a/panda/src/egg2pg/characterMaker.h b/panda/src/egg2pg/characterMaker.h index 6184ae2974..96704a44d5 100644 --- a/panda/src/egg2pg/characterMaker.h +++ b/panda/src/egg2pg/characterMaker.h @@ -59,7 +59,7 @@ public: PartGroup *egg_to_part(EggNode *egg_node) const; VertexTransform *egg_to_transform(EggNode *egg_node); int egg_to_index(EggNode *egg_node) const; - PandaNode *part_to_node(PartGroup *part) const; + PandaNode *part_to_node(PartGroup *part, const string &name) const; int create_slider(const string &name); VertexSlider *egg_to_slider(const string &name); diff --git a/panda/src/egg2pg/eggBinner.cxx b/panda/src/egg2pg/eggBinner.cxx index 270e253926..cef08d324e 100644 --- a/panda/src/egg2pg/eggBinner.cxx +++ b/panda/src/egg2pg/eggBinner.cxx @@ -79,6 +79,21 @@ get_bin_number(const EggNode *node) { return (int)BN_none; } +//////////////////////////////////////////////////////////////////// +// Function: EggBinner::get_bin_name +// Access: Public, Virtual +// Description: May be overridden in derived classes to define a name +// for each new bin, based on its bin number, and a +// sample child. +//////////////////////////////////////////////////////////////////// +string EggBinner:: +get_bin_name(int bin_number, const EggNode *child) { + if (bin_number == BN_polyset) { + return DCAST(EggPrimitive, child)->get_sort_name(); + } + + return string(); +} //////////////////////////////////////////////////////////////////// // Function: EggBinner::sorts_less @@ -96,14 +111,16 @@ sorts_less(int bin_number, const EggNode *a, const EggNode *b) { // Different render states are binned separately. const EggRenderState *rsa, *rsb; - DCAST_INTO_R(rsa, a->get_user_data(EggRenderState::get_class_type()), false); - DCAST_INTO_R(rsb, b->get_user_data(EggRenderState::get_class_type()), false); + DCAST_INTO_R(rsa, pa->get_user_data(EggRenderState::get_class_type()), false); + DCAST_INTO_R(rsb, pb->get_user_data(EggRenderState::get_class_type()), false); int compare = rsa->compare_to(*rsb); if (compare != 0) { return (compare < 0); } - return false; + // Also, if the primitive was given a name (that does not begin + // with a digit), it gets binned with similar-named primitives. + return pa->get_sort_name() < pb->get_sort_name(); } case BN_lod: diff --git a/panda/src/egg2pg/eggBinner.h b/panda/src/egg2pg/eggBinner.h index 5af327f251..9f3ac8a7fc 100644 --- a/panda/src/egg2pg/eggBinner.h +++ b/panda/src/egg2pg/eggBinner.h @@ -55,6 +55,9 @@ public: virtual int get_bin_number(const EggNode *node); + virtual string + get_bin_name(int bin_number, const EggNode *child); + virtual bool sorts_less(int bin_number, const EggNode *a, const EggNode *b); diff --git a/panda/src/egg2pg/eggLoader.cxx b/panda/src/egg2pg/eggLoader.cxx index ebbe885f8b..c2c5e921a9 100644 --- a/panda/src/egg2pg/eggLoader.cxx +++ b/panda/src/egg2pg/eggLoader.cxx @@ -176,11 +176,19 @@ build_graph() { _data->get_connected_shading(); _data->unify_attributes(true, true); + // Sequences and switches have special needs. Make sure that + // primitives parented directly to a sequence or switch are sorted + // into sub-groups first, to prevent them being unified into a + // single polyset. + separate_switches(_data); + // Then bin up the polysets and LOD nodes. _data->remove_invalid_primitives(true); EggBinner binner(*this); binner.make_bins(_data); + // ((EggGroupNode *)_data)->write(cerr, 0); + // Now build up the scene graph. _root = new ModelRoot(_data->get_egg_filename().get_basename()); make_node(_data, _root); @@ -1471,6 +1479,49 @@ setup_bucket(BuilderBucket &bucket, EggLoader::BakeInUVs &bake_in_uvs, +//////////////////////////////////////////////////////////////////// +// Function: EggLoader::separate_switches +// Access: Private +// Description: Walks the tree recursively, looking for EggPrimitives +// that are children of sequence or switch nodes. If +// any are found, they are moved within their own group +// to protect them from being flattened with their +// neighbors. +//////////////////////////////////////////////////////////////////// +void EggLoader:: +separate_switches(EggNode *egg_node) { + bool parent_has_switch = false; + if (egg_node->is_of_type(EggGroup::get_class_type())) { + EggGroup *egg_group = DCAST(EggGroup, egg_node); + parent_has_switch = egg_group->get_switch_flag(); + } + + if (egg_node->is_of_type(EggGroupNode::get_class_type())) { + EggGroupNode *egg_group = DCAST(EggGroupNode, egg_node); + + EggGroupNode::iterator ci; + ci = egg_group->begin(); + while (ci != egg_group->end()) { + EggGroupNode::iterator cnext; + cnext = ci; + ++cnext; + + PT(EggNode) child = (*ci); + if (parent_has_switch && + child->is_of_type(EggPrimitive::get_class_type())) { + // Move this child under a new node. + PT(EggGroup) new_group = new EggGroup(child->get_name()); + egg_group->replace(ci, new_group.p()); + new_group->add_child(child); + } + + separate_switches(child); + + ci = cnext; + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggLoader::make_node // Access: Private @@ -1727,7 +1778,7 @@ make_node(EggGroup *egg_group, PandaNode *parent) { bool all_polysets = false; bool any_hidden = false; if (use_qpgeom) { - check_for_polysets(egg_group, all_polysets, any_hidden); + // check_for_polysets(egg_group, all_polysets, any_hidden); } if (all_polysets && !any_hidden) { diff --git a/panda/src/egg2pg/eggLoader.h b/panda/src/egg2pg/eggLoader.h index c00ecd3460..7c4aeace84 100644 --- a/panda/src/egg2pg/eggLoader.h +++ b/panda/src/egg2pg/eggLoader.h @@ -130,6 +130,8 @@ private: void setup_bucket(BuilderBucket &bucket, BakeInUVs &bake_in_uvs, PandaNode *parent, EggPrimitive *egg_prim); + void separate_switches(EggNode *egg_node); + PandaNode *make_node(EggNode *egg_node, PandaNode *parent); PandaNode *make_node(EggBin *egg_bin, PandaNode *parent); PandaNode *make_polyset(EggBin *egg_bin, PandaNode *parent);