*** empty log message ***
This commit is contained in:
parent
aaf126da97
commit
872a902de4
|
|
@ -31,6 +31,10 @@ ConfigureFn(config_cull) {
|
|||
GeomBinAttribute::init_type();
|
||||
GeomBinFixed::init_type();
|
||||
DirectRenderTransition::init_type();
|
||||
|
||||
//Registration of writeable object's creation
|
||||
//functions with BamReader's factory
|
||||
GeomBinTransition::register_with_read_factory();
|
||||
}
|
||||
|
||||
// Set this true to force all of the caching to blow itself away every
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
INLINE void CullTraverser::
|
||||
set_default_bin(GeomBin *bin) {
|
||||
nassertv(bin != (GeomBin *)NULL);
|
||||
bin->attach_to(this, bin->get_sort());
|
||||
nassertv(bin->is_attached());
|
||||
nassertv(bin->get_traverser() == this);
|
||||
_default_bin = bin;
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +37,35 @@ get_default_bin() const {
|
|||
return _default_bin;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::has_bin
|
||||
// Access: Public
|
||||
// Description: Returns true if a bin by the given name has been
|
||||
// attached to the CullTraverser, false otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool CullTraverser::
|
||||
has_bin(const string &name) const {
|
||||
return (_toplevel_bins.count(name) != 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::get_bin
|
||||
// Access: Public
|
||||
// Description: Returns the GeomBin that was previously attached to
|
||||
// the CullTraverser that shares the indicated name, or
|
||||
// the default bin if no GeomBin with a matching name
|
||||
// was added.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBin *CullTraverser::
|
||||
get_bin(const string &name) const {
|
||||
ToplevelBins::const_iterator tbi;
|
||||
tbi = _toplevel_bins.find(name);
|
||||
if (tbi == _toplevel_bins.end()) {
|
||||
return get_default_bin();
|
||||
}
|
||||
return (*tbi).second;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::draw_geom
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include "geomBinAttribute.h"
|
||||
#include "cullStateSubtree.h"
|
||||
#include "geomBinNormal.h"
|
||||
#include "geomBinFixed.h"
|
||||
#include "directRenderTransition.h"
|
||||
#include "config_cull.h"
|
||||
|
||||
|
|
@ -42,7 +43,15 @@ CullTraverser(GraphicsStateGuardian *gsg, TypeHandle graph_type,
|
|||
const ArcChain &arc_chain) :
|
||||
RenderTraverser(gsg, graph_type, arc_chain)
|
||||
{
|
||||
_default_bin = new GeomBinNormal("default", this);
|
||||
GeomBinNormal *default_bin = new GeomBinNormal("default");
|
||||
GeomBinFixed *fixed = new GeomBinFixed("fixed");
|
||||
fixed->set_sort(30);
|
||||
|
||||
default_bin->set_traverser(this);
|
||||
fixed->set_traverser(this);
|
||||
|
||||
_default_bin = default_bin;
|
||||
|
||||
_nested_count = 0;
|
||||
}
|
||||
|
||||
|
|
@ -56,12 +65,14 @@ CullTraverser::
|
|||
// We should detach each of our associated bins when we destruct.
|
||||
// We can't just run a for loop, because this is a self-modifying
|
||||
// operation.
|
||||
while (!_bins.empty()) {
|
||||
GeomBin *bin = (*_bins.begin());
|
||||
while (!_toplevel_bins.empty()) {
|
||||
GeomBin *bin = (*_toplevel_bins.begin()).second;
|
||||
nassertv(bin != (GeomBin *)NULL);
|
||||
nassertv(bin->get_traverser() == this);
|
||||
bin->detach();
|
||||
bin->clear_traverser();
|
||||
}
|
||||
|
||||
nassertv(_sub_bins.empty());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -86,7 +97,8 @@ traverse(Node *root,
|
|||
<< *root << "\n";
|
||||
}
|
||||
|
||||
nassertv(!_bins.empty());
|
||||
nassertv(!_toplevel_bins.empty());
|
||||
nassertv(!_sub_bins.empty());
|
||||
|
||||
bool is_initial = (_nested_count == 0);
|
||||
if (is_initial) {
|
||||
|
|
@ -184,9 +196,9 @@ write(ostream &out, int indent_level) const {
|
|||
}
|
||||
*/
|
||||
|
||||
Bins::const_iterator bi;
|
||||
for (bi = _bins.begin(); bi != _bins.end(); ++bi) {
|
||||
(*bi)->write(out, indent_level);
|
||||
ToplevelBins::const_iterator tbi;
|
||||
for (tbi = _toplevel_bins.begin(); tbi != _toplevel_bins.end(); ++tbi) {
|
||||
(*tbi).second->write(out, indent_level);
|
||||
}
|
||||
_lookup.write(out, indent_level);
|
||||
}
|
||||
|
|
@ -215,9 +227,9 @@ draw() {
|
|||
<< " nonempty states of " << _states.size() << " total.\n";
|
||||
}
|
||||
|
||||
Bins::iterator bi;
|
||||
for (bi = _bins.begin(); bi != _bins.end(); ++bi) {
|
||||
(*bi)->clear_current_states();
|
||||
SubBins::const_iterator sbi;
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
(*sbi).second->clear_current_states();
|
||||
}
|
||||
|
||||
States::iterator si;
|
||||
|
|
@ -233,7 +245,7 @@ draw() {
|
|||
const GeomBinAttribute *bin_attrib;
|
||||
if (get_attribute_into(bin_attrib, cs->get_attributes(),
|
||||
GeomBinTransition::get_class_type())) {
|
||||
requested_bin = bin_attrib->get_bin();
|
||||
requested_bin = get_bin(bin_attrib->get_bin());
|
||||
draw_order = bin_attrib->get_draw_order();
|
||||
}
|
||||
|
||||
|
|
@ -244,10 +256,10 @@ draw() {
|
|||
if (_gsg != (GraphicsStateGuardian *)NULL) {
|
||||
if (cull_cat.is_debug()) {
|
||||
cull_cat.debug()
|
||||
<< "Drawing " << _bins.size() << " bins.\n";
|
||||
<< "Drawing " << _sub_bins.size() << " bins.\n";
|
||||
}
|
||||
for (bi = _bins.begin(); bi != _bins.end(); ++bi) {
|
||||
(*bi)->draw(this);
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
(*sbi).second->draw(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -394,7 +406,6 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
|
|||
bool is_geom = node->is_of_type(GeomNode::get_class_type());
|
||||
bool node_has_sub_render = node->has_sub_render();
|
||||
int arc_num_sub_render = arc->get_num_sub_render_trans();
|
||||
bool has_direct_render = arc->has_transition(DirectRenderTransition::get_class_type());
|
||||
bool has_decal = arc->has_transition(DecalTransition::get_class_type());
|
||||
|
||||
if (has_decal) {
|
||||
|
|
@ -403,6 +414,9 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
|
|||
arc_num_sub_render--;
|
||||
}
|
||||
|
||||
bool has_direct_render =
|
||||
arc->has_transition(DirectRenderTransition::get_class_type());
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (support_decals != SD_on) {
|
||||
has_direct_render = has_direct_render && !has_decal;
|
||||
|
|
@ -533,3 +547,107 @@ forward_arc(NodeRelation *arc, NullTransitionWrapper &,
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::attach_toplevel_bin
|
||||
// Access: Private
|
||||
// Description: This is intended to be called only by
|
||||
// GeomBin::attach(). It stores the bin in the
|
||||
// appropriate structures within the traverser, as a
|
||||
// toplevel bin, e.g. a bin that may be referenced by
|
||||
// name from outside the traverser, or one that geometry
|
||||
// may be explicitly assigned to by name.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void CullTraverser::
|
||||
attach_toplevel_bin(GeomBin *bin) {
|
||||
if (cull_cat.is_debug()) {
|
||||
cull_cat.debug()
|
||||
<< "Attaching toplevel bin " << *bin << "\n";
|
||||
}
|
||||
|
||||
// Insert the new bin by name.
|
||||
bool inserted =
|
||||
_toplevel_bins.insert(ToplevelBins::value_type(bin->get_name(), bin)).second;
|
||||
|
||||
// If this assertion fails, there was already a bin by the same name
|
||||
// in this traverser, an error condition.
|
||||
nassertv(inserted);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::attach_sub_bin
|
||||
// Access: Private
|
||||
// Description: This is intended to be called only by
|
||||
// GeomBin::attach(). It stores the bin in the
|
||||
// appropriate structures within the traverser, as a
|
||||
// sub bin, e.g. a bin that may have geometry assigned
|
||||
// to it, and may therefore be rendered.
|
||||
//
|
||||
// In most cases, a toplevel bin is the same as a sub
|
||||
// bin, except in the case of a GeomBinGroup, which is a
|
||||
// toplevel bin that contains a number of sub bins.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void CullTraverser::
|
||||
attach_sub_bin(GeomBin *bin) {
|
||||
if (cull_cat.is_debug()) {
|
||||
cull_cat.debug()
|
||||
<< "Attaching sub bin " << *bin << "\n";
|
||||
}
|
||||
|
||||
_sub_bins.insert(SubBins::value_type(bin->get_sort(), bin));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::detach_toplevel_bin
|
||||
// Access: Private
|
||||
// Description: This is intended to be called only by
|
||||
// GeomBin::detach(). It removes the bin from the
|
||||
// appropriate structures within the traverser, as a
|
||||
// toplevel bin. See attach_toplevel_bin().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void CullTraverser::
|
||||
detach_toplevel_bin(GeomBin *bin) {
|
||||
if (cull_cat.is_debug()) {
|
||||
cull_cat.debug()
|
||||
<< "Detaching toplevel bin " << *bin << "\n";
|
||||
}
|
||||
|
||||
ToplevelBins::iterator tbi = _toplevel_bins.find(bin->get_name());
|
||||
nassertv(tbi != _toplevel_bins.end());
|
||||
_toplevel_bins.erase(tbi);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: CullTraverser::detach_sub_bin
|
||||
// Access: Private
|
||||
// Description: This is intended to be called only by
|
||||
// GeomBin::detach(). It removes the bin from the
|
||||
// appropriate structures within the traverser, as a
|
||||
// sub bin. See attach_sub_bin().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void CullTraverser::
|
||||
detach_sub_bin(GeomBin *bin) {
|
||||
if (cull_cat.is_debug()) {
|
||||
cull_cat.debug()
|
||||
<< "Detaching sub bin " << *bin << "\n";
|
||||
}
|
||||
|
||||
// Remove the bin from its place in the sort order list. This will
|
||||
// be one of the (possibly several) entries in the multimap with the
|
||||
// same sort value.
|
||||
pair<SubBins::iterator, SubBins::iterator> range;
|
||||
range = _sub_bins.equal_range(bin->get_sort());
|
||||
|
||||
SubBins::iterator sbi;
|
||||
for (sbi = range.first; sbi != range.second; ++sbi) {
|
||||
GeomBin *consider_bin = (*sbi).second;
|
||||
nassertv(consider_bin->get_sort() == bin->get_sort());
|
||||
|
||||
if (consider_bin == bin) {
|
||||
// Here's the position!
|
||||
_sub_bins.erase(sbi);
|
||||
return;
|
||||
}
|
||||
}
|
||||
nassertv(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,8 @@ public:
|
|||
|
||||
INLINE void set_default_bin(GeomBin *bin);
|
||||
INLINE GeomBin *get_default_bin() const;
|
||||
INLINE bool has_bin(const string &name) const;
|
||||
INLINE GeomBin *get_bin(const string &name) const;
|
||||
|
||||
virtual void traverse(Node *root,
|
||||
const AllAttributesWrapper &initial_state,
|
||||
|
|
@ -92,10 +94,17 @@ public:
|
|||
const CullLevelState &level_state);
|
||||
|
||||
private:
|
||||
void attach_toplevel_bin(GeomBin *bin);
|
||||
void attach_sub_bin(GeomBin *bin);
|
||||
void detach_toplevel_bin(GeomBin *bin);
|
||||
void detach_sub_bin(GeomBin *bin);
|
||||
|
||||
AllAttributesWrapper _initial_state;
|
||||
|
||||
typedef set<PT(GeomBin)> Bins;
|
||||
Bins _bins;
|
||||
typedef map<string, PT(GeomBin)> ToplevelBins;
|
||||
typedef multimap<int, PT(GeomBin)> SubBins;
|
||||
ToplevelBins _toplevel_bins;
|
||||
SubBins _sub_bins;
|
||||
PT(GeomBin) _default_bin;
|
||||
|
||||
typedef set<PT(CullState), IndirectCompareTo<CullState> > States;
|
||||
|
|
@ -128,7 +137,8 @@ public:
|
|||
private:
|
||||
static TypeHandle _type_handle;
|
||||
|
||||
friend class GeomBin;
|
||||
friend class GeomBin;
|
||||
friend class GeomBinGroup;
|
||||
};
|
||||
|
||||
#include "cullTraverser.I"
|
||||
|
|
|
|||
|
|
@ -9,24 +9,13 @@
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBin::
|
||||
GeomBin(const string &name, CullTraverser *traverser, int sort) :
|
||||
GeomBin(const string &name) :
|
||||
Namable(name)
|
||||
{
|
||||
_traverser = (CullTraverser *)NULL;
|
||||
if (traverser != (CullTraverser *)NULL) {
|
||||
attach_to(traverser, sort);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::get_traverser
|
||||
// Access: Public
|
||||
// Description: Returns a pointer to the CullTraverser this bin is
|
||||
// currently attached to, or NULL if it is not attached.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE CullTraverser *GeomBin::
|
||||
get_traverser() const {
|
||||
return _traverser;
|
||||
_is_attached = false;
|
||||
_sort = 0;
|
||||
_parent = (GeomBin *)NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -42,19 +31,65 @@ get_sort() const {
|
|||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::set_sort
|
||||
// Function: GeomBin::has_traverser
|
||||
// Access: Public
|
||||
// Description: Changes the sort index associated with this
|
||||
// particular bin. The CullTraverser will render bins
|
||||
// in order according to their sort index.
|
||||
// Description: Returns true if the GeomBin is currently attached to
|
||||
// a traverser, false otherwise. If this is false, and
|
||||
// the bin is not attached to a parent bin
|
||||
// (i.e. has_parent() is also false), don't expect the
|
||||
// bin to be rendered.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void GeomBin::
|
||||
set_sort(int sort) {
|
||||
if (_traverser != (CullTraverser *)NULL) {
|
||||
attach_to(_traverser, sort);
|
||||
} else {
|
||||
_sort = sort;
|
||||
}
|
||||
INLINE bool GeomBin::
|
||||
has_traverser() const {
|
||||
return (_traverser != (CullTraverser *)NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::get_traverser
|
||||
// Access: Public
|
||||
// Description: Returns a pointer to the CullTraverser this bin is
|
||||
// currently attached to, or NULL if it is not attached.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE CullTraverser *GeomBin::
|
||||
get_traverser() const {
|
||||
return _traverser;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::has_parent
|
||||
// Access: Public
|
||||
// Description: Returns true if the bin is a child of some
|
||||
// GeomBinGroup, false if it is a toplevel bin in its
|
||||
// own right. See GeomBinGroup.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool GeomBin::
|
||||
has_parent() const {
|
||||
return _parent != (GeomBin *)NULL;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::get_parent
|
||||
// Access: Public
|
||||
// Description: Returns the parent pointer of the GeomBin if the bin
|
||||
// is a child of some GeomBinGropu, or NULL if the bin
|
||||
// does not have a parent. See GeomBinGroup.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBin *GeomBin::
|
||||
get_parent() const {
|
||||
return _parent;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::is_attached
|
||||
// Access: Public
|
||||
// Description: Returns true if the bin is currently attached to a
|
||||
// CullTraverser, false otherwise. This should always
|
||||
// be tree if has_traverser() is true, and false if
|
||||
// has_traverser() is false.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool GeomBin::
|
||||
is_attached() const {
|
||||
return _is_attached;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -22,49 +22,210 @@ GeomBin::
|
|||
~GeomBin() {
|
||||
// We shouldn't be attached to anything when we destruct. If we
|
||||
// are, something went screwy in the reference counting.
|
||||
nassertv(!_is_attached);
|
||||
nassertv(_traverser == (CullTraverser *)NULL);
|
||||
|
||||
// We also shouldn't be part of any parent bin.
|
||||
nassertv(_parent == (GeomBin *)NULL);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::attach_to
|
||||
// Function: GeomBin::set_name
|
||||
// Access: Public
|
||||
// Description: Detaches the bin from whichever CullTraverser it is
|
||||
// currently attached to, and attaches it to the
|
||||
// indicated CullTraverser instead, at the indicated
|
||||
// sort level. The CullTraverser will render all of its
|
||||
// attached GeomBins, in order according to sort level.
|
||||
// Description: Changes the name of the GeomBin, correctly updating
|
||||
// the CullTraverser it's attached to.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
attach_to(CullTraverser *traverser, int sort) {
|
||||
nassertv(traverser != (CullTraverser *)NULL);
|
||||
PT(GeomBin) keep = detach();
|
||||
_sort = sort;
|
||||
set_name(const string &name) {
|
||||
if (name == get_name()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're currently attached, we need to need to be unattached
|
||||
// while we change the name--but only if we're a toplevel bin. A
|
||||
// nested bin doesn't matter.
|
||||
if (is_attached() && !has_parent()) {
|
||||
PT(GeomBin) keep = this;
|
||||
|
||||
nassertv(_traverser != (CullTraverser *)NULL);
|
||||
_traverser->detach_toplevel_bin(this);
|
||||
Namable::set_name(name);
|
||||
_traverser->attach_toplevel_bin(this);
|
||||
|
||||
} else {
|
||||
Namable::set_name(name);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::clear_name
|
||||
// Access: Public
|
||||
// Description: Removes the name of the GeomBin. This also detaches
|
||||
// it (if it is a toplevel bin), and may therefore
|
||||
// inadvertently delete it! Be very careful.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
clear_name() {
|
||||
PT(GeomBin) keep;
|
||||
|
||||
if (is_attached() && !has_parent()) {
|
||||
cull_cat.warning()
|
||||
<< "GeomBin::clear_name() called on an attached GeomBin.\n";
|
||||
keep = detach();
|
||||
}
|
||||
|
||||
Namable::clear_name();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::set_sort
|
||||
// Access: Public
|
||||
// Description: Changes the sort index associated with this
|
||||
// particular bin. The CullTraverser will render bins
|
||||
// in order according to their sort index.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
set_sort(int sort) {
|
||||
if (sort == _sort) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're currently attached, even if we're a nested bin, we need
|
||||
// to be unattached while we adjust the sorting order.
|
||||
if (is_attached()) {
|
||||
PT(GeomBin) keep = this;
|
||||
|
||||
nassertv(_traverser != (CullTraverser *)NULL);
|
||||
_traverser->detach_sub_bin(this);
|
||||
_sort = sort;
|
||||
_traverser->attach_sub_bin(this);
|
||||
|
||||
} else {
|
||||
_sort = sort;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::set_traverser
|
||||
// Access: Public
|
||||
// Description: Associates the GeomBin with the indicated
|
||||
// CullTraverser. A particular bin may only be
|
||||
// associated with one traverser at a time. This is
|
||||
// normally done only after initially creating the
|
||||
// GeomBin; it is an error to call set_traverser() more
|
||||
// than once on the same bin (without calling
|
||||
// clear_traverser() first).
|
||||
//
|
||||
// It is also not necessary (and is an error) to assign
|
||||
// child bins of a GeomBinGroup to a traverser; just
|
||||
// assign the parent bin.
|
||||
//
|
||||
// This must be called before the GeomBin can be
|
||||
// rendered; it will thereafter be rendered by the
|
||||
// indicated traverser.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
set_traverser(CullTraverser *traverser) {
|
||||
if (traverser == (CullTraverser *)NULL) {
|
||||
clear_traverser();
|
||||
}
|
||||
|
||||
nassertv(!is_attached());
|
||||
nassertv(_traverser == (CullTraverser *)NULL);
|
||||
|
||||
_traverser = traverser;
|
||||
_traverser->_bins.insert(this);
|
||||
attach();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::clear_traverser
|
||||
// Access: Public
|
||||
// Description: Disassociates the GeomBin with its current traverser.
|
||||
// The bin will no longer be rendered, and may even be
|
||||
// deleted if you do not immediately save the returned
|
||||
// pointer.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(GeomBin) GeomBin::
|
||||
clear_traverser() {
|
||||
PT(GeomBin) keep = this;
|
||||
|
||||
if (is_attached()) {
|
||||
detach();
|
||||
_traverser = (CullTraverser *)NULL;
|
||||
}
|
||||
|
||||
return keep;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::attach
|
||||
// Access: Protected, Virtual
|
||||
// Description: Formally adds the GeomBin to its indicated Traverser.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
attach() {
|
||||
nassertv(has_name());
|
||||
nassertv(_traverser != (CullTraverser *)NULL);
|
||||
nassertv(!_is_attached);
|
||||
|
||||
// In the ordinary case, a bin is both a toplevel bin (visible by
|
||||
// name from the CullTraverser) and a sub bin (directly renderable).
|
||||
if (!has_parent()) {
|
||||
_traverser->attach_toplevel_bin(this);
|
||||
}
|
||||
_traverser->attach_sub_bin(this);
|
||||
|
||||
_is_attached = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::detach
|
||||
// Access: Public
|
||||
// Access: Protected, Virtual
|
||||
// Description: Detaches the bin from whichever CullTraverser it is
|
||||
// currently attached to, if any. The bin will no
|
||||
// longer be rendered. The return value is a
|
||||
// PT(GeomBin) that refers to the GeomBin itself; the
|
||||
// caller should save this pointer in a local PT
|
||||
// variable to avoid possibly destructing the GeomBin
|
||||
// (since detaching it may remove the last outstanding
|
||||
// reference count).
|
||||
// currently attached to. The bin will no longer be
|
||||
// rendered. The return value is a PT(GeomBin) that
|
||||
// refers to the GeomBin itself; the caller should save
|
||||
// this pointer in a local PT variable to avoid possibly
|
||||
// destructing the GeomBin (since detaching it may
|
||||
// remove the last outstanding reference count).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(GeomBin) GeomBin::
|
||||
detach() {
|
||||
nassertr(_is_attached, NULL);
|
||||
nassertr(_traverser != (CullTraverser *)NULL, NULL);
|
||||
|
||||
PT(GeomBin) keep = this;
|
||||
if (_traverser != (CullTraverser *)NULL) {
|
||||
_traverser->_bins.erase(this);
|
||||
|
||||
if (!has_parent()) {
|
||||
_traverser->detach_toplevel_bin(this);
|
||||
}
|
||||
_traverser = (CullTraverser *)NULL;
|
||||
_traverser->detach_sub_bin(this);
|
||||
|
||||
_is_attached = false;
|
||||
|
||||
return keep;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::output
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
output(ostream &out) const {
|
||||
out << get_type() << " " << get_name();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::output
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level) << *this << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::remove_state
|
||||
// Access: Public, Virtual
|
||||
|
|
@ -86,23 +247,3 @@ remove_state(CullState *) {
|
|||
<< "remove_state() unexpectedly called for " << get_type() << "\n";
|
||||
nassertv(false);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::output
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
output(ostream &out) const {
|
||||
out << get_type() << " " << get_name();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBin::output
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBin::
|
||||
write(ostream &out, int indent_level) const {
|
||||
indent(out, indent_level) << *this << "\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,16 +35,29 @@ class CullTraverser;
|
|||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBin : public TypedReferenceCount, public Namable {
|
||||
public:
|
||||
INLINE GeomBin(const string &name, CullTraverser *traverser = NULL,
|
||||
int sort = 0);
|
||||
INLINE GeomBin(const string &name);
|
||||
virtual ~GeomBin();
|
||||
|
||||
void attach_to(CullTraverser *traverser, int sort = 0);
|
||||
PT(GeomBin) detach();
|
||||
PUBLISHED:
|
||||
void set_name(const string &name);
|
||||
void clear_name();
|
||||
|
||||
INLINE CullTraverser *get_traverser() const;
|
||||
INLINE int get_sort() const;
|
||||
INLINE void set_sort(int sort);
|
||||
void set_sort(int sort);
|
||||
|
||||
void set_traverser(CullTraverser *traverser);
|
||||
INLINE bool has_traverser() const;
|
||||
INLINE CullTraverser *get_traverser() const;
|
||||
PT(GeomBin) clear_traverser();
|
||||
|
||||
INLINE bool has_parent() const;
|
||||
INLINE GeomBin *get_parent() const;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
public:
|
||||
INLINE bool is_attached() const;
|
||||
|
||||
virtual void clear_current_states()=0;
|
||||
virtual void record_current_state(GraphicsStateGuardian *gsg,
|
||||
|
|
@ -54,15 +67,17 @@ public:
|
|||
|
||||
virtual void draw(CullTraverser *trav)=0;
|
||||
|
||||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
protected:
|
||||
INLINE void claim_cull_state(CullState *cs);
|
||||
INLINE void disclaim_cull_state(CullState *cs);
|
||||
|
||||
virtual void attach();
|
||||
virtual PT(GeomBin) detach();
|
||||
|
||||
CullTraverser *_traverser;
|
||||
bool _is_attached;
|
||||
int _sort;
|
||||
GeomBin *_parent;
|
||||
|
||||
public:
|
||||
static TypeHandle get_class_type() {
|
||||
|
|
@ -82,6 +97,8 @@ public:
|
|||
|
||||
private:
|
||||
static TypeHandle _type_handle;
|
||||
|
||||
friend class GeomBinGroup;
|
||||
};
|
||||
|
||||
INLINE ostream &operator << (ostream &out, const GeomBin &bin) {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ GeomBinAttribute() {
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBinAttribute::
|
||||
GeomBinAttribute(GeomBin *bin, int draw_order) {
|
||||
GeomBinAttribute(const string &bin, int draw_order) {
|
||||
set_on(bin, draw_order);
|
||||
}
|
||||
|
||||
|
|
@ -29,8 +29,8 @@ GeomBinAttribute(GeomBin *bin, int draw_order) {
|
|||
// geomBin.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void GeomBinAttribute::
|
||||
set_on(GeomBin *bin, int draw_order) {
|
||||
nassertv(bin != (GeomBin *)NULL);
|
||||
set_on(const string &bin, int draw_order) {
|
||||
nassertv(!bin.empty());
|
||||
_value = bin;
|
||||
_draw_order = draw_order;
|
||||
OnOffAttribute::set_on();
|
||||
|
|
@ -39,13 +39,13 @@ set_on(GeomBin *bin, int draw_order) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinAttribute::get_bin
|
||||
// Access: Public
|
||||
// Description: Returns the bin that the GeomBinAttribute
|
||||
// Description: Returns the bin name that the GeomBinAttribute
|
||||
// represents. It is only valid to call this if is_on()
|
||||
// has returned true.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE PT(GeomBin) GeomBinAttribute::
|
||||
INLINE string GeomBinAttribute::
|
||||
get_bin() const {
|
||||
nassertr(is_on(), NULL);
|
||||
nassertr(is_on(), string());
|
||||
return _value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#include <graphicsStateGuardianBase.h>
|
||||
#include <indent.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
TypeHandle GeomBinAttribute::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -56,7 +58,7 @@ set_value_from(const OnOffTransition *other) {
|
|||
DCAST_INTO_V(ot, other);
|
||||
_value = ot->_value;
|
||||
_draw_order = ot->_draw_order;
|
||||
nassertv(_value != (GeomBin *)NULL);
|
||||
nassertv(!_value.empty());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -69,7 +71,7 @@ compare_values(const OnOffAttribute *other) const {
|
|||
const GeomBinAttribute *ot;
|
||||
DCAST_INTO_R(ot, other, false);
|
||||
if (_value != ot->_value) {
|
||||
return (int)(_value - ot->_value);
|
||||
return strcmp(_value.c_str(), ot->_value.c_str());
|
||||
}
|
||||
return _draw_order - ot->_draw_order;
|
||||
}
|
||||
|
|
@ -81,8 +83,7 @@ compare_values(const OnOffAttribute *other) const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinAttribute::
|
||||
output_value(ostream &out) const {
|
||||
nassertv(_value != (GeomBin *)NULL);
|
||||
out << *_value << ":" << _draw_order;
|
||||
out << _value << ":" << _draw_order;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -93,6 +94,5 @@ output_value(ostream &out) const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinAttribute::
|
||||
write_value(ostream &out, int indent_level) const {
|
||||
nassertv(_value != (GeomBin *)NULL);
|
||||
indent(out, indent_level) << *_value << ":" << _draw_order << "\n";
|
||||
indent(out, indent_level) << _value << ":" << _draw_order << "\n";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@
|
|||
class EXPCL_PANDA GeomBinAttribute : public OnOffAttribute {
|
||||
public:
|
||||
INLINE GeomBinAttribute();
|
||||
INLINE GeomBinAttribute(GeomBin *bin, int draw_order = 0);
|
||||
INLINE GeomBinAttribute(const string &bin, int draw_order = 0);
|
||||
|
||||
INLINE void set_on(GeomBin *bin, int draw_order = 0);
|
||||
INLINE PT(GeomBin) get_bin() const;
|
||||
INLINE void set_on(const string &bin, int draw_order = 0);
|
||||
INLINE string get_bin() const;
|
||||
INLINE int get_draw_order() const;
|
||||
|
||||
virtual TypeHandle get_handle() const;
|
||||
|
|
@ -35,7 +35,7 @@ protected:
|
|||
virtual void output_value(ostream &out) const;
|
||||
virtual void write_value(ostream &out, int indent_level) const;
|
||||
|
||||
PT(GeomBin) _value;
|
||||
string _value;
|
||||
int _draw_order;
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ draw(CullTraverser *trav) const {
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBinBackToFront::
|
||||
GeomBinBackToFront(const string &name, CullTraverser *traverser, int sort) :
|
||||
GeomBin(name, traverser, sort)
|
||||
GeomBinBackToFront(const string &name) :
|
||||
GeomBin(name)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@
|
|||
// without a Z-buffer.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBinBackToFront : public GeomBin {
|
||||
public:
|
||||
INLINE GeomBinBackToFront(const string &name,
|
||||
CullTraverser *traverser = NULL,
|
||||
int sort = 0);
|
||||
PUBLISHED:
|
||||
INLINE GeomBinBackToFront(const string &name);
|
||||
|
||||
public:
|
||||
virtual void clear_current_states();
|
||||
virtual void record_current_state(GraphicsStateGuardian *gsg,
|
||||
CullState *cs, int draw_order,
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ draw(CullTraverser *trav) const {
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBinFixed::
|
||||
GeomBinFixed(const string &name, CullTraverser *traverser, int sort) :
|
||||
GeomBin(name, traverser, sort)
|
||||
GeomBinFixed(const string &name) :
|
||||
GeomBin(name)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,11 +21,10 @@
|
|||
// specified at each GeomBinTransition.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBinFixed : public GeomBin {
|
||||
public:
|
||||
INLINE GeomBinFixed(const string &name,
|
||||
CullTraverser *traverser = NULL,
|
||||
int sort = 0);
|
||||
PUBLISHED:
|
||||
INLINE GeomBinFixed(const string &name);
|
||||
|
||||
public:
|
||||
virtual void clear_current_states();
|
||||
virtual void record_current_state(GraphicsStateGuardian *gsg,
|
||||
CullState *cs, int draw_order,
|
||||
|
|
|
|||
|
|
@ -9,23 +9,11 @@
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBinGroup::
|
||||
GeomBinGroup(const string &name, CullTraverser *traverser, int sort) :
|
||||
GeomBin(name, traverser, sort)
|
||||
GeomBinGroup(const string &name) :
|
||||
GeomBin(name)
|
||||
{
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::add_sub_bin
|
||||
// Access: Public
|
||||
// Description: Appends the indicated bin to the end of the sub_bin
|
||||
// list, and returns the new index.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE int GeomBinGroup::
|
||||
add_sub_bin(GeomBin *sub_bin) {
|
||||
_sub_bins.push_back(sub_bin);
|
||||
return _sub_bins.size() - 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::get_num_bins
|
||||
// Access: Public
|
||||
|
|
@ -46,19 +34,3 @@ get_bin(int n) {
|
|||
nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL);
|
||||
return _sub_bins[n];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::remove_bin
|
||||
// Access: Public
|
||||
// Description: Removes the nth bin. All subsequent index numbers
|
||||
// shift down by one. The return value is the
|
||||
// just-removed bin, which may be deleted if it is not
|
||||
// immediately saved.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE PT(GeomBin) GeomBinGroup::
|
||||
remove_bin(int n) {
|
||||
nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL);
|
||||
PT(GeomBin) keep = get_bin(n);
|
||||
_sub_bins.erase(_sub_bins.begin() + n);
|
||||
return keep;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,75 @@
|
|||
|
||||
TypeHandle GeomBinGroup::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::Destructor
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GeomBinGroup::
|
||||
~GeomBinGroup() {
|
||||
nassertv(!is_attached());
|
||||
|
||||
// Disassociate all of our children before we destruct.
|
||||
SubBins::iterator sbi;
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
GeomBin *sub_bin = (*sbi);
|
||||
sub_bin->_parent = (GeomBin *)NULL;
|
||||
}
|
||||
_sub_bins.clear();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::add_sub_bin
|
||||
// Access: Public
|
||||
// Description: Appends the indicated bin to the end of the sub_bin
|
||||
// list, and returns the new index.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
int GeomBinGroup::
|
||||
add_sub_bin(GeomBin *sub_bin) {
|
||||
nassertr(!sub_bin->has_parent(), -1);
|
||||
nassertr(!sub_bin->is_attached(), -1);
|
||||
nassertr(sub_bin->has_name(), -1);
|
||||
|
||||
sub_bin->_parent = this;
|
||||
_sub_bins.push_back(sub_bin);
|
||||
|
||||
if (is_attached()) {
|
||||
sub_bin->_traverser = _traverser;
|
||||
_traverser->attach_sub_bin(sub_bin);
|
||||
}
|
||||
|
||||
return _sub_bins.size() - 1;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::remove_bin
|
||||
// Access: Public
|
||||
// Description: Removes the nth bin. All subsequent index numbers
|
||||
// shift down by one. The return value is the
|
||||
// just-removed bin, which may be deleted if it is not
|
||||
// immediately saved.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(GeomBin) GeomBinGroup::
|
||||
remove_bin(int n) {
|
||||
nassertr(n >= 0 && n < (int)_sub_bins.size(), NULL);
|
||||
PT(GeomBin) sub_bin = get_bin(n);
|
||||
nassertr(sub_bin->get_parent() == this, NULL);
|
||||
|
||||
if (is_attached()) {
|
||||
nassertr(sub_bin->is_attached(), NULL);
|
||||
nassertr(sub_bin->_traverser == _traverser, NULL);
|
||||
_traverser->detach_sub_bin(sub_bin);
|
||||
sub_bin->_traverser = (CullTraverser *)NULL;
|
||||
}
|
||||
|
||||
_sub_bins.erase(_sub_bins.begin() + n);
|
||||
sub_bin->_parent = (GeomBin *)NULL;
|
||||
|
||||
return sub_bin;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::clear_current_states
|
||||
// Access: Public, Virtual
|
||||
|
|
@ -22,6 +91,13 @@ TypeHandle GeomBinGroup::_type_handle;
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinGroup::
|
||||
clear_current_states() {
|
||||
// This should never be called for toplevel bins, only sub bins--and
|
||||
// a GeomBinGroup should never be a sub bin. The function will do
|
||||
// the right thing even if it is called, but this is probably a
|
||||
// mistake.
|
||||
cull_cat.warning()
|
||||
<< "GeomBinGroup::clear_current_states() called\n";
|
||||
|
||||
SubBins::iterator sbi;
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
(*sbi)->clear_current_states();
|
||||
|
|
@ -51,6 +127,13 @@ record_current_state(GraphicsStateGuardian *gsg, CullState *cs,
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinGroup::
|
||||
draw(CullTraverser *trav) {
|
||||
// This should never be called for toplevel bins, only sub bins--and
|
||||
// a GeomBinGroup should never be a sub bin. The function will do
|
||||
// the right thing even if it is called, but this is probably a
|
||||
// mistake.
|
||||
cull_cat.warning()
|
||||
<< "GeomBinGroup::draw() called\n";
|
||||
|
||||
SubBins::iterator sbi;
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
(*sbi)->draw(trav);
|
||||
|
|
@ -84,3 +167,68 @@ write(ostream &out, int indent_level) const {
|
|||
|
||||
indent(out, indent_level) << "}\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::attach
|
||||
// Access: Protected, Virtual
|
||||
// Description: Formally adds the GeomBin to its indicated Traverser.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinGroup::
|
||||
attach() {
|
||||
nassertv(has_name());
|
||||
nassertv(_traverser != (CullTraverser *)NULL);
|
||||
nassertv(!_is_attached);
|
||||
|
||||
// In the case of a GeomBinGroup, the bin is only a toplevel bin
|
||||
// (visible by name from the CullTraverser), and not a sub bin
|
||||
// (directly renderable).
|
||||
if (!has_parent()) {
|
||||
_traverser->attach_toplevel_bin(this);
|
||||
}
|
||||
|
||||
_is_attached = true;
|
||||
|
||||
// Now attach each of our sub bins.
|
||||
SubBins::const_iterator sbi;
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
GeomBin *sub_bin = (*sbi);
|
||||
nassertv(!sub_bin->is_attached());
|
||||
sub_bin->_traverser = _traverser;
|
||||
sub_bin->attach();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinGroup::detach
|
||||
// Access: Protected, Virtual
|
||||
// Description: Detaches the bin from whichever CullTraverser it is
|
||||
// currently attached to. The bin will no longer be
|
||||
// rendered. The return value is a PT(GeomBin) that
|
||||
// refers to the GeomBin itself; the caller should save
|
||||
// this pointer in a local PT variable to avoid possibly
|
||||
// destructing the GeomBin (since detaching it may
|
||||
// remove the last outstanding reference count).
|
||||
////////////////////////////////////////////////////////////////////
|
||||
PT(GeomBin) GeomBinGroup::
|
||||
detach() {
|
||||
nassertr(_is_attached, NULL);
|
||||
nassertr(_traverser != (CullTraverser *)NULL, NULL);
|
||||
|
||||
PT(GeomBin) keep = this;
|
||||
|
||||
if (!has_parent()) {
|
||||
_traverser->detach_toplevel_bin(this);
|
||||
}
|
||||
|
||||
_is_attached = false;
|
||||
|
||||
// Now detach each of our sub bins.
|
||||
SubBins::const_iterator sbi;
|
||||
for (sbi = _sub_bins.begin(); sbi != _sub_bins.end(); ++sbi) {
|
||||
GeomBin *sub_bin = (*sbi);
|
||||
sub_bin->detach();
|
||||
sub_bin->_traverser = (CullTraverser *)NULL;
|
||||
}
|
||||
|
||||
return keep;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,15 +23,16 @@
|
|||
// a given CullState should be assigned to.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBinGroup : public GeomBin {
|
||||
public:
|
||||
INLINE GeomBinGroup(const string &name, CullTraverser *traverser = NULL,
|
||||
int sort = 0);
|
||||
PUBLISHED:
|
||||
INLINE GeomBinGroup(const string &name);
|
||||
virtual ~GeomBinGroup();
|
||||
|
||||
INLINE int add_sub_bin(GeomBin *sub_bin);
|
||||
int add_sub_bin(GeomBin *sub_bin);
|
||||
INLINE int get_num_bins() const;
|
||||
INLINE GeomBin *get_bin(int n);
|
||||
INLINE PT(GeomBin) remove_bin(int n);
|
||||
PT(GeomBin) remove_bin(int n);
|
||||
|
||||
public:
|
||||
virtual int choose_bin(CullState *cs) const=0;
|
||||
|
||||
virtual void clear_current_states();
|
||||
|
|
@ -44,6 +45,10 @@ public:
|
|||
virtual void output(ostream &out) const;
|
||||
virtual void write(ostream &out, int indent_level = 0) const;
|
||||
|
||||
protected:
|
||||
virtual void attach();
|
||||
virtual PT(GeomBin) detach();
|
||||
|
||||
private:
|
||||
typedef vector<PT(GeomBin)> SubBins;
|
||||
SubBins _sub_bins;
|
||||
|
|
|
|||
|
|
@ -19,16 +19,21 @@ TypeHandle GeomBinNormal::_type_handle;
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
GeomBinNormal::
|
||||
GeomBinNormal(const string &name, CullTraverser *traverser, int sort) :
|
||||
GeomBinGroup(name, traverser, sort)
|
||||
GeomBinNormal(const string &name) :
|
||||
GeomBinGroup(name)
|
||||
{
|
||||
add_sub_bin(new GeomBinUnsorted("opaque"));
|
||||
add_sub_bin(new GeomBinBackToFront("transparent"));
|
||||
GeomBinUnsorted *opaque = new GeomBinUnsorted("opaque");
|
||||
GeomBinBackToFront *transparent = new GeomBinBackToFront("transparent");
|
||||
opaque->set_sort(10);
|
||||
transparent->set_sort(20);
|
||||
|
||||
add_sub_bin(opaque);
|
||||
add_sub_bin(transparent);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinNormal::Constructor
|
||||
// Access: Public
|
||||
// Access: Public, Virtual
|
||||
// Description: Identifies the particular sub-bin the indicated
|
||||
// CullState should be assigned to.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@
|
|||
// back-to-front.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBinNormal : public GeomBinGroup {
|
||||
public:
|
||||
GeomBinNormal(const string &name, CullTraverser *traverser = NULL,
|
||||
int sort = 0);
|
||||
PUBLISHED:
|
||||
GeomBinNormal(const string &name);
|
||||
|
||||
public:
|
||||
virtual int choose_bin(CullState *cs) const;
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ GeomBinTransition() {
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBinTransition::
|
||||
GeomBinTransition(GeomBin *bin, int draw_order) :
|
||||
GeomBinTransition(const string &bin, int draw_order) :
|
||||
OnOffTransition(TD_on),
|
||||
_value(bin),
|
||||
_draw_order(draw_order)
|
||||
|
|
@ -44,8 +44,8 @@ off() {
|
|||
// bin.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void GeomBinTransition::
|
||||
set_on(GeomBin *bin, int draw_order) {
|
||||
nassertv(bin != (GeomBin *)NULL);
|
||||
set_on(const string &bin, int draw_order) {
|
||||
nassertv(!bin.empty());
|
||||
_value = bin;
|
||||
_draw_order = draw_order;
|
||||
OnOffTransition::set_on();
|
||||
|
|
@ -54,13 +54,13 @@ set_on(GeomBin *bin, int draw_order) {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinTransition::get_bin
|
||||
// Access: Public
|
||||
// Description: Returns the bin that the GeomBinTransition
|
||||
// Description: Returns the bin name that the GeomBinTransition
|
||||
// represents. It is only valid to call this if is_on()
|
||||
// has returned true.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE PT(GeomBin) GeomBinTransition::
|
||||
INLINE string GeomBinTransition::
|
||||
get_bin() const {
|
||||
nassertr(is_on(), NULL);
|
||||
nassertr(is_on(), string());
|
||||
return _value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include <indent.h>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
TypeHandle GeomBinTransition::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -43,7 +45,7 @@ set_value_from(const OnOffTransition *other) {
|
|||
DCAST_INTO_V(ot, other);
|
||||
_value = ot->_value;
|
||||
_draw_order = ot->_draw_order;
|
||||
nassertv(_value != (GeomBin *)NULL);
|
||||
nassertv(!_value.empty());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -56,7 +58,7 @@ compare_values(const OnOffTransition *other) const {
|
|||
const GeomBinTransition *ot;
|
||||
DCAST_INTO_R(ot, other, false);
|
||||
if (_value != ot->_value) {
|
||||
return (int)(_value - ot->_value);
|
||||
return strcmp(_value.c_str(), ot->_value.c_str());
|
||||
}
|
||||
return _draw_order - ot->_draw_order;
|
||||
}
|
||||
|
|
@ -68,8 +70,7 @@ compare_values(const OnOffTransition *other) const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinTransition::
|
||||
output_value(ostream &out) const {
|
||||
nassertv(_value != (GeomBin *)NULL);
|
||||
out << *_value << ":" << _draw_order;
|
||||
out << _value << ":" << _draw_order;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -80,6 +81,61 @@ output_value(ostream &out) const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinTransition::
|
||||
write_value(ostream &out, int indent_level) const {
|
||||
nassertv(_value != (GeomBin *)NULL);
|
||||
indent(out, indent_level) << *_value << ":" << _draw_order << "\n";
|
||||
indent(out, indent_level) << _value << ":" << _draw_order << "\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinTransition::register_with_factory
|
||||
// Access: Public, Static
|
||||
// Description: Factory method to generate a GeomBinTransition object
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinTransition::
|
||||
register_with_read_factory() {
|
||||
BamReader::get_factory()->register_factory(get_class_type(), make_GeomBinTransition);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinTransition::write_datagram
|
||||
// Access: Public
|
||||
// Description: Function to write the important information in
|
||||
// the particular object to a Datagram
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinTransition::
|
||||
write_datagram(BamWriter *manager, Datagram &me) {
|
||||
OnOffTransition::write_datagram(manager, me);
|
||||
me.add_string(_value);
|
||||
me.add_int32(_draw_order);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinTransition::make_GeomBinTransition
|
||||
// Access: Public
|
||||
// Description: Factory method to generate a GeomBinTransition object
|
||||
////////////////////////////////////////////////////////////////////
|
||||
TypedWriteable *GeomBinTransition::
|
||||
make_GeomBinTransition(const FactoryParams ¶ms) {
|
||||
GeomBinTransition *me = new GeomBinTransition;
|
||||
BamReader *manager;
|
||||
Datagram packet;
|
||||
|
||||
parse_params(params, manager, packet);
|
||||
DatagramIterator scan(packet);
|
||||
|
||||
me->fillin(scan, manager);
|
||||
return me;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: GeomBinTransition::fillin
|
||||
// Access: Protected
|
||||
// Description: Function that reads out of the datagram (or asks
|
||||
// manager to read) all of the data that is needed to
|
||||
// re-create this object and stores it in the appropiate
|
||||
// place
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void GeomBinTransition::
|
||||
fillin(DatagramIterator &scan, BamReader *manager) {
|
||||
OnOffTransition::fillin(scan, manager);
|
||||
_value = scan.get_string();
|
||||
_draw_order = scan.get_int32();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,15 +16,16 @@
|
|||
// Description :
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBinTransition : public OnOffTransition {
|
||||
public:
|
||||
PUBLISHED:
|
||||
INLINE GeomBinTransition();
|
||||
INLINE GeomBinTransition(GeomBin *bin, int draw_order = 0);
|
||||
INLINE GeomBinTransition(const string &bin, int draw_order);
|
||||
INLINE static GeomBinTransition off();
|
||||
|
||||
INLINE void set_on(GeomBin *bin, int draw_order = 0);
|
||||
INLINE PT(GeomBin) get_bin() const;
|
||||
INLINE void set_on(const string &bin, int draw_order);
|
||||
INLINE string get_bin() const;
|
||||
INLINE int get_draw_order() const;
|
||||
|
||||
|
||||
public:
|
||||
virtual NodeTransition *make_copy() const;
|
||||
virtual NodeAttribute *make_attrib() const;
|
||||
|
||||
|
|
@ -34,9 +35,18 @@ protected:
|
|||
virtual void output_value(ostream &out) const;
|
||||
virtual void write_value(ostream &out, int indent_level) const;
|
||||
|
||||
PT(GeomBin) _value;
|
||||
string _value;
|
||||
int _draw_order;
|
||||
|
||||
public:
|
||||
static void register_with_read_factory();
|
||||
virtual void write_datagram(BamWriter* manager, Datagram &me);
|
||||
|
||||
static TypedWriteable *make_GeomBinTransition(const FactoryParams ¶ms);
|
||||
|
||||
protected:
|
||||
void fillin(DatagramIterator& scan, BamReader* manager);
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE GeomBinUnsorted::
|
||||
GeomBinUnsorted(const string &name, CullTraverser *traverser, int sort) :
|
||||
GeomBin(name, traverser, sort)
|
||||
GeomBinUnsorted(const string &name) :
|
||||
GeomBin(name)
|
||||
{
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@
|
|||
// order.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
class EXPCL_PANDA GeomBinUnsorted : public GeomBin {
|
||||
public:
|
||||
INLINE GeomBinUnsorted(const string &name, CullTraverser *traverser = NULL,
|
||||
int sort = 0);
|
||||
PUBLISHED:
|
||||
INLINE GeomBinUnsorted(const string &name);
|
||||
virtual ~GeomBinUnsorted();
|
||||
|
||||
public:
|
||||
virtual void clear_current_states();
|
||||
virtual void record_current_state(GraphicsStateGuardian *gsg,
|
||||
CullState *cs, int draw_order,
|
||||
|
|
|
|||
|
|
@ -174,11 +174,18 @@ appear before they are referenced.
|
|||
is any of the other options, it specifies the type of transparency
|
||||
to be enabled.
|
||||
|
||||
<Scalar> bin { bin-name }
|
||||
|
||||
This specifies the bin name order of all polygons with this
|
||||
texture applied, in the absence of a bin name specified on the
|
||||
polygon itself. See the description for bin under polygon
|
||||
attributes.
|
||||
|
||||
<Scalar> draw_order { number }
|
||||
|
||||
This specifies the fixed drawing order of all polygons with this
|
||||
texture applied, in the absence of a drawing order specified on
|
||||
the polygon itself. See the description for draw-order under
|
||||
the polygon itself. See the description for draw_order under
|
||||
polygon attributes.
|
||||
|
||||
<Transform> { transform-definition }
|
||||
|
|
@ -404,22 +411,39 @@ GEOMETRY ENTRIES
|
|||
it to be viewed from either side.
|
||||
|
||||
|
||||
<Scalar> bin { bin-name }
|
||||
|
||||
It is sometimes important to control the order in which objects
|
||||
are rendered, particularly when transparency is in use. In Panda,
|
||||
this is achieved via the use of named bins and, within certain
|
||||
kinds of bins, sometimes an explicit draw_order is also used (see
|
||||
below).
|
||||
|
||||
In the normal (state-sorting) mode, Panda renders its geometry by
|
||||
first grouping into one or more named bins, and then rendering the
|
||||
bins in a specified order. The programmer is free to define any
|
||||
number of bins, named whatever he/she desires.
|
||||
|
||||
This scalar specifies which bin this particular polygon is to be
|
||||
rendered within. If no bin scalar is given, or if the name given
|
||||
does not match any of the known bins, the polygon will be assigned
|
||||
to the default bin, which renders all opaque geometry sorted by
|
||||
state, followed by all transparent geometry sorted back-to-front.
|
||||
|
||||
See also draw_order, below.
|
||||
|
||||
|
||||
<Scalar> draw_order { number }
|
||||
|
||||
This defines the fixed drawing order of this polygon. Drawing
|
||||
order is very important when using certain kinds of transparency.
|
||||
Normally, Performer will draw all opaque objects first, then all
|
||||
transparent objects from back to front. Sometimes the
|
||||
back-to-front sorting can fail, especially in the case of a small
|
||||
polygon immediately in front of a large polygon. In this case, it
|
||||
may be necessary to explicitly give the order in which the
|
||||
polygons should be drawn.
|
||||
This works in conjunction with bin, above, to further refine the
|
||||
order in which this polygon is drawn, relative to other geometry
|
||||
in the same bin. If (and only if) the bin type named in the bin
|
||||
scalar is a GeomBinFixed, this draw_order is used to define the
|
||||
fixed order that all geometry in the same will be rendered, from
|
||||
smaller numbers to larger numbers.
|
||||
|
||||
All objects with a given draw-order will be drawn before all
|
||||
objects with a higher draw-order. Objects with a zero or
|
||||
unspecified draw-order will be drawn first if they are opaque, and
|
||||
in order from back to front if they are transparent. A negative
|
||||
draw-order is not allowed.
|
||||
If no bin scalar is specified, the default is a bin named "fixed",
|
||||
which is a GeomBinFixed object that always exists by default.
|
||||
|
||||
|
||||
<PointLight> name {
|
||||
|
|
@ -797,11 +821,17 @@ GROUPING ENTRIES
|
|||
affected by fog. It will be created with the appropriate GeoState
|
||||
to have fog disabled.
|
||||
|
||||
<Scalar> bin { bin-name }
|
||||
|
||||
This specifies the bin name for all polygons at or below this node
|
||||
that do not explicitly set their own bin. See the description of
|
||||
bin for geometry attributes, above.
|
||||
|
||||
<Scalar> draw_order { number }
|
||||
|
||||
This specifies the drawing order for all polygons at or below this
|
||||
node that do not explicitly set their own drawing order. See the
|
||||
description of draw-order for geometry attributes, above.
|
||||
description of draw_order for geometry attributes, above.
|
||||
|
||||
<Scalar> write_through { boolean-value }
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
INLINE EggAlphaMode::
|
||||
EggAlphaMode() {
|
||||
_alpha_mode = AM_unspecified;
|
||||
_draw_order = 0.0;
|
||||
_draw_order = 0;
|
||||
_has_draw_order = false;
|
||||
}
|
||||
|
||||
|
|
@ -66,21 +66,25 @@ get_alpha_mode() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::set_draw_order
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Description: Sets the "draw-order" flag associated with this
|
||||
// object. This specifies a particular order in which
|
||||
// objects of this type should be drawn, within the
|
||||
// specified bin. If a bin is not explicitly specified,
|
||||
// "fixed" is used. See also set_bin().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void EggAlphaMode::
|
||||
set_draw_order(double order) {
|
||||
set_draw_order(int order) {
|
||||
_draw_order = order;
|
||||
_has_draw_order = true;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::get_draw_order
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Description: Returns the "draw-order" flag as set for this
|
||||
// particular object. See set_draw_order().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE double EggAlphaMode::
|
||||
INLINE int EggAlphaMode::
|
||||
get_draw_order() const {
|
||||
return _draw_order;
|
||||
}
|
||||
|
|
@ -88,7 +92,8 @@ get_draw_order() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::has_draw_order
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Description: Returns true if the draw-order flag has been set for
|
||||
// this particular object. See set_draw_order().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool EggAlphaMode::
|
||||
has_draw_order() const {
|
||||
|
|
@ -98,13 +103,64 @@ has_draw_order() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::clear_draw_order
|
||||
// Access: Public
|
||||
// Description:
|
||||
// Description: Removes the draw-order flag from this particular
|
||||
// object. See set_draw_order().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void EggAlphaMode::
|
||||
clear_draw_order() {
|
||||
_has_draw_order = false;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::set_bin
|
||||
// Access: Public
|
||||
// Description: Sets the "bin" string for this particular object.
|
||||
// This names a particular bin in which the object
|
||||
// should be rendered. The exact meaning of a bin is
|
||||
// implementation defined, but generally a GeomBin
|
||||
// matching each bin name must also be specifically
|
||||
// added to the rendering engine (e.g. the
|
||||
// CullTraverser) in use for this to work. See also
|
||||
// set_draw_order().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void EggAlphaMode::
|
||||
set_bin(const string &bin) {
|
||||
_bin = bin;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::get_bin
|
||||
// Access: Public
|
||||
// Description: Returns the bin name that has been set for this
|
||||
// particular object, if any. See set_bin().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE string EggAlphaMode::
|
||||
get_bin() const {
|
||||
return _bin;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::has_bin
|
||||
// Access: Public
|
||||
// Description: Returns true if a bin name has been set for this
|
||||
// particular object. See set_bin().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE bool EggAlphaMode::
|
||||
has_bin() const {
|
||||
return !_bin.empty();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::clear_bin
|
||||
// Access: Public
|
||||
// Description: Removes the bin name that was set for this particular
|
||||
// object. See set_bin().
|
||||
////////////////////////////////////////////////////////////////////
|
||||
INLINE void EggAlphaMode::
|
||||
clear_bin() {
|
||||
_bin = string();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggAlphaMode::Inequality Operator
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ write(ostream &out, int indent_level) const {
|
|||
indent(out, indent_level)
|
||||
<< "<Scalar> draw-order { " << get_draw_order() << " }\n";
|
||||
}
|
||||
if (has_bin()) {
|
||||
indent(out, indent_level)
|
||||
<< "<Scalar> bin { " << get_bin() << " }\n";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -46,6 +50,10 @@ operator == (const EggAlphaMode &other) const {
|
|||
}
|
||||
}
|
||||
|
||||
if (_bin != other._bin) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +78,10 @@ operator < (const EggAlphaMode &other) const {
|
|||
}
|
||||
}
|
||||
|
||||
if (_bin != other._bin) {
|
||||
return _bin < other._bin;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,11 +40,16 @@ public:
|
|||
INLINE void set_alpha_mode(AlphaMode mode);
|
||||
INLINE AlphaMode get_alpha_mode() const;
|
||||
|
||||
INLINE void set_draw_order(double order);
|
||||
INLINE double get_draw_order() const;
|
||||
INLINE void set_draw_order(int order);
|
||||
INLINE int get_draw_order() const;
|
||||
INLINE bool has_draw_order() const;
|
||||
INLINE void clear_draw_order();
|
||||
|
||||
INLINE void set_bin(const string &bin);
|
||||
INLINE string get_bin() const;
|
||||
INLINE bool has_bin() const;
|
||||
INLINE void clear_bin();
|
||||
|
||||
// Comparison operators are handy.
|
||||
bool operator == (const EggAlphaMode &other) const;
|
||||
INLINE bool operator != (const EggAlphaMode &other) const;
|
||||
|
|
@ -54,8 +59,9 @@ public:
|
|||
|
||||
private:
|
||||
AlphaMode _alpha_mode;
|
||||
double _draw_order;
|
||||
int _draw_order;
|
||||
bool _has_draw_order;
|
||||
string _bin;
|
||||
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -265,6 +265,57 @@ write(ostream &out, int indent_level) const {
|
|||
indent(out, indent_level) << "}\n";
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggGroup::determine_alpha_mode
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggGroup
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this group that has an alpha_mode other than
|
||||
// AM_unspecified. Returns a valid EggAlphaMode pointer
|
||||
// if one is found, or NULL otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggGroup::
|
||||
determine_alpha_mode() {
|
||||
if (get_alpha_mode() != AM_unspecified) {
|
||||
return this;
|
||||
}
|
||||
return EggGroupNode::determine_alpha_mode();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggGroup::determine_draw_order
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggGroup
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this group that has a draw_order specified.
|
||||
// Returns a valid EggAlphaMode pointer if one is found,
|
||||
// or NULL otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggGroup::
|
||||
determine_draw_order() {
|
||||
if (has_draw_order()) {
|
||||
return this;
|
||||
}
|
||||
return EggGroupNode::determine_draw_order();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggGroup::determine_bin
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggGroup
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this group that has a bin specified. Returns a
|
||||
// valid EggAlphaMode pointer if one is found, or NULL
|
||||
// otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggGroup::
|
||||
determine_bin() {
|
||||
if (has_bin()) {
|
||||
return this;
|
||||
}
|
||||
return EggGroupNode::determine_bin();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggGroup::parse_egg
|
||||
// Access: Public
|
||||
|
|
|
|||
|
|
@ -77,6 +77,10 @@ public:
|
|||
virtual void write(ostream &out, int indent_level) const;
|
||||
bool parse_egg(const string &egg_syntax);
|
||||
|
||||
virtual EggAlphaMode *determine_alpha_mode();
|
||||
virtual EggAlphaMode *determine_draw_order();
|
||||
virtual EggAlphaMode *determine_bin();
|
||||
|
||||
void set_group_type(GroupType type);
|
||||
INLINE GroupType get_group_type() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,59 @@
|
|||
|
||||
TypeHandle EggNode::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggNode::determine_alpha_mode
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggGroup
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this node that has an alpha_mode other than
|
||||
// AM_unspecified. Returns a valid EggAlphaMode pointer
|
||||
// if one is found, or NULL otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggNode::
|
||||
determine_alpha_mode() {
|
||||
if (_parent == (EggGroupNode *)NULL) {
|
||||
// Too bad; we're done.
|
||||
return (EggAlphaMode *)NULL;
|
||||
}
|
||||
return _parent->determine_alpha_mode();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggNode::determine_draw_order
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggGroup
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this node that has a draw_order specified.
|
||||
// Returns a valid EggAlphaMode pointer if one is found,
|
||||
// or NULL otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggNode::
|
||||
determine_draw_order() {
|
||||
if (_parent == (EggGroupNode *)NULL) {
|
||||
// Too bad; we're done.
|
||||
return (EggAlphaMode *)NULL;
|
||||
}
|
||||
return _parent->determine_draw_order();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggNode::determine_bin
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggGroup
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this node that has a bin specified. Returns a
|
||||
// valid EggAlphaMode pointer if one is found, or NULL
|
||||
// otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggNode::
|
||||
determine_bin() {
|
||||
if (_parent == (EggGroupNode *)NULL) {
|
||||
// Too bad; we're done.
|
||||
return (EggAlphaMode *)NULL;
|
||||
}
|
||||
return _parent->determine_bin();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
#include <referenceCount.h>
|
||||
|
||||
class EggGroupNode;
|
||||
class EggAlphaMode;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Class : EggNode
|
||||
|
|
@ -36,6 +37,10 @@ public:
|
|||
INLINE bool is_under_transform() const;
|
||||
INLINE bool is_local_coord() const;
|
||||
|
||||
virtual EggAlphaMode *determine_alpha_mode();
|
||||
virtual EggAlphaMode *determine_draw_order();
|
||||
virtual EggAlphaMode *determine_bin();
|
||||
|
||||
INLINE const LMatrix4d &get_vertex_frame() const;
|
||||
INLINE const LMatrix4d &get_node_frame() const;
|
||||
INLINE const LMatrix4d &get_vertex_frame_inv() const;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,66 @@
|
|||
TypeHandle EggPrimitive::_type_handle;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggPrimitive::determine_alpha_mode
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggPrimitive
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this primitive that has an alpha_mode other than
|
||||
// AM_unspecified. Returns a valid EggAlphaMode pointer
|
||||
// if one is found, or NULL otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggPrimitive::
|
||||
determine_alpha_mode() {
|
||||
if (get_alpha_mode() != AM_unspecified) {
|
||||
return this;
|
||||
}
|
||||
if (has_texture() && get_texture()->get_alpha_mode() != AM_unspecified) {
|
||||
return get_texture();
|
||||
}
|
||||
return EggNode::determine_alpha_mode();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggPrimitive::determine_draw_order
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggPrimitive
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this primitive that has a draw_order specified.
|
||||
// Returns a valid EggAlphaMode pointer if one is found,
|
||||
// or NULL otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggPrimitive::
|
||||
determine_draw_order() {
|
||||
if (has_draw_order()) {
|
||||
return this;
|
||||
}
|
||||
if (has_texture() && get_texture()->has_draw_order()) {
|
||||
return get_texture();
|
||||
}
|
||||
return EggNode::determine_draw_order();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggPrimitive::determine_bin
|
||||
// Access: Public, Virtual
|
||||
// Description: Walks back up the hierarchy, looking for an EggPrimitive
|
||||
// or EggPrimitive or some such object at this level or
|
||||
// above this primitive that has a bin specified. Returns a
|
||||
// valid EggAlphaMode pointer if one is found, or NULL
|
||||
// otherwise.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
EggAlphaMode *EggPrimitive::
|
||||
determine_bin() {
|
||||
if (has_bin()) {
|
||||
return this;
|
||||
}
|
||||
if (has_texture() && get_texture()->has_bin()) {
|
||||
return get_texture();
|
||||
}
|
||||
return EggNode::determine_bin();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: EggPrimitive::reverse_vertex_ordering
|
||||
// Access: Public, Virtual
|
||||
|
|
|
|||
|
|
@ -51,6 +51,10 @@ public:
|
|||
INLINE EggPrimitive &operator = (const EggPrimitive ©);
|
||||
INLINE ~EggPrimitive();
|
||||
|
||||
virtual EggAlphaMode *determine_alpha_mode();
|
||||
virtual EggAlphaMode *determine_draw_order();
|
||||
virtual EggAlphaMode *determine_bin();
|
||||
|
||||
INLINE void set_texture(PT(EggTexture) texture);
|
||||
INLINE void clear_texture();
|
||||
INLINE PT(EggTexture) get_texture() const;
|
||||
|
|
|
|||
|
|
@ -384,7 +384,10 @@ texture_body:
|
|||
}
|
||||
|
||||
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
||||
texture->set_draw_order(value);
|
||||
texture->set_draw_order((int)value);
|
||||
|
||||
} else if (cmp_nocase_uh(name, "bin") == 0) {
|
||||
texture->set_bin(strval);
|
||||
|
||||
} else if (cmp_nocase_uh(name, "alpha_file") == 0) {
|
||||
texture->set_alpha_file(strval);
|
||||
|
|
@ -811,7 +814,9 @@ group_body:
|
|||
group->set_alpha_mode(a);
|
||||
}
|
||||
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
||||
group->set_draw_order(value);
|
||||
group->set_draw_order((int)value);
|
||||
} else if (cmp_nocase_uh(name, "bin") == 0) {
|
||||
group->set_bin(strval);
|
||||
} else if (cmp_nocase_uh(name, "collide_mask") == 0) {
|
||||
group->set_collide_mask(value);
|
||||
} else if (cmp_nocase_uh(name, "from_collide_mask") == 0) {
|
||||
|
|
@ -1325,7 +1330,9 @@ primitive_body:
|
|||
primitive->set_alpha_mode(a);
|
||||
}
|
||||
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
||||
primitive->set_draw_order(value);
|
||||
primitive->set_draw_order((int)value);
|
||||
} else if (cmp_nocase_uh(name, "bin") == 0) {
|
||||
primitive->set_bin(strval);
|
||||
} else {
|
||||
eggyywarning("Unknown scalar " + name);
|
||||
}
|
||||
|
|
@ -1373,7 +1380,9 @@ nurbs_surface_body:
|
|||
primitive->set_alpha_mode(a);
|
||||
}
|
||||
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
||||
primitive->set_draw_order(value);
|
||||
primitive->set_draw_order((int)value);
|
||||
} else if (cmp_nocase_uh(name, "bin") == 0) {
|
||||
primitive->set_bin(strval);
|
||||
} else if (cmp_nocase_uh(name, "u_subdiv") == 0) {
|
||||
primitive->set_u_subdiv(value);
|
||||
} else if (cmp_nocase_uh(name, "v_subdiv") == 0) {
|
||||
|
|
@ -1418,7 +1427,9 @@ nurbs_curve_body:
|
|||
primitive->set_alpha_mode(a);
|
||||
}
|
||||
} else if (cmp_nocase_uh(name, "draw_order") == 0) {
|
||||
primitive->set_draw_order(value);
|
||||
primitive->set_draw_order((int)value);
|
||||
} else if (cmp_nocase_uh(name, "bin") == 0) {
|
||||
primitive->set_bin(strval);
|
||||
} else if (cmp_nocase_uh(name, "subdiv") == 0) {
|
||||
primitive->set_subdiv(value);
|
||||
} else if (cmp_nocase_uh(name, "type") == 0) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#begin lib_target
|
||||
#define TARGET egg2sg
|
||||
#define LOCAL_LIBS \
|
||||
collide egg builder loader chan char switchnode cull
|
||||
cull collide egg builder loader chan char switchnode
|
||||
|
||||
#define SOURCES \
|
||||
animBundleMaker.cxx animBundleMaker.h characterMaker.cxx \
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@
|
|||
#include <decalTransition.h>
|
||||
#include <directRenderTransition.h>
|
||||
#include <pruneTransition.h>
|
||||
#include <depthWriteTransition.h>
|
||||
#include <animBundleNode.h>
|
||||
#include <character.h>
|
||||
#include <notify.h>
|
||||
|
|
@ -49,6 +50,7 @@
|
|||
#include <collisionPlane.h>
|
||||
#include <collisionSphere.h>
|
||||
#include <dftraverser.h>
|
||||
#include <geomBinTransition.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <algorithm>
|
||||
|
|
@ -895,17 +897,39 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
|
|||
}
|
||||
|
||||
// Assign the appropriate properties to the bucket.
|
||||
EggAlphaMode::AlphaMode am = egg_prim->get_alpha_mode();
|
||||
|
||||
// The three "alpha mode"-associated properties--alpha mode, draw
|
||||
// order, and bin--can be defined directly at the primitive, at a
|
||||
// group above the primitive, or an a texture applied to the
|
||||
// primitive. The EggNode::determine_*() functions can find the
|
||||
// right pointer to the level at which this is actually defined for
|
||||
// a given primitive.
|
||||
EggAlphaMode::AlphaMode am = EggAlphaMode::AM_unspecified;
|
||||
bool implicit_alpha = false;
|
||||
bool has_draw_order = false;
|
||||
int draw_order = 0;
|
||||
bool has_bin = false;
|
||||
string bin;
|
||||
|
||||
EggAlphaMode *egg_alpha;
|
||||
egg_alpha = egg_prim->determine_alpha_mode();
|
||||
if (egg_alpha != (EggAlphaMode *)NULL) {
|
||||
am = egg_alpha->get_alpha_mode();
|
||||
}
|
||||
egg_alpha = egg_prim->determine_draw_order();
|
||||
if (egg_alpha != (EggAlphaMode *)NULL) {
|
||||
has_draw_order = true;
|
||||
draw_order = egg_alpha->get_draw_order();
|
||||
}
|
||||
egg_alpha = egg_prim->determine_bin();
|
||||
if (egg_alpha != (EggAlphaMode *)NULL) {
|
||||
has_bin = true;
|
||||
bin = egg_alpha->get_bin();
|
||||
}
|
||||
|
||||
bucket._trans.set_transition(new TextureTransition(TextureTransition::off()));
|
||||
if (egg_prim->has_texture()) {
|
||||
PT(EggTexture) egg_tex = egg_prim->get_texture();
|
||||
// If the primitive didn't specify an alpha mode, allow the
|
||||
// texture to specify one.
|
||||
if (am == EggAlphaMode::AM_unspecified) {
|
||||
am = egg_tex->get_alpha_mode();
|
||||
}
|
||||
|
||||
const TextureDef &def = _textures[egg_tex];
|
||||
if (def._texture != (TextureTransition *)NULL) {
|
||||
|
|
@ -957,7 +981,8 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
|
|||
break;
|
||||
|
||||
case EggAlphaMode::AM_blend_no_occlude:
|
||||
bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha_sorted));
|
||||
bucket._trans.set_transition(new TransparencyTransition(TransparencyProperty::M_alpha));
|
||||
bucket._trans.set_transition(new DepthWriteTransition(DepthWriteTransition::off()));
|
||||
break;
|
||||
|
||||
case EggAlphaMode::AM_ms:
|
||||
|
|
@ -973,6 +998,13 @@ setup_bucket(BuilderBucket &bucket, NamedNode *parent,
|
|||
break;
|
||||
}
|
||||
|
||||
if (has_bin) {
|
||||
bucket._trans.set_transition(new GeomBinTransition(bin, draw_order));
|
||||
|
||||
} else if (has_draw_order) {
|
||||
bucket._trans.set_transition(new GeomBinTransition("fixed", draw_order));
|
||||
}
|
||||
|
||||
if (egg_prim->get_bface_flag()) {
|
||||
// The primitive is marked with backface culling disabled--we want
|
||||
// to see both sides.
|
||||
|
|
|
|||
|
|
@ -667,3 +667,19 @@ get_parent_towards(TypeHandle type) const {
|
|||
TypedObject::
|
||||
~TypedObject() {
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: TypedObject::get_type
|
||||
// Access: Public, Virtual
|
||||
// Description:
|
||||
////////////////////////////////////////////////////////////////////
|
||||
TypeHandle TypedObject::
|
||||
get_type() const {
|
||||
// Normally, this function should never be called, because it is a
|
||||
// pure virtual function. If it is called, you probably called
|
||||
// get_type() on a recently-destructed object.
|
||||
express_cat.warning()
|
||||
<< "TypedObject::get_type() called!\n";
|
||||
return _type_handle;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -186,15 +186,16 @@ ConfigureFn(config_sgattrib) {
|
|||
|
||||
//Registration of writeable object's creation
|
||||
//functions with BamReader's factory
|
||||
RenderRelation::register_with_read_factory();
|
||||
TransformTransition::register_with_read_factory();
|
||||
TextureTransition::register_with_read_factory();
|
||||
TextureApplyTransition::register_with_read_factory();
|
||||
TransparencyTransition::register_with_read_factory();
|
||||
CullFaceTransition::register_with_read_factory();
|
||||
DecalTransition::register_with_read_factory();
|
||||
PruneTransition::register_with_read_factory();
|
||||
BillboardTransition::register_with_read_factory();
|
||||
ColorMatrixTransition::register_with_read_factory();
|
||||
CullFaceTransition::register_with_read_factory();
|
||||
DecalTransition::register_with_read_factory();
|
||||
DepthWriteTransition::register_with_read_factory();
|
||||
PruneTransition::register_with_read_factory();
|
||||
RenderRelation::register_with_read_factory();
|
||||
TextureApplyTransition::register_with_read_factory();
|
||||
TextureTransition::register_with_read_factory();
|
||||
TransformTransition::register_with_read_factory();
|
||||
TransparencyTransition::register_with_read_factory();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@
|
|||
#include "depthWriteTransition.h"
|
||||
#include "depthWriteAttribute.h"
|
||||
|
||||
#include <datagram.h>
|
||||
#include <datagramIterator.h>
|
||||
#include <bamReader.h>
|
||||
#include <bamWriter.h>
|
||||
|
||||
TypeHandle DepthWriteTransition::_type_handle;
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -28,3 +33,55 @@ NodeAttribute *DepthWriteTransition::
|
|||
make_attrib() const {
|
||||
return new DepthWriteAttribute;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DepthWriteTransition::register_with_factory
|
||||
// Access: Public, Static
|
||||
// Description: Factory method to generate a DepthWriteTransition object
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DepthWriteTransition::
|
||||
register_with_read_factory() {
|
||||
BamReader::get_factory()->register_factory(get_class_type(), make_DepthWriteTransition);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DepthWriteTransition::write_datagram
|
||||
// Access: Public
|
||||
// Description: Function to write the important information in
|
||||
// the particular object to a Datagram
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DepthWriteTransition::
|
||||
write_datagram(BamWriter *manager, Datagram &me) {
|
||||
OnOffTransition::write_datagram(manager, me);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DepthWriteTransition::make_DepthWriteTransition
|
||||
// Access: Public
|
||||
// Description: Factory method to generate a DepthWriteTransition object
|
||||
////////////////////////////////////////////////////////////////////
|
||||
TypedWriteable *DepthWriteTransition::
|
||||
make_DepthWriteTransition(const FactoryParams ¶ms) {
|
||||
DepthWriteTransition *me = new DepthWriteTransition;
|
||||
BamReader *manager;
|
||||
Datagram packet;
|
||||
|
||||
parse_params(params, manager, packet);
|
||||
DatagramIterator scan(packet);
|
||||
|
||||
me->fillin(scan, manager);
|
||||
return me;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: DepthWriteTransition::fillin
|
||||
// Access: Protected
|
||||
// Description: Function that reads out of the datagram (or asks
|
||||
// manager to read) all of the data that is needed to
|
||||
// re-create this object and stores it in the appropiate
|
||||
// place
|
||||
////////////////////////////////////////////////////////////////////
|
||||
void DepthWriteTransition::
|
||||
fillin(DatagramIterator &scan, BamReader *manager) {
|
||||
OnOffTransition::fillin(scan, manager);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,15 @@ public:
|
|||
virtual NodeTransition *make_copy() const;
|
||||
virtual NodeAttribute *make_attrib() const;
|
||||
|
||||
public:
|
||||
static void register_with_read_factory();
|
||||
virtual void write_datagram(BamWriter* manager, Datagram &me);
|
||||
|
||||
static TypedWriteable *make_DepthWriteTransition(const FactoryParams ¶ms);
|
||||
|
||||
protected:
|
||||
void fillin(DatagramIterator& scan, BamReader* manager);
|
||||
|
||||
public:
|
||||
virtual TypeHandle get_type() const {
|
||||
return get_class_type();
|
||||
|
|
|
|||
Loading…
Reference in New Issue