// Filename: softNodeDesc.cxx // Created by: masad (03Oct03) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2003, 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://www.panda3d.org/license.txt . // // To contact the maintainers of this program write to // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// #include "softNodeDesc.h" #include "config_softegg.h" #include "eggGroup.h" #include "eggXfmSAnim.h" #include "eggSAnimData.h" #include "softToEggConverter.h" #include "dcast.h" TypeHandle SoftNodeDesc::_type_handle; //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// SoftNodeDesc:: SoftNodeDesc(SoftNodeDesc *parent, const string &name) : Namable(name), _parent(parent) { _model = (SAA_Elem *)NULL; _egg_group = (EggGroup *)NULL; _egg_table = (EggTable *)NULL; _anim = (EggXfmSAnim *)NULL; _joint_type = JT_none; // Add ourselves to our parent. if (_parent != (SoftNodeDesc *)NULL) { softegg_cat.spam() << "parent name " << _parent->get_name(); _parent->_children.push_back(this); } // set the _parentJoint to Null _parentJoint = NULL; fullname = NULL; numTexLoc = 0; numTexGlb = 0; uScale = NULL; vScale = NULL; uOffset = NULL; vOffset = NULL; valid; uv_swap; // SAA_Boolean visible; numTexTri = NULL; textures = NULL; materials = NULL; triangles = NULL; gtype = SAA_GEOM_ORIGINAL; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::Destructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// SoftNodeDesc:: ~SoftNodeDesc() { if (_model != (SAA_Elem *)NULL) { delete _model; } } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::set_model // Access: Public // Description: Indicates an associated between the SoftNodeDesc and // some SAA_Elem instance. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: set_model(SAA_Elem *model) { _model = model; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::set_parent // Access: Public // Description: Sometimes, parent is not known at node creation // As soon as it is known, set the parent //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: set_parent(SoftNodeDesc *parent) { if (_parent) { softegg_cat.spam() << endl; /* softegg_cat.spam() << " expected _parent to be null!?\n"; if (_parent == parent) softegg_cat.spam() << " parent already set\n"; else { softegg_cat.spam() << " current parent " << _parent->get_name() << " new parent " << parent << endl; } */ return; } _parent = parent; softegg_cat.spam() << " set parent to " << _parent->get_name() << endl; // Add ourselves to our parent. _parent->_children.push_back(this); } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::set_parent // Access: Public // Description: Sometimes, parent is not known at node creation // As soon as it is known, set the parent //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: force_set_parent(SoftNodeDesc *parent) { if (_parent) softegg_cat.spam() << " current parent " << _parent->get_name(); _parent = parent; if (_parent) softegg_cat.spam() << " new parent " << _parent->get_name() << endl; // Add ourselves to our parent. _parent->_children.push_back(this); } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::has_model // Access: Public // Description: Returns true if a Soft dag path has been associated // with this node, false otherwise. //////////////////////////////////////////////////////////////////// bool SoftNodeDesc:: has_model() const { return (_model != (SAA_Elem *)NULL); } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::get_model // Access: Public // Description: Returns the SAA_Elem * associated with this node. It // is an error to call this unless has_model() // returned true. //////////////////////////////////////////////////////////////////// SAA_Elem *SoftNodeDesc:: get_model() const { nassertr(_model != (SAA_Elem *)NULL, _model); return _model; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::is_joint // Access: Private // Description: Returns true if the node should be treated as a joint // by the converter. //////////////////////////////////////////////////////////////////// bool SoftNodeDesc:: is_joint() const { // return _joint_type == JT_joint || _joint_type == JT_pseudo_joint; return _joint_type == JT_joint; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::is_junk // Access: Private // Description: Returns true if the node should be treated as a junk // by the converter. //////////////////////////////////////////////////////////////////// bool SoftNodeDesc:: is_junk() const { return _joint_type == JT_junk; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::set_joint // Access: Private // Description: sets the _joint_type to JT_joint //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: set_joint() { _joint_type = JT_joint; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::is_joint_parent // Access: Private // Description: Returns true if the node is the parent or ancestor of // a joint. //////////////////////////////////////////////////////////////////// bool SoftNodeDesc:: is_joint_parent() const { return _joint_type == JT_joint_parent; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::clear_egg // Access: Private // Description: Recursively clears the egg pointers from this node // and all children. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: clear_egg() { _egg_group = (EggGroup *)NULL; _egg_table = (EggTable *)NULL; _anim = (EggXfmSAnim *)NULL; Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); child->clear_egg(); } } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::mark_joint_parent // Access: Private // Description: Indicates that this node has at least one child that // is a joint or a pseudo-joint. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: mark_joint_parent() { if (_joint_type == JT_none) { _joint_type = JT_joint_parent; softegg_cat.spam() << " marked parent " << get_name(); } else softegg_cat.spam() << " ?parent " << get_name() << " joint type " << _joint_type; if (_parent != (SoftNodeDesc *)NULL) { _parent->mark_joint_parent(); } softegg_cat.spam() << endl; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::check_joint_parent // Access: Private // Description: Walks the hierarchy, if a node is joint, make // sure all its parents are marked JT_joint_parent //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: check_joint_parent() { Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); if (child->is_joint()) { softegg_cat.spam() << "child " << child->get_name(); mark_joint_parent(); } child->check_joint_parent(); } } /////////////////////////////////////////////////////////////////////// // Function: SoftNodeTree::check_junk // Access: Public // Description: check to see if this is a branch we don't want to // descend - this will prevent creating geometry for // animation control structures /////////////////////////////////////////////////////////////////////// void SoftNodeDesc:: check_junk(bool parent_junk) { const char *name = get_name().c_str(); if (parent_junk) { _joint_type = JT_junk; softegg_cat.spam() << "junk node " << get_name() << endl; } if ( (strstr(name, "con-") != NULL) || (strstr(name, "con_") != NULL) || (strstr(name, "fly_") != NULL) || (strstr(name, "fly-") != NULL) || (strstr(name, "camRIG") != NULL) || (strstr(name, "cam_rig") != NULL) || (strstr(name, "bars") != NULL) ) { _joint_type = JT_junk; softegg_cat.spam() << "junk node " << get_name() << endl; parent_junk = true; Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); softegg_cat.spam() << child->get_name() << ","; } softegg_cat.spam() << endl; } Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); child->check_junk(parent_junk); } } /////////////////////////////////////////////////////////////////////// // Function: SoftNodeTree::is_partial // Access: Public // Description: check to see if this is a selected branch we want to // descend - this will prevent creating geometry for // other parts /////////////////////////////////////////////////////////////////////// bool SoftNodeDesc:: is_partial(char *search_prefix) { const char *name = fullname; // if no search prefix then return false if (!search_prefix) return false; // if name is search_prefix, return false if (strstr(name, search_prefix) != NULL) { softegg_cat.debug() << "matched " << name << " "; return false; } // if name is not search_prefix, look in its parent if (strstr(name, search_prefix) == NULL) { softegg_cat.debug() << "node " << name << " "; if (_parent) return _parent->is_partial(search_prefix); } // neither name nor its parent is search_prefix return true; } /////////////////////////////////////////////////////////////////////// // Function: SoftNodeTree::set_parentJoint // Access: Public // Description: Go through the ancestors and figure out who is the // immediate _parentJoint of this node /////////////////////////////////////////////////////////////////////// void SoftNodeDesc:: set_parentJoint(SAA_Scene *scene, SoftNodeDesc *lastJoint) { if (is_junk()) return; //set its parent joint to the lastJoint _parentJoint = lastJoint; softegg_cat.spam() << get_name() << ": parent joint set to :" << lastJoint; if (lastJoint) softegg_cat.spam() << "(" << lastJoint->get_name() << ")"; softegg_cat.spam() << endl; // is this node a joint? SAA_Boolean isSkeleton = false; if (has_model()) SAA_modelIsSkeleton( scene, get_model(), &isSkeleton ); // if already a joint or name has "joint" in it const char *name = get_name().c_str(); if (is_joint() || isSkeleton || strstr(name, "joint") != NULL) { lastJoint = this; } if ( _parentJoint && strstr( _parentJoint->get_name().c_str(), "scale" ) != NULL ) { _parentJoint = NULL; // _parentJoint = lastJoint = NULL; softegg_cat.spam() << "scale joint flag set!\n"; } // look in the children Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); child->set_parentJoint(scene, lastJoint); } } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::check_pseudo_joints // Access: Private // Description: Walks the hierarchy, looking for non-joint nodes that // are both children and parents of a joint. These // nodes are deemed to be pseudo joints, since the // converter must treat them as joints. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: check_pseudo_joints(bool joint_above) { if (_joint_type == JT_joint_parent && joint_above) { // This is one such node: it is the parent of a joint // (JT_joint_parent is set), and it is the child of a joint // (joint_above is set). _joint_type = JT_pseudo_joint; softegg_cat.debug() << "pseudo " << get_name() << " case1\n"; } if (_joint_type == JT_joint) { // If this node is itself a joint, then joint_above is true for // all child nodes. joint_above = true; } // Don't bother traversing further if _joint_type is none or junk, since // that means this node has no joint children. if (_joint_type != JT_none && _joint_type != JT_junk) { bool any_joints = false; Children::const_iterator ci; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); child->check_pseudo_joints(joint_above); if (child->is_joint()) { softegg_cat.spam() << get_name() << " any_joint true by " << child->get_name() << endl; any_joints = true; } } // If any children qualify as joints, then any sibling nodes that // are parents of joints are also elevated to joints. if (any_joints) { bool all_joints = true; for (ci = _children.begin(); ci != _children.end(); ++ci) { SoftNodeDesc *child = (*ci); if (child->_joint_type == JT_joint_parent) { child->_joint_type = JT_pseudo_joint; softegg_cat.debug() << "pseudo " << child->get_name() << " case2 by parent " << get_name() << "\n"; } else if (child->_joint_type == JT_none || child->_joint_type == JT_junk) { all_joints = false; } } if (all_joints || any_joints) { // Finally, if all children or at least one is a joint, then we are too. if (_joint_type == JT_joint_parent) { _joint_type = JT_pseudo_joint; softegg_cat.debug() << "pseudo " << get_name() << " case3\n"; } } } } else softegg_cat.spam() << "found null joint " << get_name() << endl; } //////////////////////////////////////////////////////////////////// // Function: SoftToEggConverter::get_transform // Access: Private // Description: Extracts the transform on the indicated Soft node, // and applies it to the corresponding Egg node. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: get_transform(SAA_Scene *scene, EggGroup *egg_group, bool global) { // Get the model's matrix int scale_joint = 0; if (!global && _parentJoint && !stec.flatten && !scale_joint) { SAA_modelGetMatrix( scene, get_model(), SAA_COORDSYS_LOCAL, matrix ); softegg_cat.debug() << get_name() << " using local matrix :parent "; } else { SAA_modelGetMatrix( scene, get_model(), SAA_COORDSYS_GLOBAL, matrix ); softegg_cat.debug() << get_name() << " using global matrix :parent "; } if (_parentJoint && !stec.flatten) softegg_cat.debug() << _parentJoint->get_name() << endl; else softegg_cat.debug() << _parentJoint << endl; softegg_cat.spam() << "model matrix = " << matrix[0][0] << " " << matrix[0][1] << " " << matrix[0][2] << " " << matrix[0][3] << "\n"; softegg_cat.spam() << "model matrix = " << matrix[1][0] << " " << matrix[1][1] << " " << matrix[1][2] << " " << matrix[1][3] << "\n"; softegg_cat.spam() << "model matrix = " << matrix[2][0] << " " << matrix[2][1] << " " << matrix[2][2] << " " << matrix[2][3] << "\n"; softegg_cat.spam() << "model matrix = " << matrix[3][0] << " " << matrix[3][1] << " " << matrix[3][2] << " " << matrix[3][3] << "\n"; if (!global && is_joint()) { LMatrix4d m4d(matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3], matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3], matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3], matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]); if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) { egg_group->set_transform(m4d); softegg_cat.spam() << "set transform in egg_group\n"; } } return; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::get_joint_transform // Access: Private // Description: Extracts the transform on the indicated Soft node, // as appropriate for a joint in an animated character, // and applies it to the indicated node. This is // different from get_transform() in that it does not // respect the _transform_type flag, and it does not // consider the relative transforms within the egg file. // more added functionality: now fills in components of // anim (EffXfmSAnim) class (masad). //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: get_joint_transform(SAA_Scene *scene, EggGroup *egg_group, EggXfmSAnim *anim, bool global) { // SI_Error result; SAA_Elem *skeletonPart = _model; const char *name = get_name().c_str(); if ( skeletonPart != NULL ) { float i,j,k; float h,p,r; float x,y,z; int scale_joint = 0; softegg_cat.spam() << "\n\nanimating child " << name << endl; if (_parentJoint && !stec.flatten && !scale_joint ) { softegg_cat.debug() << "using local matrix\n"; //get SAA orientation SAA_modelGetRotation( scene, skeletonPart, SAA_COORDSYS_LOCAL, &p, &h, &r ); //get SAA translation SAA_modelGetTranslation( scene, skeletonPart, SAA_COORDSYS_LOCAL, &x, &y, &z ); //get SAA scaling SAA_modelGetScaling( scene, skeletonPart, SAA_COORDSYS_LOCAL, &i, &j, &k ); } else { softegg_cat.debug() << " using global matrix\n"; //get SAA orientation SAA_modelGetRotation( scene, skeletonPart, SAA_COORDSYS_GLOBAL, &p, &h, &r ); //get SAA translation SAA_modelGetTranslation( scene, skeletonPart, SAA_COORDSYS_GLOBAL, &x, &y, &z ); //get SAA scaling SAA_modelGetScaling( scene, skeletonPart, SAA_COORDSYS_GLOBAL, &i, &j, &k ); } softegg_cat.spam() << "\nanim data: " << i << " " << j << " " << k << endl; softegg_cat.spam() << "\t" << p << " " << h << " " << r << endl; softegg_cat.spam() << "\t" << x << " " << y << " " << z << endl; // make sure the ordering is correct anim->set_order(anim->get_standard_order()); // Add each component by their names anim->add_component_data("i", i); anim->add_component_data("j", j); anim->add_component_data("k", k); anim->add_component_data("p", p); anim->add_component_data("h", h); anim->add_component_data("r", r); anim->add_component_data("x", x); anim->add_component_data("y", y); anim->add_component_data("z", z); } else { softegg_cat.debug() << "Cannot build anim table - no skeleton\n"; } } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::load_poly_model // Access: Private // Description: Converts the indicated Soft polyset to a bunch of // EggPolygons and parents them to the indicated egg // group. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: load_poly_model(SAA_Scene *scene, SAA_ModelType type) { SI_Error result; const char *name = get_name().c_str(); int i; int id = 0; // if making a pose - get deformed geometry if ( stec.make_pose ) gtype = SAA_GEOM_DEFORMED; // If the model is a PATCH in soft, set its step before tesselating else if ( type == SAA_MPTCH ) SAA_patchSetStep( scene, _model, stec.nurbs_step, stec.nurbs_step ); // Get the number of triangles result = SAA_modelGetNbTriangles( scene, _model, gtype, id, &numTri); softegg_cat.spam() << "triangles: " << numTri << "\n"; if ( result != SI_SUCCESS ) { softegg_cat.spam() << "Error: couldn't get number of triangles!\n"; softegg_cat.debug() << "\tbailing on model: " << name << "\n"; return; } // check to see if surface is also skeleton... SAA_Boolean isSkeleton = FALSE; SAA_modelIsSkeleton( scene, _model, &isSkeleton ); // check to see if this surface 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) softegg_cat.spam() << "is Skeleton? " << isSkeleton << "\n"; /*************************************************************************************/ // model is not a null and has no triangles! if ( !numTri ) { softegg_cat.spam() << "no triangles!\n"; } else { // allocate array of triangles triangles = (SAA_SubElem *) new SAA_SubElem[numTri]; if (!triangles) { softegg_cat.info() << "Not enough Memory for triangles...\n"; exit(1); } // triangulate model and read the triangles into array SAA_modelGetTriangles( scene, _model, gtype, id, numTri, triangles ); softegg_cat.spam() << "got triangles\n"; /***********************************************************************************/ // allocate array of materials (Asad: it gives a warning if try to get one triangle // at a time...investigate later // read each triangle's material into array materials = (SAA_Elem*) new SAA_Elem[numTri]; SAA_triangleGetMaterials( scene, _model, numTri, triangles, materials ); if (!materials) { softegg_cat.info() << "Not enough Memory for materials...\n"; exit(1); } softegg_cat.spam() << "got materials\n"; /***********************************************************************************/ // allocate array of textures per triangle numTexTri = new int[numTri]; const void *relinfo; // find out how many local textures per triangle for (i = 0; i < numTri; i++) { result = SAA_materialRelationGetT2DLocNbElements( scene, &materials[i], FALSE, &relinfo, &numTexTri[i] ); // polytex if ( result == SI_SUCCESS ) numTexLoc += numTexTri[i]; } // don't need this anymore... //free( numTexTri ); // get local textures if present if ( numTexLoc ) { softegg_cat.spam() << "numTexLoc = " << numTexLoc << endl; // allocate arrays of texture info uScale = new float[numTri]; vScale = new float[numTri]; uOffset = new float[numTri]; vOffset = new float[numTri]; texNameArray = new char *[numTri]; // ASSUME only one texture per material textures = new SAA_Elem[numTri]; for ( i = 0; i < numTri; i++ ) { // and read all referenced local textures into array SAA_materialRelationGetT2DLocElements( scene, &materials[i], TEX_PER_MAT , &textures[i] ); // initialize the array value texNameArray[i] = NULL; // see if this triangle has texture info if (numTexTri[i] == 0) continue; // check to see if texture is present result = SAA_elementIsValid( scene, &textures[i], &valid ); if ( result != SI_SUCCESS ) softegg_cat.spam() << "SAA_elementIsValid failed!!!!\n"; // texture present - get the name and uv info if ( valid ) { // according to drose, we don't need to convert .pic files to .rgb, // panda can now read the .pic files. texNameArray[i] = stec.GetTextureName(scene, &textures[i]); softegg_cat.spam() << " tritex[" << i << "] named: " << texNameArray[i] << endl; SAA_texture2DGetUVSwap( scene, &textures[i], &uv_swap ); if ( uv_swap == TRUE ) softegg_cat.spam() << " swapping u and v...\n" ; SAA_texture2DGetUScale( scene, &textures[i], &uScale[i] ); SAA_texture2DGetVScale( scene, &textures[i], &vScale[i] ); SAA_texture2DGetUOffset( scene, &textures[i], &uOffset[i] ); SAA_texture2DGetVOffset( scene, &textures[i], &vOffset[i] ); softegg_cat.spam() << "tritex[" << i << "] uScale: " << uScale[i] << " vScale: " << vScale[i] << endl; softegg_cat.spam() << " uOffset: " << uOffset[i] << " vOffset: " << vOffset[i] << endl; SAA_texture2DGetRepeats( scene, &textures[i], &uRepeat, &vRepeat ); softegg_cat.spam() << "uRepeat = " << uRepeat << ", vRepeat = " << vRepeat << endl; } else { softegg_cat.spam() << "Invalid texture...\n"; softegg_cat.spam() << " tritex[" << i << "] named: (null)\n"; } } } else { // if no local textures, try to get global textures SAA_modelRelationGetT2DGlbNbElements( scene, _model, FALSE, &relinfo, &numTexGlb ); if ( numTexGlb ) { // ASSUME only one texture per model textures = new SAA_Elem; // get the referenced texture SAA_modelRelationGetT2DGlbElements( scene, _model, TEX_PER_MAT, textures ); softegg_cat.spam() << "numTexGlb = " << numTexGlb << endl; // check to see if texture is present SAA_elementIsValid( scene, textures, &valid ); if ( valid ) { // texture present - get the name and uv info SAA_texture2DGetUVSwap( scene, textures, &uv_swap ); if ( uv_swap == TRUE ) softegg_cat.spam() << " swapping u and v...\n"; // according to drose, we don't need to convert .pic files to .rgb, // panda can now read the .pic files. texNameArray = new char *[1]; *texNameArray = stec.GetTextureName(scene, textures); softegg_cat.spam() << " global tex named: " << *texNameArray << endl; // allocate arrays of texture info uScale = new float; vScale = new float; uOffset = new float; vOffset = new float; SAA_texture2DGetUScale( scene, textures, uScale ); SAA_texture2DGetVScale( scene, textures, vScale ); SAA_texture2DGetUOffset( scene, textures, uOffset ); SAA_texture2DGetVOffset( scene, textures, vOffset ); softegg_cat.spam() << " global tex uScale: " << *uScale << " vScale: " << *vScale << endl; softegg_cat.spam() << " uOffset: " << *uOffset << " vOffset: " << *vOffset << endl; SAA_texture2DGetRepeats( scene, textures, &uRepeat, &vRepeat ); softegg_cat.spam() << "uRepeat = " << uRepeat << ", vRepeat = " << vRepeat << endl; } else { softegg_cat.spam() << "Invalid Texture...\n"; } } } } softegg_cat.spam() << "got textures" << endl; } //////////////////////////////////////////////////////////////////// // Function: SoftNodeDesc::load_nurbs_model // Access: Private // Description: Converts the indicated Soft polyset to a bunch of // EggPolygons and parents them to the indicated egg // group. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: load_nurbs_model(SAA_Scene *scene, SAA_ModelType type) { SI_Error result; const char *name = get_name().c_str(); // if making a pose - get deformed geometry if ( stec.make_pose ) gtype = SAA_GEOM_DEFORMED; // If the model is a NURBS in soft, set its step before tesselating if ( type == SAA_MNSRF ) SAA_nurbsSurfaceSetStep( scene, _model, stec.nurbs_step, stec.nurbs_step ); // get the materials /***********************************************************************************/ const void *relinfo; SAA_modelRelationGetMatNbElements( scene, get_model(), FALSE, &relinfo, &numNurbMats ); softegg_cat.spam() << "nurbs surf has " << numNurbMats << " materials\n"; if ( numNurbMats ) { materials = new SAA_Elem[numNurbMats]; if (!materials) { softegg_cat.info() << "Out Of Memory on allocating materials\n"; exit(1); } SAA_modelRelationGetMatElements( scene, get_model(), relinfo, numNurbMats, materials ); softegg_cat.spam() << "got materials\n"; // get the textures /***********************************************************************************/ numNurbTexLoc = 0; numNurbTexGlb = 0; // find out how many local textures per NURBS surface // ASSUME it only has one material SAA_materialRelationGetT2DLocNbElements( scene, &materials[0], FALSE, &relinfo, &numNurbTexLoc ); // if present, get local textures if ( numNurbTexLoc ) { softegg_cat.spam() << name << " had " << numNurbTexLoc << " local tex\n"; nassertv(numNurbTexLoc == 1); textures = new SAA_Elem[numNurbTexLoc]; // get the referenced texture SAA_materialRelationGetT2DLocElements( scene, &materials[0], TEX_PER_MAT, &textures[0] ); } // if no locals, try to get globals else { SAA_modelRelationGetT2DGlbNbElements( scene, get_model(), FALSE, &relinfo, &numNurbTexGlb ); if ( numNurbTexGlb ) { softegg_cat.spam() << name << " had " << numNurbTexGlb << " global tex\n"; nassertv(numNurbTexGlb == 1); textures = new SAA_Elem[numNurbTexGlb]; // get the referenced texture SAA_modelRelationGetT2DGlbElements( scene, get_model(), TEX_PER_MAT, &textures[0] ); } } if ( numNurbTexLoc || numNurbTexGlb) { // allocate the texture name array texNameArray = new char *[1]; // allocate arrays of texture info uScale = new float; vScale = new float; uOffset = new float; vOffset = new float; // check to see if texture is present result = SAA_elementIsValid( scene, &textures[0], &valid ); if ( result != SI_SUCCESS ) softegg_cat.spam() << "SAA_elementIsValid failed!!!!\n"; // texture present - get the name and uv info if ( valid ) { // according to drose, we don't need to convert .pic files to .rgb, // panda can now read the .pic files. texNameArray[0] = stec.GetTextureName(scene, &textures[0]); softegg_cat.spam() << " tritex[0] named: " << texNameArray[0] << endl; SAA_texture2DGetUVSwap( scene, &textures[0], &uv_swap ); if ( uv_swap == TRUE ) softegg_cat.spam() << " swapping u and v...\n" ; SAA_texture2DGetUScale( scene, &textures[0], &uScale[0] ); SAA_texture2DGetVScale( scene, &textures[0], &vScale[0] ); SAA_texture2DGetUOffset( scene, &textures[0], &uOffset[0] ); SAA_texture2DGetVOffset( scene, &textures[0], &vOffset[0] ); softegg_cat.spam() << "tritex[0] uScale: " << uScale[0] << " vScale: " << vScale[0] << endl; softegg_cat.spam() << " uOffset: " << uOffset[0] << " vOffset: " << vOffset[0] << endl; SAA_texture2DGetRepeats( scene, &textures[0], &uRepeat, &vRepeat ); softegg_cat.spam() << "uRepeat = " << uRepeat << ", vRepeat = " << vRepeat << endl; } else { softegg_cat.spam() << "Invalid texture...\n"; softegg_cat.spam() << " tritex[0] named: (null)\n"; } } softegg_cat.spam() << "got textures\n"; } } //////////////////////////////////////////////////////////////////// // Function: find_shape_vert // Access: Public // Description: given a vertex, find its corresponding shape vertex // and return its index. //////////////////////////////////////////////////////////////////// int SoftNodeDesc:: find_shape_vert(LPoint3d p3d, SAA_DVector *vertices, int numVert) { int i, found = 0; for (i = 0; i < numVert && !found ; i++) { if ((p3d[0] == vertices[i].x) && (p3d[1] == vertices[i].y) && (p3d[2] == vertices[i].z)) { found = 1; softegg_cat.spam() << "found shape vert at index " << i << endl; } } if (!found ) i = -1; else i--; return i; } //////////////////////////////////////////////////////////////////// // Function: make_vertex_offsets // Access: Public // Description: Given a scene, a model , the vertices of its original // shape and its name find the difference between the // geometry of its key shapes and the models original // geometry and add morph vertices to the egg data to // reflect these changes. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: make_vertex_offsets(int numShapes) { int i, j; int offset; int numCV; char tableName[_MAX_PATH]; SAA_DVector *shapeVerts = NULL; SAA_DVector *uniqueVerts = NULL; SAA_Elem *model = get_model(); SAA_Scene *scene = &stec.scene; EggVertexPool *vpool = NULL; string vpool_name = get_name() + ".verts"; EggNode *t = stec._tree.get_egg_root()->find_child(vpool_name); if (t) DCAST_INTO_V(vpool, t); int numOrigVert = (int) vpool->size(); EggVertexPool::iterator vi; if ((type == SAA_MNSRF) && stec.make_nurbs) SAA_nurbsSurfaceSetStep( scene, model, stec.nurbs_step, stec.nurbs_step ); SAA_modelGetNbVertices( scene, model, &numCV ); // get the shape verts uniqueVerts = new SAA_DVector[numCV]; SAA_modelGetVertices( scene, model, SAA_GEOM_ORIGINAL, 0, numCV, uniqueVerts ); softegg_cat.spam() << numCV << " CV's\n"; for ( i = 0; i < numCV; i++ ) // convert vertices to global _VCT_X_MAT( uniqueVerts[i], uniqueVerts[i], matrix); softegg_cat.spam() << "uniqueVerts[" << i << "] = " << uniqueVerts[i].x << " " << uniqueVerts[i].y << " " << uniqueVerts[i].z << " " << uniqueVerts[i].w << endl; // iterate through for each key shape (except original) for ( i = 1; i < numShapes; i++ ) { sprintf(tableName, "%s.%d", get_name().c_str(), i); softegg_cat.spam() << "\nMaking geometry offsets for " << tableName << "...\n"; if ((type == SAA_MNSRF) && stec.make_nurbs) softegg_cat.spam() << "calculating NURBS morphs...\n"; else softegg_cat.spam() << "calculating triangle morphs...\n"; // get the shape verts shapeVerts = new SAA_DVector[numCV]; SAA_modelGetVertices( scene, model, SAA_GEOM_SHAPE, i+1, numCV, shapeVerts ); for ( j=0; j < numCV; j++ ) { // convert vertices to global _VCT_X_MAT( shapeVerts[j], shapeVerts[j], matrix); softegg_cat.spam() << "shapeVerts[" << j << "] = " << shapeVerts[j].x << " " << shapeVerts[j].y << " " << shapeVerts[j].z << endl; } softegg_cat.spam() << endl; // for every original vertex, compare to the corresponding // key shape vertex and see if a vertex offset is needed j = 0; for (vi = vpool->begin(); vi != vpool->end(); ++vi, ++j) { double dx, dy, dz; EggVertex *vert = (*vi); LPoint3d p3d = vert->get_pos3(); softegg_cat.spam() << "oVert[" << j << "] = " << p3d[0] << " " << p3d[1] << " " << p3d[2] << endl; if ((type == SAA_MNSRF) && stec.make_nurbs) { dx = shapeVerts[j].x - p3d[0]; dy = shapeVerts[j].y - p3d[1]; dz = shapeVerts[j].z - p3d[2]; softegg_cat.spam() << "global shapeVerts[" << j << "] = " << shapeVerts[j].x << " " << shapeVerts[j].y << " " << shapeVerts[j].z << " " << shapeVerts[j].w << endl; } else { // we need to map from original vertices // to triangle shape vertices here offset = find_shape_vert(p3d, uniqueVerts, numCV); dx = shapeVerts[offset].x - p3d[0]; dy = shapeVerts[offset].y - p3d[1]; dz = shapeVerts[offset].z - p3d[2]; softegg_cat.spam() << "global shapeVerts[" << offset << "] = " << shapeVerts[offset].x << " " << shapeVerts[offset].y << " " << shapeVerts[offset].z << endl; } softegg_cat.spam() << j << ": dx = " << dx << ", dy = " << dy << ", dz = " << dz << endl; // if change isn't negligible, make a morph vertex entry double total = fabs(dx)+fabs(dy)+fabs(dz); if ( total > 0.00001 ) { if ( vpool != NULL ) { // create offset LVector3d p(dx, dy, dz); EggMorphVertex *dxyz = new EggMorphVertex(tableName, p); // add the offset to the vertex vert->_dxyzs.insert(*dxyz); } else softegg_cat.spam() << "Error: couldn't find vertex pool " << vpool_name << endl; } // if total } //for j } //for i } //////////////////////////////////////////////////////////////////// // Function: make_morph_table // Access: Public // Description: Given a scene, a model, a name and a frame time, // determine what type of shape interpolation is // used and call the appropriate function to extract // the shape weight info for this frame... //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: make_morph_table( float time ) { int numShapes; SAA_Elem *model = NULL; SAA_AnimInterpType type; SAA_Scene *scene = &stec.scene; if (has_model()) model = get_model(); else return; // Get the number of key shapes SAA_modelGetNbShapes( scene, model, &numShapes ); if ( numShapes <= 0 ) { return; } stec.has_morph = true; softegg_cat.spam() << "make_morph_table: " << get_name() << " : num shapes: " << numShapes << endl; SAA_modelGetShapeInterpolation( scene, model, &type ); if ( type == SAA_ANIM_LINEAR || type == SAA_ANIM_CARDINAL ) { softegg_cat.spam() << "linear morph" << endl; make_linear_morph_table( numShapes, time ); } else { // must be weighted... // check first for expressions softegg_cat.spam() << "expression morph" << endl; make_expression_morph_table( numShapes, time ); } } //////////////////////////////////////////////////////////////////// // Function: make_linear_morph_table // Access: Public // Description: Given a scene, a model, its name, and the time, // get the shape fcurve for the model and determine // the shape weights for the given time and use them // to populate the morph table. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: make_linear_morph_table(int numShapes, float time) { int i; float curveVal; char tableName[_MAX_PATH]; SAA_Elem fcurve; //SAnimTable *thisTable; EggSAnimData *anim; SAA_Elem *model = get_model(); SAA_Scene *scene = &stec.scene; softegg_cat.spam() << "linear interp, getting fcurve\n"; SAA_modelFcurveGetShape( scene, model, &fcurve ); SAA_fcurveEval( scene, &fcurve, time, &curveVal ); softegg_cat.spam() << "at time " << time << ", fcurve for " << get_name() << " = " << curveVal << endl; float nextVal = 0.0f; // populate morph table values for this frame for ( i = 1; i < numShapes; i++ ) { // derive table name from the model name sprintf(tableName, "%s.%d", get_name().c_str(), i); softegg_cat.spam() << "Linear: looking for table '" << tableName << "'\n"; //find the morph table associated with this key shape anim = stec.find_morph_table(tableName); if ( anim != NULL ) { if ( i == (int)curveVal ) { if ( curveVal - i == 0 ) { anim->add_data(1.0f ); softegg_cat.spam() << "adding element 1.0f\n"; } else { anim->add_data(1.0f - (curveVal - i)); nextVal = curveVal - i; softegg_cat.spam() << "adding element " << 1.0f - (curveVal - i) << endl; } } else { if ( nextVal ) { anim->add_data(nextVal ); nextVal = 0.0f; softegg_cat.spam() << "adding element " << nextVal << endl; } else { anim->add_data(0.0f); softegg_cat.spam() << "adding element 0.0f\n"; } } softegg_cat.spam() <<" to '" << tableName << "'\n"; } else softegg_cat.spam() << i << " : Couldn't find table '" << tableName << "'\n"; } } //////////////////////////////////////////////////////////////////// // Function: make_weighted_morph_table // Access: Public // Description: Given a scene, a model, a list of all models in the // scene, the number of models in the scece, the number // of key shapes for this model, the name of the model // and the current time, determine what method of // controlling the shape weights is used and call the // appropriate routine. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: make_weighted_morph_table(int numShapes, float time) { float curveVal; SI_Error result; char tableName[_MAX_PATH]; SAA_Elem *weightCurves; //SAnimTable *thisTable; EggSAnimData *anim; SAA_Elem *model = get_model(); SAA_Scene *scene = &stec.scene; // allocate array of weight curves (one for each shape) weightCurves = new SAA_Elem[numShapes]; result = SAA_modelFcurveGetShapeWeights(scene, model, numShapes, weightCurves); if ( result == SI_SUCCESS ) { for ( int i = 1; i < numShapes; i++ ) { SAA_fcurveEval( scene, &weightCurves[i], time, &curveVal ); // make sure soft gave us a reasonable number //if (!isNum(curveVal)) //curveVal = 0.0f; softegg_cat.spam() << "at time " << time << ", weightCurve[" << i << "] for " << get_name() << " = " << curveVal << endl; // derive table name from the model name sprintf(tableName, "%s.%d", get_name().c_str(), i); // find and populate shape table softegg_cat.spam() << "Weight: looking for table '" << tableName << "'\n"; //find the morph table associated with this key shape anim = stec.find_morph_table(tableName); if ( anim != NULL ) { anim->add_data(curveVal); softegg_cat.spam() << "adding element " << curveVal << endl; } else softegg_cat.spam() << i << " : Couldn't find table '" << tableName << "'\n"; } } } //////////////////////////////////////////////////////////////////// // Function: make_expression_morph_table // Access: Public // Description: Given a scene, a model and its number of key shapes // generate a morph table describing transitions btwn // the key shapes by evaluating the positions of the // controlling sliders. //////////////////////////////////////////////////////////////////// void SoftNodeDesc:: make_expression_morph_table(int numShapes, float time) { //int j; int numExp; char *track; //float expVal; //float sliderVal; //char *tableName; //char *sliderName; //SAnimTable *thisTable; SAA_Elem *expressions; SI_Error result; SAA_Elem *model = get_model(); SAA_Scene *scene = &stec.scene; // populate morph table values for this frame // compose track name track = NULL; // find how many expressions for this shape SAA_elementGetNbExpressions( scene, model, track, FALSE, &numExp ); softegg_cat.spam() << get_name() << " has " << numExp << " RHS expressions\n"; if ( numExp ) { // get the expressions for this shape expressions = new SAA_Elem[numExp]; softegg_cat.spam() << "getting " << numExp << " RHS expressions...\n"; result = SAA_elementGetExpressions( scene, model, track, FALSE, numExp, expressions ); /* if ( !result ) { for ( j = 1; j < numExp; j++ ) { if ( verbose >= 2 ) { // debug see what we got int numvars; SAA_expressionGetNbVars( scene, &expressions[j], &numvars ); int *varnamelen; int *varstrlen; int expstrlen; varnamelen = (int *)malloc(sizeof(int)*numvars); varstrlen = (int *)malloc(sizeof(int)*numvars); SAA_expressionGetStringLengths( scene, &expressions[j], numvars, varnamelen, varstrlen, &expstrlen ); int *varnamesizes; int *varstrsizes; varnamesizes = (int *)malloc(sizeof(int)*numvars); varstrsizes = (int *)malloc(sizeof(int)*numvars); for ( int k = 0; k < numvars; k++ ) { varnamesizes[k] = varnamelen[k] + 1; varstrsizes[k] = varstrlen[k] + 1; } int expstrsize = expstrlen + 1; char **varnames; char **varstrs; varnames = (char **)malloc(sizeof(char *)*numvars); varstrs = (char **)malloc(sizeof(char *)*numvars); for ( k = 0; k < numvars; k++ ) { varnames[k] = (char *)malloc(sizeof(char)* varnamesizes[k]); varstrs[k] = (char *)malloc(sizeof(char)* varstrsizes[k]); } char *expstr = (char *)malloc(sizeof(char)* expstrsize ); SAA_expressionGetStrings( scene, &expressions[j], numvars, varnamesizes, varstrsizes, expstrsize, varnames, varstrs, expstr ); if ( verbose >= 2 ) { fprintf( outStream, "expression = '%s'\n", expstr ); fprintf( outStream, "has %d variables\n", numvars ); } } //if verbose if ( verbose >= 2 ) fprintf( outStream, "evaling expression...\n" ); SAA_expressionEval( scene, &expressions[j], time, &expVal ); if ( verbose >= 2 ) fprintf( outStream, "time %f: exp val %f\n", time, expVal ); // derive table name from the model name tableName = MakeTableName( name, j ); if ( verbose >= 2 ) fprintf( outStream, "Exp: looking for table '%s'\n", tableName ); //find the morph table associated with this key shape anim = (SAnimTable *) (morphRoot->FindDescendent( tableName )); if ( anim != NULL ) { anim->AddElement( expVal ); if ( verbose >= 1 ) fprintf( outStream, "%d: adding element %f to %s\n", j, expVal, tableName ); fflush( outStream ); } else { fprintf( outStream, "%d: Couldn't find table '%s'", j, tableName ); fprintf( outStream, " for value %f\n", expVal ); } } } else fprintf( outStream, "couldn't get expressions!!!\n" ); */ } else { softegg_cat.spam() << "weighted morph" << endl; // no expression, use weight curves make_weighted_morph_table(numShapes, time ); } } // // //