pgraph flatten

This commit is contained in:
David Rose 2002-03-16 00:24:10 +00:00
parent a56e32de31
commit 9c3a3c9c22
31 changed files with 801 additions and 280 deletions

View File

@ -118,6 +118,7 @@ pgraphClassRenameDictionary = {
'PGEntry' : 'SpPGEntry',
'PGWaitBar' : 'SpPGWaitBar',
'PartBundleNode' : 'SpPartBundleNode',
'SceneGraphReducer' : 'SpSceneGraphReducer',
'SequenceNode' : 'SpSequenceNode',
'TextNode' : 'SpTextNode',
'Trackball' : 'SpTrackball',
@ -150,6 +151,7 @@ pgraphClassRenameDictionary = {
'QpPGEntry' : 'PGEntry',
'QpPGWaitBar' : 'PGWaitBar',
'QpPartBundleNode' : 'PartBundleNode',
'QpSceneGraphReducer' : 'SceneGraphReducer',
'QpSequenceNode' : 'SequenceNode',
'QpTextNode' : 'TextNode',
'QpTrackball' : 'Trackball',

View File

@ -216,6 +216,7 @@ class ShowBase:
rendering 3-d geometry.
"""
self.render = NodePath('render')
self.render.setTwoSided(0)
def setupRender2d(self):
"""setupRender2d(self)

View File

@ -1008,7 +1008,6 @@ setup_bucket(BuilderBucket &bucket, PandaNode *parent,
break;
default:
bucket.add_attrib(TransparencyAttrib::make(TransparencyAttrib::M_none));
break;
}
@ -1050,9 +1049,6 @@ setup_bucket(BuilderBucket &bucket, PandaNode *parent,
// The primitive is marked with backface culling disabled--we want
// to see both sides.
bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_none));
} else {
bucket.add_attrib(CullFaceAttrib::make(CullFaceAttrib::M_cull_clockwise));
}
}

View File

@ -19,11 +19,8 @@
#include "qpload_egg_file.h"
#include "qpeggLoader.h"
#include "config_egg2pg.h"
/*
#include "sceneGraphReducer.h"
#include "qpsceneGraphReducer.h"
#include "renderRelation.h"
*/
static PT(PandaNode)
load_from_loader(qpEggLoader &loader) {
@ -37,13 +34,11 @@ load_from_loader(qpEggLoader &loader) {
return NULL;
}
/*
if (loader._root != (NamedNode *)NULL && egg_flatten) {
SceneGraphReducer gr(RenderRelation::get_class_type());
if (loader._root != (PandaNode *)NULL && egg_flatten) {
qpSceneGraphReducer gr;
int num_reduced = gr.flatten(loader._root, egg_flatten_siblings);
egg2pg_cat.info() << "Flattened " << num_reduced << " arcs.\n";
egg2pg_cat.info() << "Flattened " << num_reduced << " nodes.\n";
}
*/
return loader._root;
}

View File

@ -35,6 +35,7 @@
qpfog.I qpfog.h \
fogAttrib.I fogAttrib.h \
qpgeomNode.I qpgeomNode.h \
qpgeomTransformer.I qpgeomTransformer.h \
qplensNode.I qplensNode.h \
qplodNode.I qplodNode.h \
materialAttrib.I materialAttrib.h \
@ -47,6 +48,7 @@
renderEffects.I renderEffects.h \
renderModeAttrib.I renderModeAttrib.h \
renderState.I renderState.h \
qpsceneGraphReducer.I qpsceneGraphReducer.h \
selectiveChildNode.I selectiveChildNode.h \
qpsequenceNode.I qpsequenceNode.h \
texMatrixAttrib.I texMatrixAttrib.h \
@ -86,6 +88,7 @@
qpfog.cxx \
fogAttrib.cxx \
qpgeomNode.cxx \
qpgeomTransformer.cxx \
qplensNode.cxx \
qplodNode.cxx \
materialAttrib.cxx \
@ -98,6 +101,7 @@
renderEffects.cxx \
renderModeAttrib.cxx \
renderState.cxx \
qpsceneGraphReducer.cxx \
selectiveChildNode.cxx \
qpsequenceNode.cxx \
texMatrixAttrib.cxx \
@ -139,6 +143,7 @@
qpfog.I qpfog.h \
fogAttrib.I fogAttrib.h \
qpgeomNode.I qpgeomNode.h \
qpgeomTransformer.I qpgeomTransformer.h \
qplensNode.I qplensNode.h \
qplodNode.I qplodNode.h \
materialAttrib.I materialAttrib.h \
@ -151,6 +156,7 @@
renderEffects.I renderEffects.h \
renderModeAttrib.I renderModeAttrib.h \
renderState.I renderState.h \
qpsceneGraphReducer.I qpsceneGraphReducer.h \
selectiveChildNode.I selectiveChildNode.h \
qpsequenceNode.I qpsequenceNode.h \
texMatrixAttrib.I texMatrixAttrib.h \

View File

@ -47,15 +47,15 @@ make(const LVector3f &up_vector, bool eye_relative,
}
////////////////////////////////////////////////////////////////////
// Function: BillboardEffect::safe_to_combine
// Function: BillboardEffect::safe_to_transform
// Access: Public, Virtual
// Description: Returns true if this kind of effect can safely be
// combined with sibling nodes that share the exact same
// effect, or false if this is not a good idea.
// Description: Returns true if it is generally safe to transform
// this particular kind of RenderEffect by calling the
// xform() method, false otherwise.
////////////////////////////////////////////////////////////////////
bool BillboardEffect::
safe_to_combine() const {
return true;
safe_to_transform() const {
return false;
}
////////////////////////////////////////////////////////////////////

View File

@ -55,7 +55,7 @@ PUBLISHED:
INLINE const LPoint3f &get_look_at_point() const;
public:
virtual bool safe_to_combine() const;
virtual bool safe_to_transform() const;
virtual void output(ostream &out) const;
CPT(TransformState) do_billboard(const TransformState *net_transform,

View File

@ -70,6 +70,10 @@ ConfigureFn(config_pgraph) {
// helps make culling errors obvious.
const bool qpfake_view_frustum_cull = config_pgraph.GetBool("fake-view-frustum-cull", false);
// Set this true to make ambiguous path warning messages generate an
// assertion failure instead of just a warning (which can then be
// trapped with assert-abort).
const bool unambiguous_graph = config_pgraph.GetBool("unambiguous-graph", false);
////////////////////////////////////////////////////////////////////
// Function: init_libpgraph

View File

@ -27,6 +27,7 @@ ConfigureDecl(config_pgraph, EXPCL_PANDA, EXPTP_PANDA);
NotifyCategoryDecl(pgraph, EXPCL_PANDA, EXPTP_PANDA);
extern const bool qpfake_view_frustum_cull;
extern const bool unambiguous_graph;
extern EXPCL_PANDA void init_libpgraph();

View File

@ -38,7 +38,7 @@ PUBLISHED:
};
private:
INLINE CullFaceAttrib(Mode mode = M_cull_none);
INLINE CullFaceAttrib(Mode mode = M_cull_clockwise);
PUBLISHED:
static CPT(RenderAttrib) make(Mode mode);

View File

@ -35,6 +35,18 @@ make() {
return return_new(effect);
}
////////////////////////////////////////////////////////////////////
// Function: DecalEffect::safe_to_combine
// Access: Public, Virtual
// Description: Returns true if this kind of effect can safely be
// combined with sibling nodes that share the exact same
// effect, or false if this is not a good idea.
////////////////////////////////////////////////////////////////////
bool DecalEffect::
safe_to_combine() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: DecalEffect::compare_to_impl
// Access: Protected, Virtual

View File

@ -37,6 +37,7 @@ PUBLISHED:
static CPT(RenderEffect) make();
protected:
virtual bool safe_to_combine() const;
virtual int compare_to_impl(const RenderEffect *other) const;
virtual RenderEffect *make_default_impl() const;

View File

@ -52,6 +52,16 @@ get_child() const {
return _child;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::DownConnection::set_child
// Access: Public
// Description: This is only called by PandaNode::replace_child().
////////////////////////////////////////////////////////////////////
INLINE void PandaNode::DownConnection::
set_child(PandaNode *child) {
_child = child;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::DownConnection::get_sort
// Access: Public
@ -151,6 +161,16 @@ Children(const PandaNode::Children &copy) :
{
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::Children::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void PandaNode::Children::
operator = (const PandaNode::Children &copy) {
_cdata = copy._cdata;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::Children::get_num_children
// Access: Public
@ -172,6 +192,48 @@ get_child(int n) const {
return _cdata->_down[n].get_child();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::ChildrenCopy::Copy Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE PandaNode::ChildrenCopy::
ChildrenCopy(const PandaNode::ChildrenCopy &copy) :
_list(copy._list)
{
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::ChildrenCopy::Copy Assignment Operator
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
INLINE void PandaNode::ChildrenCopy::
operator = (const PandaNode::ChildrenCopy &copy) {
_list = copy._list;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::ChildrenCopy::get_num_children
// Access: Public
// Description: Returns the number of children of the node.
////////////////////////////////////////////////////////////////////
INLINE int PandaNode::ChildrenCopy::
get_num_children() const {
return _list.size();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::ChildrenCopy::get_child
// Access: Public
// Description: Returns the nth child of the node.
////////////////////////////////////////////////////////////////////
INLINE PandaNode *PandaNode::ChildrenCopy::
get_child(int n) const {
nassertr(n >= 0 && n < (int)_list.size(), NULL);
return _list[n];
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_num_parents
// Access: Published
@ -715,3 +777,19 @@ get_children() const {
return Children(cdata);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::get_children_copy
// Access: Public
// Description: Returns an object that can be used to walk through
// the list of children of the node. Unlike
// get_children(), this function actually returns an
// object that protects you from self-modifying loops,
// because it makes and returns a copy of the complete
// children list.
////////////////////////////////////////////////////////////////////
INLINE PandaNode::ChildrenCopy PandaNode::
get_children_copy() const {
CDReader cdata(_cycler);
return ChildrenCopy(cdata);
}

View File

@ -27,6 +27,79 @@
TypeHandle PandaNode::_type_handle;
//
// We go through some considerable effort in this class to ensure that
// NodePaths are kept consistent as we attach and detach nodes. We
// must enforce the following rules:
//
// 1) Each NodePath (i.e. chain of NodePathComponents) represents a
// complete unbroken chain from a PandaNode to the root of the graph.
//
// 2) Each NodePathComponent chain is unique. There are no two
// different NodePathComponents that reference the same path to the
// root.
//
// 3) If a PandaNode with no parents is attached to a new parent, all
// NodePaths that previously indicated this node as the root of graph
// must now be updated to include the complete chain to the new root.
//
// 4) If a PandaNode with other parents is attached to a new parent,
// any previously existing NodePaths are not affected.
//
// 5) If a PandaNode is disconnected from its parent, and it has no
// other parents, all NodePaths that previously passed through this
// node to the old parent must now be updated to indicate this node is
// now the root.
//
// 6) If a PandaNode is disconnected from its parent, and it has at
// least one other parent, all NodePaths that previously passed
// through this node to the old parent must now be updated to pass
// through one of the other parents instead.
//
// Rules (5) and (6) can especially complicate things because they
// introduce the possibility that two formerly distinct NodePaths are
// now equivalent, which violates rule (2). For example, if A is the
// top of the graph with children B and C, and D is instanced to both
// B and C, and E is a child of D, there might be two different
// NodePaths to D: A/B/D/E and A/C/D/E. If you then break the
// connection between D and E, both NodePaths must now become just the
// singleton E.
//
// Unfortunately, we cannot simply remove one of the extra
// NodePathComponents, because there may be any number of NodePath
// objects that reference them. Instead, we define the concept of
// "collapsed" NodePathComponents, which means one NodePathComponent
// can be "collapsed" into a different one so that any attempt to
// reference the first actually retrieves the second, rather like a
// symbolic link in the file system. When the NodePath traverses its
// component chain, it will pass right over the collapsed component in
// favor of the one it has been collapsed into.
//
//
// There are two different interfaces here for making and breaking
// parent-child connections: the fundamental PandaNode interface, via
// add_child() and remove_child() (and related functions), and the
// NodePath support interface, via attach(), detach(), and reparent().
// They both do essentially the same thing, but with slightly
// different inputs. Both are responsible for keeping all NodePaths
// up-to-date according to the above rules.
//
// The NodePath support interface functions are strictly called from
// within the NodePath class, and are used to implement
// NodePath::reparent_to() and NodePath::remove_node(), etc. The
// fundamental interface, on the other hand, is designed to be called
// directly by the user.
//
// The fundamental interface has a slightly lower overhead because it
// does not need to create a NodePathComponent chain where one does
// not already exist; however, the NodePath support interface is more
// useful when the NodePath already does exist, because it ensures
// that the particular NodePath calling it is kept appropriately
// up-to-date.
//
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::make_copy
@ -260,6 +333,20 @@ fillin_down_list(PandaNode::Down &down_list,
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::ChildrenCopy::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
PandaNode::ChildrenCopy::
ChildrenCopy(const PandaNode::CDReader &cdata) {
Children cr(cdata);
int num_children = cr.get_num_children();
for (int i = 0; i < num_children; i++) {
_list.push_back(cr.get_child(i));
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::Constructor
// Access: Published
@ -396,6 +483,18 @@ safe_to_combine() const {
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::preserve_name
// Access: Public, Virtual
// Description: Returns true if the node's name has extrinsic meaning
// and must be preserved across a flatten operation,
// false otherwise.
////////////////////////////////////////////////////////////////////
bool PandaNode::
preserve_name() const {
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::xform
// Access: Public, Virtual
@ -577,20 +676,8 @@ add_child(PandaNode *child_node, int sort) {
cdata->_down.insert(DownConnection(child_node, sort));
cdata_child->_up.insert(UpConnection(this));
// We also have to adjust any qpNodePathComponents the child might
// have that reference the child as a top node. Any other
// components we can leave alone, because we are making a new
// instance of the child.
Paths::iterator pi;
for (pi = cdata_child->_paths.begin();
pi != cdata_child->_paths.end();
++pi) {
if ((*pi)->is_top_node()) {
(*pi)->set_next(get_generic_component());
}
}
child_node->fix_path_lengths(cdata_child);
new_connection(this, child_node);
// Mark the bounding volumes stale.
force_bound_stale();
@ -616,35 +703,8 @@ remove_child(int n) {
cdata->_down.erase(cdata->_down.begin() + n);
int num_erased = cdata_child->_up.erase(UpConnection(this));
nassertv(num_erased == 1);
// Now sever any qpNodePathComponents on the child that reference
// this node. If we have multiple of these, we have to collapse
// them together.
qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
Paths::iterator pi;
pi = cdata_child->_paths.begin();
while (pi != cdata_child->_paths.end()) {
Paths::iterator pnext = pi;
++pnext;
if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) {
if (collapsed == (qpNodePathComponent *)NULL) {
(*pi)->set_top_node();
collapsed = (*pi);
} else {
// This is a different component that used to reference a
// different instance, but now it's all just the same topnode.
// We have to collapse this and the previous one together.
// However, there might be some qpNodePaths out there that
// still keep a pointer to this one, so we can't remove it
// altogether.
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
}
pi = pnext;
}
child_node->fix_path_lengths(cdata_child);
sever_connection(this, child_node);
// Mark the bounding volumes stale.
force_bound_stale();
@ -659,68 +719,115 @@ remove_child(int n) {
// Access: Published
// Description: Removes the indicated child from the node. Returns
// true if the child was removed, false if it was not
// already a child of the node.
// already a child of the node. This will also
// successfully remove the child if it had been stashed.
////////////////////////////////////////////////////////////////////
bool PandaNode::
remove_child(PandaNode *child_node) {
// Ensure the child_node is not deleted while we do this.
PT(PandaNode) keep_child = child_node;
CDWriter cdata(_cycler);
CDWriter cdata_child(child_node->_cycler);
// First, look for and remove this node from the child's parent
// list.
int num_erased = cdata_child->_up.erase(UpConnection(this));
if (num_erased == 0) {
// No such node; it wasn't our child to begin with.
// First, look for the parent in the child's up list, to ensure the
// child is known.
int parent_index = child_node->find_parent(this);
if (parent_index < 0) {
// Nope, no relation.
return false;
}
// Now sever any qpNodePathComponents on the child that reference
// this node. If we have multiple of these, we have to collapse
// them together (see above).
qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
Paths::iterator pi;
pi = cdata_child->_paths.begin();
while (pi != cdata_child->_paths.end()) {
Paths::iterator pnext = pi;
++pnext;
if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) {
if (collapsed == (qpNodePathComponent *)NULL) {
(*pi)->set_top_node();
collapsed = (*pi);
} else {
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
}
pi = pnext;
int child_index = find_child(child_node);
if (child_index >= 0) {
// The child exists; remove it.
remove_child(child_index);
return true;
}
int stashed_index = find_stashed(child_node);
if (stashed_index >= 0) {
// The child has been stashed; remove it.
remove_stashed(stashed_index);
return true;
}
// Never heard of this child. This shouldn't be possible, because
// the parent was in the child's up list, above. Must be some
// internal error.
nassertr(false, false);
return false;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::replace_child
// Access: Published
// Description: Searches for the orig_child node in the node's list
// of children, and replaces it with the new_child
// instead. Returns true if the replacement is made, or
// false if the node is not a child.
////////////////////////////////////////////////////////////////////
bool PandaNode::
replace_child(PandaNode *orig_child, PandaNode *new_child) {
// First, look for the parent in the child's up list, to ensure the
// child is known.
int parent_index = orig_child->find_parent(this);
if (parent_index < 0) {
// Nope, no relation.
return false;
}
if (orig_child == new_child) {
// Trivial no-op.
return true;
}
child_node->fix_path_lengths(cdata_child);
// Now, look for and remove the child node from our down list.
Down::iterator di;
bool found = false;
for (di = cdata->_down.begin(); di != cdata->_down.end() && !found; ++di) {
if ((*di).get_child() == child_node) {
cdata->_down.erase(di);
found = true;
// Don't let orig_child be destructed yet.
PT(PandaNode) keep_orig_child = orig_child;
int child_index = find_child(orig_child);
if (child_index >= 0) {
// The child exists; replace it.
CDWriter cdata(_cycler);
DownConnection &down = cdata->_down[child_index];
nassertr(down.get_child() == orig_child, false);
down.set_child(new_child);
} else {
int stashed_index = find_stashed(orig_child);
if (stashed_index >= 0) {
// The child has been stashed; remove it.
CDWriter cdata(_cycler);
DownConnection &down = cdata->_stashed[stashed_index];
nassertr(down.get_child() == orig_child, false);
down.set_child(new_child);
} else {
// Never heard of this child. This shouldn't be possible, because
// the parent was in the child's up list, above. Must be some
// internal error.
nassertr(false, false);
return false;
}
}
// Now adjust the bookkeeping on both children.
CDWriter cdata_orig_child(orig_child->_cycler);
CDWriter cdata_new_child(new_child->_cycler);
nassertr(found, false);
cdata_new_child->_up.insert(UpConnection(this));
int num_erased = cdata_orig_child->_up.erase(UpConnection(this));
nassertr(num_erased == 1, false);
sever_connection(this, orig_child);
orig_child->parents_changed();
new_connection(this, new_child);
new_child->parents_changed();
// Mark the bounding volumes stale.
force_bound_stale();
// Call callback hooks.
children_changed();
child_node->parents_changed();
return true;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::stash_child
// Access: Published
@ -745,20 +852,8 @@ stash_child(int child_index) {
cdata->_stashed.insert(DownConnection(child_node, sort));
cdata_child->_up.insert(UpConnection(this));
// We also have to adjust any qpNodePathComponents the child might
// have that reference the child as a top node. Any other
// components we can leave alone, because we are making a new
// instance of the child.
Paths::iterator pi;
for (pi = cdata_child->_paths.begin();
pi != cdata_child->_paths.end();
++pi) {
if ((*pi)->is_top_node()) {
(*pi)->set_next(get_generic_component());
}
}
child_node->fix_path_lengths(cdata_child);
new_connection(this, child_node);
// Mark the bounding volumes stale.
force_bound_stale();
@ -792,20 +887,8 @@ unstash_child(int stashed_index) {
cdata->_down.insert(DownConnection(child_node, sort));
cdata_child->_up.insert(UpConnection(this));
// We also have to adjust any qpNodePathComponents the child might
// have that reference the child as a top node. Any other
// components we can leave alone, because we are making a new
// instance of the child.
Paths::iterator pi;
for (pi = cdata_child->_paths.begin();
pi != cdata_child->_paths.end();
++pi) {
if ((*pi)->is_top_node()) {
(*pi)->set_next(get_generic_component());
}
}
child_node->fix_path_lengths(cdata_child);
new_connection(this, child_node);
// Mark the bounding volumes stale.
force_bound_stale();
@ -837,12 +920,40 @@ find_stashed(PandaNode *node) const {
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::add_stashed
// Access: Published
// Description: Adds a new child to the node, directly as a stashed
// child. The child is not added in the normal sense,
// but will be revealed if unstash_child() is called on
// it later.
//
// If the same child is added to a node more than once,
// the previous instance is first removed.
////////////////////////////////////////////////////////////////////
void PandaNode::
add_stashed(PandaNode *child_node, int sort) {
// Ensure the child_node is not deleted while we do this.
PT(PandaNode) keep_child = child_node;
remove_child(child_node);
CDWriter cdata(_cycler);
CDWriter cdata_child(child_node->_cycler);
cdata->_stashed.insert(DownConnection(child_node, sort));
cdata_child->_up.insert(UpConnection(this));
new_connection(this, child_node);
// Call callback hooks.
children_changed();
child_node->parents_changed();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::remove_stashed
// Access: Published
// Description: Removes the nth stashed child from the node. This is
// the only way to remove a child from the node that has
// previously been stashed, without unstashing it first.
// Description: Removes the nth stashed child from the node.
////////////////////////////////////////////////////////////////////
void PandaNode::
remove_stashed(int n) {
@ -855,35 +966,8 @@ remove_stashed(int n) {
cdata->_stashed.erase(cdata->_stashed.begin() + n);
int num_erased = cdata_child->_up.erase(UpConnection(this));
nassertv(num_erased == 1);
// Now sever any qpNodePathComponents on the child that reference
// this node. If we have multiple of these, we have to collapse
// them together.
qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
Paths::iterator pi;
pi = cdata_child->_paths.begin();
while (pi != cdata_child->_paths.end()) {
Paths::iterator pnext = pi;
++pnext;
if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) {
if (collapsed == (qpNodePathComponent *)NULL) {
(*pi)->set_top_node();
collapsed = (*pi);
} else {
// This is a different component that used to reference a
// different instance, but now it's all just the same topnode.
// We have to collapse this and the previous one together.
// However, there might be some qpNodePaths out there that
// still keep a pointer to this one, so we can't remove it
// altogether.
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
}
pi = pnext;
}
child_node->fix_path_lengths(cdata_child);
sever_connection(this, child_node);
// Mark the bounding volumes stale.
force_bound_stale();
@ -907,29 +991,8 @@ remove_all_children() {
PT(PandaNode) child_node = (*di).get_child();
CDWriter cdata_child(child_node->_cycler);
cdata_child->_up.erase(UpConnection(this));
// Now sever any qpNodePathComponents on the child that
// reference this node. If we have multiple of these, we have
// to collapse them together (see above).
qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
Paths::iterator pi;
pi = cdata_child->_paths.begin();
while (pi != cdata_child->_paths.end()) {
Paths::iterator pnext = pi;
++pnext;
if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) {
if (collapsed == (qpNodePathComponent *)NULL) {
(*pi)->set_top_node();
collapsed = (*pi);
} else {
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
}
pi = pnext;
}
child_node->fix_path_lengths(cdata_child);
sever_connection(this, child_node);
child_node->parents_changed();
}
cdata->_down.clear();
@ -938,29 +1001,8 @@ remove_all_children() {
PT(PandaNode) child_node = (*di).get_child();
CDWriter cdata_child(child_node->_cycler);
cdata_child->_up.erase(UpConnection(this));
// Now sever any qpNodePathComponents on the child that
// reference this node. If we have multiple of these, we have
// to collapse them together (see above).
qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
Paths::iterator pi;
pi = cdata_child->_paths.begin();
while (pi != cdata_child->_paths.end()) {
Paths::iterator pnext = pi;
++pnext;
if (!(*pi)->is_top_node() && (*pi)->get_next()->get_node() == this) {
if (collapsed == (qpNodePathComponent *)NULL) {
(*pi)->set_top_node();
collapsed = (*pi);
} else {
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
}
pi = pnext;
}
child_node->fix_path_lengths(cdata_child);
sever_connection(this, child_node);
child_node->parents_changed();
}
cdata->_stashed.clear();
@ -970,6 +1012,69 @@ remove_all_children() {
children_changed();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::steal_children
// Access: Published
// Description: Moves all the children from the other node onto this
// node.
////////////////////////////////////////////////////////////////////
void PandaNode::
steal_children(PandaNode *other) {
if (other == this) {
// Trivial.
return;
}
// We do this through the high-level interface for convenience.
// This could begin to be a problem if we have a node with hundreds
// of children to copy; this could break down the ov_set.insert()
// method, which is an O(n^2) operation. If this happens, we should
// rewrite this to do a simpler add_child() operation that involves
// push_back() instead of insert(), and then sort the down list at
// the end.
int num_children = other->get_num_children();
for (int i = 0; i < num_children; i++) {
PandaNode *child_node = other->get_child(i);
int sort = other->get_child_sort(i);
add_child(child_node, sort);
}
int num_stashed = other->get_num_stashed();
for (int i = 0; i < num_stashed; i++) {
PandaNode *child_node = other->get_stashed(i);
int sort = other->get_stashed_sort(i);
add_stashed(child_node, sort);
}
other->remove_all_children();
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::copy_children
// Access: Published
// Description: Makes another instance of all the children of the
// other node, copying them to this node.
////////////////////////////////////////////////////////////////////
void PandaNode::
copy_children(PandaNode *other) {
if (other == this) {
// Trivial.
return;
}
int num_children = other->get_num_children();
for (int i = 0; i < num_children; i++) {
PandaNode *child_node = other->get_child(i);
int sort = other->get_child_sort(i);
add_child(child_node, sort);
}
int num_stashed = other->get_num_stashed();
for (int i = 0; i < num_stashed; i++) {
PandaNode *child_node = other->get_stashed(i);
int sort = other->get_stashed_sort(i);
add_stashed(child_node, sort);
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::output
// Access: Published, Virtual
@ -1191,6 +1296,7 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) {
CDReader from_cdata(from->_cycler);
Down::const_iterator di;
for (di = from_cdata->_down.begin(); di != from_cdata->_down.end(); ++di) {
int sort = (*di).get_sort();
PandaNode *source_child = (*di).get_child();
PT(PandaNode) dest_child;
@ -1207,7 +1313,7 @@ r_copy_children(const PandaNode *from, PandaNode::InstanceMap &inst_map) {
inst_map[source_child] = dest_child;
}
add_child(dest_child);
add_child(dest_child, sort);
}
}
@ -1230,7 +1336,7 @@ attach(qpNodePathComponent *parent, PandaNode *child_node, int sort) {
if (child == (qpNodePathComponent *)NULL) {
// The child was not already attached to the parent, so get a new
// component.
child = get_top_component(child_node);
child = get_top_component(child_node, true);
}
reparent(parent, child, sort);
@ -1403,9 +1509,14 @@ get_component(qpNodePathComponent *parent, PandaNode *child_node) {
// this for a node that has parents, unless you are
// about to create a new instance (and immediately
// reconnect the qpNodePathComponent elsewhere).
//
// If force is true, this will always return something,
// even if it needs to create a new top component;
// otherwise, if force is false, it will return NULL if
// there is not already a top component available.
////////////////////////////////////////////////////////////////////
PT(qpNodePathComponent) PandaNode::
get_top_component(PandaNode *child_node) {
get_top_component(PandaNode *child_node, bool force) {
{
CDReader cdata_child(child_node->_cycler);
@ -1422,6 +1533,12 @@ get_top_component(PandaNode *child_node) {
}
}
if (!force) {
// If we don't care to force the point, return NULL to indicate
// there's not already a top component.
return NULL;
}
// We don't already have such a qpNodePathComponent; create and
// return a new one.
PT(qpNodePathComponent) child =
@ -1445,17 +1562,28 @@ PT(qpNodePathComponent) PandaNode::
get_generic_component() {
int num_parents = get_num_parents();
if (num_parents == 0) {
return get_top_component(this);
// No parents; no ambiguity. This is the root.
return get_top_component(this, true);
}
PT(qpNodePathComponent) result;
if (num_parents == 1) {
// Only one parent; no ambiguity.
PT(qpNodePathComponent) parent = get_parent(0)->get_generic_component();
result = get_component(parent, this);
} else {
if (num_parents != 1) {
pgraph_cat.warning()
<< *this << " has " << num_parents
<< " parents; choosing arbitrary path to root.\n";
}
// Oops, multiple parents; the NodePath is ambiguous.
pgraph_cat.warning()
<< *this << " has " << num_parents
<< " parents; choosing arbitrary path to root.\n";
PT(qpNodePathComponent) parent = get_parent(0)->get_generic_component();
return get_component(parent, this);
result = get_component(parent, this);
nassertr(!unambiguous_graph, result);
}
return result;
}
////////////////////////////////////////////////////////////////////
@ -1480,7 +1608,106 @@ delete_component(qpNodePathComponent *component) {
_cycler.release_write_stage(i, cdata);
}
}
nassertv(max_num_erased == 1);
// This may legitimately be zero if we are deleting a collapsed NodePath.
// nassertv(max_num_erased == 1);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::sever_connection
// Access: Private, Static
// Description: This is called internally when a parent-child
// connection is broken to update the NodePathComponents
// that reflected this connection.
//
// It severs any NodePathComponents on the child node
// that reference the indicated parent node. If the
// child node has additional parents, chooses one of
// them arbitrarily instead. Collapses together
// instances below this node that used to differentiate
// on some instance above the old parent.
////////////////////////////////////////////////////////////////////
void PandaNode::
sever_connection(PandaNode *parent_node, PandaNode *child_node) {
CDWriter cdata_child(child_node->_cycler);
qpNodePathComponent *collapsed = (qpNodePathComponent *)NULL;
Paths::iterator pi;
pi = cdata_child->_paths.begin();
while (pi != cdata_child->_paths.end()) {
Paths::iterator pnext = pi;
++pnext;
if (!(*pi)->is_top_node() &&
(*pi)->get_next()->get_node() == parent_node) {
if (collapsed == (qpNodePathComponent *)NULL) {
// This is the first component we've found that references
// this node. Should we sever it or reattach it?
if (child_node->get_num_parents() == 0) {
// If the node no longer has any parents, all of its paths will be
// severed here. Collapse them all with the existing top
// component if there is one.
collapsed = get_top_component(child_node, false);
} else {
// If the node still has one parent, all of its paths that
// referenced the old parent will be combined with the remaining
// parent. If there are multiple parents, choose one arbitrarily
// to combine with.
collapsed = child_node->get_generic_component();
}
if (collapsed == (qpNodePathComponent *)NULL) {
// Sever the component here; there's nothing to attach it
// to.
(*pi)->set_top_node();
collapsed = (*pi);
} else {
// Collapse the new component with the pre-existing
// component.
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
} else {
// This is the second (or later) component we've found that
// references this node. We should collapse it with the first
// one.
(*pi)->collapse_with(collapsed);
cdata_child->_paths.erase(pi);
}
}
pi = pnext;
}
child_node->fix_path_lengths(cdata_child);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::new_connection
// Access: Private, Static
// Description: This is called internally when a parent-child
// connection is establshed to update the
// NodePathComponents that might be involved.
//
// It adjusts any NodePathComponents the child has that
// reference the child as a top node. Any other
// components we can leave alone, because we are making
// a new instance of the child.
////////////////////////////////////////////////////////////////////
void PandaNode::
new_connection(PandaNode *parent_node, PandaNode *child_node) {
CDWriter cdata_child(child_node->_cycler);
Paths::iterator pi;
for (pi = cdata_child->_paths.begin();
pi != cdata_child->_paths.end();
++pi) {
if ((*pi)->is_top_node()) {
(*pi)->set_next(parent_node->get_generic_component());
}
}
child_node->fix_path_lengths(cdata_child);
}
////////////////////////////////////////////////////////////////////

View File

@ -36,6 +36,7 @@
#include "luse.h"
#include "ordered_vector.h"
#include "pointerTo.h"
#include "pointerToArray.h"
#include "notify.h"
class qpNodePathComponent;
@ -67,6 +68,7 @@ public:
virtual bool safe_to_flatten() const;
virtual bool safe_to_transform() const;
virtual bool safe_to_combine() const;
virtual bool preserve_name() const;
virtual void xform(const LMatrix4f &mat);
virtual PandaNode *combine_with(PandaNode *other);
@ -89,18 +91,24 @@ PUBLISHED:
void add_child(PandaNode *child_node, int sort = 0);
void remove_child(int n);
bool remove_child(PandaNode *child_node);
bool replace_child(PandaNode *orig_child, PandaNode *new_child);
INLINE bool stash_child(PandaNode *child_node);
void stash_child(int child_index);
INLINE bool unstash_child(PandaNode *child_node);
void unstash_child(int stashed_index);
INLINE int get_num_stashed() const;
INLINE PandaNode *get_stashed(int n) const;
INLINE int get_stashed_sort(int n) const;
int find_stashed(PandaNode *node) const;
void add_stashed(PandaNode *child_node, int sort = 0);
void remove_stashed(int n);
void remove_all_children();
void steal_children(PandaNode *other);
void copy_children(PandaNode *other);
INLINE void set_attrib(const RenderAttrib *attrib, int override = 0);
INLINE const RenderAttrib *get_attrib(TypeHandle type) const;
@ -173,6 +181,8 @@ protected:
BoundedObject _internal_bound;
private:
class CData;
// parent-child manipulation for qpNodePath support. Don't try to
// call these directly.
static PT(qpNodePathComponent) attach(qpNodePathComponent *parent,
@ -182,10 +192,12 @@ private:
qpNodePathComponent *child, int sort);
static PT(qpNodePathComponent) get_component(qpNodePathComponent *parent,
PandaNode *child);
static PT(qpNodePathComponent) get_top_component(PandaNode *child);
static PT(qpNodePathComponent) get_top_component(PandaNode *child,
bool force);
PT(qpNodePathComponent) get_generic_component();
void delete_component(qpNodePathComponent *component);
class CData;
static void sever_connection(PandaNode *parent_node, PandaNode *child_node);
static void new_connection(PandaNode *parent_node, PandaNode *child_node);
void fix_path_lengths(const CData *cdata);
void r_list_descendants(ostream &out, int indent_level) const;
@ -195,6 +207,7 @@ private:
INLINE DownConnection(PandaNode *child, int sort);
INLINE bool operator < (const DownConnection &other) const;
INLINE PandaNode *get_child() const;
INLINE void set_child(PandaNode *child);
INLINE int get_sort() const;
private:
@ -273,6 +286,7 @@ public:
public:
INLINE Children(const CDReader &cdata);
INLINE Children(const Children &copy);
INLINE void operator = (const Children &copy);
INLINE int get_num_children() const;
INLINE PandaNode *get_child(int n) const;
@ -283,6 +297,24 @@ public:
INLINE Children get_children() const;
// This interface *does* protect you from self-modifying loops, by
// copying the list of children.
class EXPCL_PANDA ChildrenCopy {
public:
ChildrenCopy(const CDReader &cdata);
INLINE ChildrenCopy(const ChildrenCopy &copy);
INLINE void operator = (const ChildrenCopy &copy);
INLINE int get_num_children() const;
INLINE PandaNode *get_child(int n) const;
private:
typedef PTA(PT(PandaNode)) List;
List _list;
};
INLINE ChildrenCopy get_children_copy() const;
public:
static void register_with_read_factory();
virtual void write_datagram(BamWriter *manager, Datagram &dg);

View File

@ -4,6 +4,7 @@
#include "qpfog.cxx"
#include "fogAttrib.cxx"
#include "qpgeomNode.cxx"
#include "qpgeomTransformer.cxx"
#include "qplensNode.cxx"
#include "qplodNode.cxx"
#include "materialAttrib.cxx"
@ -16,6 +17,7 @@
#include "renderEffects.cxx"
#include "renderModeAttrib.cxx"
#include "renderState.cxx"
#include "qpsceneGraphReducer.cxx"
#include "selectiveChildNode.cxx"
#include "qpsequenceNode.cxx"
#include "test_pgraph.cxx"

View File

@ -112,6 +112,7 @@ add_geom(Geom *geom, const RenderState *state) {
CDWriter cdata(_cycler);
cdata->_geoms.push_back(GeomEntry(geom, state));
mark_bound_stale();
return cdata->_geoms.size() - 1;
}
@ -126,6 +127,7 @@ remove_geom(int n) {
nassertv(n >= 0 && n < (int)cdata->_geoms.size());
cdata->_geoms.erase(cdata->_geoms.begin() + n);
mark_bound_stale();
}
////////////////////////////////////////////////////////////////////
@ -137,4 +139,5 @@ INLINE void qpGeomNode::
remove_all_geoms() {
CDWriter cdata(_cycler);
cdata->_geoms.clear();
mark_bound_stale();
}

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "qpgeomNode.h"
#include "qpgeomTransformer.h"
#include "bamReader.h"
#include "bamWriter.h"
#include "datagram.h"
@ -153,6 +154,73 @@ make_copy() const {
return new qpGeomNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomNode::xform
// Access: Public, Virtual
// Description: Transforms the contents of this node by the indicated
// matrix, if it means anything to do so. For most
// kinds of nodes, this does nothing.
//
// For a GeomNode, this does the right thing, but it is
// better to use a GeomTransformer instead, since it
// will share the new arrays properly between different
// GeomNodes.
////////////////////////////////////////////////////////////////////
void qpGeomNode::
xform(const LMatrix4f &mat) {
qpGeomTransformer transformer;
transformer.transform_vertices(this, mat);
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomNode::combine_with
// Access: Public, Virtual
// Description: Collapses this node with the other node, if possible,
// and returns a pointer to the combined node, or NULL
// if the two nodes cannot safely be combined.
//
// The return value may be this, other, or a new node
// altogether.
//
// This function is called from GraphReducer::flatten(),
// and need not deal with children; its job is just to
// decide whether to collapse the two nodes and what the
// collapsed node should look like.
////////////////////////////////////////////////////////////////////
PandaNode *qpGeomNode::
combine_with(PandaNode *other) {
if (is_exact_type(get_class_type()) &&
other->is_exact_type(get_class_type())) {
// Two GeomNodes can combine by moving Geoms from one to the other.
qpGeomNode *gother = DCAST(qpGeomNode, other);
add_geoms_from(gother);
return this;
}
return PandaNode::combine_with(other);
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomNode::add_geoms_from
// Access: Published
// Description: Copies the Geoms (and their associated RenderStates)
// from the indicated GeomNode into this one.
////////////////////////////////////////////////////////////////////
void qpGeomNode::
add_geoms_from(const qpGeomNode *other) {
CDReader cdata_other(other->_cycler);
CDWriter cdata(_cycler);
Geoms::const_iterator gi;
for (gi = cdata_other->_geoms.begin();
gi != cdata_other->_geoms.end();
++gi) {
const GeomEntry &entry = (*gi);
cdata->_geoms.push_back(entry);
}
mark_bound_stale();
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomNode::write_geoms
// Access: Published

View File

@ -44,6 +44,8 @@ protected:
public:
virtual ~qpGeomNode();
virtual PandaNode *make_copy() const;
virtual void xform(const LMatrix4f &mat);
virtual PandaNode *combine_with(PandaNode *other);
PUBLISHED:
INLINE int get_num_geoms() const;
@ -52,6 +54,7 @@ PUBLISHED:
INLINE void set_geom_state(int n, const RenderState *state);
INLINE int add_geom(Geom *geom, const RenderState *state = RenderState::make_empty());
void add_geoms_from(const qpGeomNode *other);
INLINE void remove_geom(int n);
INLINE void remove_all_geoms();
@ -118,6 +121,7 @@ private:
static TypeHandle _type_handle;
friend class PandaNode::Children;
friend class qpGeomTransformer;
};
#include "qpgeomNode.I"

View File

@ -159,6 +159,7 @@ fail() {
////////////////////////////////////////////////////////////////////
INLINE bool qpNodePath::
is_empty() const {
uncollapse_head();
return (_head == (qpNodePathComponent *)NULL);
}
@ -272,6 +273,7 @@ attach_new_node(const string &name, int sort) const {
////////////////////////////////////////////////////////////////////
INLINE void qpNodePath::
output(ostream &out) const {
uncollapse_head();
if (_head == (qpNodePathComponent *)NULL) {
out << "(empty)";
} else {
@ -1113,6 +1115,9 @@ operator < (const qpNodePath &other) const {
////////////////////////////////////////////////////////////////////
INLINE int qpNodePath::
compare_to(const qpNodePath &other) const {
uncollapse_head();
other.uncollapse_head();
// Nowadays, the NodePathComponents at the head are pointerwise
// equivalent if and only iff the NodePaths are equivalent. So we
// only have to compare pointers.

View File

@ -38,6 +38,7 @@
#include "plist.h"
#include "boundingSphere.h"
#include "qpgeomNode.h"
#include "qpsceneGraphReducer.h"
// stack seems to overflow on Intel C++ at 7000. If we need more than
// 7000, need to increase stack size.
@ -2403,10 +2404,8 @@ calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point) {
int qpNodePath::
flatten_light() {
nassertr(!is_empty(), 0);
/*
SceneGraphReducer gr(_graph_type);
gr.apply_transitions(arc());
*/
qpSceneGraphReducer gr;
gr.apply_attribs(node());
return 0;
}
@ -2436,17 +2435,13 @@ flatten_light() {
// The return value is the number of arcs removed.
////////////////////////////////////////////////////////////////////
int qpNodePath::
flatten_medium(int max_children) {
flatten_medium() {
nassertr(!is_empty(), 0);
/*
SceneGraphReducer gr(_graph_type);
gr.set_max_children(max_children);
gr.apply_transitions(arc());
qpSceneGraphReducer gr;
gr.apply_attribs(node());
int num_removed = gr.flatten(node(), false);
return num_removed;
*/
return 0;
}
////////////////////////////////////////////////////////////////////
@ -2467,17 +2462,13 @@ flatten_medium(int max_children) {
// because of less-effective culling.
////////////////////////////////////////////////////////////////////
int qpNodePath::
flatten_strong(int max_children) {
flatten_strong() {
nassertr(!is_empty(), 0);
/*
SceneGraphReducer gr(_graph_type);
gr.set_max_children(max_children);
gr.apply_transitions(arc());
qpSceneGraphReducer gr;
gr.apply_attribs(node());
int num_removed = gr.flatten(node(), true);
return num_removed;
*/
return 0;
}
////////////////////////////////////////////////////////////////////

View File

@ -477,8 +477,8 @@ PUBLISHED:
bool calc_tight_bounds(LPoint3f &min_point, LPoint3f &max_point);
int flatten_light();
int flatten_medium(int max_children = 1);
int flatten_strong(int max_children = 1);
int flatten_medium();
int flatten_strong();
bool write_bam_file(const string &filename) const;

View File

@ -72,8 +72,7 @@ RenderEffect::
// Access: Public, Virtual
// Description: Returns true if it is generally safe to transform
// this particular kind of RenderEffect by calling the
// xform() method, false otherwise. For instance, it's
// usually a bad idea to attempt to xform a Character.
// xform() method, false otherwise.
////////////////////////////////////////////////////////////////////
bool RenderEffect::
safe_to_transform() const {
@ -89,7 +88,7 @@ safe_to_transform() const {
////////////////////////////////////////////////////////////////////
bool RenderEffect::
safe_to_combine() const {
return false;
return true;
}
////////////////////////////////////////////////////////////////////

View File

@ -66,21 +66,27 @@ output(ostream &out) const {
switch (get_mode()) {
case M_none:
out << "none";
break;
case M_alpha:
out << "alpha";
break;
case M_alpha_sorted:
out << "alpha sorted";
break;
case M_multisample:
out << "multisample";
break;
case M_multisample_mask:
out << "multisample mask";
break;
case M_binary:
out << "binary";
break;
}
}

View File

@ -23,6 +23,7 @@
#include "config_pgui.h"
#include "pandaNode.h"
#include "qpsceneGraphReducer.h"
#include "throw_event.h"
#include "string_utils.h"
#include "qpnodePath.h"
@ -122,11 +123,9 @@ xform(const LMatrix4f &mat) {
// Apply the matrix to the previous transform.
node->set_transform(node->get_transform()->compose(TransformState::make_mat(mat)));
/*
// Now flatten the transform into the subgraph.
SceneGraphReducer gr;
gr.apply_transitions(arc);
*/
qpSceneGraphReducer gr;
gr.apply_attribs(node);
}
// Transform the frame style too.

View File

@ -28,9 +28,9 @@
template<class CycleDataType>
INLINE CycleDataReader<CycleDataType>::
CycleDataReader(const PipelineCycler<CycleDataType> &cycler) :
_cycler(cycler)
_cycler(&cycler)
{
_pointer = _cycler.read();
_pointer = _cycler->read();
_write_pointer = (CycleDataType *)NULL;
}
@ -47,10 +47,29 @@ CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
_write_pointer(copy._write_pointer)
{
nassertv(_pointer != (const CycleDataType *)NULL);
// We cannot copy-construct a CycleDataReader that has elevated
// itself to a write pointer.
// We cannot copy a CycleDataReader that has elevated itself to a
// write pointer.
nassertv(_write_pointer == (const CycleDataType *)NULL);
_cycler.increment_read(_pointer);
_cycler->increment_read(_pointer);
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataReader::Copy Assignment (full)
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE void CycleDataReader<CycleDataType>::
operator = (const CycleDataReader<CycleDataType> &copy) {
_cycler = copy._cycler;
_pointer = copy._pointer;
_write_pointer = copy._write_pointer;
nassertv(_pointer != (const CycleDataType *)NULL);
// We cannot copy a CycleDataReader that has elevated itself to a
// write pointer.
nassertv(_write_pointer == (const CycleDataType *)NULL);
_cycler->increment_read(_pointer);
}
////////////////////////////////////////////////////////////////////
@ -65,9 +84,9 @@ INLINE CycleDataReader<CycleDataType>::
// If the _write_pointer is non-NULL, then someone called
// elevate_to_write() at some point, and we now actually hold a
// write pointer, not a read pointer.
((PipelineCycler<CycleDataType> &)_cycler).release_write(_write_pointer);
((PipelineCycler<CycleDataType> *)_cycler)->release_write(_write_pointer);
} else if (_pointer != (CycleDataType *)NULL) {
_cycler.release_read(_pointer);
_cycler->release_read(_pointer);
}
}
@ -80,7 +99,7 @@ INLINE CycleDataReader<CycleDataType>::
template<class CycleDataType>
INLINE const CycleDataType *CycleDataReader<CycleDataType>::
operator -> () const {
nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat());
nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
return _pointer;
}
@ -93,7 +112,7 @@ operator -> () const {
template<class CycleDataType>
INLINE CycleDataReader<CycleDataType>::
operator const CycleDataType * () const {
nassertr(_pointer != (const CycleDataType *)NULL, _cycler.cheat());
nassertr(_pointer != (const CycleDataType *)NULL, _cycler->cheat());
return _pointer;
}
@ -113,7 +132,7 @@ take_pointer() {
const CycleDataType *pointer = _pointer;
_pointer = (CycleDataType *)NULL;
_write_pointer = (CycleDataType *)NULL;
nassertr(pointer != (const CycleDataType *)NULL, _cycler.cheat());
nassertr(pointer != (const CycleDataType *)NULL, _cycler->cheat());
return pointer;
}
@ -162,6 +181,17 @@ CycleDataReader(const CycleDataReader<CycleDataType> &copy) :
{
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataReader::Copy Assignment (trivial)
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE void CycleDataReader<CycleDataType>::
operator = (const CycleDataReader<CycleDataType> &copy) {
_pointer = copy._pointer;
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataReader::Destructor (trivial)
// Access: Public

View File

@ -41,6 +41,7 @@ class CycleDataReader {
public:
INLINE CycleDataReader(const PipelineCycler<CycleDataType> &cycler);
INLINE CycleDataReader(const CycleDataReader<CycleDataType> &copy);
INLINE void operator = (const CycleDataReader<CycleDataType> &copy);
INLINE ~CycleDataReader();
@ -53,7 +54,7 @@ public:
private:
#ifdef DO_PIPELINING
// This is the data stored for a real pipelining implementation.
const PipelineCycler<CycleDataType> &_cycler;
const PipelineCycler<CycleDataType> *_cycler;
const CycleDataType *_pointer;
CycleDataType *_write_pointer;
#else // !DO_PIPELINING

View File

@ -28,9 +28,39 @@
template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
CycleDataWriter(PipelineCycler<CycleDataType> &cycler) :
_cycler(cycler)
_cycler(&cycler)
{
_pointer = _cycler.write();
_pointer = _cycler->write();
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataWriter::Copy Constructor (full)
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
CycleDataWriter(const CycleDataWriter<CycleDataType> &copy) :
_cycler(copy._cycler),
_pointer(copy._pointer)
{
nassertv(_pointer != (CycleDataType *)NULL);
_cycler->increment_write(_pointer);
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataWriter::Copy Assigment (full)
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE void CycleDataWriter<CycleDataType>::
operator = (const CycleDataWriter<CycleDataType> &copy) {
_cycler = copy._cycler;
_pointer = copy._pointer;
nassertv(_pointer != (CycleDataType *)NULL);
_cycler->increment_write(_pointer);
}
////////////////////////////////////////////////////////////////////
@ -46,7 +76,7 @@ template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
CycleDataWriter<CycleDataType> &take_from) :
_cycler(cycler),
_cycler(&cycler),
_pointer(take_from._pointer)
{
take_from._pointer = (CycleDataType *)NULL;
@ -64,9 +94,9 @@ template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
CycleDataWriter(PipelineCycler<CycleDataType> &cycler,
CycleDataReader<CycleDataType> &take_from) :
_cycler(cycler)
_cycler(&cycler)
{
_pointer = _cycler.elevate_read(take_from.take_pointer());
_pointer = _cycler->elevate_read(take_from.take_pointer());
}
////////////////////////////////////////////////////////////////////
@ -78,7 +108,7 @@ template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
~CycleDataWriter() {
if (_pointer != (CycleDataType *)NULL) {
_cycler.release_write(_pointer);
_cycler->release_write(_pointer);
}
}
@ -91,7 +121,7 @@ INLINE CycleDataWriter<CycleDataType>::
template<class CycleDataType>
INLINE CycleDataType *CycleDataWriter<CycleDataType>::
operator -> () {
nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat());
nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
return _pointer;
}
@ -104,7 +134,7 @@ operator -> () {
template<class CycleDataType>
INLINE const CycleDataType *CycleDataWriter<CycleDataType>::
operator -> () const {
nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat());
nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
return _pointer;
}
@ -117,7 +147,7 @@ operator -> () const {
template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
operator CycleDataType * () {
nassertr(_pointer != (CycleDataType *)NULL, _cycler.cheat());
nassertr(_pointer != (CycleDataType *)NULL, _cycler->cheat());
return _pointer;
}
@ -135,6 +165,29 @@ CycleDataWriter(PipelineCycler<CycleDataType> &cycler) {
_pointer = cycler.write();
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataWriter::Copy Constructor (trivial)
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE CycleDataWriter<CycleDataType>::
CycleDataWriter(const CycleDataWriter<CycleDataType> &copy) :
_pointer(copy._pointer)
{
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataWriter::Copy Assigment (trivial)
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
template<class CycleDataType>
INLINE void CycleDataWriter<CycleDataType>::
operator = (const CycleDataWriter<CycleDataType> &copy) {
_pointer = copy._pointer;
}
////////////////////////////////////////////////////////////////////
// Function: CycleDataWriter::Constructor (trivial)
// Access: Public

View File

@ -40,6 +40,9 @@ template<class CycleDataType>
class CycleDataWriter {
public:
INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler);
INLINE CycleDataWriter(const CycleDataWriter<CycleDataType> &copy);
INLINE void operator = (const CycleDataWriter<CycleDataType> &copy);
INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataWriter<CycleDataType> &take_from);
INLINE CycleDataWriter(PipelineCycler<CycleDataType> &cycler, CycleDataReader<CycleDataType> &take_from);
@ -53,7 +56,7 @@ public:
private:
#ifdef DO_PIPELINING
// This is the data stored for a real pipelining implementation.
PipelineCycler<CycleDataType> &_cycler;
PipelineCycler<CycleDataType> *_cycler;
CycleDataType *_pointer;
#else // !DO_PIPELINING
// This is all we need for the trivial, do-nothing implementation.

View File

@ -378,6 +378,9 @@ main(int argc, char *argv[]) {
render.attach_new_node(camera);
camera->set_scene(render);
// This is maybe here temporarily, and maybe not.
render.set_two_sided(0);
// Set up a data graph for tracking user input.
PT(PandaNode) data_root = new PandaNode("data_root");
PandaNode *mouse = setup_mouse(data_root, window);

View File

@ -33,6 +33,7 @@
#include "cullBinAttrib.h"
#include "textureAttrib.h"
#include "transparencyAttrib.h"
#include "qpsceneGraphReducer.h"
#include "indent.h"
#include <stdio.h>
@ -362,11 +363,9 @@ generate() {
// applying them to the vertices.
if (text_flatten) {
/* ****
SceneGraphReducer gr(RenderRelation::get_class_type());
gr.apply_transitions(root_arc);
qpSceneGraphReducer gr;
gr.apply_attribs(root);
gr.flatten(root, true);
*/
}
return root;