pgraph copy subgraph

This commit is contained in:
David Rose 2002-03-13 19:50:39 +00:00
parent adfa89b233
commit 0e6b7053e0
5 changed files with 275 additions and 100 deletions

View File

@ -51,56 +51,9 @@ write_datagram(BamWriter *manager, Datagram &dg) const {
dg.add_uint32(_draw_mask.get_word());
// When we write a PandaNode, we write out its complete list of
// child node pointers, but we only write out the parent node
// pointers that have already been added to the bam file by a
// previous write operation. This is a bit of trickery that allows
// us to write out just a subgraph (instead of the complete graph)
// when we write out an arbitrary node in the graph, yet also allows
// us to keep nodes completely in sync when we use the bam format
// for streaming scene graph operations over the network.
int num_parents = 0;
Up::const_iterator ui;
for (ui = _up.begin(); ui != _up.end(); ++ui) {
PandaNode *parent_node = (*ui).get_parent();
if (manager->has_object(parent_node)) {
num_parents++;
}
}
nassertv(num_parents == (int)(PN_uint16)num_parents);
dg.add_uint16(num_parents);
for (ui = _up.begin(); ui != _up.end(); ++ui) {
PandaNode *parent_node = (*ui).get_parent();
if (manager->has_object(parent_node)) {
manager->write_pointer(dg, parent_node);
}
}
int num_children = _down.size();
nassertv(num_children == (int)(PN_uint16)num_children);
dg.add_uint16(num_children);
// **** We should smarten up the writing of the sort number--most of
// the time these will all be zero.
Down::const_iterator di;
for (di = _down.begin(); di != _down.end(); ++di) {
PandaNode *child_node = (*di).get_child();
int sort = (*di).get_sort();
manager->write_pointer(dg, child_node);
dg.add_int32(sort);
}
int num_stashed = _stashed.size();
nassertv(num_stashed == (int)(PN_uint16)num_stashed);
dg.add_uint16(num_stashed);
for (di = _stashed.begin(); di != _stashed.end(); ++di) {
PandaNode *stashed_node = (*di).get_child();
int sort = (*di).get_sort();
manager->write_pointer(dg, stashed_node);
dg.add_int32(sort);
}
write_up_list(_up, manager, dg);
write_down_list(_down, manager, dg);
write_down_list(_stashed, manager, dg);
}
////////////////////////////////////////////////////////////////////
@ -118,33 +71,9 @@ complete_pointers(TypedWritable **p_list, BamReader *manager) {
_state = DCAST(RenderState, p_list[pi++]);
_transform = DCAST(TransformState, p_list[pi++]);
// Get the parent pointers.
Up::iterator ui;
for (ui = _up.begin(); ui != _up.end(); ++ui) {
PandaNode *parent_node = DCAST(PandaNode, p_list[pi++]);
// For some reason, VC++ won't accept UpConnection as an inline
// temporary constructor here ("C2226: unexpected type
// PandaNode::UpConnection"), so we must make this assignment
// using an explicit temporary variable.
UpConnection connection(parent_node);
(*ui) = connection;
}
// Get the child pointers.
Down::iterator di;
for (di = _down.begin(); di != _down.end(); ++di) {
int sort = (*di).get_sort();
PT(PandaNode) child_node = DCAST(PandaNode, p_list[pi++]);
(*di) = DownConnection(child_node, sort);
}
// Get the stashed pointers.
for (di = _stashed.begin(); di != _stashed.end(); ++di) {
int sort = (*di).get_sort();
PT(PandaNode) stashed_node = DCAST(PandaNode, p_list[pi++]);
(*di) = DownConnection(stashed_node, sort);
}
pi += complete_up_list(_up, p_list + pi, manager);
pi += complete_down_list(_down, p_list + pi, manager);
pi += complete_down_list(_stashed, p_list + pi, manager);
return pi;
}
@ -164,6 +93,139 @@ fillin(DatagramIterator &scan, BamReader *manager) {
_draw_mask.set_word(scan.get_uint32());
fillin_up_list(_up, scan, manager);
fillin_down_list(_down, scan, manager);
fillin_down_list(_stashed, scan, manager);
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::write_up_list
// Access: Private
// Description: Writes the indicated list of parent node pointers to
// the datagram.
////////////////////////////////////////////////////////////////////
void PandaNode::CData::
write_up_list(const PandaNode::Up &up_list,
BamWriter *manager, Datagram &dg) const {
// When we write a PandaNode, we write out its complete list of
// child node pointers, but we only write out the parent node
// pointers that have already been added to the bam file by a
// previous write operation. This is a bit of trickery that allows
// us to write out just a subgraph (instead of the complete graph)
// when we write out an arbitrary node in the graph, yet also allows
// us to keep nodes completely in sync when we use the bam format
// for streaming scene graph operations over the network.
int num_parents = 0;
Up::const_iterator ui;
for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
PandaNode *parent_node = (*ui).get_parent();
if (manager->has_object(parent_node)) {
num_parents++;
}
}
nassertv(num_parents == (int)(PN_uint16)num_parents);
dg.add_uint16(num_parents);
for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
PandaNode *parent_node = (*ui).get_parent();
if (manager->has_object(parent_node)) {
manager->write_pointer(dg, parent_node);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::write_down_list
// Access: Private
// Description: Writes the indicated list of child node pointers to
// the datagram.
////////////////////////////////////////////////////////////////////
void PandaNode::CData::
write_down_list(const PandaNode::Down &down_list,
BamWriter *manager, Datagram &dg) const {
int num_children = down_list.size();
nassertv(num_children == (int)(PN_uint16)num_children);
dg.add_uint16(num_children);
// Should we smarten up the writing of the sort number? Most of the
// time these will all be zero.
Down::const_iterator di;
for (di = down_list.begin(); di != down_list.end(); ++di) {
PandaNode *child_node = (*di).get_child();
int sort = (*di).get_sort();
manager->write_pointer(dg, child_node);
dg.add_int32(sort);
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::complete_up_list
// Access: Private
// Description: Calls complete_pointers() on the list of parent node
// pointers.
////////////////////////////////////////////////////////////////////
int PandaNode::CData::
complete_up_list(PandaNode::Up &up_list,
TypedWritable **p_list, BamReader *manager) {
int pi = 0;
// Get the parent pointers.
Up::iterator ui;
for (ui = up_list.begin(); ui != up_list.end(); ++ui) {
PandaNode *parent_node = DCAST(PandaNode, p_list[pi++]);
// For some reason, VC++ won't accept UpConnection as an inline
// temporary constructor here ("C2226: unexpected type
// PandaNode::UpConnection"), so we must make this assignment
// using an explicit temporary variable.
UpConnection connection(parent_node);
(*ui) = connection;
}
// Now we should sort the list, since the sorting is based on
// pointer order, which might be different from one session to the
// next.
up_list.sort();
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::complete_down_list
// Access: Private
// Description: Calls complete_pointers() on the list of child node
// pointers.
////////////////////////////////////////////////////////////////////
int PandaNode::CData::
complete_down_list(PandaNode::Down &down_list,
TypedWritable **p_list, BamReader *manager) {
int pi = 0;
Down::iterator di;
for (di = down_list.begin(); di != down_list.end(); ++di) {
int sort = (*di).get_sort();
PT(PandaNode) child_node = DCAST(PandaNode, p_list[pi++]);
(*di) = DownConnection(child_node, sort);
}
// Now we should sort the list, since the sorting is based on
// pointer order, which might be different from one session to the
// next.
down_list.sort();
return pi;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::fillin_up_list
// Access: Private
// Description: Reads the indicated list parent node pointers from
// the datagram (or at least calls read_pointer() for
// each one).
////////////////////////////////////////////////////////////////////
void PandaNode::CData::
fillin_up_list(PandaNode::Up &up_list,
DatagramIterator &scan, BamReader *manager) {
int num_parents = scan.get_uint16();
// Read the list of parent nodes. Push back a NULL for each one.
_up.reserve(num_parents);
@ -171,23 +233,25 @@ fillin(DatagramIterator &scan, BamReader *manager) {
manager->read_pointer(scan);
_up.push_back(UpConnection(NULL));
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::CData::fillin_down_list
// Access: Private
// Description: Reads the indicated list child node pointers from
// the datagram (or at least calls read_pointer() for
// each one).
////////////////////////////////////////////////////////////////////
void PandaNode::CData::
fillin_down_list(PandaNode::Down &down_list,
DatagramIterator &scan, BamReader *manager) {
int num_children = scan.get_uint16();
// Read the list of child nodes. Push back a NULL for each one.
_down.reserve(num_children);
down_list.reserve(num_children);
for (int i = 0; i < num_children; i++) {
manager->read_pointer(scan);
int sort = scan.get_int32();
_down.push_back(DownConnection(NULL, sort));
}
int num_stashed = scan.get_uint16();
// Read the list of stashed nodes. Push back a NULL for each one.
_stashed.reserve(num_stashed);
for (int i = 0; i < num_stashed; i++) {
manager->read_pointer(scan);
int sort = scan.get_int32();
_stashed.push_back(DownConnection(NULL, sort));
down_list.push_back(DownConnection(NULL, sort));
}
}
@ -278,11 +342,10 @@ make_copy() const {
// original (e.g. vertex index tables), but nothing that
// will impede normal use of the PandaNode.
////////////////////////////////////////////////////////////////////
PandaNode *PandaNode::
PT(PandaNode) PandaNode::
copy_subgraph() const {
// *** Do something here.
nassertr(false, (PandaNode *)NULL);
return (PandaNode *)NULL;
InstanceMap inst_map;
return r_copy_subgraph(inst_map);
}
////////////////////////////////////////////////////////////////////
@ -1064,6 +1127,80 @@ void PandaNode::
children_changed() {
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::r_copy_subgraph
// Access: Protected, Virtual
// Description: This is the recursive implementation of copy_subgraph().
// It returns a copy of the entire subgraph rooted at
// this node.
//
// Note that it includes the parameter inst_map, which
// is a map type, and is not (and cannot be) exported
// from PANDA.DLL. Thus, any derivative of PandaNode
// that is not also a member of PANDA.DLL *cannot*
// access this map.
////////////////////////////////////////////////////////////////////
PT(PandaNode) PandaNode::
r_copy_subgraph(PandaNode::InstanceMap &inst_map) const {
if (!safe_to_flatten()) {
// If this kind of node cannot be copied, quietly return the same
// pointer, making an instance instead of a copy.
return (PandaNode *)this;
}
PT(PandaNode) copy = make_copy();
nassertr(copy != (PandaNode *)NULL, NULL);
if (copy->get_type() != get_type()) {
pgraph_cat.warning()
<< "Don't know how to copy nodes of type " << get_type() << "\n";
}
copy->r_copy_children(this, inst_map);
copy->set_state(get_state());
copy->set_transform(get_transform());
return copy;
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::r_copy_children
// Access: Protected, Virtual
// Description: This is called by r_copy_subgraph(); the copy has
// already been made of this particular node (and this
// is the copy); this function's job is to copy all of
// the children from the original.
//
// Note that it includes the parameter inst_map, which
// is a map type, and is not (and cannot be) exported
// from PANDA.DLL. Thus, any derivative of PandaNode
// that is not also a member of PANDA.DLL *cannot*
// access this map, and probably should not even
// override this function.
////////////////////////////////////////////////////////////////////
void PandaNode::
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) {
PandaNode *source_child = (*di).get_child();
PT(PandaNode) dest_child;
// Check to see if we have already copied this child. If we
// have, use the copy. In this way, a subgraph that contains
// instances will be correctly duplicated into another subgraph
// that also contains its own instances.
InstanceMap::const_iterator ci;
ci = inst_map.find(source_child);
if (ci != inst_map.end()) {
dest_child = (*ci).second;
} else {
dest_child = source_child->r_copy_subgraph(inst_map);
inst_map[source_child] = dest_child;
}
add_child(dest_child);
}
}
////////////////////////////////////////////////////////////////////
// Function: PandaNode::attach
// Access: Private, Static

View File

@ -60,7 +60,7 @@ private:
public:
virtual PandaNode *make_copy() const;
PandaNode *copy_subgraph() const;
PT(PandaNode) copy_subgraph() const;
virtual bool safe_to_flatten() const;
virtual bool safe_to_transform() const;
@ -152,6 +152,10 @@ protected:
virtual void parents_changed();
virtual void children_changed();
typedef pmap<PandaNode *, PandaNode *> InstanceMap;
virtual PT(PandaNode) r_copy_subgraph(InstanceMap &inst_map) const;
virtual void r_copy_children(const PandaNode *from, InstanceMap &inst_map);
// This is the bounding volume around the contents of the node
// itself (without including all of the node's children).
// BoundedObject is itself cycled, so we don't need to protect it.
@ -221,6 +225,19 @@ private:
virtual int complete_pointers(TypedWritable **plist, BamReader *manager);
virtual void fillin(DatagramIterator &scan, BamReader *manager);
void write_up_list(const Up &up_list,
BamWriter *manager, Datagram &dg) const;
void write_down_list(const Down &down_list,
BamWriter *manager, Datagram &dg) const;
int complete_up_list(Up &up_list,
TypedWritable **p_list, BamReader *manager);
int complete_down_list(Down &down_list,
TypedWritable **p_list, BamReader *manager);
void fillin_up_list(Up &up_list,
DatagramIterator &scan, BamReader *manager);
void fillin_down_list(Down &down_list,
DatagramIterator &scan, BamReader *manager);
Down _down;
Down _stashed;
Up _up;

View File

@ -139,6 +139,20 @@ qpGeomNode::
~qpGeomNode() {
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomNode::make_copy
// Access: Public, Virtual
// Description: Returns a newly-allocated PandaNode that is a shallow
// copy of this one. It will be a different pointer,
// but its internal data may or may not be shared with
// that of the original PandaNode. No children will be
// copied.
////////////////////////////////////////////////////////////////////
PandaNode *qpGeomNode::
make_copy() const {
return new qpGeomNode(*this);
}
////////////////////////////////////////////////////////////////////
// Function: qpGeomNode::write_geoms
// Access: Published

View File

@ -43,6 +43,7 @@ protected:
qpGeomNode(const qpGeomNode &copy);
public:
virtual ~qpGeomNode();
virtual PandaNode *make_copy() const;
PUBLISHED:
INLINE int get_num_geoms() const;

View File

@ -265,16 +265,22 @@ instance_to(const qpNodePath &other, int sort) const {
// node. A qpNodePath to the newly created copy is
// returned.
//
// Certain kinds of nodes may not be copied; if one of
// these is encountered, the node will be "copied" as
// the nearest copyable base class. For instance, a
// Camera node in the graph will become a simple
// NamedNode.
// Certain kinds of nodes (like a Camera, for instance,
// whose pointer value is important) may not be copied;
// if one of these is encountered, the node will be
// instanced, instead of actually copied.
////////////////////////////////////////////////////////////////////
qpNodePath qpNodePath::
copy_to(const qpNodePath &other, int sort) const {
//*****
return instance_to(other, sort);
nassertr(verify_complete(), fail());
nassertr(!is_empty(), fail());
nassertr(!other.is_empty(), fail());
PandaNode *source_node = node();
PT(PandaNode) copy_node = source_node->copy_subgraph();
nassertr(copy_node != (PandaNode *)NULL, fail());
return other.attach_new_node(copy_node, sort);
}
////////////////////////////////////////////////////////////////////