1194 lines
43 KiB
C++
1194 lines
43 KiB
C++
// Filename: renderState.cxx
|
|
// Created by: drose (21Feb02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d@yahoogroups.com .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "renderState.h"
|
|
#include "transparencyAttrib.h"
|
|
#include "cullBinAttrib.h"
|
|
#include "cullBinManager.h"
|
|
#include "fogAttrib.h"
|
|
#include "transparencyAttrib.h"
|
|
#include "config_pgraph.h"
|
|
#include "bamReader.h"
|
|
#include "bamWriter.h"
|
|
#include "datagramIterator.h"
|
|
#include "indent.h"
|
|
#include "compareTo.h"
|
|
|
|
RenderState::States *RenderState::_states = NULL;
|
|
CPT(RenderState) RenderState::_empty_state;
|
|
TypeHandle RenderState::_type_handle;
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::Constructor
|
|
// Access: Protected
|
|
// Description: Actually, this could be a private constructor, since
|
|
// no one inherits from RenderState, but gcc gives us a
|
|
// spurious warning if all constructors are private.
|
|
////////////////////////////////////////////////////////////////////
|
|
RenderState::
|
|
RenderState() {
|
|
if (_states == (States *)NULL) {
|
|
// Make sure the global _states map is allocated. This only has
|
|
// to be done once. We could make this map static, but then we
|
|
// run into problems if anyone creates a RenderState object at
|
|
// static init time; it also seems to cause problems when the
|
|
// Panda shared library is unloaded at application exit time.
|
|
_states = new States;
|
|
}
|
|
_saved_entry = _states->end();
|
|
_self_compose = (RenderState *)NULL;
|
|
_flags = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::Copy Constructor
|
|
// Access: Private
|
|
// Description: RenderStates are not meant to be copied.
|
|
////////////////////////////////////////////////////////////////////
|
|
RenderState::
|
|
RenderState(const RenderState &) {
|
|
nassertv(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::Copy Assignment Operator
|
|
// Access: Private
|
|
// Description: RenderStates are not meant to be copied.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
operator = (const RenderState &) {
|
|
nassertv(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::Destructor
|
|
// Access: Public, Virtual
|
|
// Description: The destructor is responsible for removing the
|
|
// RenderState from the global set if it is there.
|
|
////////////////////////////////////////////////////////////////////
|
|
RenderState::
|
|
~RenderState() {
|
|
// We'd better not call the destructor twice on a particular object.
|
|
nassertv(!is_destructing());
|
|
set_destructing();
|
|
|
|
if (_saved_entry != _states->end()) {
|
|
nassertv(_states->find(this) == _saved_entry);
|
|
_states->erase(_saved_entry);
|
|
_saved_entry = _states->end();
|
|
}
|
|
|
|
// Now make sure we clean up all other floating pointers to the
|
|
// RenderState. These may be scattered around in the various
|
|
// CompositionCaches from other RenderState objects.
|
|
|
|
// Fortunately, since we added CompositionCache records in pairs, we
|
|
// know exactly the set of RenderState objects that have us in their
|
|
// cache: it's the same set of RenderState objects that we have in
|
|
// our own cache.
|
|
|
|
// We do need to put considerable thought into this loop, because as
|
|
// we clear out cache entries we'll cause other RenderState
|
|
// objects to destruct, which could cause things to get pulled out
|
|
// of our own _composition_cache map. We want to allow this (so
|
|
// that we don't encounter any just-destructed pointers in our
|
|
// cache), but we don't want to get bitten by this cascading effect.
|
|
// Instead of walking through the map from beginning to end,
|
|
// therefore, we just pull out the first one each time, and erase
|
|
// it.
|
|
|
|
// There are lots of ways to do this loop wrong. Be very careful if
|
|
// you need to modify it for any reason.
|
|
while (!_composition_cache.empty()) {
|
|
CompositionCache::iterator ci = _composition_cache.begin();
|
|
|
|
// It is possible that the "other" RenderState object is
|
|
// currently within its own destructor. We therefore can't use a
|
|
// PT() to hold its pointer; that could end up calling its
|
|
// destructor twice. Fortunately, we don't need to hold its
|
|
// reference count to ensure it doesn't destruct while we process
|
|
// this loop; as long as we ensure that no *other* RenderState
|
|
// objects destruct, there will be no reason for that one to.
|
|
RenderState *other = (RenderState *)(*ci).first;
|
|
|
|
// We should never have a reflexive entry in this map. If we
|
|
// do, something got screwed up elsewhere.
|
|
nassertv(other != this);
|
|
|
|
// We hold a copy of the composition result to ensure that the
|
|
// result RenderState object (if there is one) doesn't
|
|
// destruct.
|
|
Composition comp = (*ci).second;
|
|
|
|
// Now we can remove the element from our cache. We do this now,
|
|
// rather than later, before any other RenderState objects have
|
|
// had a chance to destruct, so we are confident that our iterator
|
|
// is still valid.
|
|
_composition_cache.erase(ci);
|
|
|
|
CompositionCache::iterator oci = other->_composition_cache.find(this);
|
|
|
|
// We may or may not still be listed in the other's cache (it
|
|
// might be halfway through pulling entries out, from within its
|
|
// own destructor).
|
|
if (oci != other->_composition_cache.end()) {
|
|
// Hold a copy of the other composition result, too.
|
|
Composition ocomp = (*oci).second;
|
|
|
|
// Now we're holding a reference count to both computed
|
|
// results, so no objects will be tempted to destruct while we
|
|
// erase the other cache entry.
|
|
other->_composition_cache.erase(oci);
|
|
}
|
|
|
|
// It's finally safe to let our held pointers go away. This may
|
|
// have cascading effects as other RenderState objects are
|
|
// destructed, but there will be no harm done if they destruct
|
|
// now.
|
|
}
|
|
|
|
// A similar bit of code for the invert cache.
|
|
while (!_invert_composition_cache.empty()) {
|
|
CompositionCache::iterator ci = _invert_composition_cache.begin();
|
|
RenderState *other = (RenderState *)(*ci).first;
|
|
nassertv(other != this);
|
|
Composition comp = (*ci).second;
|
|
_invert_composition_cache.erase(ci);
|
|
CompositionCache::iterator oci =
|
|
other->_invert_composition_cache.find(this);
|
|
if (oci != other->_invert_composition_cache.end()) {
|
|
Composition ocomp = (*oci).second;
|
|
other->_invert_composition_cache.erase(oci);
|
|
}
|
|
}
|
|
|
|
// Also, if we called compose(this) at some point and the return
|
|
// value was something other than this, we need to decrement the
|
|
// associated reference count.
|
|
if (_self_compose != (RenderState *)NULL && _self_compose != this) {
|
|
unref_delete((RenderState *)_self_compose);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::operator <
|
|
// Access: Public
|
|
// Description: Provides an arbitrary ordering among all unique
|
|
// RenderStates, so we can store the essentially
|
|
// different ones in a big set and throw away the rest.
|
|
//
|
|
// This method is not needed outside of the RenderState
|
|
// class because all equivalent RenderState objects are
|
|
// guaranteed to share the same pointer; thus, a pointer
|
|
// comparison is always sufficient.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderState::
|
|
operator < (const RenderState &other) const {
|
|
// We must compare all the properties of the attributes, not just
|
|
// the type; thus, we compare them one at a time using compare_to().
|
|
return lexicographical_compare(_attributes.begin(), _attributes.end(),
|
|
other._attributes.begin(), other._attributes.end(),
|
|
CompareTo<Attribute>());
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::find_attrib
|
|
// Access: Published
|
|
// Description: Searches for an attribute with the indicated type in
|
|
// the state, and returns its index if it is found, or
|
|
// -1 if it is not.
|
|
////////////////////////////////////////////////////////////////////
|
|
int RenderState::
|
|
find_attrib(TypeHandle type) const {
|
|
Attributes::const_iterator ai = _attributes.find(Attribute(type));
|
|
if (ai == _attributes.end()) {
|
|
return -1;
|
|
}
|
|
return ai - _attributes.begin();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make_empty
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderState with no attributes set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
make_empty() {
|
|
// The empty state is asked for so often, we make it a special case
|
|
// and store a pointer forever once we find it the first time.
|
|
if (_empty_state == (RenderState *)NULL) {
|
|
RenderState *state = new RenderState;
|
|
_empty_state = return_new(state);
|
|
}
|
|
|
|
return _empty_state;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderState with one attribute set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
make(const RenderAttrib *attrib, int override) {
|
|
RenderState *state = new RenderState;
|
|
state->_attributes.reserve(1);
|
|
state->_attributes.insert(Attribute(attrib, override));
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderState with two attributes set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
make(const RenderAttrib *attrib1,
|
|
const RenderAttrib *attrib2, int override) {
|
|
RenderState *state = new RenderState;
|
|
state->_attributes.reserve(2);
|
|
state->_attributes.push_back(Attribute(attrib1, override));
|
|
state->_attributes.push_back(Attribute(attrib2, override));
|
|
state->_attributes.sort();
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderState with three attributes set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
make(const RenderAttrib *attrib1,
|
|
const RenderAttrib *attrib2,
|
|
const RenderAttrib *attrib3, int override) {
|
|
RenderState *state = new RenderState;
|
|
state->_attributes.reserve(3);
|
|
state->_attributes.push_back(Attribute(attrib1, override));
|
|
state->_attributes.push_back(Attribute(attrib2, override));
|
|
state->_attributes.push_back(Attribute(attrib3, override));
|
|
state->_attributes.sort();
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderState with four attributes set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
make(const RenderAttrib *attrib1,
|
|
const RenderAttrib *attrib2,
|
|
const RenderAttrib *attrib3,
|
|
const RenderAttrib *attrib4, int override) {
|
|
RenderState *state = new RenderState;
|
|
state->_attributes.reserve(4);
|
|
state->_attributes.push_back(Attribute(attrib1, override));
|
|
state->_attributes.push_back(Attribute(attrib2, override));
|
|
state->_attributes.push_back(Attribute(attrib3, override));
|
|
state->_attributes.push_back(Attribute(attrib4, override));
|
|
state->_attributes.sort();
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderState with n attributes set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
make(const RenderAttrib * const *attrib, int num_attribs, int override) {
|
|
RenderState *state = new RenderState;
|
|
state->_attributes.reserve(num_attribs);
|
|
for (int i = 0; i < num_attribs; i++) {
|
|
state->_attributes.push_back(Attribute(attrib[i], override));
|
|
}
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::compose
|
|
// Access: Published
|
|
// Description: Returns a new RenderState object that represents the
|
|
// composition of this state with the other state.
|
|
//
|
|
// The result of this operation is cached, and will be
|
|
// retained as long as both this RenderState object and
|
|
// the other RenderState object continue to exist.
|
|
// Should one of them destruct, the cached entry will be
|
|
// removed, and its pointer will be allowed to destruct
|
|
// as well.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
compose(const RenderState *other) const {
|
|
// This method isn't strictly const, because it updates the cache,
|
|
// but we pretend that it is because it's only a cache which is
|
|
// transparent to the rest of the interface.
|
|
|
|
// We handle empty state (identity) as a trivial special case.
|
|
if (is_empty()) {
|
|
return other;
|
|
}
|
|
if (other->is_empty()) {
|
|
return this;
|
|
}
|
|
|
|
if (other == this) {
|
|
// compose(this) has to be handled as a special case, because the
|
|
// caching problem is so different.
|
|
if (_self_compose != (RenderState *)NULL) {
|
|
return _self_compose;
|
|
}
|
|
CPT(RenderState) result = do_compose(this);
|
|
((RenderState *)this)->_self_compose = result;
|
|
|
|
if (result != (const RenderState *)this) {
|
|
// If the result of compose(this) is something other than this,
|
|
// explicitly increment the reference count. We have to be sure
|
|
// to decrement it again later, in our destructor.
|
|
_self_compose->ref();
|
|
|
|
// (If the result was just this again, we still store the
|
|
// result, but we don't increment the reference count, since
|
|
// that would be a self-referential leak. What a mess this is.)
|
|
}
|
|
return _self_compose;
|
|
}
|
|
|
|
// Is this composition already cached?
|
|
CompositionCache::const_iterator ci = _composition_cache.find(other);
|
|
if (ci != _composition_cache.end()) {
|
|
const Composition &comp = (*ci).second;
|
|
if (comp._result == (const RenderState *)NULL) {
|
|
// Well, it wasn't cached already, but we already had an entry
|
|
// (probably created for the reverse direction), so use the same
|
|
// entry to store the new result.
|
|
((Composition &)comp)._result = do_compose(other);
|
|
}
|
|
// Here's the cache!
|
|
return comp._result;
|
|
}
|
|
|
|
// We need to make a new cache entry, both in this object and in the
|
|
// other object. We make both records so the other RenderState
|
|
// object will know to delete the entry from this object when it
|
|
// destructs, and vice-versa.
|
|
|
|
// The cache entry in this object is the only one that indicates the
|
|
// result; the other will be NULL for now.
|
|
CPT(RenderState) result = do_compose(other);
|
|
|
|
((RenderState *)other)->_composition_cache[this]._result = NULL;
|
|
((RenderState *)this)->_composition_cache[other]._result = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::invert_compose
|
|
// Access: Published
|
|
// Description: Returns a new RenderState object that represents the
|
|
// composition of this state's inverse with the other
|
|
// state.
|
|
//
|
|
// This is similar to compose(), but is particularly
|
|
// useful for computing the relative state of a node as
|
|
// viewed from some other node.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
invert_compose(const RenderState *other) const {
|
|
// This method isn't strictly const, because it updates the cache,
|
|
// but we pretend that it is because it's only a cache which is
|
|
// transparent to the rest of the interface.
|
|
|
|
// We handle empty state (identity) as a trivial special case.
|
|
if (is_empty()) {
|
|
return other;
|
|
}
|
|
// Unlike compose(), the case of other->is_empty() is not quite as
|
|
// trivial for invert_compose().
|
|
|
|
if (other == this) {
|
|
// a->invert_compose(a) always produces identity.
|
|
return make_empty();
|
|
}
|
|
|
|
// Is this composition already cached?
|
|
CompositionCache::const_iterator ci = _invert_composition_cache.find(other);
|
|
if (ci != _invert_composition_cache.end()) {
|
|
const Composition &comp = (*ci).second;
|
|
if (comp._result == (const RenderState *)NULL) {
|
|
// Well, it wasn't cached already, but we already had an entry
|
|
// (probably created for the reverse direction), so use the same
|
|
// entry to store the new result.
|
|
((Composition &)comp)._result = do_invert_compose(other);
|
|
}
|
|
// Here's the cache!
|
|
return comp._result;
|
|
}
|
|
|
|
// We need to make a new cache entry, both in this object and in the
|
|
// other object. We make both records so the other RenderState
|
|
// object will know to delete the entry from this object when it
|
|
// destructs, and vice-versa.
|
|
|
|
// The cache entry in this object is the only one that indicates the
|
|
// result; the other will be NULL for now.
|
|
CPT(RenderState) result = do_invert_compose(other);
|
|
|
|
((RenderState *)other)->_invert_composition_cache[this]._result = NULL;
|
|
((RenderState *)this)->_invert_composition_cache[other]._result = result;
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::add_attrib
|
|
// Access: Published
|
|
// Description: Returns a new RenderState object that represents the
|
|
// same as the source state, with the new RenderAttrib
|
|
// added. If there is already a RenderAttrib with the
|
|
// same type, it is replaced.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
add_attrib(const RenderAttrib *attrib, int override) const {
|
|
RenderState *new_state = new RenderState;
|
|
back_insert_iterator<Attributes> result =
|
|
back_inserter(new_state->_attributes);
|
|
|
|
Attribute new_attribute(attrib, override);
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
|
|
while (ai != _attributes.end() && (*ai) < new_attribute) {
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
}
|
|
*result = new_attribute;
|
|
++result;
|
|
|
|
if (ai != _attributes.end() && !(new_attribute < (*ai))) {
|
|
// At this point we know:
|
|
// !((*ai) < new_attribute) && !(new_attribute < (*ai))
|
|
// which means (*ai) == new_attribute--so we should leave it out,
|
|
// to avoid duplicating attributes in the set.
|
|
++ai;
|
|
}
|
|
|
|
while (ai != _attributes.end()) {
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::remove_attrib
|
|
// Access: Published
|
|
// Description: Returns a new RenderState object that represents the
|
|
// same as the source state, with the indicated
|
|
// RenderAttrib removed.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
remove_attrib(TypeHandle type) const {
|
|
RenderState *new_state = new RenderState;
|
|
back_insert_iterator<Attributes> result =
|
|
back_inserter(new_state->_attributes);
|
|
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
|
|
while (ai != _attributes.end()) {
|
|
if ((*ai)._type != type) {
|
|
*result = *ai;
|
|
++result;
|
|
}
|
|
++ai;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::remove_attrib
|
|
// Access: Published
|
|
// Description: Returns a new RenderState object that represents the
|
|
// same as the source state, with all attributes'
|
|
// override values incremented (or decremented, if
|
|
// negative) by the indicated amount. If the override
|
|
// would drop below zero, it is set to zero.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
adjust_all_priorities(int adjustment) const {
|
|
RenderState *new_state = new RenderState;
|
|
new_state->_attributes.reserve(_attributes.size());
|
|
|
|
Attributes::const_iterator ai;
|
|
for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) {
|
|
Attribute attrib = *ai;
|
|
attrib._override = max(attrib._override + adjustment, 0);
|
|
new_state->_attributes.push_back(attrib);
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::get_attrib
|
|
// Access: Published, Virtual
|
|
// Description: Looks for a RenderAttrib of the indicated type in the
|
|
// state, and returns it if it is found, or NULL if it
|
|
// is not.
|
|
////////////////////////////////////////////////////////////////////
|
|
const RenderAttrib *RenderState::
|
|
get_attrib(TypeHandle type) const {
|
|
Attributes::const_iterator ai;
|
|
ai = _attributes.find(Attribute(type));
|
|
if (ai != _attributes.end()) {
|
|
return (*ai)._attrib;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::output
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
output(ostream &out) const {
|
|
out << "S:";
|
|
if (_attributes.empty()) {
|
|
out << "(empty)";
|
|
|
|
} else {
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
out << "(" << (*ai)._type;
|
|
++ai;
|
|
while (ai != _attributes.end()) {
|
|
out << " " << (*ai)._type;
|
|
++ai;
|
|
}
|
|
out << ")";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::write
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
write(ostream &out, int indent_level) const {
|
|
indent(out, indent_level) << _attributes.size() << " attribs:\n";
|
|
Attributes::const_iterator ai;
|
|
for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) {
|
|
const Attribute &attribute = (*ai);
|
|
attribute._attrib->write(out, indent_level + 2);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::get_max_priority
|
|
// Access: Published, Static
|
|
// Description: Returns the maximum priority number (sometimes called
|
|
// override) that may be set on any node. This may or
|
|
// may not be enforced, but the scene graph code assumes
|
|
// that no priority numbers will be larger than this,
|
|
// and some effects may not work properly if you use a
|
|
// larger number.
|
|
////////////////////////////////////////////////////////////////////
|
|
int RenderState::
|
|
get_max_priority() {
|
|
return 1000000000;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::issue_delta_modify
|
|
// Access: Public
|
|
// Description: This is intended to be called only from
|
|
// GraphicsStateGuardian::modify_state(). It calls
|
|
// issue() for each attribute given in the other state
|
|
// that differs from the current state (which is assumed
|
|
// to represent the GSG's current state). Returns the
|
|
// RenderState representing the newly composed result.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
issue_delta_modify(const RenderState *other,
|
|
GraphicsStateGuardianBase *gsg) const {
|
|
if (other->is_empty()) {
|
|
// If the other state is empty, that's a trivial special case.
|
|
return this;
|
|
}
|
|
|
|
// First, build a new Attributes member that represents the union of
|
|
// this one and that one.
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
Attributes::const_iterator bi = other->_attributes.begin();
|
|
|
|
// Create a new RenderState that will hold the result.
|
|
RenderState *new_state = new RenderState;
|
|
back_insert_iterator<Attributes> result =
|
|
back_inserter(new_state->_attributes);
|
|
|
|
bool any_changed = false;
|
|
|
|
while (ai != _attributes.end() && bi != other->_attributes.end()) {
|
|
if ((*ai) < (*bi)) {
|
|
// Here is an attribute that we have in the original, which is
|
|
// not present in the secondary. Leave it alone.
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
} else if ((*bi) < (*ai)) {
|
|
// Here is a new attribute we have in the secondary, that was
|
|
// not present in the original. Issue the new one, and save it.
|
|
(*bi)._attrib->issue(gsg);
|
|
*result = *bi;
|
|
++bi;
|
|
++result;
|
|
any_changed = true;
|
|
} else {
|
|
// Here is an attribute we have in both. Issue the new one if
|
|
// it's different, and save it.
|
|
if ((*ai)._attrib != (*bi)._attrib) {
|
|
any_changed = true;
|
|
(*bi)._attrib->issue(gsg);
|
|
}
|
|
*result = *bi;
|
|
++ai;
|
|
++bi;
|
|
++result;
|
|
}
|
|
}
|
|
|
|
while (ai != _attributes.end()) {
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
}
|
|
|
|
while (bi != other->_attributes.end()) {
|
|
(*bi)._attrib->issue(gsg);
|
|
*result = *bi;
|
|
++bi;
|
|
++result;
|
|
any_changed = true;
|
|
}
|
|
|
|
if (any_changed) {
|
|
return return_new(new_state);
|
|
} else {
|
|
delete new_state;
|
|
return this;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::issue_delta_set
|
|
// Access: Public
|
|
// Description: This is intended to be called only from
|
|
// GraphicsStateGuardian::set_state(). It calls issue()
|
|
// for each attribute given in the other state that
|
|
// differs from the current state (which is assumed to
|
|
// represent the GSG's current state). Returns the
|
|
// RenderState representing the newly composed result
|
|
// (which will be the same as other).
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
issue_delta_set(const RenderState *other,
|
|
GraphicsStateGuardianBase *gsg) const {
|
|
if (other == this) {
|
|
// If the state doesn't change, that's a trivial special case.
|
|
return other;
|
|
}
|
|
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
Attributes::const_iterator bi = other->_attributes.begin();
|
|
|
|
while (ai != _attributes.end() && bi != other->_attributes.end()) {
|
|
if ((*ai) < (*bi)) {
|
|
// Here is an attribute that we have in the original, which is
|
|
// not present in the secondary. Issue the default state instead.
|
|
(*ai)._attrib->make_default()->issue(gsg);
|
|
++ai;
|
|
|
|
} else if ((*bi) < (*ai)) {
|
|
// Here is a new attribute we have in the secondary, that was
|
|
// not present in the original. Issue the new one.
|
|
(*bi)._attrib->issue(gsg);
|
|
++bi;
|
|
|
|
} else {
|
|
// Here is an attribute we have in both. Issue the new one if
|
|
// it's different.
|
|
if ((*ai)._attrib != (*bi)._attrib) {
|
|
(*bi)._attrib->issue(gsg);
|
|
}
|
|
++ai;
|
|
++bi;
|
|
}
|
|
}
|
|
|
|
while (ai != _attributes.end()) {
|
|
(*ai)._attrib->make_default()->issue(gsg);
|
|
++ai;
|
|
}
|
|
|
|
while (bi != other->_attributes.end()) {
|
|
(*bi)._attrib->issue(gsg);
|
|
++bi;
|
|
}
|
|
|
|
return other;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::bin_removed
|
|
// Access: Public, Static
|
|
// Description: Intended to be called by
|
|
// CullBinManager::remove_bin(), this informs all the
|
|
// RenderStates in the world to remove the indicated
|
|
// bin_index from their cache if it has been cached.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
bin_removed(int bin_index) {
|
|
// Do something here.
|
|
nassertv(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::return_new
|
|
// Access: Private, Static
|
|
// Description: This function is used to share a common RenderState
|
|
// pointer for all equivalent RenderState objects.
|
|
//
|
|
// See the similar logic in RenderAttrib. The idea is
|
|
// to create a new RenderState object and pass it
|
|
// through this function, which will share the pointer
|
|
// with a previously-created RenderState object if it is
|
|
// equivalent.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
return_new(RenderState *state) {
|
|
nassertr(state != (RenderState *)NULL, state);
|
|
|
|
// This should be a newly allocated pointer, not one that was used
|
|
// for anything else.
|
|
nassertr(state->_saved_entry == _states->end(), state);
|
|
|
|
// Save the state in a local PointerTo so that it will be freed at
|
|
// the end of this function if no one else uses it.
|
|
CPT(RenderState) pt_state = state;
|
|
|
|
pair<States::iterator, bool> result = _states->insert(state);
|
|
|
|
if (result.second) {
|
|
// The state was inserted; save the iterator and return the
|
|
// input state.
|
|
state->_saved_entry = result.first;
|
|
return pt_state;
|
|
}
|
|
|
|
// The state was not inserted; there must be an equivalent one
|
|
// already in the set. Return that one.
|
|
return *(result.first);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::do_compose
|
|
// Access: Private
|
|
// Description: The private implemention of compose(); this actually
|
|
// composes two RenderStates, without bothering with the
|
|
// cache.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
do_compose(const RenderState *other) const {
|
|
// First, build a new Attributes member that represents the union of
|
|
// this one and that one.
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
Attributes::const_iterator bi = other->_attributes.begin();
|
|
|
|
// Create a new RenderState that will hold the result.
|
|
RenderState *new_state = new RenderState;
|
|
back_insert_iterator<Attributes> result =
|
|
back_inserter(new_state->_attributes);
|
|
|
|
while (ai != _attributes.end() && bi != other->_attributes.end()) {
|
|
if ((*ai) < (*bi)) {
|
|
// Here is an attribute that we have in the original, which is
|
|
// not present in the secondary.
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
} else if ((*bi) < (*ai)) {
|
|
// Here is a new attribute we have in the secondary, that was
|
|
// not present in the original.
|
|
*result = *bi;
|
|
++bi;
|
|
++result;
|
|
} else {
|
|
// Here is an attribute we have in both. Does one override the
|
|
// other?
|
|
const Attribute &a = (*ai);
|
|
const Attribute &b = (*bi);
|
|
if (a._override < b._override) {
|
|
// B overrides.
|
|
*result = *bi;
|
|
|
|
} else if (b._override < a._override) {
|
|
// A overrides.
|
|
*result = *ai;
|
|
|
|
} else {
|
|
// No, they're equivalent, so compose them.
|
|
*result = Attribute(a._attrib->compose(b._attrib), b._override);
|
|
}
|
|
++ai;
|
|
++bi;
|
|
++result;
|
|
}
|
|
}
|
|
|
|
while (ai != _attributes.end()) {
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
}
|
|
|
|
while (bi != other->_attributes.end()) {
|
|
*result = *bi;
|
|
++bi;
|
|
++result;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::do_invert_compose
|
|
// Access: Private
|
|
// Description: The private implemention of invert_compose().
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderState) RenderState::
|
|
do_invert_compose(const RenderState *other) const {
|
|
Attributes::const_iterator ai = _attributes.begin();
|
|
Attributes::const_iterator bi = other->_attributes.begin();
|
|
|
|
// Create a new RenderState that will hold the result.
|
|
RenderState *new_state = new RenderState;
|
|
back_insert_iterator<Attributes> result =
|
|
back_inserter(new_state->_attributes);
|
|
|
|
while (ai != _attributes.end() && bi != other->_attributes.end()) {
|
|
if ((*ai) < (*bi)) {
|
|
// Here is an attribute that we have in the original, which is
|
|
// not present in the secondary.
|
|
*result = Attribute((*ai)._attrib->invert_compose((*ai)._attrib->make_default()), 0);
|
|
++ai;
|
|
++result;
|
|
} else if ((*bi) < (*ai)) {
|
|
// Here is a new attribute we have in the secondary, that was
|
|
// not present in the original.
|
|
*result = *bi;
|
|
++bi;
|
|
++result;
|
|
} else {
|
|
// Here is an attribute we have in both. In this case, override
|
|
// is meaningless.
|
|
*result = Attribute((*ai)._attrib->invert_compose((*bi)._attrib), (*bi)._override);
|
|
++ai;
|
|
++bi;
|
|
++result;
|
|
}
|
|
}
|
|
|
|
while (ai != _attributes.end()) {
|
|
*result = Attribute((*ai)._attrib->invert_compose((*ai)._attrib->make_default()), 0);
|
|
++ai;
|
|
++result;
|
|
}
|
|
|
|
while (bi != other->_attributes.end()) {
|
|
*result = *bi;
|
|
++bi;
|
|
++result;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::determine_bin_index
|
|
// Access: Private
|
|
// Description: This is the private implementation of
|
|
// get_bin_index() and get_draw_order().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
determine_bin_index() {
|
|
string bin_name;
|
|
_draw_order = 0;
|
|
|
|
const CullBinAttrib *bin_attrib = get_bin();
|
|
if (bin_attrib != (const CullBinAttrib *)NULL) {
|
|
bin_name = bin_attrib->get_bin_name();
|
|
_draw_order = bin_attrib->get_draw_order();
|
|
}
|
|
|
|
if (bin_name.empty()) {
|
|
// No explicit bin is specified; put in the in the default bin,
|
|
// either opaque or transparent, based on the transparency
|
|
// setting.
|
|
bin_name = "opaque";
|
|
const TransparencyAttrib *trans = get_transparency();
|
|
if (trans != (const TransparencyAttrib *)NULL) {
|
|
switch (trans->get_mode()) {
|
|
case TransparencyAttrib::M_alpha:
|
|
case TransparencyAttrib::M_alpha_sorted:
|
|
case TransparencyAttrib::M_dual:
|
|
// These transparency modes require special back-to-front sorting.
|
|
bin_name = "transparent";
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
CullBinManager *bin_manager = CullBinManager::get_global_ptr();
|
|
_bin_index = bin_manager->find_bin(bin_name);
|
|
if (_bin_index == -1) {
|
|
pgraph_cat.warning()
|
|
<< "No bin named " << bin_name << "; creating default bin.\n";
|
|
_bin_index = bin_manager->add_bin(bin_name, CullBinManager::BT_unsorted, 0);
|
|
}
|
|
_flags |= F_checked_bin_index;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::determine_fog
|
|
// Access: Private
|
|
// Description: This is the private implementation of get_fog().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
determine_fog() {
|
|
const RenderAttrib *attrib = get_attrib(FogAttrib::get_class_type());
|
|
_fog = (const FogAttrib *)NULL;
|
|
if (attrib != (const RenderAttrib *)NULL) {
|
|
_fog = DCAST(FogAttrib, attrib);
|
|
}
|
|
_flags |= F_checked_fog;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::determine_bin
|
|
// Access: Private
|
|
// Description: This is the private implementation of get_bin().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
determine_bin() {
|
|
const RenderAttrib *attrib = get_attrib(CullBinAttrib::get_class_type());
|
|
_bin = (const CullBinAttrib *)NULL;
|
|
if (attrib != (const RenderAttrib *)NULL) {
|
|
_bin = DCAST(CullBinAttrib, attrib);
|
|
}
|
|
_flags |= F_checked_bin;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::determine_transparency
|
|
// Access: Private
|
|
// Description: This is the private implementation of get_transparency().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
determine_transparency() {
|
|
const RenderAttrib *attrib =
|
|
get_attrib(TransparencyAttrib::get_class_type());
|
|
_transparency = (const TransparencyAttrib *)NULL;
|
|
if (attrib != (const RenderAttrib *)NULL) {
|
|
_transparency = DCAST(TransparencyAttrib, attrib);
|
|
}
|
|
_flags |= F_checked_transparency;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::register_with_read_factory
|
|
// Access: Public, Static
|
|
// Description: Tells the BamReader how to create objects of type
|
|
// RenderState.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
register_with_read_factory() {
|
|
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
write_datagram(BamWriter *manager, Datagram &dg) {
|
|
TypedWritable::write_datagram(manager, dg);
|
|
|
|
int num_attribs = _attributes.size();
|
|
nassertv(num_attribs == (int)(PN_uint16)num_attribs);
|
|
dg.add_uint16(num_attribs);
|
|
|
|
// **** We should smarten up the writing of the override
|
|
// number--most of the time these will all be zero.
|
|
Attributes::const_iterator ai;
|
|
for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) {
|
|
const Attribute &attribute = (*ai);
|
|
|
|
manager->write_pointer(dg, attribute._attrib);
|
|
dg.add_int32(attribute._override);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::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 RenderState::
|
|
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|
int pi = TypedWritable::complete_pointers(p_list, manager);
|
|
|
|
// Get the attribute pointers.
|
|
Attributes::iterator ai;
|
|
for (ai = _attributes.begin(); ai != _attributes.end(); ++ai) {
|
|
Attribute &attribute = (*ai);
|
|
|
|
attribute._attrib = DCAST(RenderAttrib, p_list[pi++]);
|
|
nassertr(attribute._attrib != (RenderAttrib *)NULL, pi);
|
|
attribute._type = attribute._attrib->get_type();
|
|
}
|
|
|
|
// Now make sure the array is properly sorted. (It won't
|
|
// necessarily preserve its correct sort after being read from bam,
|
|
// because the sort is based on TypeHandle indices and raw pointers,
|
|
// both of which can change from session to session.)
|
|
_attributes.sort();
|
|
|
|
return pi;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::change_this
|
|
// Access: Public, Static
|
|
// Description: Called immediately after complete_pointers(), this
|
|
// gives the object a chance to adjust its own pointer
|
|
// if desired. Most objects don't change pointers after
|
|
// completion, but some need to.
|
|
//
|
|
// Once this function has been called, the old pointer
|
|
// will no longer be accessed.
|
|
////////////////////////////////////////////////////////////////////
|
|
TypedWritable *RenderState::
|
|
change_this(TypedWritable *old_ptr, BamReader *manager) {
|
|
// First, uniquify the pointer.
|
|
RenderState *state = DCAST(RenderState, old_ptr);
|
|
CPT(RenderState) pointer = return_new(state);
|
|
|
|
// But now we have a problem, since we have to hold the reference
|
|
// count and there's no way to return a TypedWritable while still
|
|
// holding the reference count! We work around this by explicitly
|
|
// upping the count, and also setting a finalize() callback to down
|
|
// it later.
|
|
if (pointer == state) {
|
|
pointer->ref();
|
|
manager->register_finalize(state);
|
|
}
|
|
|
|
// We have to cast the pointer back to non-const, because the bam
|
|
// reader expects that.
|
|
return (RenderState *)pointer.p();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::finalize
|
|
// Access: Public, Virtual
|
|
// Description: Called by the BamReader to perform any final actions
|
|
// needed for setting up the object after all objects
|
|
// have been read and all pointers have been completed.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
finalize() {
|
|
// Unref the pointer that we explicitly reffed in make_from_bam().
|
|
unref();
|
|
|
|
// We should never get back to zero after unreffing our own count,
|
|
// because we expect to have been stored in a pointer somewhere. If
|
|
// we do get to zero, it's a memory leak; the way to avoid this is
|
|
// to call unref_delete() above instead of unref(), but this is
|
|
// dangerous to do from within a virtual function.
|
|
nassertv(get_ref_count() != 0);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::make_from_bam
|
|
// Access: Protected, Static
|
|
// Description: This function is called by the BamReader's factory
|
|
// when a new object of type RenderState is encountered
|
|
// in the Bam file. It should create the RenderState
|
|
// and extract its information from the file.
|
|
////////////////////////////////////////////////////////////////////
|
|
TypedWritable *RenderState::
|
|
make_from_bam(const FactoryParams ¶ms) {
|
|
RenderState *state = new RenderState;
|
|
DatagramIterator scan;
|
|
BamReader *manager;
|
|
|
|
parse_params(params, scan, manager);
|
|
state->fillin(scan, manager);
|
|
manager->register_change_this(change_this, state);
|
|
|
|
return state;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderState::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 RenderState.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderState::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
TypedWritable::fillin(scan, manager);
|
|
|
|
int num_attribs = scan.get_uint16();
|
|
|
|
// Push back a NULL pointer for each attribute for now, until we get
|
|
// the actual list of pointers later in complete_pointers().
|
|
_attributes.reserve(num_attribs);
|
|
for (int i = 0; i < num_attribs; i++) {
|
|
manager->read_pointer(scan);
|
|
int override = scan.get_int32();
|
|
_attributes.push_back(Attribute(override));
|
|
}
|
|
}
|