/** * 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 using std::endl; /** * */ SoftNodeTree:: SoftNodeTree() { _root = new SoftNodeDesc(nullptr, "----root"); _root->fullname = "----root"; _fps = 0.0; _use_prefix = 0; _search_prefix = nullptr; _egg_data = nullptr; _egg_root = nullptr; _skeleton_node = nullptr; } /** * 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 = nullptr; 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 != nullptr) *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 != nullptr) && 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 != nullptr ) { 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, nullptr); 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(), nullptr); return _nodes[n]; } /** * Returns the node named 'name' in the hierarchy, in an arbitrary ordering. */ SoftNodeDesc *SoftNodeTree:: get_node(std::string name) const { NodesByName::const_iterator ni = _nodes_by_name.find(name); if (ni != _nodes_by_name.end()) return (*ni).second; return nullptr; } /** * 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 != nullptr, nullptr); // 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 == nullptr) { // 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 != nullptr, nullptr); nassertr(node_desc->is_joint(), nullptr); // 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 == nullptr) { 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" ) != nullptr) ) { // 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; std::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(nullptr, 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") != nullptr)) 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") != nullptr)) 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 std::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; }