560 lines
16 KiB
C++
560 lines
16 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 softNodeTree.cxx
|
|
* @author masad
|
|
* @date 2003-09-26
|
|
*/
|
|
|
|
// Includes
|
|
|
|
#include "softNodeTree.h"
|
|
#include "softEggGroupUserData.h"
|
|
#include "config_softegg.h"
|
|
#include "eggGroup.h"
|
|
#include "eggTable.h"
|
|
#include "eggXfmSAnim.h"
|
|
#include "eggData.h"
|
|
#include "softToEggConverter.h"
|
|
#include "dcast.h"
|
|
|
|
#include <SAA.h>
|
|
|
|
/**
|
|
*
|
|
*/
|
|
SoftNodeTree::
|
|
SoftNodeTree() {
|
|
_root = new SoftNodeDesc(NULL, "----root");
|
|
_root->fullname = "----root";
|
|
_fps = 0.0;
|
|
_use_prefix = 0;
|
|
_search_prefix = NULL;
|
|
_egg_data = (EggData *)NULL;
|
|
_egg_root = (EggGroupNode *)NULL;
|
|
_skeleton_node = (EggGroupNode *)NULL;
|
|
}
|
|
/**
|
|
* Given an element, return a copy of the element's name WITHOUT prefix.
|
|
*/
|
|
char *SoftNodeTree::
|
|
GetName( SAA_Scene *scene, SAA_Elem *element ) {
|
|
int nameLen;
|
|
char *name;
|
|
|
|
// get the name
|
|
SAA_elementGetNameLength( scene, element, &nameLen );
|
|
name = new char[++nameLen];
|
|
SAA_elementGetName( scene, element, nameLen, name );
|
|
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* Given an element, return a copy of the element's name complete with prefix.
|
|
*/
|
|
char *SoftNodeTree::
|
|
GetFullName( SAA_Scene *scene, SAA_Elem *element )
|
|
{
|
|
int nameLen, prefixLen;
|
|
char *name, *prefix;
|
|
|
|
// get the name length
|
|
SAA_elementGetNameLength( scene, element, &nameLen );
|
|
// get the prefix length
|
|
SAA_elementGetPrefixLength( scene, element, &prefixLen );
|
|
// allocate the array to hold name
|
|
name = new char[++nameLen];
|
|
// allocate the array to hold prefix and length + hyphen
|
|
prefix = new char[++prefixLen + nameLen + 4];
|
|
// get the name
|
|
SAA_elementGetName( scene, element, nameLen, name );
|
|
// get the prefix
|
|
SAA_elementGetPrefix( scene, element, prefixLen, prefix );
|
|
// add 'em together
|
|
strcat(prefix, "-");
|
|
strcat(prefix, name);
|
|
|
|
// return string
|
|
return prefix;
|
|
}
|
|
|
|
/**
|
|
* Given an element, return a string containing the contents of its MODEL NOTE
|
|
* entry
|
|
*/
|
|
char *SoftNodeTree::
|
|
GetModelNoteInfo( SAA_Scene *scene, SAA_Elem *model ) {
|
|
int size;
|
|
char *modelNote = NULL;
|
|
SAA_Boolean bigEndian;
|
|
|
|
SAA_elementGetUserDataSize( scene, model, "MNOT", &size );
|
|
|
|
if ( size != 0 ) {
|
|
// allocate modelNote string
|
|
modelNote = new char[size + 1];
|
|
|
|
// get ModelNote data from this model
|
|
SAA_elementGetUserData( scene, model, "MNOT", size,
|
|
&bigEndian, (void *)modelNote );
|
|
|
|
// strip off newline, if present
|
|
char *eol = (char *)memchr( modelNote, '\n', size );
|
|
if ( eol != NULL)
|
|
*eol = '\0';
|
|
else
|
|
modelNote[size] = '\0';
|
|
|
|
softegg_cat.spam() << "\nmodelNote = " << modelNote << endl;
|
|
}
|
|
|
|
return modelNote;
|
|
}
|
|
|
|
/**
|
|
* Given a string, return a copy of the string up to the first occurence of
|
|
* '-'.
|
|
*/
|
|
char *SoftNodeTree::
|
|
GetRootName( const char *name ) {
|
|
const char *hyphen;
|
|
char *root;
|
|
int len;
|
|
|
|
hyphen = strchr( name, '-' );
|
|
len = hyphen-name;
|
|
|
|
if ( (hyphen != NULL) && len ) {
|
|
root = new char[len+1];
|
|
strncpy( root, name, len );
|
|
root[len] = '\0';
|
|
}
|
|
else {
|
|
root = new char[strlen(name)+1];
|
|
strcpy( root, name );
|
|
}
|
|
return( root );
|
|
}
|
|
|
|
/**
|
|
* Walks through the complete Soft hierarchy and builds up the corresponding
|
|
* tree.
|
|
*/
|
|
bool SoftNodeTree::
|
|
build_complete_hierarchy(SAA_Scene &scene, SAA_Database &database) {
|
|
SI_Error status;
|
|
SoftNodeDesc *node;
|
|
|
|
// Get the entire Soft scene.
|
|
int numModels;
|
|
SAA_Elem *models;
|
|
|
|
SAA_sceneGetNbModels( &scene, &numModels );
|
|
softegg_cat.spam() << "Scene has " << numModels << " model(s)...\n";
|
|
|
|
// This while loop walks through the entire Soft hierarchy, one node at a
|
|
// time.
|
|
bool all_ok = true;
|
|
if ( numModels ) {
|
|
// allocate array of models
|
|
models = (SAA_Elem *) new SAA_Elem[numModels];
|
|
if ( models != NULL ) {
|
|
if ((status = SAA_sceneGetModels( &scene, numModels, models )) != SI_SUCCESS) {
|
|
return false;
|
|
}
|
|
for ( int i = 0; i < numModels; i++ ) {
|
|
int level;
|
|
status = SAA_elementGetHierarchyLevel( &scene, &models[i], &level );
|
|
softegg_cat.spam() << "model[" << i << "]" << endl;
|
|
softegg_cat.spam() << " level " << level << endl;
|
|
softegg_cat.spam() << " status is " << status << "\n";
|
|
|
|
node = build_node(&scene, &models[i]);
|
|
if (!level && node)
|
|
node->set_parent(_root);
|
|
}
|
|
}
|
|
}
|
|
|
|
softegg_cat.spam() << "jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj\n";
|
|
|
|
// check the nodes that are junk for animationartist control purposes
|
|
_root->check_junk(false);
|
|
|
|
softegg_cat.spam() << "jpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjpjp\n";
|
|
|
|
// check the nodes that are parent of ancestors of a joint
|
|
_root->check_joint_parent();
|
|
|
|
softegg_cat.spam() << "pppppppppppppppppppppppppppppppppppppppppppppppppppppppp\n";
|
|
|
|
// check the nodes that are pseudo joints
|
|
_root->check_pseudo_joints(false);
|
|
|
|
softegg_cat.spam() << "========================================================\n";
|
|
|
|
// find _parentJoint for each node
|
|
_root->set_parentJoint(&scene, NULL);
|
|
|
|
return all_ok;
|
|
}
|
|
#if 0
|
|
/**
|
|
* Walks through the selected subset of the Soft hierarchy (or the complete
|
|
* hierarchy, if nothing is selected) and builds up the corresponding tree.
|
|
*/
|
|
bool SoftNodeTree::
|
|
build_selected_hierarchy(char *scene_name) {
|
|
MStatus status;
|
|
|
|
MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status);
|
|
if (!status) {
|
|
status.perror("MItDag constructor");
|
|
return false;
|
|
}
|
|
|
|
// Get only the selected geometry.
|
|
MSelectionList selection;
|
|
status = MGlobal::getActiveSelectionList(selection);
|
|
if (!status) {
|
|
status.perror("MGlobal::getActiveSelectionList");
|
|
return false;
|
|
}
|
|
|
|
// Get the selected geometry only if the selection is nonempty; otherwise,
|
|
// get the whole scene anyway.
|
|
if (selection.isEmpty()) {
|
|
softegg_cat.info()
|
|
<< "Selection list is empty.\n";
|
|
return build_complete_hierarchy();
|
|
}
|
|
|
|
bool all_ok = true;
|
|
unsigned int length = selection.length();
|
|
for (unsigned int i = 0; i < length; i++) {
|
|
MDagPath root_path;
|
|
status = selection.getDagPath(i, root_path);
|
|
if (!status) {
|
|
status.perror("MSelectionList::getDagPath");
|
|
} else {
|
|
// Now traverse through the selected dag path and all nested dag paths.
|
|
dag_iterator.reset(root_path);
|
|
while (!dag_iterator.isDone()) {
|
|
MDagPath dag_path;
|
|
status = dag_iterator.getPath(dag_path);
|
|
if (!status) {
|
|
status.perror("MItDag::getPath");
|
|
} else {
|
|
build_node(dag_path);
|
|
}
|
|
|
|
dag_iterator.next();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (all_ok) {
|
|
_root->check_pseudo_joints(false);
|
|
}
|
|
|
|
return all_ok;
|
|
}
|
|
#endif
|
|
/**
|
|
* Returns the total number of nodes in the hierarchy, not counting the root
|
|
* node.
|
|
*/
|
|
int SoftNodeTree::
|
|
get_num_nodes() const {
|
|
return _nodes.size();
|
|
}
|
|
|
|
/**
|
|
* Returns the nth node in the hierarchy, in an arbitrary ordering.
|
|
*/
|
|
SoftNodeDesc *SoftNodeTree::
|
|
get_node(int n) const {
|
|
nassertr(n >= 0 && n < (int)_nodes.size(), NULL);
|
|
return _nodes[n];
|
|
}
|
|
|
|
/**
|
|
* Returns the node named 'name' in the hierarchy, in an arbitrary ordering.
|
|
*/
|
|
SoftNodeDesc *SoftNodeTree::
|
|
get_node(string name) const {
|
|
NodesByName::const_iterator ni = _nodes_by_name.find(name);
|
|
if (ni != _nodes_by_name.end())
|
|
return (*ni).second;
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Removes all of the references to generated egg structures from the tree,
|
|
* and prepares the tree for generating new egg structures.
|
|
*/
|
|
void SoftNodeTree::
|
|
clear_egg(EggData *egg_data, EggGroupNode *egg_root,
|
|
EggGroupNode *skeleton_node) {
|
|
_root->clear_egg();
|
|
_egg_data = egg_data;
|
|
_egg_root = egg_root;
|
|
_skeleton_node = skeleton_node;
|
|
}
|
|
|
|
/**
|
|
* Returns the EggGroupNode corresponding to the group or joint for the
|
|
* indicated node. Creates the group node if it has not already been created.
|
|
*/
|
|
EggGroup *SoftNodeTree::
|
|
get_egg_group(SoftNodeDesc *node_desc) {
|
|
nassertr(_egg_root != (EggGroupNode *)NULL, NULL);
|
|
|
|
// lets print some relationship
|
|
softegg_cat.spam() << " group " << node_desc->get_name() << "(" << node_desc->_egg_group << ")";
|
|
if (node_desc->_parent)
|
|
softegg_cat.spam() << " parent " << node_desc->_parent->get_name() << "(" << node_desc->_parent << ")";
|
|
else
|
|
softegg_cat.spam() << " parent " << node_desc->_parent;
|
|
softegg_cat.spam() << endl;
|
|
|
|
if (node_desc->_egg_group == (EggGroup *)NULL) {
|
|
// We need to make a new group node.
|
|
EggGroup *egg_group;
|
|
|
|
egg_group = new EggGroup(node_desc->get_name());
|
|
if (node_desc->is_joint()) {
|
|
egg_group->set_group_type(EggGroup::GT_joint);
|
|
}
|
|
|
|
if (stec.flatten || (!node_desc->_parentJoint || node_desc->_parentJoint == _root)) {
|
|
// The parent is the root.
|
|
softegg_cat.spam() << "came hereeeee\n";
|
|
_egg_root->add_child(egg_group);
|
|
} else {
|
|
// The parent is another node.
|
|
EggGroup *parent_egg_group = get_egg_group(node_desc->_parentJoint);
|
|
parent_egg_group->add_child(egg_group);
|
|
}
|
|
|
|
node_desc->_egg_group = egg_group;
|
|
}
|
|
|
|
return node_desc->_egg_group;
|
|
}
|
|
|
|
/**
|
|
* Returns the EggTable corresponding to the joint for the indicated node.
|
|
* Creates the table node if it has not already been created.
|
|
*/
|
|
EggTable *SoftNodeTree::
|
|
get_egg_table(SoftNodeDesc *node_desc) {
|
|
nassertr(_skeleton_node != (EggGroupNode *)NULL, NULL);
|
|
nassertr(node_desc->is_joint(), NULL);
|
|
|
|
// lets print some relationship
|
|
softegg_cat.spam() << " group " << node_desc->get_name() << "(" << node_desc->_egg_group << ")";
|
|
if (node_desc->_parent)
|
|
softegg_cat.spam() << " parent " << node_desc->_parent->get_name() << "(" << node_desc->_parent << ")";
|
|
else
|
|
softegg_cat.spam() << " parent " << node_desc->_parent;
|
|
softegg_cat.spam() << endl;
|
|
|
|
if (node_desc->_egg_table == (EggTable *)NULL) {
|
|
softegg_cat.spam() << "creating a new table\n";
|
|
// We need to make a new table node. nassertr(node_desc->_parent !=
|
|
// (SoftNodeDesc *)NULL, NULL);
|
|
|
|
EggTable *egg_table = new EggTable(node_desc->get_name());
|
|
node_desc->_anim = new EggXfmSAnim("xform", _egg_data->get_coordinate_system());
|
|
node_desc->_anim->set_fps(_fps);
|
|
egg_table->add_child(node_desc->_anim);
|
|
|
|
if (stec.flatten || (!node_desc->_parentJoint || node_desc->_parentJoint == _root)) {
|
|
// if (!node_desc->_parent->is_joint()) { The parent is not a joint; put
|
|
// it at the top.
|
|
_skeleton_node->add_child(egg_table);
|
|
} else {
|
|
// The parent is another joint.
|
|
EggTable *parent_egg_table = get_egg_table(node_desc->_parentJoint);
|
|
parent_egg_table->add_child(egg_table);
|
|
}
|
|
|
|
node_desc->_egg_table = egg_table;
|
|
}
|
|
|
|
return node_desc->_egg_table;
|
|
}
|
|
|
|
/**
|
|
* Returns the anim table corresponding to the joint for the indicated node.
|
|
* Creates the table node if it has not already been created.
|
|
*/
|
|
EggXfmSAnim *SoftNodeTree::
|
|
get_egg_anim(SoftNodeDesc *node_desc) {
|
|
get_egg_table(node_desc);
|
|
return node_desc->_anim;
|
|
}
|
|
|
|
/**
|
|
* Sets joint information for MNILL node
|
|
*/
|
|
void SoftNodeTree::
|
|
handle_null(SAA_Scene *scene, SoftNodeDesc *node_desc, const char *node_name) {
|
|
const char *name = node_name;
|
|
SAA_AlgorithmType algo;
|
|
SAA_Elem *model = node_desc->get_model();
|
|
|
|
SAA_modelGetAlgorithm( scene, model, &algo );
|
|
softegg_cat.spam() << " null algorithm: " << algo << endl;
|
|
|
|
if ( algo == SAA_ALG_INV_KIN ) {
|
|
// MakeJoint( &scene, lastJoint, lastAnim, model, name );
|
|
node_desc->set_joint();
|
|
softegg_cat.spam() << " encountered IK root: " << name << endl;
|
|
}
|
|
else if ( algo == SAA_ALG_INV_KIN_LEAF ) {
|
|
// MakeJoint( &scene, lastJoint, lastAnim, model, name );
|
|
node_desc->set_joint();
|
|
softegg_cat.spam() << " encountered IK leaf: " << name << endl;
|
|
}
|
|
else if ( algo == SAA_ALG_STANDARD ) {
|
|
SAA_Boolean isSkeleton = FALSE;
|
|
softegg_cat.spam() << " encountered Standard null: " << name << endl;
|
|
|
|
SAA_modelIsSkeleton( scene, model, &isSkeleton );
|
|
|
|
// check to see if this NULL is used as a skeleton or is animated via
|
|
// constraint only ( these nodes are tagged by the animator with the
|
|
// keyword "joint" somewhere in the nodes name)
|
|
if ( isSkeleton || (strstr( name, "joint" ) != NULL) ) {
|
|
// MakeJoint( &scene, lastJoint, lastAnim, model, name );
|
|
node_desc->set_joint();
|
|
softegg_cat.spam() << " animating Standard null!!!\n";
|
|
softegg_cat.spam() << "isSkeleton: " << isSkeleton << endl;
|
|
}
|
|
}
|
|
else
|
|
softegg_cat.spam() << " encountered some other NULL: " << algo << endl;
|
|
}
|
|
|
|
/**
|
|
* Returns a pointer to the node corresponding to the indicated dag_path
|
|
* object, creating it first if necessary.
|
|
*/
|
|
SoftNodeDesc *SoftNodeTree::
|
|
build_node(SAA_Scene *scene, SAA_Elem *model) {
|
|
char *name, *fullname;
|
|
string node_name;
|
|
int numChildren;
|
|
int thisChild;
|
|
SAA_Elem *children;
|
|
SAA_ModelType type;
|
|
SAA_Boolean isSkeleton = FALSE;
|
|
|
|
fullname = GetFullName(scene, model);
|
|
if (_use_prefix)
|
|
name = fullname;
|
|
else
|
|
name = GetName(scene, model);
|
|
|
|
node_name = name;
|
|
|
|
SoftNodeDesc *node_desc = r_build_node(NULL, node_name);
|
|
|
|
node_desc->fullname = fullname;
|
|
node_desc->set_model(model);
|
|
SAA_modelIsSkeleton( scene, model, &isSkeleton );
|
|
|
|
// find out what type of node we're dealing with
|
|
SAA_modelGetType( scene, node_desc->get_model(), &type );
|
|
|
|
if (type == SAA_MJNT || isSkeleton || (strstr(node_desc->get_name().c_str(), "joint") != NULL))
|
|
node_desc->set_joint();
|
|
|
|
// treat the MNILL differently, because it needs to detect and set some
|
|
// joints
|
|
if (type == SAA_MNILL)
|
|
handle_null(scene, node_desc, name);
|
|
|
|
if (node_desc->is_joint())
|
|
softegg_cat.spam() << "type: " << type << " isSkeleton: " << isSkeleton << endl;
|
|
|
|
// get to the children
|
|
SAA_modelGetNbChildren( scene, model, &numChildren );
|
|
softegg_cat.spam() << " Model " << node_name << " children: " << numChildren << endl;
|
|
|
|
if ( numChildren ) {
|
|
children = new SAA_Elem[numChildren];
|
|
SAA_modelGetChildren( scene, model, numChildren, children );
|
|
if (!children)
|
|
softegg_cat.info() << "Not enough Memory for children...\n";
|
|
|
|
for ( thisChild = 0; thisChild < numChildren; thisChild++ ) {
|
|
fullname = GetFullName(scene, &children[thisChild]);
|
|
if (_use_prefix)
|
|
node_name = fullname;
|
|
else
|
|
node_name = GetName(scene, &children[thisChild]);
|
|
|
|
softegg_cat.spam() << " building child " << thisChild << "...";
|
|
|
|
SoftNodeDesc *node_child = r_build_node(node_desc, node_name);
|
|
|
|
node_child->fullname = fullname;
|
|
node_child->set_model(&children[thisChild]);
|
|
SAA_modelIsSkeleton( scene, &children[thisChild], &isSkeleton );
|
|
|
|
// find out what type of node we're dealing with
|
|
SAA_modelGetType( scene, node_child->get_model(), &type );
|
|
|
|
if (type == SAA_MJNT || isSkeleton || (strstr(node_child->get_name().c_str(), "joint") != NULL))
|
|
node_child->set_joint();
|
|
|
|
// treat the MNILL differently, because it needs to detect and set some
|
|
// joints
|
|
if (type == SAA_MNILL)
|
|
handle_null(scene, node_child, node_name.c_str());
|
|
|
|
if (node_child->is_joint())
|
|
softegg_cat.spam() << "type: " << type << " isSkeleton: " << isSkeleton << endl;
|
|
}
|
|
}
|
|
return node_desc;
|
|
}
|
|
|
|
/**
|
|
* The recursive implementation of build_node().
|
|
*/
|
|
SoftNodeDesc *SoftNodeTree::
|
|
r_build_node(SoftNodeDesc *parent_node, const string &name) {
|
|
SoftNodeDesc *node_desc;
|
|
|
|
// If we have already encountered this pathname, return the corresponding
|
|
// SoftNodeDesc immediately.
|
|
NodesByName::const_iterator ni = _nodes_by_name.find(name);
|
|
if (ni != _nodes_by_name.end()) {
|
|
softegg_cat.spam() << " already built node " << (*ni).first;
|
|
node_desc = (*ni).second;
|
|
node_desc->set_parent(parent_node);
|
|
return node_desc;
|
|
}
|
|
|
|
// Otherwise, we have to create it. Do this recursively, so we create each
|
|
// node along the path.
|
|
node_desc = new SoftNodeDesc(parent_node, name);
|
|
|
|
softegg_cat.spam() << " node name : " << name << endl;
|
|
_nodes.push_back(node_desc);
|
|
|
|
_nodes_by_name.insert(NodesByName::value_type(name, node_desc));
|
|
|
|
return node_desc;
|
|
}
|