// Filename: textureAttrib.cxx // Created by: drose (21Feb02) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://etc.cmu.edu/panda3d/docs/license/ . // // To contact the maintainers of this program write to // panda3d-general@lists.sourceforge.net . // //////////////////////////////////////////////////////////////////// #include "textureAttrib.h" #include "attribSlots.h" #include "graphicsStateGuardianBase.h" #include "bamReader.h" #include "bamWriter.h" #include "datagram.h" #include "datagramIterator.h" #include "dcast.h" CPT(RenderAttrib) TextureAttrib::_empty_attrib; CPT(RenderAttrib) TextureAttrib::_all_off_attrib; TypeHandle TextureAttrib::_type_handle; // This STL Function object is used in filter_to_max(), below, to sort // a list of TextureStages in reverse order by priority and, within // priority, in order by sort. class CompareTextureStagePriorities { public: bool operator ()(const TextureStage *a, const TextureStage *b) const { if (a->get_priority() != b->get_priority()) { return a->get_priority() > b->get_priority(); } return a->get_sort() < b->get_sort(); } }; //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::make // Access: Published, Static // Description: Constructs a new TextureAttrib object suitable for // rendering the indicated texture onto geometry, using // the default TextureStage. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: make(Texture *texture) { return DCAST(TextureAttrib, make())->add_on_stage(TextureStage::get_default(), texture); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::make_off // Access: Published, Static // Description: Constructs a new TextureAttrib object suitable for // rendering untextured geometry. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: make_off() { return make_all_off(); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::make // Access: Published, Static // Description: Constructs a new TextureAttrib object that does // nothing. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: make() { // We make it a special case and store a pointer to the empty attrib // forever once we find it the first time, as an optimization. if (_empty_attrib == (RenderAttrib *)NULL) { _empty_attrib = return_new(new TextureAttrib); } return _empty_attrib; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::make_all_off // Access: Published, Static // Description: Constructs a new TextureAttrib object that turns off // all stages (and hence disables texturing). //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: make_all_off() { // We make it a special case and store a pointer to the off attrib // forever once we find it the first time, as an optimization. if (_all_off_attrib == (RenderAttrib *)NULL) { TextureAttrib *attrib = new TextureAttrib; attrib->_off_all_stages = true; _all_off_attrib = return_new(attrib); } return _all_off_attrib; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::find_on_stage // Access: Published // Description: Returns the index number of the indicated // TextureStage within the list of on_stages, or -1 if // the indicated stage is not listed. //////////////////////////////////////////////////////////////////// int TextureAttrib:: find_on_stage(const TextureStage *stage) const { for (int n = 0; n < (int)_on_stages.size(); ++n) { if (_on_stages[n] == stage) { return n; } } return -1; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::add_on_stage // Access: Published // Description: Returns a new TextureAttrib, just like this one, but // with the indicated stage added to the list of stages // turned on by this attrib. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: add_on_stage(TextureStage *stage, Texture *tex) const { TextureAttrib *attrib = new TextureAttrib(*this); pair insert_result = attrib->_on_textures.insert(OnTextures::value_type(stage, tex)); if (insert_result.second) { // If the insert was successful--we have added a new stage that // wasn't present before--then add the stage to the linear list // also. attrib->_on_stages.push_back(stage); attrib->_sort_seq = UpdateSeq::old(); // Also ensure it is removed from the off_stages list. attrib->_off_stages.erase(stage); } else { // If the insert was unsuccessful, it means there was already a // definition for that stage. Replace it. (*insert_result.first).second = tex; } return return_new(attrib); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::remove_on_stage // Access: Published // Description: Returns a new TextureAttrib, just like this one, but // with the indicated stage removed from the list of // stages turned on by this attrib. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: remove_on_stage(TextureStage *stage) const { TextureAttrib *attrib = new TextureAttrib(*this); OnTextures::iterator ti = attrib->_on_textures.find(stage); if (ti != attrib->_on_textures.end()) { attrib->_on_textures.erase(ti); OnStages::iterator si = find(attrib->_on_stages.begin(), attrib->_on_stages.end(), PT(TextureStage)(stage)); if (si != attrib->_on_stages.end()) { attrib->_on_stages.erase(si); } } return return_new(attrib); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::add_off_stage // Access: Published // Description: Returns a new TextureAttrib, just like this one, but // with the indicated stage added to the list of stages // turned off by this attrib. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: add_off_stage(TextureStage *stage) const { TextureAttrib *attrib = new TextureAttrib(*this); if (!_off_all_stages) { attrib->_off_stages.insert(stage); // Also ensure it is removed from the on_stages list. OnTextures::iterator ti = attrib->_on_textures.find(stage); if (ti != attrib->_on_textures.end()) { attrib->_on_textures.erase(ti); OnStages::iterator si = find(attrib->_on_stages.begin(), attrib->_on_stages.end(), PT(TextureStage)(stage)); if (si != attrib->_on_stages.end()) { attrib->_on_stages.erase(si); } } } return return_new(attrib); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::remove_off_stage // Access: Published // Description: Returns a new TextureAttrib, just like this one, but // with the indicated stage removed from the list of // stages turned off by this attrib. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: remove_off_stage(TextureStage *stage) const { TextureAttrib *attrib = new TextureAttrib(*this); attrib->_off_stages.erase(stage); return return_new(attrib); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::unify_texture_stages // Access: Published // Description: Returns a new TextureAttrib, just like this one, but // with any included TextureAttribs that happen to have // the same name as the given object replaced with the // object. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: unify_texture_stages(TextureStage *stage) const { PT(TextureAttrib) attrib = new TextureAttrib; attrib->_off_all_stages = _off_all_stages; bool any_changed = false; OnTextures::const_iterator nti; for (nti = _on_textures.begin(); nti != _on_textures.end(); ++nti) { if ((*nti).first != stage && (*nti).first->get_name() == stage->get_name()) { attrib->_on_textures[stage] = (*nti).second; any_changed = true; } else { attrib->_on_textures.insert(*nti); } } // Now copy from _on_textures to the _on_stages list. We can't do // this as we walk through the list the first pass, since we might // have collapsed together multiple different stages. for (nti = attrib->_on_textures.begin(); nti != attrib->_on_textures.end(); ++nti) { attrib->_on_stages.push_back((*nti).first); } OffStages::const_iterator fsi; for (fsi = _off_stages.begin(); fsi != _off_stages.end(); ++fsi) { if ((*fsi) != stage && (*fsi)->get_name() == stage->get_name()) { attrib->_off_stages.insert(stage); any_changed = true; } else { attrib->_off_stages.insert(*fsi); } } if (!any_changed) { return this; } return return_new(attrib); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::filter_to_max // Access: Public // Description: Returns a new TextureAttrib, very much like this one, // but with the number of on_stages reduced to be no // more than max_texture_stages. The number of // off_stages in the new TextureAttrib is undefined. //////////////////////////////////////////////////////////////////// CPT(TextureAttrib) TextureAttrib:: filter_to_max(int max_texture_stages) const { if ((int)_on_stages.size() <= max_texture_stages) { // Trivial case: this TextureAttrib qualifies. return this; } // Since check_sorted() will clear the _filtered list if we are out // of date, we should call it first. check_sorted(); Filtered::const_iterator fi; fi = _filtered.find(max_texture_stages); if (fi != _filtered.end()) { // Easy case: we have already computed this for this particular // TextureAttrib. return (*fi).second; } // Harder case: we have to compute it now. We must choose the n // stages with the highest priority in our list of stages. In the // case of equal priority, we prefer the stage with the lower sort. OnStages priority_stages = _on_stages; // This sort function uses the STL function object defined above. sort(priority_stages.begin(), priority_stages.end(), CompareTextureStagePriorities()); // Now lop off all of the stages after the first max_texture_stages. priority_stages.erase(priority_stages.begin() + max_texture_stages, priority_stages.end()); // And create a new attrib reflecting these stages. PT(TextureAttrib) attrib = new TextureAttrib; OnStages::const_iterator si; for (si = priority_stages.begin(); si != priority_stages.end(); ++si) { TextureStage *stage = (*si); attrib->_on_textures[stage] = get_on_texture(stage); } attrib->_on_stages.swap(priority_stages); CPT(RenderAttrib) new_attrib = return_new(attrib); // Finally, record this newly-created attrib in the map for next // time. CPT(TextureAttrib) tex_attrib = (const TextureAttrib *)new_attrib.p(); ((TextureAttrib *)this)->_filtered[max_texture_stages] = tex_attrib; return tex_attrib; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::output // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// void TextureAttrib:: output(ostream &out) const { check_sorted(); out << get_type() << ":"; if (_off_stages.empty()) { if (_on_stages.empty()) { if (_off_all_stages) { out << "all off"; } else { out << "identity"; } } else { if (_off_all_stages) { out << "set"; } else { out << "on"; } } } else { out << "off"; OffStages::const_iterator fi; for (fi = _off_stages.begin(); fi != _off_stages.end(); ++fi) { TextureStage *stage = (*fi); out << " " << stage->get_name(); } if (!_on_stages.empty()) { out << " on"; } } OnStages::const_iterator li; for (li = _on_stages.begin(); li != _on_stages.end(); ++li) { TextureStage *stage = (*li); OnTextures::const_iterator ti = _on_textures.find(stage); if (ti != _on_textures.end()) { Texture *tex = (*ti).second; out << " " << stage->get_name() << ":" << tex->get_name(); } else { out << " " << stage->get_name(); } } } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::has_cull_callback // Access: Public, Virtual // Description: Should be overridden by derived classes to return // true if cull_callback() has been defined. Otherwise, // returns false to indicate cull_callback() does not // need to be called for this node during the cull // traversal. //////////////////////////////////////////////////////////////////// bool TextureAttrib:: has_cull_callback() const { OnTextures::const_iterator nti; for (nti = _on_textures.begin(); nti != _on_textures.end(); ++nti) { Texture *texture = (*nti).second; if (texture->has_cull_callback()) { return true; } } return false; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::cull_callback // Access: Public, Virtual // Description: If has_cull_callback() returns true, this function // will be called during the cull traversal to perform // any additional operations that should be performed at // cull time. // // This is called each time the RenderAttrib is // discovered applied to a Geom in the traversal. It // should return true if the Geom is visible, false if // it should be omitted. //////////////////////////////////////////////////////////////////// bool TextureAttrib:: cull_callback(CullTraverser *trav, const CullTraverserData &data) const { OnTextures::const_iterator nti; for (nti = _on_textures.begin(); nti != _on_textures.end(); ++nti) { Texture *texture = (*nti).second; if (!texture->cull_callback(trav, data)) { return false; } } return true; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::compare_to_impl // Access: Protected, Virtual // Description: Intended to be overridden by derived TextureAttrib // types to return a unique number indicating whether // this TextureAttrib is equivalent to the other one. // // This should return 0 if the two TextureAttrib objects // are equivalent, a number less than zero if this one // should be sorted before the other one, and a number // greater than zero otherwise. // // This will only be called with two TextureAttrib // objects whose get_type() functions return the same. //////////////////////////////////////////////////////////////////// int TextureAttrib:: compare_to_impl(const RenderAttrib *other) const { const TextureAttrib *ta; DCAST_INTO_R(ta, other, 0); if (_off_all_stages != ta->_off_all_stages) { return (int)_off_all_stages - (int)ta->_off_all_stages; } OnTextures::const_iterator li = _on_textures.begin(); OnTextures::const_iterator oli = ta->_on_textures.begin(); while (li != _on_textures.end() && oli != ta->_on_textures.end()) { TextureStage *stage = (*li).first; TextureStage *other_stage = (*oli).first; if (stage != other_stage) { return stage < other_stage ? -1 : 1; } Texture *tex = (*li).second; Texture *other_tex = (*oli).second; if (tex != other_tex) { return tex < other_tex ? -1 : 1; } ++li; ++oli; } if (li != _on_textures.end()) { return 1; } if (oli != ta->_on_textures.end()) { return -1; } OffStages::const_iterator fi = _off_stages.begin(); OffStages::const_iterator ofi = ta->_off_stages.begin(); while (fi != _off_stages.end() && ofi != ta->_off_stages.end()) { TextureStage *stage = (*fi); TextureStage *other_stage = (*ofi); if (stage != other_stage) { return stage < other_stage ? -1 : 1; } ++fi; ++ofi; } if (fi != _off_stages.end()) { return 1; } if (ofi != ta->_off_stages.end()) { return -1; } return 0; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::compose_impl // Access: Protected, Virtual // Description: Intended to be overridden by derived RenderAttrib // types to specify how two consecutive RenderAttrib // objects of the same type interact. // // This should return the result of applying the other // RenderAttrib to a node in the scene graph below this // RenderAttrib, which was already applied. In most // cases, the result is the same as the other // RenderAttrib (that is, a subsequent RenderAttrib // completely replaces the preceding one). On the other // hand, some kinds of RenderAttrib (for instance, // ColorTransformAttrib) might combine in meaningful // ways. //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: compose_impl(const RenderAttrib *other) const { const TextureAttrib *ta; DCAST_INTO_R(ta, other, 0); if (ta->_off_all_stages) { // If the other type turns off all stages, it doesn't matter what // we are. return ta; } // This is a three-way merge between ai, bi, and ci, except that bi // and ci should have no intersection and therefore needn't be // compared to each other. OnTextures::const_iterator ai = _on_textures.begin(); OnTextures::const_iterator bi = ta->_on_textures.begin(); OffStages::const_iterator ci = ta->_off_stages.begin(); // Create a new TextureAttrib that will hold the result. TextureAttrib *new_attrib = new TextureAttrib; while (ai != _on_textures.end() && bi != ta->_on_textures.end() && ci != ta->_off_stages.end()) { if ((*ai).first < (*bi).first) { if ((*ai).first < (*ci)) { // Here is a stage that we have in the original, which is not // present in the secondary. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *ai); new_attrib->_on_stages.push_back((*ai).first); ++ai; } else if ((*ci) < (*ai).first) { // Here is a stage that is turned off in the secondary, but // was not present in the original. ++ci; } else { // (*ci) == (*ai).first // Here is a stage that is turned off in the secondary, and // was present in the original. ++ai; ++ci; } } else if ((*bi).first < (*ai).first) { // Here is a new stage we have in the secondary, that was not // present in the original. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *bi); new_attrib->_on_stages.push_back((*bi).first); ++bi; } else { // (*bi).first == (*ai).first // Here is a stage we have in both. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *bi); new_attrib->_on_stages.push_back((*ai).first); ++ai; ++bi; } } while (ai != _on_textures.end() && bi != ta->_on_textures.end()) { if ((*ai).first < (*bi).first) { // Here is a stage that we have in the original, which is not // present in the secondary. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *ai); new_attrib->_on_stages.push_back((*ai).first); ++ai; } else if ((*bi).first < (*ai).first) { // Here is a new stage we have in the secondary, that was not // present in the original. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *bi); new_attrib->_on_stages.push_back((*bi).first); ++bi; } else { // Here is a stage we have in both. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *bi); new_attrib->_on_stages.push_back((*ai).first); ++ai; ++bi; } } while (ai != _on_textures.end() && ci != ta->_off_stages.end()) { if ((*ai).first < (*ci)) { // Here is a stage that we have in the original, which is not // present in the secondary. new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *ai); new_attrib->_on_stages.push_back((*ai).first); ++ai; } else if ((*ci) < (*ai).first) { // Here is a stage that is turned off in the secondary, but // was not present in the original. ++ci; } else { // (*ci) == (*ai).first // Here is a stage that is turned off in the secondary, and // was present in the original. ++ai; ++ci; } } while (ai != _on_textures.end()) { new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *ai); new_attrib->_on_stages.push_back((*ai).first); ++ai; } while (bi != ta->_on_textures.end()) { new_attrib->_on_textures.insert(new_attrib->_on_textures.end(), *bi); new_attrib->_on_stages.push_back((*bi).first); ++bi; } return return_new(new_attrib); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::invert_compose_impl // Access: Protected, Virtual // Description: Intended to be overridden by derived RenderAttrib // types to specify how two consecutive RenderAttrib // objects of the same type interact. // // See invert_compose() and compose_impl(). //////////////////////////////////////////////////////////////////// CPT(RenderAttrib) TextureAttrib:: invert_compose_impl(const RenderAttrib *other) const { // I think in this case the other attrib always wins. Maybe this // needs a bit more thought. It's hard to imagine that it's even // important to compute this properly. return other; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::make_default_impl // Access: Protected, Virtual // Description: Intended to be overridden by derived TextureAttrib // types to specify what the default property for a // TextureAttrib of this type should be. // // This should return a newly-allocated TextureAttrib of // the same type that corresponds to whatever the // standard default for this kind of TextureAttrib is. //////////////////////////////////////////////////////////////////// RenderAttrib *TextureAttrib:: make_default_impl() const { return new TextureAttrib; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::store_into_slot // Access: Public, Virtual // Description: Stores this attrib into the appropriate slot of // an object of class AttribSlots. //////////////////////////////////////////////////////////////////// void TextureAttrib:: store_into_slot(AttribSlots *slots) const { slots->_texture = this; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::register_with_read_factory // Access: Public, Static // Description: Tells the BamReader how to create objects of type // TextureAttrib. //////////////////////////////////////////////////////////////////// void TextureAttrib:: register_with_read_factory() { BamReader::get_factory()->register_factory(get_class_type(), make_from_bam); } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::write_datagram // Access: Public, Virtual // Description: Writes the contents of this object to the datagram // for shipping out to a Bam file. //////////////////////////////////////////////////////////////////// void TextureAttrib:: write_datagram(BamWriter *manager, Datagram &dg) { RenderAttrib::write_datagram(manager, dg); #if 1 // TODO: write the multitexture data. // write the boolean if _off_all_stages dg.add_bool(_off_all_stages); // write the number of off_stages dg.add_uint16(get_num_off_stages()); // write the off stages pointers if any OffStages::const_iterator fi; for (fi = _off_stages.begin(); fi != _off_stages.end(); ++fi) { TextureStage *stage = (*fi); manager->write_pointer(dg, stage); } // write the number of on stages dg.add_uint16(get_num_on_stages()); // write the on stages pointers if any OnTextures::const_iterator nti; for (nti = _on_textures.begin(); nti != _on_textures.end(); ++nti) { TextureStage *stage = (*nti).first; manager->write_pointer(dg, stage); Texture *texture = (*nti).second; manager->write_pointer(dg,texture); } #else manager->write_pointer(dg, get_texture()); #endif } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::complete_pointers // Access: Public, Virtual // Description: Receives an array of pointers, one for each time // manager->read_pointer() was called in fillin(). // Returns the number of pointers processed. //////////////////////////////////////////////////////////////////// int TextureAttrib:: complete_pointers(TypedWritable **p_list, BamReader *manager) { int pi = RenderAttrib::complete_pointers(p_list, manager); OffStages::iterator ci = _off_stages.begin(); while (ci != _off_stages.end()) { TextureStage *ts = DCAST(TextureStage, p_list[pi++]); *ci = ts; ++ci; } // read the pointers of the on_textures _on_stages.reserve(_num_on_textures); for (int i = 0; i < _num_on_textures; ++i) { TextureStage *ts = DCAST(TextureStage, p_list[pi++]); Texture *tx = DCAST(Texture, p_list[pi++]); if (tx != (Texture *)NULL) { _on_textures[ts] = tx; _on_stages.push_back(ts); } else { // If we couldn't load a texture pointer, turn off that // particular texture stage. _off_stages.push_back(ts); } } _sort_seq = UpdateSeq::old(); return pi; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::make_from_bam // Access: Protected, Static // Description: This function is called by the BamReader's factory // when a new object of type TextureAttrib is encountered // in the Bam file. It should create the TextureAttrib // and extract its information from the file. //////////////////////////////////////////////////////////////////// TypedWritable *TextureAttrib:: make_from_bam(const FactoryParams ¶ms) { TextureAttrib *attrib = new TextureAttrib; DatagramIterator scan; BamReader *manager; parse_params(params, scan, manager); attrib->fillin(scan, manager); return attrib; } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::fillin // Access: Protected // Description: This internal function is called by make_from_bam to // read in all of the relevant data from the BamFile for // the new TextureAttrib. //////////////////////////////////////////////////////////////////// void TextureAttrib:: fillin(DatagramIterator &scan, BamReader *manager) { RenderAttrib::fillin(scan, manager); // read the boolean if _off_all_stages _off_all_stages = scan.get_bool(); // read the number of off_stages int num_off_stages = scan.get_uint16(); // Push back a NULL pointer for each off TextureStage for now, until // we get the actual list of pointers later in complete_pointers(). int i; _off_stages.reserve(num_off_stages); for (i = 0; i < num_off_stages; i++) { manager->read_pointer(scan); _off_stages.push_back(NULL); } // read the number of on stages _num_on_textures = scan.get_uint16(); // just read the pointers, all allocation will happen from // complete_pointers because it is a map template we get the actual // list of pointers later in complete_pointers(). for (i = 0; i < _num_on_textures; i++) { manager->read_pointer(scan); manager->read_pointer(scan); } } //////////////////////////////////////////////////////////////////// // Function: TextureAttrib::sort_on_stages // Access: Private // Description: Sorts the list of stages so that they are listed in // render order. Also clears the _filtered map and // recalculates the list of fixed-function stages. //////////////////////////////////////////////////////////////////// void TextureAttrib:: sort_on_stages() { sort(_on_stages.begin(), _on_stages.end(), IndirectLess()); _sort_seq = TextureStage::get_sort_seq(); _on_ff_stages.clear(); OnStages::const_iterator osi; for (osi = _on_stages.begin(); osi != _on_stages.end(); ++osi) { if ((*osi)->is_fixed_function()) { _on_ff_stages.push_back(*osi); } } // Also clear the _filtered map, so we'll have to recompute those // (in case the priority orders have changed as well). _filtered.clear(); }