234 lines
6.9 KiB
C++
234 lines
6.9 KiB
C++
/**
|
|
* PANDA 3D SOFTWARE
|
|
* Copyright (c) Carnegie Mellon University. All rights reserved.
|
|
*
|
|
* All use of this software is subject to the terms of the revised BSD
|
|
* license. You should have received a copy of this license along
|
|
* with this source code in a file named "LICENSE."
|
|
*
|
|
* @file auto_bind.cxx
|
|
* @author drose
|
|
* @date 1999-02-23
|
|
*/
|
|
|
|
#include "auto_bind.h"
|
|
#include "animBundleNode.h"
|
|
#include "partBundleNode.h"
|
|
#include "config_chan.h"
|
|
#include "string_utils.h"
|
|
#include "partGroup.h"
|
|
|
|
typedef pset<AnimBundle *> AnimBundles;
|
|
typedef pmap<string, AnimBundles> Anims;
|
|
|
|
typedef pset<PartBundle *> PartBundles;
|
|
typedef pmap<string, PartBundles> Parts;
|
|
|
|
|
|
/**
|
|
* A support function for auto_bind(), below. Given a set of AnimBundles and
|
|
* a set of PartBundles that all share the same name, perform whatever
|
|
* bindings make sense.
|
|
*/
|
|
static void
|
|
bind_anims(const PartBundles &parts, const AnimBundles &anims,
|
|
AnimControlCollection &controls,
|
|
int hierarchy_match_flags) {
|
|
PartBundles::const_iterator pbi;
|
|
|
|
for (pbi = parts.begin(); pbi != parts.end(); ++pbi) {
|
|
PartBundle *part = (*pbi);
|
|
AnimBundles::const_iterator abi;
|
|
for (abi = anims.begin(); abi != anims.end(); ++abi) {
|
|
AnimBundle *anim = (*abi);
|
|
if (chan_cat.is_info()) {
|
|
chan_cat.info()
|
|
<< "Attempting to bind " << *part << " to " << *anim << "\n";
|
|
}
|
|
|
|
PT(AnimControl) control =
|
|
part->bind_anim(anim, hierarchy_match_flags);
|
|
string name = (*abi)->get_name();
|
|
if (name.empty()) {
|
|
name = anim->get_name();
|
|
}
|
|
if (control != (AnimControl *)NULL) {
|
|
if (controls.find_anim(name) != (AnimControl *)NULL) {
|
|
// That name's already used; synthesize another one.
|
|
int index = 0;
|
|
string new_name;
|
|
do {
|
|
index++;
|
|
new_name = name + '.' + format_string(index);
|
|
} while (controls.find_anim(new_name) != (AnimControl *)NULL);
|
|
name = new_name;
|
|
}
|
|
|
|
controls.store_anim(control, name);
|
|
}
|
|
|
|
if (chan_cat.is_info()) {
|
|
if (control == (AnimControl *)NULL) {
|
|
chan_cat.info()
|
|
<< "Bind failed.\n";
|
|
} else {
|
|
chan_cat.info()
|
|
<< "Bind succeeded, index "
|
|
<< control->get_channel_index() << "; accessible as "
|
|
<< name << "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A support function for auto_bind(), below. Walks through the hierarchy and
|
|
* finds all of the PartBundles and AnimBundles.
|
|
*/
|
|
static void
|
|
r_find_bundles(PandaNode *node, Anims &anims, Parts &parts) {
|
|
if (node->is_of_type(AnimBundleNode::get_class_type())) {
|
|
AnimBundleNode *bn = DCAST(AnimBundleNode, node);
|
|
AnimBundle *bundle = bn->get_bundle();
|
|
anims[bundle->get_name()].insert(bundle);
|
|
|
|
} else if (node->is_of_type(PartBundleNode::get_class_type())) {
|
|
PartBundleNode *bn = DCAST(PartBundleNode, node);
|
|
int num_bundles = bn->get_num_bundles();
|
|
for (int i = 0; i < num_bundles; ++i) {
|
|
PartBundle *bundle = bn->get_bundle(i);
|
|
parts[bundle->get_name()].insert(bundle);
|
|
}
|
|
}
|
|
|
|
PandaNode::Children cr = node->get_children();
|
|
int num_children = cr.get_num_children();
|
|
for (int i = 0; i < num_children; i++) {
|
|
r_find_bundles(cr.get_child(i), anims, parts);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Walks the scene graph or subgraph beginning at the indicated node, and
|
|
* attempts to bind any AnimBundles found to their matching PartBundles, when
|
|
* possible.
|
|
*
|
|
* The list of all resulting AnimControls created is filled into controls.
|
|
*/
|
|
void
|
|
auto_bind(PandaNode *root_node, AnimControlCollection &controls,
|
|
int hierarchy_match_flags) {
|
|
// First, locate all the bundles in the subgraph.
|
|
Anims anims;
|
|
AnimBundles extra_anims;
|
|
Parts parts;
|
|
PartBundles extra_parts;
|
|
r_find_bundles(root_node, anims, parts);
|
|
|
|
if (chan_cat.is_debug()) {
|
|
int anim_count = 0;
|
|
Anims::const_iterator ai;
|
|
for (ai = anims.begin(); ai != anims.end(); ++ai) {
|
|
anim_count += (int)(*ai).second.size();
|
|
}
|
|
chan_cat.debug()
|
|
<< "Found " << anim_count << " anims:\n";
|
|
for (ai = anims.begin(); ai != anims.end(); ++ai) {
|
|
chan_cat.debug(false)
|
|
<< " " << (*ai).first;
|
|
if ((*ai).second.size() != 1) {
|
|
chan_cat.debug(false)
|
|
<< "*" << ((*ai).second.size());
|
|
}
|
|
}
|
|
chan_cat.debug(false)
|
|
<< "\n";
|
|
|
|
int part_count = 0;
|
|
Parts::const_iterator pi;
|
|
for (pi = parts.begin(); pi != parts.end(); ++pi) {
|
|
part_count += (int)(*pi).second.size();
|
|
}
|
|
chan_cat.debug()
|
|
<< "Found " << part_count << " parts:\n";
|
|
for (pi = parts.begin(); pi != parts.end(); ++pi) {
|
|
chan_cat.debug(false)
|
|
<< " " << (*pi).first;
|
|
if ((*pi).second.size() != 1) {
|
|
chan_cat.debug(false)
|
|
<< "*" << ((*pi).second.size());
|
|
}
|
|
}
|
|
chan_cat.debug(false)
|
|
<< "\n";
|
|
}
|
|
|
|
// Now, match up the bundles by name.
|
|
|
|
Anims::const_iterator ai = anims.begin();
|
|
Parts::const_iterator pi = parts.begin();
|
|
|
|
while (ai != anims.end() && pi != parts.end()) {
|
|
if ((*ai).first < (*pi).first) {
|
|
// Here's an anim with no matching parts.
|
|
if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
|
|
AnimBundles::const_iterator abi;
|
|
for (abi = (*ai).second.begin(); abi != (*ai).second.end(); ++abi) {
|
|
extra_anims.insert(*abi);
|
|
}
|
|
}
|
|
++ai;
|
|
|
|
} else if ((*pi).first < (*ai).first) {
|
|
// And here's a part with no matching anims.
|
|
if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
|
|
PartBundles::const_iterator pbi;
|
|
for (pbi = (*pi).second.begin(); pbi != (*pi).second.end(); ++pbi) {
|
|
extra_parts.insert(*pbi);
|
|
}
|
|
}
|
|
++pi;
|
|
|
|
} else {
|
|
// But here we have (at least one) match!
|
|
bind_anims((*pi).second, (*ai).second, controls,
|
|
hierarchy_match_flags);
|
|
++pi;
|
|
|
|
// We don't increment the anim counter yet. That way, the same anim may
|
|
// bind to multiple parts, if they all share the same name.
|
|
}
|
|
}
|
|
|
|
if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
|
|
// Continue searching through the remaining anims and parts.
|
|
|
|
while (ai != anims.end()) {
|
|
// Here's an anim with no matching parts.
|
|
if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
|
|
AnimBundles::const_iterator abi;
|
|
for (abi = (*ai).second.begin(); abi != (*ai).second.end(); ++abi) {
|
|
extra_anims.insert(*abi);
|
|
}
|
|
}
|
|
++ai;
|
|
}
|
|
|
|
while (pi != parts.end()) {
|
|
// And here's a part with no matching anims.
|
|
if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
|
|
PartBundles::const_iterator pbi;
|
|
for (pbi = (*pi).second.begin(); pbi != (*pi).second.end(); ++pbi) {
|
|
extra_parts.insert(*pbi);
|
|
}
|
|
}
|
|
++pi;
|
|
}
|
|
|
|
bind_anims(extra_parts, extra_anims, controls,
|
|
hierarchy_match_flags);
|
|
}
|
|
}
|