mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
931 lines
33 KiB
C++
931 lines
33 KiB
C++
// Filename: renderEffects.cxx
|
|
// Created by: drose (14Mar02)
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// PANDA 3D SOFTWARE
|
|
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
|
|
//
|
|
// All use of this software is subject to the terms of the Panda 3d
|
|
// Software license. You should have received a copy of this license
|
|
// along with this source code; you will also find a current copy of
|
|
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
|
|
//
|
|
// To contact the maintainers of this program write to
|
|
// panda3d-general@lists.sourceforge.net .
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
#include "renderEffects.h"
|
|
#include "billboardEffect.h"
|
|
#include "decalEffect.h"
|
|
#include "compassEffect.h"
|
|
#include "polylightEffect.h"
|
|
#include "showBoundsEffect.h"
|
|
#include "config_pgraph.h"
|
|
#include "bamReader.h"
|
|
#include "bamWriter.h"
|
|
#include "datagramIterator.h"
|
|
#include "indent.h"
|
|
#include "compareTo.h"
|
|
#include "reMutexHolder.h"
|
|
#include "mutexHolder.h"
|
|
#include "thread.h"
|
|
|
|
ReMutex *RenderEffects::_states_lock = NULL;
|
|
RenderEffects::States *RenderEffects::_states = NULL;
|
|
CPT(RenderEffects) RenderEffects::_empty_state;
|
|
TypeHandle RenderEffects::_type_handle;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::Constructor
|
|
// Access: Protected
|
|
// Description: Actually, this could be a private constructor, since
|
|
// no one inherits from RenderEffects, but gcc gives us a
|
|
// spurious warning if all constructors are private.
|
|
////////////////////////////////////////////////////////////////////
|
|
RenderEffects::
|
|
RenderEffects() : _lock("RenderEffects") {
|
|
if (_states == (States *)NULL) {
|
|
init_states();
|
|
}
|
|
_saved_entry = _states->end();
|
|
_flags = 0;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::Copy Constructor
|
|
// Access: Private
|
|
// Description: RenderEffectss are not meant to be copied.
|
|
////////////////////////////////////////////////////////////////////
|
|
RenderEffects::
|
|
RenderEffects(const RenderEffects &) {
|
|
nassertv(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::Copy Assignment Operator
|
|
// Access: Private
|
|
// Description: RenderEffectss are not meant to be copied.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
operator = (const RenderEffects &) {
|
|
nassertv(false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::Destructor
|
|
// Access: Public, Virtual
|
|
// Description: The destructor is responsible for removing the
|
|
// RenderEffects from the global set if it is there.
|
|
////////////////////////////////////////////////////////////////////
|
|
RenderEffects::
|
|
~RenderEffects() {
|
|
// Remove the deleted RenderEffects object from the global pool.
|
|
ReMutexHolder holder(*_states_lock);
|
|
|
|
// unref() should have cleared this.
|
|
nassertv(_saved_entry == _states->end());
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::safe_to_transform
|
|
// Access: Public
|
|
// Description: Returns true if all of the effects in this set can
|
|
// safely be transformed, and therefore the complete set
|
|
// can be transformed, by calling xform().
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderEffects::
|
|
safe_to_transform() const {
|
|
Effects::const_iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
const Effect &effect = (*ai);
|
|
if (!effect._effect->safe_to_transform()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::prepare_flatten_transform
|
|
// Access: Public, Virtual
|
|
// Description: Preprocesses the accumulated transform that is about
|
|
// to be applied to (or through) this node due to a
|
|
// flatten operation. The returned value will be used
|
|
// instead.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(TransformState) RenderEffects::
|
|
prepare_flatten_transform(const TransformState *net_transform) const {
|
|
CPT(TransformState) result = net_transform;
|
|
Effects::const_iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
const Effect &effect = (*ai);
|
|
result = effect._effect->prepare_flatten_transform(result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::safe_to_combine
|
|
// Access: Public
|
|
// Description: Returns true if all of the effects in this set can
|
|
// safely be shared with a sibling node that has the
|
|
// exact same set of effects, or false if this would be
|
|
// bad for any of the effects.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderEffects::
|
|
safe_to_combine() const {
|
|
Effects::const_iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
const Effect &effect = (*ai);
|
|
if (!effect._effect->safe_to_combine()) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::xform
|
|
// Access: Public, Virtual
|
|
// Description: Returns a new RenderEffects transformed by the
|
|
// indicated matrix.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
xform(const LMatrix4f &mat) const {
|
|
if (is_empty()) {
|
|
return this;
|
|
}
|
|
|
|
RenderEffects *new_state = new RenderEffects;
|
|
back_insert_iterator<Effects> result =
|
|
back_inserter(new_state->_effects);
|
|
|
|
Effects::const_iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
const Effect &effect = (*ai);
|
|
Effect new_effect(effect);
|
|
new_effect._effect = effect._effect->xform(mat);
|
|
*result = new_effect;
|
|
++result;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::operator <
|
|
// Access: Published
|
|
// Description: Provides an arbitrary ordering among all unique
|
|
// RenderEffectss, 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 RenderEffects
|
|
// class because all equivalent RenderEffects objects are
|
|
// guaranteed to share the same pointer; thus, a pointer
|
|
// comparison is always sufficient.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderEffects::
|
|
operator < (const RenderEffects &other) const {
|
|
// We must compare all the properties of the effects, not just
|
|
// the type; thus, we compare them one at a time using compare_to().
|
|
return lexicographical_compare(_effects.begin(), _effects.end(),
|
|
other._effects.begin(), other._effects.end(),
|
|
CompareTo<Effect>());
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::find_effect
|
|
// Access: Published
|
|
// Description: Searches for an effect with the indicated type in
|
|
// the state, and returns its index if it is found, or
|
|
// -1 if it is not.
|
|
////////////////////////////////////////////////////////////////////
|
|
int RenderEffects::
|
|
find_effect(TypeHandle type) const {
|
|
Effects::const_iterator ai = _effects.find(Effect(type));
|
|
if (ai == _effects.end()) {
|
|
return -1;
|
|
}
|
|
return ai - _effects.begin();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::make_empty
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderEffects with no effects set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
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 == (RenderEffects *)NULL) {
|
|
RenderEffects *state = new RenderEffects;
|
|
_empty_state = return_new(state);
|
|
}
|
|
|
|
return _empty_state;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderEffects with one effect set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
make(const RenderEffect *effect) {
|
|
RenderEffects *state = new RenderEffects;
|
|
state->_effects.reserve(1);
|
|
state->_effects.insert(Effect(effect));
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderEffects with two effects set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
make(const RenderEffect *effect1,
|
|
const RenderEffect *effect2) {
|
|
RenderEffects *state = new RenderEffects;
|
|
state->_effects.reserve(2);
|
|
state->_effects.push_back(Effect(effect1));
|
|
state->_effects.push_back(Effect(effect2));
|
|
state->_effects.sort();
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderEffects with three effects set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
make(const RenderEffect *effect1,
|
|
const RenderEffect *effect2,
|
|
const RenderEffect *effect3) {
|
|
RenderEffects *state = new RenderEffects;
|
|
state->_effects.reserve(2);
|
|
state->_effects.push_back(Effect(effect1));
|
|
state->_effects.push_back(Effect(effect2));
|
|
state->_effects.push_back(Effect(effect3));
|
|
state->_effects.sort();
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::make
|
|
// Access: Published, Static
|
|
// Description: Returns a RenderEffects with four effects set.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
make(const RenderEffect *effect1,
|
|
const RenderEffect *effect2,
|
|
const RenderEffect *effect3,
|
|
const RenderEffect *effect4) {
|
|
RenderEffects *state = new RenderEffects;
|
|
state->_effects.reserve(2);
|
|
state->_effects.push_back(Effect(effect1));
|
|
state->_effects.push_back(Effect(effect2));
|
|
state->_effects.push_back(Effect(effect3));
|
|
state->_effects.push_back(Effect(effect4));
|
|
state->_effects.sort();
|
|
return return_new(state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::add_effect
|
|
// Access: Published
|
|
// Description: Returns a new RenderEffects object that represents the
|
|
// same as the source state, with the new RenderEffect
|
|
// added. If there is already a RenderEffect with the
|
|
// same type, it is replaced.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
add_effect(const RenderEffect *effect) const {
|
|
RenderEffects *new_state = new RenderEffects;
|
|
back_insert_iterator<Effects> result =
|
|
back_inserter(new_state->_effects);
|
|
|
|
Effect new_effect(effect);
|
|
Effects::const_iterator ai = _effects.begin();
|
|
|
|
while (ai != _effects.end() && (*ai) < new_effect) {
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
}
|
|
*result = new_effect;
|
|
++result;
|
|
|
|
if (ai != _effects.end() && !(new_effect < (*ai))) {
|
|
// At this point we know:
|
|
// !((*ai) < new_effect) && !(new_effect < (*ai))
|
|
// which means (*ai) == new_effect--so we should leave it out,
|
|
// to avoid duplicating effects in the set.
|
|
++ai;
|
|
}
|
|
|
|
while (ai != _effects.end()) {
|
|
*result = *ai;
|
|
++ai;
|
|
++result;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::remove_effect
|
|
// Access: Published
|
|
// Description: Returns a new RenderEffects object that represents the
|
|
// same as the source state, with the indicated
|
|
// RenderEffect removed.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
remove_effect(TypeHandle type) const {
|
|
RenderEffects *new_state = new RenderEffects;
|
|
back_insert_iterator<Effects> result =
|
|
back_inserter(new_state->_effects);
|
|
|
|
Effects::const_iterator ai = _effects.begin();
|
|
|
|
while (ai != _effects.end()) {
|
|
if ((*ai)._type != type) {
|
|
*result = *ai;
|
|
++result;
|
|
}
|
|
++ai;
|
|
}
|
|
|
|
return return_new(new_state);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::get_effect
|
|
// Access: Published, Virtual
|
|
// Description: Looks for a RenderEffect of the indicated type in the
|
|
// state, and returns it if it is found, or NULL if it
|
|
// is not.
|
|
////////////////////////////////////////////////////////////////////
|
|
const RenderEffect *RenderEffects::
|
|
get_effect(TypeHandle type) const {
|
|
Effects::const_iterator ai;
|
|
ai = _effects.find(Effect(type));
|
|
if (ai != _effects.end()) {
|
|
return (*ai)._effect;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::unref
|
|
// Access: Published
|
|
// Description: This method overrides ReferenceCount::unref() to
|
|
// check whether the remaining reference count is
|
|
// entirely in the cache, and if so, it checks for and
|
|
// breaks a cycle in the cache involving this object.
|
|
// This is designed to prevent leaks from cyclical
|
|
// references within the cache.
|
|
//
|
|
// Note that this is not a virtual method, and cannot be
|
|
// because ReferenceCount itself declares no virtual
|
|
// methods (it avoids the overhead of a virtual function
|
|
// pointer). But this doesn't matter, because
|
|
// PT(TransformState) is a template class, and will call
|
|
// the appropriate method even though it is non-virtual.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderEffects::
|
|
unref() const {
|
|
ReMutexHolder holder(*_states_lock);
|
|
|
|
if (ReferenceCount::unref()) {
|
|
// The reference count is still nonzero.
|
|
return true;
|
|
}
|
|
|
|
// The reference count has just reached zero. Make sure the object
|
|
// is removed from the global object pool, before anyone else finds
|
|
// it and tries to ref it.
|
|
((RenderEffects *)this)->release_new();
|
|
|
|
return false;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::output
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
output(ostream &out) const {
|
|
out << "E:";
|
|
if (_effects.empty()) {
|
|
out << "(empty)";
|
|
|
|
} else {
|
|
Effects::const_iterator ai = _effects.begin();
|
|
out << "(" << (*ai)._type;
|
|
++ai;
|
|
while (ai != _effects.end()) {
|
|
out << " " << (*ai)._type;
|
|
++ai;
|
|
}
|
|
out << ")";
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::write
|
|
// Access: Published, Virtual
|
|
// Description:
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
write(ostream &out, int indent_level) const {
|
|
indent(out, indent_level) << _effects.size() << " effects:\n";
|
|
Effects::const_iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
const Effect &effect = (*ai);
|
|
effect._effect->write(out, indent_level + 2);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::get_num_states
|
|
// Access: Published, Static
|
|
// Description: Returns the total number of unique RenderEffects
|
|
// objects allocated in the world. This will go up and
|
|
// down during normal operations.
|
|
////////////////////////////////////////////////////////////////////
|
|
int RenderEffects::
|
|
get_num_states() {
|
|
if (_states == (States *)NULL) {
|
|
return 0;
|
|
}
|
|
ReMutexHolder holder(*_states_lock);
|
|
return _states->size();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::list_states
|
|
// Access: Published, Static
|
|
// Description: Lists all of the RenderEffectss in the cache to the
|
|
// output stream, one per line. This can be quite a lot
|
|
// of output if the cache is large, so be prepared.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
list_states(ostream &out) {
|
|
out << _states->size() << " states:\n";
|
|
States::const_iterator si;
|
|
for (si = _states->begin(); si != _states->end(); ++si) {
|
|
const RenderEffects *state = (*si);
|
|
state->write(out, 2);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::validate_states
|
|
// Access: Published, Static
|
|
// Description: Ensures that the cache is still stored in sorted
|
|
// order. Returns true if so, false if there is a
|
|
// problem (which implies someone has modified one of
|
|
// the supposedly-const RenderEffects objects).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderEffects::
|
|
validate_states() {
|
|
if (_states->empty()) {
|
|
return true;
|
|
}
|
|
ReMutexHolder holder(*_states_lock);
|
|
|
|
States::const_iterator si = _states->begin();
|
|
States::const_iterator snext = si;
|
|
++snext;
|
|
while (snext != _states->end()) {
|
|
if (!(*(*si) < *(*snext))) {
|
|
pgraph_cat.error()
|
|
<< "RenderEffectss out of order!\n";
|
|
(*si)->write(pgraph_cat.error(false), 2);
|
|
(*snext)->write(pgraph_cat.error(false), 2);
|
|
return false;
|
|
}
|
|
si = snext;
|
|
++snext;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::cull_callback
|
|
// Access: Public
|
|
// Description: Calls cull_callback() on all effects. You may check
|
|
// has_cull_callback() first to see if any effects
|
|
// define this method to do anything useful.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
cull_callback(CullTraverser *trav, CullTraverserData &data,
|
|
CPT(TransformState) &node_transform,
|
|
CPT(RenderState) &node_state) const {
|
|
Effects::const_iterator ei;
|
|
for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
|
|
(*ei)._effect->cull_callback(trav, data, node_transform, node_state);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::adjust_transform
|
|
// Access: Public
|
|
// Description: Calls adjust_transform() on all effects. You may check
|
|
// has_adjust_transform() first to see if any effects
|
|
// define this method to do anything useful.
|
|
//
|
|
// The order in which the individual effects are applied
|
|
// is not defined, so if more than one effect applies a
|
|
// change to the transform on any particular node, you
|
|
// might get indeterminate results.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
adjust_transform(CPT(TransformState) &net_transform,
|
|
CPT(TransformState) &node_transform,
|
|
PandaNode *node) const {
|
|
Effects::const_iterator ei;
|
|
for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
|
|
(*ei)._effect->adjust_transform(net_transform, node_transform, node);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::init_states
|
|
// Access: Public, Static
|
|
// Description: 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 RenderEffects object at static init time;
|
|
// it also seems to cause problems when the Panda shared
|
|
// library is unloaded at application exit time.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
init_states() {
|
|
_states = new States;
|
|
|
|
// TODO: we should have a global Panda mutex to allow us to safely
|
|
// create _states_lock without a startup race condition. For the
|
|
// meantime, this is OK because we guarantee that this method is
|
|
// called at static init time, presumably when there is still only
|
|
// one thread in the world.
|
|
_states_lock = new ReMutex("RenderEffects::_states_lock");
|
|
nassertv(Thread::get_current_thread() == Thread::get_main_thread());
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::return_new
|
|
// Access: Private, Static
|
|
// Description: This function is used to share a common RenderEffects
|
|
// pointer for all equivalent RenderEffects objects.
|
|
//
|
|
// See the similar logic in RenderEffect. The idea is
|
|
// to create a new RenderEffects object and pass it
|
|
// through this function, which will share the pointer
|
|
// with a previously-created RenderEffects object if it is
|
|
// equivalent.
|
|
////////////////////////////////////////////////////////////////////
|
|
CPT(RenderEffects) RenderEffects::
|
|
return_new(RenderEffects *state) {
|
|
nassertr(state != (RenderEffects *)NULL, state);
|
|
|
|
#ifndef NDEBUG
|
|
if (!state_cache) {
|
|
return state;
|
|
}
|
|
#endif
|
|
|
|
#ifndef NDEBUG
|
|
if (paranoid_const) {
|
|
nassertr(validate_states(), state);
|
|
}
|
|
#endif
|
|
|
|
ReMutexHolder holder(*_states_lock);
|
|
|
|
// 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(RenderEffects) 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: RenderEffects::release_new
|
|
// Access: Private
|
|
// Description: This inverse of return_new, this releases this object
|
|
// from the global RenderEffects table.
|
|
//
|
|
// You must already be holding _states_lock before you
|
|
// call this method.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
release_new() {
|
|
nassertv(_states_lock->debug_is_locked());
|
|
|
|
if (_saved_entry != _states->end()) {
|
|
nassertv(_states->find(this) == _saved_entry);
|
|
_states->erase(_saved_entry);
|
|
_saved_entry = _states->end();
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::determine_decal
|
|
// Access: Private
|
|
// Description: This is the private implementation of has_decal().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
determine_decal() {
|
|
MutexHolder holder(_lock);
|
|
if ((_flags & F_checked_decal) != 0) {
|
|
// Someone else checked it first.
|
|
return;
|
|
}
|
|
|
|
const RenderEffect *effect = get_effect(DecalEffect::get_class_type());
|
|
if (effect != (const RenderEffect *)NULL) {
|
|
_flags |= F_has_decal;
|
|
}
|
|
_flags |= F_checked_decal;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::determine_show_bounds
|
|
// Access: Private
|
|
// Description: This is the private implementation of has_show_bounds().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
determine_show_bounds() {
|
|
MutexHolder holder(_lock);
|
|
if ((_flags & F_checked_show_bounds) != 0) {
|
|
// Someone else checked it first.
|
|
return;
|
|
}
|
|
|
|
const RenderEffect *effect = get_effect(ShowBoundsEffect::get_class_type());
|
|
if (effect != (const RenderEffect *)NULL) {
|
|
_flags |= F_has_show_bounds;
|
|
const ShowBoundsEffect *sba = DCAST(ShowBoundsEffect, effect);
|
|
if (sba->get_tight()) {
|
|
_flags |= F_has_show_tight_bounds;
|
|
}
|
|
}
|
|
_flags |= F_checked_show_bounds;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::determine_cull_callback
|
|
// Access: Private
|
|
// Description: This is the private implementation of has_cull_callback().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
determine_cull_callback() {
|
|
MutexHolder holder(_lock);
|
|
if ((_flags & F_checked_cull_callback) != 0) {
|
|
// Someone else checked it first.
|
|
return;
|
|
}
|
|
|
|
_flags |= F_checked_cull_callback;
|
|
|
|
Effects::const_iterator ei;
|
|
for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
|
|
if ((*ei)._effect->has_cull_callback()) {
|
|
_flags |= F_has_cull_callback;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::determine_adjust_transform
|
|
// Access: Private
|
|
// Description: This is the private implementation of has_adjust_transform().
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
determine_adjust_transform() {
|
|
MutexHolder holder(_lock);
|
|
if ((_flags & F_checked_adjust_transform) != 0) {
|
|
// Someone else checked it first.
|
|
return;
|
|
}
|
|
|
|
_flags |= F_checked_adjust_transform;
|
|
|
|
Effects::const_iterator ei;
|
|
for (ei = _effects.begin(); ei != _effects.end(); ++ei) {
|
|
if ((*ei)._effect->has_adjust_transform()) {
|
|
_flags |= F_has_adjust_transform;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::register_with_read_factory
|
|
// Access: Public, Static
|
|
// Description: Tells the BamReader how to create objects of type
|
|
// RenderEffects.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
register_with_read_factory() {
|
|
BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::write_datagram
|
|
// Access: Public, Virtual
|
|
// Description: Writes the contents of this object to the datagram
|
|
// for shipping out to a Bam file.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
write_datagram(BamWriter *manager, Datagram &dg) {
|
|
TypedWritable::write_datagram(manager, dg);
|
|
|
|
int num_effects = _effects.size();
|
|
nassertv(num_effects == (int)(PN_uint16)num_effects);
|
|
dg.add_uint16(num_effects);
|
|
|
|
Effects::const_iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
const Effect &effect = (*ai);
|
|
|
|
manager->write_pointer(dg, effect._effect);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::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 RenderEffects::
|
|
complete_pointers(TypedWritable **p_list, BamReader *manager) {
|
|
int pi = TypedWritable::complete_pointers(p_list, manager);
|
|
|
|
// Get the effect pointers.
|
|
Effects::iterator ai;
|
|
for (ai = _effects.begin(); ai != _effects.end(); ++ai) {
|
|
Effect &effect = (*ai);
|
|
|
|
effect._effect = DCAST(RenderEffect, p_list[pi++]);
|
|
nassertr(effect._effect != (RenderEffect *)NULL, pi);
|
|
effect._type = effect._effect->get_type();
|
|
}
|
|
ReMutexHolder holder(*_states_lock);
|
|
|
|
// 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, which can change
|
|
// from session to session.)
|
|
_effects.sort();
|
|
|
|
return pi;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::require_fully_complete
|
|
// Access: Public, Virtual
|
|
// Description: Some objects require all of their nested pointers to
|
|
// have been completed before the objects themselves can
|
|
// be completed. If this is the case, override this
|
|
// method to return true, and be careful with circular
|
|
// references (which would make the object unreadable
|
|
// from a bam file).
|
|
////////////////////////////////////////////////////////////////////
|
|
bool RenderEffects::
|
|
require_fully_complete() const {
|
|
// Since we sort _states based on each RenderEffects' operator <
|
|
// method, which in turn compares based on each nested RenderEffect
|
|
// object's compare_to() method, some of which depend on the
|
|
// RenderEffect's pointers having already been completed
|
|
// (e.g. CharacterJointEffect), we therefore require each of out our
|
|
// nested RenderEffect objects to have been completed before we can
|
|
// be completed.
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::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 *RenderEffects::
|
|
change_this(TypedWritable *old_ptr, BamReader *manager) {
|
|
// First, uniquify the pointer.
|
|
RenderEffects *state = DCAST(RenderEffects, old_ptr);
|
|
CPT(RenderEffects) 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 (RenderEffects *)pointer.p();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::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 RenderEffects::
|
|
finalize(BamReader *) {
|
|
// Unref the pointer that we explicitly reffed in change_this().
|
|
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: RenderEffects::make_from_bam
|
|
// Access: Protected, Static
|
|
// Description: This function is called by the BamReader's factory
|
|
// when a new object of type RenderEffects is encountered
|
|
// in the Bam file. It should create the RenderEffects
|
|
// and extract its information from the file.
|
|
////////////////////////////////////////////////////////////////////
|
|
TypedWritable *RenderEffects::
|
|
make_from_bam(const FactoryParams ¶ms) {
|
|
RenderEffects *state = new RenderEffects;
|
|
DatagramIterator scan;
|
|
BamReader *manager;
|
|
|
|
parse_params(params, scan, manager);
|
|
state->fillin(scan, manager);
|
|
manager->register_change_this(change_this, state);
|
|
|
|
return state;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: RenderEffects::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 RenderEffects.
|
|
////////////////////////////////////////////////////////////////////
|
|
void RenderEffects::
|
|
fillin(DatagramIterator &scan, BamReader *manager) {
|
|
TypedWritable::fillin(scan, manager);
|
|
|
|
int num_effects = scan.get_uint16();
|
|
|
|
// Push back a NULL pointer for each effect for now, until we get
|
|
// the actual list of pointers later in complete_pointers().
|
|
_effects.reserve(num_effects);
|
|
for (int i = 0; i < num_effects; i++) {
|
|
manager->read_pointer(scan);
|
|
_effects.push_back(Effect());
|
|
}
|
|
}
|