mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-04 10:54:24 -04:00
756 lines
26 KiB
C++
756 lines
26 KiB
C++
// Filename: SceneGraphReducer.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 "sceneGraphReducer.h"
|
|
#include "config_pgraph.h"
|
|
#include "accumulatedAttribs.h"
|
|
#include "boundingSphere.h"
|
|
#include "modelNode.h"
|
|
#include "pointerTo.h"
|
|
#include "plist.h"
|
|
#include "pmap.h"
|
|
#include "geomNode.h"
|
|
|
|
PStatCollector SceneGraphReducer::_flatten_collector("*:Flatten:flatten");
|
|
PStatCollector SceneGraphReducer::_apply_collector("*:Flatten:apply");
|
|
PStatCollector SceneGraphReducer::_collect_collector("*:Flatten:collect");
|
|
PStatCollector SceneGraphReducer::_make_nonindexed_collector("*:Flatten:make nonindexed");
|
|
PStatCollector SceneGraphReducer::_unify_collector("*:Flatten:unify");
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::flatten
|
|
// Access: Published
|
|
// Description: Simplifies the graph by removing unnecessary nodes
|
|
// and nodes.
|
|
//
|
|
// In general, a node (and its parent node) is a
|
|
// candidate for removal if the node has no siblings and
|
|
// the node has no special properties.
|
|
//
|
|
// If combine_siblings_bits is nonzero, some sibling
|
|
// nodes (according to the bits set in
|
|
// combine_siblings_bits) may also be collapsed into a
|
|
// single node. This will further reduce scene graph
|
|
// complexity, sometimes substantially, at the cost of
|
|
// reduced spatial separation.
|
|
//
|
|
// Returns the number of nodes removed from the graph.
|
|
////////////////////////////////////////////////////////////////////
|
|
int SceneGraphReducer::
|
|
flatten(PandaNode *root, int combine_siblings_bits) {
|
|
PStatTimer timer(_flatten_collector);
|
|
int num_total_nodes = 0;
|
|
int num_pass_nodes;
|
|
|
|
do {
|
|
num_pass_nodes = 0;
|
|
|
|
// Get a copy of the children list, so we don't have to worry
|
|
// about self-modifications.
|
|
PandaNode::Children cr = root->get_children();
|
|
|
|
// Now visit each of the children in turn.
|
|
int num_children = cr.get_num_children();
|
|
for (int i = 0; i < num_children; i++) {
|
|
PandaNode *child_node = cr.get_child(i);
|
|
num_pass_nodes += r_flatten(root, child_node, combine_siblings_bits);
|
|
}
|
|
|
|
if (combine_siblings_bits != 0 && root->get_num_children() >= 2) {
|
|
num_pass_nodes += flatten_siblings(root, combine_siblings_bits);
|
|
}
|
|
|
|
num_total_nodes += num_pass_nodes;
|
|
|
|
// If combine_siblings_bits has CS_recurse set, we should repeat
|
|
// the above until we don't get any more benefit from flattening,
|
|
// because each pass could convert cousins into siblings, which
|
|
// may get flattened next pass.
|
|
} while ((combine_siblings_bits & CS_recurse) != 0 && num_pass_nodes != 0);
|
|
|
|
return num_total_nodes;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::r_apply_attribs
|
|
// Access: Protected
|
|
// Description: The recursive implementation of apply_attribs().
|
|
////////////////////////////////////////////////////////////////////
|
|
void SceneGraphReducer::
|
|
r_apply_attribs(PandaNode *node, const AccumulatedAttribs &attribs,
|
|
int attrib_types, GeomTransformer &transformer) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "r_apply_attribs(" << *node << "), node's attribs are:\n";
|
|
node->get_transform()->write(pgraph_cat.spam(false), 2);
|
|
node->get_state()->write(pgraph_cat.spam(false), 2);
|
|
node->get_effects()->write(pgraph_cat.spam(false), 2);
|
|
}
|
|
|
|
AccumulatedAttribs next_attribs(attribs);
|
|
next_attribs.collect(node, attrib_types);
|
|
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Got attribs from " << *node << "\n"
|
|
<< "Accumulated attribs are:\n";
|
|
next_attribs.write(pgraph_cat.spam(false), attrib_types, 2);
|
|
}
|
|
|
|
// Check to see if we can't propagate any of these attribs past
|
|
// this node for some reason.
|
|
int apply_types = 0;
|
|
|
|
const RenderEffects *effects = node->get_effects();
|
|
if (!effects->safe_to_transform()) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Node " << *node
|
|
<< " contains a non-transformable effect; leaving transform here.\n";
|
|
}
|
|
next_attribs._transform = effects->prepare_flatten_transform(next_attribs._transform);
|
|
apply_types |= TT_transform;
|
|
}
|
|
if (!node->safe_to_transform()) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Cannot safely transform nodes of type " << node->get_type()
|
|
<< "; leaving a transform here but carrying on otherwise.\n";
|
|
}
|
|
apply_types |= TT_transform;
|
|
}
|
|
apply_types |= node->get_unsafe_to_apply_attribs();
|
|
|
|
// Also, check the children of this node. If any of them indicates
|
|
// it is not safe to modify its transform, we must drop our
|
|
// transform here.
|
|
int num_children = node->get_num_children();
|
|
int i;
|
|
if ((apply_types & TT_transform) == 0) {
|
|
bool children_transform_friendly = true;
|
|
for (i = 0; i < num_children && children_transform_friendly; i++) {
|
|
PandaNode *child_node = node->get_child(i);
|
|
children_transform_friendly = child_node->safe_to_modify_transform();
|
|
}
|
|
|
|
if (!children_transform_friendly) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Node " << *node
|
|
<< " has a child that cannot modify its transform; leaving transform here.\n";
|
|
}
|
|
apply_types |= TT_transform;
|
|
}
|
|
}
|
|
|
|
// Directly store whatever attributes we must,
|
|
next_attribs.apply_to_node(node, attrib_types & apply_types);
|
|
|
|
// And apply the rest to the vertices.
|
|
node->apply_attribs_to_vertices(next_attribs, attrib_types, transformer);
|
|
|
|
// Do we need to copy any children to flatten instances?
|
|
bool resist_copy = false;
|
|
for (i = 0; i < num_children; i++) {
|
|
PandaNode *child_node = node->get_child(i);
|
|
|
|
if (child_node->get_num_parents() > 1) {
|
|
if (!child_node->safe_to_flatten()) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Cannot duplicate nodes of type " << child_node->get_type()
|
|
<< ".\n";
|
|
resist_copy = true;
|
|
}
|
|
|
|
} else {
|
|
PT(PandaNode) new_node = child_node->make_copy();
|
|
if (new_node->get_type() != child_node->get_type()) {
|
|
pgraph_cat.error()
|
|
<< "Don't know how to copy nodes of type "
|
|
<< child_node->get_type() << "\n";
|
|
resist_copy = true;
|
|
|
|
} else {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Duplicated " << *child_node << "\n";
|
|
}
|
|
|
|
new_node->copy_children(child_node);
|
|
node->replace_child(child_node, new_node);
|
|
child_node = new_node;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (resist_copy) {
|
|
// If any of our children should have been copied but weren't, we
|
|
// need to drop the state here before continuing.
|
|
next_attribs.apply_to_node(node, attrib_types);
|
|
}
|
|
|
|
// Now it's safe to traverse through all of our children.
|
|
nassertv(num_children == node->get_num_children());
|
|
for (i = 0; i < num_children; i++) {
|
|
PandaNode *child_node = node->get_child(i);
|
|
r_apply_attribs(child_node, next_attribs, attrib_types, transformer);
|
|
}
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::r_flatten
|
|
// Access: Protected
|
|
// Description: The recursive implementation of flatten().
|
|
////////////////////////////////////////////////////////////////////
|
|
int SceneGraphReducer::
|
|
r_flatten(PandaNode *grandparent_node, PandaNode *parent_node,
|
|
int combine_siblings_bits) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "SceneGraphReducer::r_flatten(" << *grandparent_node << ", "
|
|
<< *parent_node << ", " << hex << combine_siblings_bits << dec
|
|
<< ")\n";
|
|
}
|
|
int num_nodes = 0;
|
|
|
|
if (!parent_node->safe_to_flatten_below()) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Not traversing farther; " << *parent_node
|
|
<< " doesn't allow flattening below itself.\n";
|
|
}
|
|
|
|
} else {
|
|
if ((combine_siblings_bits & CS_within_radius) != 0) {
|
|
CPT(BoundingVolume) bv = parent_node->get_bounds();
|
|
if (bv->is_of_type(BoundingSphere::get_class_type())) {
|
|
const BoundingSphere *bs = DCAST(BoundingSphere, bv);
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "considering radius of " << *parent_node
|
|
<< ": " << *bs << " vs. " << _combine_radius << "\n";
|
|
}
|
|
if (bs->is_empty() || bs->get_radius() <= _combine_radius) {
|
|
// This node fits within the specified radius; from here on
|
|
// down, we will have CS_other set, instead of
|
|
// CS_within_radius.
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "node fits within radius; flattening tighter.\n";
|
|
}
|
|
combine_siblings_bits &= ~CS_within_radius;
|
|
combine_siblings_bits |= (CS_geom_node | CS_other | CS_recurse);
|
|
}
|
|
}
|
|
}
|
|
|
|
// First, recurse on each of the children.
|
|
{
|
|
PandaNode::Children cr = parent_node->get_children();
|
|
int num_children = cr.get_num_children();
|
|
for (int i = 0; i < num_children; i++) {
|
|
PandaNode *child_node = cr.get_child(i);
|
|
num_nodes += r_flatten(parent_node, child_node, combine_siblings_bits);
|
|
}
|
|
}
|
|
|
|
// Now that the above loop has removed some children, the child
|
|
// list saved above is no longer accurate, so hereafter we must
|
|
// ask the node for its real child list.
|
|
|
|
// If we have CS_recurse set, then we flatten siblings before
|
|
// trying to flatten children. Otherwise, we flatten children
|
|
// first, and then flatten siblings, which avoids overly
|
|
// enthusiastic flattening.
|
|
if ((combine_siblings_bits & CS_recurse) != 0 &&
|
|
parent_node->get_num_children() >= 2 &&
|
|
parent_node->safe_to_combine_children()) {
|
|
num_nodes += flatten_siblings(parent_node, combine_siblings_bits);
|
|
}
|
|
|
|
if (parent_node->get_num_children() == 1) {
|
|
// If we now have exactly one child, consider flattening the node
|
|
// out.
|
|
PT(PandaNode) child_node = parent_node->get_child(0);
|
|
int child_sort = parent_node->get_child_sort(0);
|
|
|
|
if (consider_child(grandparent_node, parent_node, child_node)) {
|
|
// Ok, do it.
|
|
parent_node->remove_child(child_node);
|
|
|
|
if (do_flatten_child(grandparent_node, parent_node, child_node)) {
|
|
// Done!
|
|
num_nodes++;
|
|
} else {
|
|
// Chicken out.
|
|
parent_node->add_child(child_node, child_sort);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((combine_siblings_bits & CS_recurse) == 0 &&
|
|
(combine_siblings_bits & ~CS_recurse) != 0 &&
|
|
parent_node->get_num_children() >= 2 &&
|
|
parent_node->safe_to_combine_children()) {
|
|
num_nodes += flatten_siblings(parent_node, combine_siblings_bits);
|
|
}
|
|
|
|
// Finally, if any of our remaining children are plain PandaNodes
|
|
// with no children, just remove them.
|
|
{
|
|
for (int i = parent_node->get_num_children() - 1; i >= 0; --i) {
|
|
PandaNode *child_node = parent_node->get_child(i);
|
|
if (child_node->is_exact_type(PandaNode::get_class_type()) &&
|
|
child_node->get_num_children() == 0 &&
|
|
child_node->get_transform()->is_identity() &&
|
|
child_node->get_effects()->is_empty()) {
|
|
parent_node->remove_child(child_node);
|
|
++num_nodes;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_nodes;
|
|
}
|
|
|
|
class SortByState {
|
|
public:
|
|
INLINE bool
|
|
operator () (const PandaNode *node1, const PandaNode *node2) const;
|
|
};
|
|
|
|
INLINE bool SortByState::
|
|
operator () (const PandaNode *node1, const PandaNode *node2) const {
|
|
if (node1->get_transform() != node2->get_transform()) {
|
|
return node1->get_transform() < node2->get_transform();
|
|
}
|
|
if (node1->get_state() != node2->get_state()) {
|
|
return node1->get_state() < node2->get_state();
|
|
}
|
|
if (node1->get_effects() != node2->get_effects()) {
|
|
return node1->get_effects() < node2->get_effects();
|
|
}
|
|
if (node1->get_draw_control_mask() != node2->get_draw_control_mask()) {
|
|
return node1->get_draw_control_mask() < node2->get_draw_control_mask();
|
|
}
|
|
if (node1->get_draw_show_mask() != node2->get_draw_show_mask()) {
|
|
return node1->get_draw_show_mask() < node2->get_draw_show_mask();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::flatten_siblings
|
|
// Access: Protected
|
|
// Description: Attempts to collapse together any pairs of siblings
|
|
// of the indicated node that share the same properties.
|
|
////////////////////////////////////////////////////////////////////
|
|
int SceneGraphReducer::
|
|
flatten_siblings(PandaNode *parent_node, int combine_siblings_bits) {
|
|
int num_nodes = 0;
|
|
|
|
// First, collect the children into groups of nodes with common
|
|
// properties.
|
|
typedef plist< PT(PandaNode) > NodeList;
|
|
typedef pmap<PandaNode *, NodeList, SortByState> Collected;
|
|
Collected collected;
|
|
|
|
{
|
|
// Protect this within a local scope, so the Children member will
|
|
// destruct and free the read pointer before we try to write to
|
|
// these nodes.
|
|
PandaNode::Children cr = parent_node->get_children();
|
|
int num_children = cr.get_num_children();
|
|
for (int i = 0; i < num_children; i++) {
|
|
PandaNode *child_node = cr.get_child(i);
|
|
bool safe_to_combine = child_node->safe_to_combine();
|
|
if (safe_to_combine) {
|
|
if (child_node->is_geom_node()) {
|
|
safe_to_combine = (combine_siblings_bits & CS_geom_node) != 0;
|
|
} else {
|
|
safe_to_combine = (combine_siblings_bits & CS_other) != 0;
|
|
}
|
|
}
|
|
|
|
if (safe_to_combine) {
|
|
collected[child_node].push_back(child_node);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now visit each of those groups and try to collapse them together.
|
|
// A O(n^2) operation, but presumably the number of nodes in each
|
|
// group is small. And if each node in the group can collapse with
|
|
// any other node, it becomes a O(n) operation.
|
|
Collected::iterator ci;
|
|
for (ci = collected.begin(); ci != collected.end(); ++ci) {
|
|
const RenderEffects *effects = (*ci).first->get_effects();
|
|
if (effects->safe_to_combine()) {
|
|
NodeList &nodes = (*ci).second;
|
|
|
|
NodeList::iterator ai1;
|
|
ai1 = nodes.begin();
|
|
while (ai1 != nodes.end()) {
|
|
NodeList::iterator ai1_hold = ai1;
|
|
PandaNode *child1 = (*ai1);
|
|
++ai1;
|
|
NodeList::iterator ai2 = ai1;
|
|
while (ai2 != nodes.end()) {
|
|
NodeList::iterator ai2_hold = ai2;
|
|
PandaNode *child2 = (*ai2);
|
|
++ai2;
|
|
|
|
if (consider_siblings(parent_node, child1, child2)) {
|
|
PT(PandaNode) new_node =
|
|
do_flatten_siblings(parent_node, child1, child2);
|
|
if (new_node != (PandaNode *)NULL) {
|
|
// We successfully collapsed a node.
|
|
nodes.erase(ai2_hold);
|
|
nodes.erase(ai1_hold);
|
|
nodes.push_back(new_node);
|
|
ai1 = nodes.begin();
|
|
ai2 = nodes.end();
|
|
num_nodes++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return num_nodes;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::consider_child
|
|
// Access: Protected
|
|
// Description: Decides whether or not the indicated child node is a
|
|
// suitable candidate for removal. Returns true if the
|
|
// node may be removed, false if it should be kept.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool SceneGraphReducer::
|
|
consider_child(PandaNode *grandparent_node, PandaNode *parent_node,
|
|
PandaNode *child_node) {
|
|
if (!parent_node->safe_to_combine() || !child_node->safe_to_combine()) {
|
|
// One or both nodes cannot be safely combined with another node;
|
|
// do nothing.
|
|
return false;
|
|
}
|
|
|
|
if (parent_node->get_transform() != child_node->get_transform() ||
|
|
parent_node->get_state() != child_node->get_state() ||
|
|
parent_node->get_effects() != child_node->get_effects() ||
|
|
parent_node->get_draw_control_mask() != child_node->get_draw_control_mask() ||
|
|
parent_node->get_draw_show_mask() != child_node->get_draw_show_mask()) {
|
|
// The two nodes have a different state; too bad.
|
|
return false;
|
|
}
|
|
|
|
if (!parent_node->get_effects()->safe_to_combine()) {
|
|
// The effects don't want to be combined.
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::consider_siblings
|
|
// Access: Protected
|
|
// Description: Decides whether or not the indicated sibling nodes
|
|
// should be collapsed into a single node or not.
|
|
// Returns true if the nodes may be collapsed, false if
|
|
// they should be kept distinct.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool SceneGraphReducer::
|
|
consider_siblings(PandaNode *parent_node, PandaNode *child1,
|
|
PandaNode *child2) {
|
|
// We don't have to worry about the states being different betweeen
|
|
// child1 and child2, since the SortByState object already
|
|
// guaranteed we only consider children that have the same state.
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::do_flatten_child
|
|
// Access: Protected
|
|
// Description: Collapses together the indicated parent node and
|
|
// child node and leaves the result attached to the
|
|
// grandparent. The return value is true if the node is
|
|
// successfully collapsed, false if we chickened out.
|
|
////////////////////////////////////////////////////////////////////
|
|
bool SceneGraphReducer::
|
|
do_flatten_child(PandaNode *grandparent_node, PandaNode *parent_node,
|
|
PandaNode *child_node) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Collapsing " << *parent_node << " and " << *child_node << "\n";
|
|
}
|
|
|
|
PT(PandaNode) new_parent = collapse_nodes(parent_node, child_node, false);
|
|
if (new_parent == (PandaNode *)NULL) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Decided not to collapse " << *parent_node
|
|
<< " and " << *child_node << "\n";
|
|
}
|
|
return false;
|
|
}
|
|
|
|
choose_name(new_parent, parent_node, child_node);
|
|
|
|
if (new_parent != child_node) {
|
|
new_parent->steal_children(child_node);
|
|
new_parent->copy_tags(child_node);
|
|
}
|
|
|
|
if (new_parent != parent_node) {
|
|
grandparent_node->replace_child(parent_node, new_parent);
|
|
new_parent->copy_tags(parent_node);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::do_flatten_siblings
|
|
// Access: Protected
|
|
// Description: Performs the work of collapsing two sibling nodes
|
|
// together into a single node, leaving the resulting
|
|
// node attached to the parent.
|
|
//
|
|
// Returns a pointer to a PandaNode that reflects the
|
|
// combined node (which may be either of the source nodes,
|
|
// or a new node altogether) if the siblings are
|
|
// successfully collapsed, or NULL if we chickened out.
|
|
////////////////////////////////////////////////////////////////////
|
|
PandaNode *SceneGraphReducer::
|
|
do_flatten_siblings(PandaNode *parent_node, PandaNode *child1,
|
|
PandaNode *child2) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Collapsing " << *child1 << " and " << *child2 << "\n";
|
|
}
|
|
|
|
PT(PandaNode) new_child = collapse_nodes(child2, child1, true);
|
|
if (new_child == (PandaNode *)NULL) {
|
|
if (pgraph_cat.is_spam()) {
|
|
pgraph_cat.spam()
|
|
<< "Decided not to collapse " << *child1 << " and " << *child2 << "\n";
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
choose_name(new_child, child2, child1);
|
|
|
|
if (new_child == child1) {
|
|
new_child->steal_children(child2);
|
|
parent_node->remove_child(child2);
|
|
new_child->copy_tags(child2);
|
|
|
|
} else if (new_child == child2) {
|
|
new_child->steal_children(child1);
|
|
parent_node->remove_child(child1);
|
|
new_child->copy_tags(child1);
|
|
|
|
} else {
|
|
new_child->steal_children(child1);
|
|
new_child->steal_children(child2);
|
|
parent_node->remove_child(child2);
|
|
parent_node->replace_child(child1, new_child);
|
|
new_child->copy_tags(child1);
|
|
new_child->copy_tags(child2);
|
|
}
|
|
|
|
return new_child;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::collapse_nodes
|
|
// Access: Protected
|
|
// Description: Collapses the two nodes into a single node, if
|
|
// possible. The 'siblings' flag is true if the two
|
|
// nodes are siblings nodes; otherwise, node1 is a
|
|
// parent of node2. The return value is the resulting
|
|
// node, which may be either one of the source nodes, or
|
|
// a new node altogether, or it may be NULL to indicate
|
|
// that the collapse operation could not take place.
|
|
////////////////////////////////////////////////////////////////////
|
|
PT(PandaNode) SceneGraphReducer::
|
|
collapse_nodes(PandaNode *node1, PandaNode *node2, bool siblings) {
|
|
return node2->combine_with(node1);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::choose_name
|
|
// Access: Protected
|
|
// Description: Chooses a suitable name for the collapsed node, based
|
|
// on the names of the two sources nodes.
|
|
////////////////////////////////////////////////////////////////////
|
|
void SceneGraphReducer::
|
|
choose_name(PandaNode *preserve, PandaNode *source1, PandaNode *source2) {
|
|
string name;
|
|
bool got_name = false;
|
|
|
|
name = source1->get_name();
|
|
got_name = !name.empty() || source1->preserve_name();
|
|
|
|
if (source2->preserve_name() || !got_name) {
|
|
name = source2->get_name();
|
|
got_name = !name.empty() || source2->preserve_name();
|
|
}
|
|
|
|
if (got_name) {
|
|
preserve->set_name(name);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::r_collect_vertex_data
|
|
// Access: Private
|
|
// Description: The recursive implementation of
|
|
// collect_vertex_data().
|
|
////////////////////////////////////////////////////////////////////
|
|
int SceneGraphReducer::
|
|
r_collect_vertex_data(PandaNode *node, int collect_bits,
|
|
GeomTransformer &transformer) {
|
|
int num_created = 0;
|
|
|
|
int this_node_bits = 0;
|
|
if (node->is_of_type(ModelNode::get_class_type())) {
|
|
this_node_bits |= CVD_model;
|
|
}
|
|
if (!node->get_transform()->is_identity()) {
|
|
this_node_bits |= CVD_transform;
|
|
}
|
|
if (node->is_geom_node()) {
|
|
this_node_bits |= CVD_one_node_only;
|
|
}
|
|
|
|
if ((collect_bits & this_node_bits) != 0) {
|
|
// We need to start a unique collection here.
|
|
GeomTransformer new_transformer(transformer);
|
|
|
|
if (node->is_geom_node()) {
|
|
// When we come to geom node, collect.
|
|
if (new_transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits)) {
|
|
++num_created;
|
|
}
|
|
}
|
|
|
|
PandaNode::Children children = node->get_children();
|
|
int num_children = children.get_num_children();
|
|
for (int i = 0; i < num_children; ++i) {
|
|
num_created +=
|
|
r_collect_vertex_data(children.get_child(i), collect_bits, new_transformer);
|
|
}
|
|
|
|
} else {
|
|
// Keep the same collection.
|
|
|
|
if (node->is_geom_node()) {
|
|
if (transformer.collect_vertex_data(DCAST(GeomNode, node), collect_bits)) {
|
|
++num_created;
|
|
}
|
|
}
|
|
|
|
PandaNode::Children children = node->get_children();
|
|
int num_children = children.get_num_children();
|
|
for (int i = 0; i < num_children; ++i) {
|
|
num_created +=
|
|
r_collect_vertex_data(children.get_child(i), collect_bits, transformer);
|
|
}
|
|
}
|
|
|
|
return num_created;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::r_make_nonindexed
|
|
// Access: Private
|
|
// Description: The recursive implementation of
|
|
// make_nonindexed().
|
|
////////////////////////////////////////////////////////////////////
|
|
int SceneGraphReducer::
|
|
r_make_nonindexed(PandaNode *node, int nonindexed_bits) {
|
|
int num_changed = 0;
|
|
|
|
if (node->is_geom_node()) {
|
|
GeomNode *geom_node = DCAST(GeomNode, node);
|
|
int num_geoms = geom_node->get_num_geoms();
|
|
for (int i = 0; i < num_geoms; ++i) {
|
|
const Geom *geom = geom_node->get_geom(i);
|
|
|
|
// Check whether the geom is animated or dynamic, and skip it
|
|
// if the user specified so.
|
|
const GeomVertexData *data = geom->get_vertex_data();
|
|
int this_geom_bits = 0;
|
|
if (data->get_format()->get_animation().get_animation_type() !=
|
|
Geom::AT_none) {
|
|
this_geom_bits |= MN_avoid_animated;
|
|
}
|
|
if (data->get_usage_hint() != Geom::UH_static ||
|
|
geom->get_usage_hint() != Geom::UH_static) {
|
|
this_geom_bits |= MN_avoid_dynamic;
|
|
}
|
|
|
|
if ((nonindexed_bits & this_geom_bits) == 0) {
|
|
// The geom meets the user's qualifications for making
|
|
// nonindexed, so do it.
|
|
Geom *mgeom = DCAST(Geom, geom_node->modify_geom(i));
|
|
num_changed += mgeom->make_nonindexed((nonindexed_bits & MN_composite_only) != 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
PandaNode::Children children = node->get_children();
|
|
int num_children = children.get_num_children();
|
|
for (int i = 0; i < num_children; ++i) {
|
|
num_changed +=
|
|
r_make_nonindexed(children.get_child(i), nonindexed_bits);
|
|
}
|
|
|
|
return num_changed;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Function: SceneGraphReducer::r_unify
|
|
// Access: Private
|
|
// Description: The recursive implementation of unify().
|
|
////////////////////////////////////////////////////////////////////
|
|
void SceneGraphReducer::
|
|
r_unify(PandaNode *node) {
|
|
if (node->is_geom_node()) {
|
|
GeomNode *geom_node = DCAST(GeomNode, node);
|
|
geom_node->unify(max_collect_indices);
|
|
}
|
|
|
|
PandaNode::Children children = node->get_children();
|
|
int num_children = children.get_num_children();
|
|
for (int i = 0; i < num_children; ++i) {
|
|
r_unify(children.get_child(i));
|
|
}
|
|
}
|