Browse Source

added support for morph of vertices

Asad M. Zaman 22 years ago
parent
commit
9c1088c7a7

+ 472 - 0
pandatool/src/softegg/softNodeDesc.cxx

@@ -20,6 +20,7 @@
 #include "config_softegg.h"
 #include "eggGroup.h"
 #include "eggXfmSAnim.h"
+#include "eggSAnimData.h"
 #include "softToEggConverter.h"
 
 TypeHandle SoftNodeDesc::_type_handle;
@@ -893,6 +894,477 @@ load_nurbs_model(SAA_Scene *scene, SAA_ModelType type) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     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 );
+  }
+}
+
 //
 //
 //

+ 9 - 1
pandatool/src/softegg/softNodeDesc.h

@@ -99,7 +99,7 @@ public:
 
   char **texNameArray;
   int uRepeat, vRepeat;
-  float        matrix[4][4];
+  float matrix[4][4];
 
   const char *fullname;
 
@@ -134,6 +134,14 @@ public:
   void load_poly_model(SAA_Scene *scene, SAA_ModelType type);
   void load_nurbs_model(SAA_Scene *scene, SAA_ModelType type);
 
+  void make_morph_table(float time);
+  void make_linear_morph_table(int numShapes, float time);
+  void make_weighted_morph_table(int numShapes, float time);
+  void make_expression_morph_table(int numShapes, float time);
+
+  void make_vertex_offsets(int numShapes);
+  int find_shape_vert(LPoint3d p3d, SAA_DVector *vertices, int numVert);
+
   static TypeHandle get_class_type() {
     return _type_handle;
   }

+ 62 - 32
pandatool/src/softegg/softToEggConverter.cxx

@@ -34,6 +34,7 @@
 #include "eggTexture.h"
 #include "eggTextureCollection.h"
 #include "eggXfmSAnim.h"
+#include "eggSAnimData.h"
 #include "string_utils.h"
 #include "dcast.h"
 
@@ -730,14 +731,6 @@ close_api() {
 ////////////////////////////////////////////////////////////////////
 bool SoftToEggConverter::
 convert_char_model() {
-#if 0
-  if (has_neutral_frame()) {
-    MTime frame(get_neutral_frame(), MTime::uiUnit());
-    softegg_cat.info(false)
-      << "neutral frame " << frame.value() << "\n";
-    MGlobal::viewFrame(frame);
-  }
-#endif
   softegg_cat.spam() << "character name " << _character_name << "\n";
   EggGroup *char_node = new EggGroup(eggGroupName);
   get_egg_data().add_child(char_node);
@@ -746,6 +739,31 @@ convert_char_model() {
   return convert_hierarchy(char_node);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: SoftToEggConverter::find_morph_table
+//       Access: Public
+//  Description: Given a tablename, it either creates a new 
+//               eggSAnimData structure (if doesn't exist) or 
+//               locates it.
+////////////////////////////////////////////////////////////////////
+EggSAnimData *SoftToEggConverter::
+find_morph_table(char *name) {
+  EggSAnimData *anim = NULL;
+  MorphTable::iterator mt;
+  for (mt = _morph_table.begin(); mt != _morph_table.end(); ++mt) {
+    anim = (*mt);
+    if (!strcmp(anim->get_name().c_str(), name))
+      return anim;
+  }
+
+  // create an entry
+  anim = new EggSAnimData(name);
+  anim->set_fps(_tree._fps);
+  _morph_table.push_back(anim);
+  morph_node->add_child(anim);
+  return anim;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: SoftToEggConverter::convert_char_chan
 //       Access: Private
@@ -769,10 +787,8 @@ convert_char_chan() {
   root_table_node->add_child(bundle_node);
   EggTable *skeleton_node = new EggTable("<skeleton>");
   bundle_node->add_child(skeleton_node);
-#if 0
-  EggTable *root_node = new EggTable("root");
-  skeleton_node->add_child(root_node);
-#endif
+
+  morph_node = new EggTable("morph");
 
   // Set the frame rate before we start asking for anim tables to be
   // created.
@@ -836,6 +852,9 @@ convert_char_chan() {
         softegg_cat.debug() << endl;
         continue;
       }
+      if (make_morph) {
+        node_desc->make_morph_table(time);
+      }
       if (node_desc->is_joint()) {
         softegg_cat.spam() << "-----joint " << node_desc->get_name() << "\n";
         EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
@@ -847,6 +866,9 @@ convert_char_chan() {
     //    frame += frame_inc;
   }
 
+  if (has_morph)
+    bundle_node->add_child(morph_node);
+
   // Now optimize all of the tables we just filled up, for no real
   // good reason, except that it makes the resulting egg file a little
   // easier to read.
@@ -979,19 +1001,15 @@ process_model_node(SoftNodeDesc *node_desc) {
 ////////////////////////////////////////////////////////////////////
 void SoftToEggConverter::
 make_polyset(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
-  string name = node_desc->get_name();
   int id = 0;
-
-  float *uCoords = NULL;
-  float *vCoords = NULL;
-
-
+  int i, idx;
+  int numShapes;
   SAA_Boolean valid;
   SAA_Boolean visible;
-
-  int i, idx;
-
-  
+  float *uCoords = NULL;
+  float *vCoords = NULL;
+  string name = node_desc->get_name();
+ 
   SAA_modelGetNodeVisibility( &scene, node_desc->get_model(), &visible ); 
   softegg_cat.spam() << "model visibility: " << visible << endl; 
   
@@ -1009,6 +1027,10 @@ make_polyset(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
          ((type == SAA_MSMSH) || (type == SAA_MFACE )) ))
        )
     {
+      // Get the number of key shapes
+      SAA_modelGetNbShapes( &scene, node_desc->get_model(), &numShapes );
+      softegg_cat.spam() << "process_model_node: num shapes: " << numShapes << endl;
+      
       // load all node data from soft for this node_desc
       node_desc->load_poly_model(&scene, type);
 
@@ -1166,7 +1188,7 @@ make_polyset(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
                                  << v << endl;
               
               vert.set_uv(TexCoordd(u, v));
-              //        vert.set_uv(TexCoordd(uCoords[i], vCoords[i]));
+              //vert.set_uv(TexCoordd(uCoords[i], vCoords[i]));
             }
           }
           vert.set_external_index(indices[i]);
@@ -1202,6 +1224,10 @@ make_polyset(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
       }
       }
     }
+
+  // if model has key shapes, generate vertex offsets
+  if ( numShapes > 0 && make_morph )
+    node_desc->make_vertex_offsets( numShapes);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1213,18 +1239,14 @@ make_polyset(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
 ////////////////////////////////////////////////////////////////////
 void SoftToEggConverter::
 make_nurb_surface(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType type) {
-  string name = node_desc->get_name();
   int id = 0;
-
-  float *uCoords = NULL;
-  float *vCoords = NULL;
-
-
+  int i, j, k;
+  int numShapes;
   SAA_Boolean valid;
   SAA_Boolean visible;
-
-  int i, j, k;
-
+  float *uCoords = NULL;
+  float *vCoords = NULL;
+  string name = node_desc->get_name();
   
   SAA_modelGetNodeVisibility( &scene, node_desc->get_model(), &visible ); 
   softegg_cat.spam() << "model visibility: " << visible << endl; 
@@ -1236,6 +1258,10 @@ make_nurb_surface(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType ty
   if ( (type == SAA_MNSRF) && ( visible ) && (( make_nurbs ) 
                                               || ( !make_nurbs && !make_poly &&  make_duv )) )
     {
+      // Get the number of key shapes
+      SAA_modelGetNbShapes( &scene, node_desc->get_model(), &numShapes );
+      softegg_cat.spam() << "process_model_node: num shapes: " << numShapes << endl;
+      
       // load all node data from soft for this node_desc
       node_desc->load_nurbs_model(&scene, type);
 
@@ -1473,6 +1499,10 @@ make_nurb_surface(SoftNodeDesc *node_desc, EggGroup *egg_group, SAA_ModelType ty
           softegg_cat.spam() << "texname :" << node_desc->texNameArray[0] << endl;
       }
     }
+
+  // if model has key shapes, generate vertex offsets
+  if ( numShapes > 0 && make_morph )
+    node_desc->make_vertex_offsets( numShapes);
 }
 
 ////////////////////////////////////////////////////////////////////

+ 7 - 0
pandatool/src/softegg/softToEggConverter.h

@@ -44,6 +44,7 @@ class EggVertexPool;
 class EggNurbsCurve;
 class EggPrimitive;
 class EggXfmSAnim;
+class EggSAnimData;
 
 
 ////////////////////////////////////////////////////////////////////
@@ -171,6 +172,12 @@ public:
   TransformType _transform_type;
 
   static TransformType string_transform_type(const string &arg);
+
+  typedef pvector<EggSAnimData *> MorphTable;
+  MorphTable _morph_table;
+
+  EggTable *morph_node;
+  EggSAnimData *find_morph_table(char *name);
 };
 
 extern const int TEX_PER_MAT;