Browse Source

initial softimage extractor tool port: only model, no animation yet

Asad M. Zaman 22 years ago
parent
commit
5d5cc31024
1 changed files with 4933 additions and 0 deletions
  1. 4933 0
      pandatool/src/softegg/soft2Egg.c

+ 4933 - 0
pandatool/src/softegg/soft2Egg.c

@@ -0,0 +1,4933 @@
+// Filename: soft2Egg.c
+// Created by:  masad (26Sep03)
+// 
+////////////////////////////////////////////////////////////////////
+//
+// 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
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////
+// Includes
+////////////////////////////////////////////////////////////////////
+
+#include <SAA.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "pandatoolbase.h"
+
+int init_soft2egg(int, char **);
+
+#if 0
+// DWD includes
+#include "eggBase.h"
+#include <eggParametrics.h>
+#include <animTable.h>
+#include <linMathOutput.h>
+
+// system includes
+#include <fstream.h>
+#include <strstream.h>
+#include <math.h>
+#include <assert.h>
+#include <unistd.h>
+#include <ieeefp.h>
+
+// Performer includes
+#include <Performer/pr/pfLinMath.h>
+
+// SoftImage includes
+#include <SAA.h>
+#include <SI_macros.h>
+
+static const int    TEX_PER_MAT = 1;
+static FILE            *outStream  = stdout;
+//static FILE        *outStream  = stderr;
+
+class soft2egg : public EggBase
+{
+  public:
+
+    soft2egg() : EggBase("r:d:s:m:t:P:b:e:f:T:S:M:A:N:v:o:FhknpaxiucCD")
+    {
+        rsrc_path = "/ful/ufs/soft371_mips2/3D/rsrc";
+        database_name = NULL;
+        scene_name = NULL;
+        model_name = NULL;
+        animFileName = NULL;
+        eggFileName = NULL;
+        tex_path = NULL;
+        eggGroupName = NULL;
+        tex_filename = NULL;
+        search_prefix = NULL;
+        result = SI_SUCCESS;
+    
+        skeleton = new EggGroup();
+        foundRoot = FALSE;
+        animRoot = NULL;
+        morphRoot = NULL;
+        geom_as_joint = 0;
+        make_anim = 0;
+        make_nurbs = 0;
+        make_poly = 0;
+        make_soft = 0;
+        make_morph = 1;
+        make_duv = 1;
+        make_dart = TRUE;
+        has_morph = 0;
+        make_pose = 0;
+        animData.is_z_up = FALSE;
+        nurbs_step = 1;
+         anim_start = -1000;
+        anim_end = -1000;
+        anim_rate = 24;
+        pose_frame = -1;
+        verbose = 0;
+        flatten = 0;
+        shift_textures = 0;
+        ignore_tex_offsets = 0;
+        use_prefix = 0;
+    }
+
+    virtual void Help();
+    virtual void Usage();
+    virtual void ShowOpts();
+
+    virtual boolean UseOutputSwitch() const {
+       return false;
+    }
+
+    virtual boolean
+    HandleGetopts(char flag, char *optarg, int &optind, int argc, char **argv);
+
+    int   isNum( float );
+    char *GetRootName( const char * );
+    char *RemovePathName( const char * );
+    char *GetSliderName( const char * );
+    char *GetFullName( SAA_Scene *, SAA_Elem * );
+    char *GetName( SAA_Scene *, SAA_Elem * );
+    char *GetModelNoteInfo( SAA_Scene *, SAA_Elem * );
+    char *MakeTableName( const char *, int );
+    char *DepointellizeName( char * );
+    SAA_Elem *FindModelByName( char *, SAA_Scene *, SAA_Elem *, int );
+    char *ConvertTexture( SAA_Scene *, SAA_Elem * );
+    int  *FindClosestTriVert( EggVertexPool *, SAA_DVector *, int );
+    int  *MakeIndexMap( int *, int, int );
+    int     findShapeVert( SAA_DVector, SAA_DVector *, int );
+    void LoadSoft();
+    void MakeEgg( EggGroup *, EggJoint *, AnimGroup *, SAA_Scene *, SAA_Elem * );
+    void MakeSurfaceCurve(  SAA_Scene *, SAA_Elem *, EggGroup *,
+        EggNurbsSurface *&, int , SAA_SubElem *, bool );
+
+    EggNurbsCurve *MakeUVNurbsCurve( int, long *,  double *, double *, 
+        EggGroup *, char * );
+
+    EggNurbsCurve *MakeNurbsCurve( SAA_Scene *, SAA_Elem *, EggGroup *,
+        float [4][4], char * );
+
+    void AddKnots( perf_vector<double> &, double *, int, SAA_Boolean, int );
+    void MakeJoint( SAA_Scene *, EggJoint *&, AnimGroup *&, SAA_Elem *, char * );
+    void MakeSoftSkin( SAA_Scene *, SAA_Elem *, SAA_Elem *, int, char * );
+    void CleanUpSoftSkin( SAA_Scene *, SAA_Elem *, char * );
+    void MakeAnimTable( SAA_Scene *, SAA_Elem *, char * );
+    void MakeVertexOffsets( SAA_Scene *, SAA_Elem *, SAA_ModelType type,
+        int, int, SAA_DVector *, float (*)[4], char * );
+    void MakeMorphTable( SAA_Scene *, SAA_Elem *, SAA_Elem *, int,  char *, 
+        float );
+    void MakeLinearMorphTable( SAA_Scene *, SAA_Elem *, int, char *, float );
+    void MakeWeightedMorphTable( SAA_Scene *, SAA_Elem *, SAA_Elem *, int,  
+        int, char *, float );
+    void MakeExpressionMorphTable( SAA_Scene *, SAA_Elem *, SAA_Elem *, int,  
+        int, char *, float );
+    void MakeTexAnim( SAA_Scene *, SAA_Elem *, char * );
+
+  private:
+
+    char        *rsrc_path;
+    char        *database_name;
+    char        *scene_name;
+    char        *model_name;
+    char        *eggFileName;
+    char         *animFileName;
+    char         *eggGroupName;
+    char        *tex_path;
+    char        *tex_filename;
+    char        *search_prefix;
+
+    SI_Error            result;
+    SAA_Scene           scene;
+    SAA_Elem            model;
+    SAA_Database        database;
+    EggGroup           *dart;
+    EggGroup           *skeleton;
+    AnimGroup          *rootAnim;
+    EggJoint           *rootJnt;
+    AnimGroup          *animRoot;
+    AnimGroup          *morphRoot;
+    EggData             animData;
+
+    int                    nurbs_step;
+    int                    anim_start;
+    int                    anim_end;
+    int                    anim_rate;
+    int                    pose_frame;
+    int                    verbose;
+    int                    flatten;
+    int                    shift_textures;
+    int                    ignore_tex_offsets;
+    int                    use_prefix;
+ 
+    bool                foundRoot;
+    bool                geom_as_joint;
+    bool                make_anim;
+    bool                make_nurbs;
+    bool                make_poly;
+    bool                make_soft;
+    bool                make_morph;
+    bool                make_duv;
+    bool                make_dart;
+    bool                has_morph;
+    bool                make_pose;
+
+    ofstream    eggFile;
+    ofstream    animFile;
+    ofstream    texFile;
+
+};
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: Help
+//       Access: Public, Virtual
+//  Description: Displays the "what is this program" message, along
+//               with the usage message.  Should be overridden in base
+//               classes to describe the current program.
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+Help() 
+{
+    cerr <<
+    "soft2egg takes a SoftImage scene or model\n"
+    "and outputs its contents as an egg file\n";
+
+    Usage();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Usage
+//       Access: Public, Virtual
+//  Description: Displays the usage message.
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+Usage() {
+  cerr << "\nUsage:\n"
+       << _commandName << " [opts] (must specify -m or -s)\n\n"
+       << "Options:\n";
+
+  ShowOpts();
+  cerr << "\n";
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ShowOpts
+//       Access: Public, Virtual
+//  Description: Displays the valid options.  Should be extended in
+//               base classes to show additional options relevant to
+//               the current program.
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+ShowOpts() 
+{
+    cerr <<
+    "  -r <path>  - Used to provide soft with the resource\n" 
+    "               Defaults to 'c:/Softimage/SOFT_3.9.2/3D/test'.\n"
+      //    "               Defaults to '/ful/ufs/soft371_mips2/3D/rsrc'.\n"
+    "  -d <path>  - Database path.\n"
+    "  -s <scene> - Indicates that a scene will be converted.\n"
+    "  -m <model> - Indicates that a model will be converted.\n"
+    "  -t <path>  - Specify path to place converted textures.\n"
+    "  -T <name>  - Specify filename for texture map listing.\n"
+    "  -S <step>  - Specify step for nurbs surface triangulation.\n"
+    "  -M <name>  - Specify model output filename. Defaults to scene name.\n"
+    "  -A <name>  - Specify anim output filename. Defaults to scene name.\n"
+    "  -N <name>  - Specify egg group name.\n"
+    "  -k         - Enable soft assignment for geometry.\n"
+    "  -n         - Specify egg NURBS representation instead of poly's.\n"
+    "  -p         - Specify egg polygon output for geometry.\n"
+    "  -P <frame> - Specify frame number for static pose.\n"
+    "  -b <frame> - Specify starting frame for animation (default = first).\n"
+    "  -e <frame> - Specify ending frame for animation (default = last).\n"
+    "  -f <fps>   - Specify frame rate for animation playback.\n"
+    "  -a         - Compile animation tables if animation present.\n"
+    "  -F         - Ignore hierarchy and build a completely flat skeleton.\n"
+    "  -v <level> - Set debug level.\n"
+    "  -x         - Shift NURBS parameters to preserve Alias textures.\n"
+    "  -i         - Ignore Soft texture uv offsets.\n"
+    "  -u         - Use Soft prefix in model names.\n"
+    "  -c         - Cancel morph conversion.\n"
+    "  -C         - Cancel duv conversion.\n"
+    "  -D         - Don't make the output model a character.\n"
+    "  -o <prefix>- Convert only models with given prefix.\n";
+
+      EggBase::ShowOpts();
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: HandleGetopts
+//       Access: Public, Virtual
+//  Description:
+////////////////////////////////////////////////////////////////////
+boolean soft2egg::
+HandleGetopts(char flag, char *optarg, int &optind, int argc, char **argv) 
+{
+    boolean okflag = true;
+
+    switch (flag) 
+    {
+      case 'r':       // Set the resource path for soft.
+        if ( strcmp( optarg, "" ) )
+        {
+            // Get the path.
+            rsrc_path = optarg;
+            fprintf( outStream, "using rsrc path %s\n", rsrc_path );
+        }
+        break;
+
+    case 'd':       // Set the database path.
+        if ( strcmp( optarg, "" ) )
+        {
+            // Get the path.
+            database_name = optarg;
+            fprintf( outStream, "using database %s\n", database_name );
+        }
+        break;
+
+    case 's':     // Check if its a scene.
+        if ( strcmp( optarg, "" ) )
+        {
+            // Get scene name.
+            scene_name = optarg;
+            fprintf( outStream, "loading scene %s\n", scene_name );
+        }
+        break;
+    
+    case 'm':     // Check if its a model.
+        if ( strcmp( optarg, "" ) )
+        {
+            // Get model name.
+            model_name = optarg;
+            fprintf( outStream, "loading model %s\n", model_name );
+        }
+        break;
+    
+    case 't':     // Get converted texture path.
+        if ( strcmp( optarg, "" ) )
+        {
+            // Get tex path name.
+            tex_path = optarg;
+            fprintf( outStream, "texture path:  %s\n", tex_path );
+        }
+        break;
+
+    case 'T':      // Specify texture list filename. 
+        if ( strcmp( optarg, "") )
+        {
+            // Get the name.
+            tex_filename = optarg;
+            fprintf( outStream, "creating texture list file: %s\n", 
+                tex_filename );
+        }
+        break;
+    case 'S':     // Set NURBS step.
+        if ( strcmp( optarg, "" ) )
+        {
+            nurbs_step = atoi(optarg);
+            fprintf( outStream, "NURBS step:  %d\n", nurbs_step );
+        }
+        break;
+ 
+    case 'M':     // Set model output file name.
+        if ( strcmp( optarg, "" ) )
+        {
+            eggFileName = optarg;
+            fprintf( outStream, "Model output filename:  %s\n", eggFileName );
+        }
+        break;
+ 
+    case 'A':     // Set anim output file name.
+        if ( strcmp( optarg, "" ) )
+        {
+            animFileName = optarg;
+            fprintf( outStream, "Anim output filename:  %s\n", animFileName );
+        }
+        break;
+ 
+    case 'N':     // Set egg model name.
+        if ( strcmp( optarg, "" ) )
+        {
+            eggGroupName = optarg;
+            fprintf( outStream, "Egg group name:  %s\n", eggGroupName );
+        }
+        break;
+
+    case 'o':     // Set search_prefix.
+        if ( strcmp( optarg, "" ) )
+        {
+            search_prefix = optarg;
+            fprintf( outStream, "Only converting models with prefix:  %s\n", 
+                search_prefix );
+        }
+        break;
+ 
+    case 'h':    // print help message
+        Help();
+        exit(1);
+        break;
+    
+    case 'c':    // Cancel morph animation conversion
+        make_morph = FALSE;
+        fprintf( outStream, "canceling morph conversion\n" );
+        break;
+
+    case 'C':    // Cancel uv animation conversion
+        make_duv = FALSE;
+        fprintf( outStream, "canceling uv animation conversion\n" );
+        break;
+    
+    case 'D':    // Omit the Dart flag
+        make_dart = FALSE;
+        fprintf( outStream, "making a non-character model\n" );
+        break;
+    
+    case 'k':    // Enable soft skinning
+        //make_soft = TRUE;
+        //fprintf( outStream, "enabling soft skinning\n" );
+        fprintf( outStream, "-k flag no longer necessary\n" );
+        break;
+    
+    case 'n':    // Generate egg NURBS output
+        make_nurbs = TRUE;
+        fprintf( outStream, "outputting egg NURBS info\n" );
+        break;
+    
+    case 'p':    // Generate egg polygon output
+        make_poly = TRUE;
+        fprintf( outStream, "outputting egg polygon info\n" );
+        break;
+    
+    case 'P':    // Generate static pose from given frame
+        if ( strcmp( optarg, "" ) )
+        {
+            make_pose = TRUE;
+            pose_frame = atoi(optarg);
+            fprintf( outStream, "generating static pose from frame %d\n",
+                pose_frame );
+        }
+        break;
+    
+    case 'a':     // Compile animation tables.
+        make_anim = TRUE;
+        fprintf( outStream, "attempting to compile anim tables\n" );
+        break;
+
+    case 'F':     // Build a flat skeleton.
+        flatten = TRUE;
+        fprintf( outStream, "building a flat skeleton!!!\n" );
+        break;
+
+    case 'x':     // Shift NURBS parameters to preserve Alias textures.
+        shift_textures = TRUE;
+        fprintf( outStream, "shifting NURBS parameters...\n" );
+        break;
+
+    case 'i':     // Ignore Soft uv texture offsets 
+        ignore_tex_offsets = TRUE;
+        fprintf( outStream, "ignoring texture offsets...\n" );
+        break;
+
+    case 'u':     // Use Soft prefix in model names 
+        use_prefix = TRUE;
+        fprintf( outStream, "using prefix in model names...\n" );
+        break;
+
+
+    case 'v':     // print debug messages.
+        if ( strcmp( optarg, "" ) )
+        {
+            verbose = atoi(optarg);
+            fprintf( outStream, "using debug level %d\n", verbose );
+        }
+        break;
+
+    case 'b':     // Set animation start frame.
+        if ( strcmp( optarg, "" ) )
+        {
+            anim_start = atoi(optarg);
+            fprintf( outStream, "animation starting at frame:  %d\n", 
+                anim_start );
+        }
+        break;
+
+    case 'e':     /// Set animation end frame.
+        if ( strcmp( optarg, "" ) )
+        {
+            anim_end = atoi(optarg);
+            fprintf( outStream, "animation ending at frame:  %d\n", anim_end );
+        }
+        break;
+ 
+    case 'f':     /// Set animation frame rate.
+        if ( strcmp( optarg, "" ) )
+        {
+            anim_rate = atoi(optarg);
+            fprintf( outStream, "animation frame rate:  %d\n", anim_rate );
+        }
+        break;
+ 
+    default:
+        okflag = EggBase::HandleGetopts(flag, optarg, optind, argc, argv);
+  }
+
+  return (okflag);
+}
+
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: isNum
+//       Access: Public, Virtual
+//  Description: Take a float and make sure it is of the body. 
+////////////////////////////////////////////////////////////////////
+int soft2egg::
+isNum( float num ) 
+{
+    return( ( num < HUGE_VAL ) && finite( num ) );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: GetRootName
+//       Access: Public
+//  Description: Given a string, return a copy of the string up to
+//                 the first occurence of '-'. 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+GetRootName( const char *name )
+{
+    char *hyphen;
+    char *root;
+    int      len;
+
+    hyphen = strchr( name, '-' );
+    len = hyphen-name;
+
+    if ( (hyphen != NULL) && len )
+    {
+        root = (char *)malloc(sizeof(char)*(len+1));
+        strncpy( root, name, len );
+        root[sizeof(char)*(len)] = '\0';
+    }
+    else
+    {
+        root = (char *)malloc( sizeof(char)*(strlen(name)+1));
+        strcpy( root, name );
+    }
+
+    return( root );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: RemovePathName
+//       Access: Public
+//  Description: Given a string, return a copy of the string after 
+//                 the last occurence of '/ 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+RemovePathName( const char *name )
+{
+    char *slash;
+    char *root;
+
+    if ( *name != NULL )
+    {
+        slash = strrchr( name, '/' );
+
+        root = (char *)malloc( sizeof(char)*(strlen(name)+1));
+
+        if ( slash != NULL )
+            strcpy( root, ++slash );
+        else
+            strcpy( root, name );
+
+        return( root );
+    }
+
+    fprintf( stderr, "Error: RemovePathName received NULL string!\n" );
+    return ( (char *)name );
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GetSliderName
+//       Access: Public
+//  Description: Given a string, return that part of the string after 
+//               the first occurence of '-' and before the last
+//                 occurance of '.' 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+GetSliderName( const char *name )
+{
+    if ( name != NULL )
+    {
+        strstream newStr;
+        char *hyphen;
+        char *end;
+
+        hyphen = strchr( name, '-' );
+
+        // pull off stuff before first hyphen
+        if (hyphen != NULL)
+        {
+            newStr << ++hyphen;
+            end = newStr.str();
+        }
+
+        char *lastPeriod;
+
+        lastPeriod = strrchr( end, '.' );
+
+        // ignore stuff after last period
+        if ( lastPeriod != NULL )
+        {
+            *lastPeriod = '\0';
+        }
+
+        if ( verbose >= 1 )
+            fprintf( stdout, "slider name: '%s'\n", end );
+
+        return( end );
+    }
+    
+    return( (char *)name );
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GetName
+//       Access: Public
+//  Description: Given an element, return a copy of the element's 
+//                 name WITHOUT prefix. 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+GetName( SAA_Scene *scene, SAA_Elem *element )
+{
+    int    nameLen;
+    char *name;
+
+    // get the name
+    SAA_elementGetNameLength( scene, element, &nameLen ); 
+    name = (char *)malloc(sizeof(char)*++nameLen);
+    SAA_elementGetName( scene, element, nameLen, name );
+    
+    return name;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GetFullName
+//       Access: Public
+//  Description: Given an element, return a copy of the element's 
+//                 name complete with prefix. 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+GetFullName( SAA_Scene *scene, SAA_Elem *element )
+{
+    int    nameLen;
+    char *name;
+
+    // get the name
+    SAA_elementGetNameLength( scene, element, &nameLen ); 
+    name = (char *)malloc(sizeof(char)*++nameLen);
+    SAA_elementGetName( scene, element, nameLen, name );
+    
+    int prefixLen;
+    char *prefix;
+
+    // get the prefix
+    SAA_elementGetPrefixLength( scene, element, &prefixLen ); 
+    prefix = (char *)malloc(sizeof(char)*++prefixLen);
+    SAA_elementGetPrefix( scene, element, prefixLen, prefix );
+
+    strstream fullNameStrm;
+
+    // add 'em together
+    fullNameStrm << prefix << "-" << name << ends;
+
+    //free( name );
+    //free( prefix );
+    
+    return fullNameStrm.str();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GetModelNoteInfo
+//       Access: Public
+//  Description: Given an element, return a string containing the
+//                 contents of its MODEL NOTE entry 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+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 = (char *)malloc(sizeof(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 = strchr( modelNote, '\n' );
+        if ( eol != NULL)
+            *eol = '\0';
+        else
+            modelNote[size] = '\0';
+
+        if ( verbose >= 1 )
+            fprintf( outStream, "\nmodelNote = %s\n", 
+                modelNote );    
+    }
+
+    return modelNote;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeTableName
+//       Access: Public
+//  Description: Given a string, and a number, return a new string
+//                 consisting of "string.number". 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+MakeTableName( const char *name, int number )
+{
+    strstream namestrm;
+
+    namestrm << name << "." << number << ends;
+    return namestrm.str();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindModelByName
+//       Access: Public
+//  Description: Given a string, find the model in the scene 
+//                 whose name corresponds to the given string.                  
+////////////////////////////////////////////////////////////////////
+SAA_Elem *soft2egg::
+FindModelByName( char *name, SAA_Scene *scene, SAA_Elem *models, 
+    int numModels )
+{
+    char     *foundName;
+    SAA_Elem *foundModel = NULL;
+    
+    for ( int model = 0; model < numModels; model++ )
+    {
+        foundName = GetName( scene, &models[model] );
+    
+        if ( !strcmp( name, foundName ) )
+        {
+            if ( verbose >= 1 )
+                fprintf( outStream, "foundModel: '%s' = '%s'\n",
+                    name, foundName );
+
+            foundModel = &models[model];
+            return( foundModel );
+        }    
+    }    
+
+    fprintf( outStream, "findModelByName: failed to find model named: '%s'\n",
+        name );
+
+    return ( foundModel );    
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: DepointellizeName
+//       Access: Public
+//  Description: Given a string, return the string up to the first
+//                 period. 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+DepointellizeName( char *name )
+{
+    char    *endPtr;
+    char    *newName;
+
+    newName = (char *)malloc(sizeof(char)*(strlen(name)+1));
+    sprintf( newName, "%s", name );
+
+    endPtr = strchr( newName, '.' );
+    if ( endPtr != NULL ) 
+      *endPtr = '\0';
+
+    return ( newName );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: ConvertTexture
+//       Access: Public
+//  Description: Given a string, return a copy of the string without
+//               the leading file path, and make an rgb file of the
+//                 same name in the tex_path directory. 
+////////////////////////////////////////////////////////////////////
+char *soft2egg::
+ConvertTexture( SAA_Scene *scene, SAA_Elem *texture )
+{
+  char *fileName = NULL;
+  int  fileNameLen = 0;
+
+  // get the texture's name
+  SAA_texture2DGetPicNameLength( scene, texture, &fileNameLen);
+
+  if ( fileNameLen )
+  {
+	fileName = (char *)malloc(sizeof(char)*++fileNameLen);
+	SAA_texture2DGetPicName( scene, texture, fileNameLen, fileName );
+  }
+	
+  // make sure we are not being passed a NULL image, an empty image
+  // string or the default image created by egg2soft
+  if ( (fileName != NULL) && strlen( fileName ) && strcmp( fileName, 
+        "/fat/people/gregw/new_test/PICTURES/default") &&
+				( strstr( fileName, "noIcon" ) == NULL) )
+  {
+    char *texName = NULL;
+    char *texNamePath = NULL;
+    char *tmpName = NULL;
+    char *fileNameExt = NULL;
+
+    // strip off path and add .rgb
+    tmpName = strrchr( fileName, '/' );
+
+    if ( tmpName == NULL )
+        tmpName = fileName;
+    else
+        tmpName++;
+
+    float transp;
+
+	// check for alpha
+	SAA_texture2DGetTransparency( scene, texture, &transp );
+
+	if ( transp != 0.0f ) {
+        texName = (char *)malloc(sizeof(char)*(strlen(tmpName)+6));
+        sprintf( texName, "%s.rgba", tmpName );
+	} else {
+        texName = (char *)malloc(sizeof(char)*(strlen(tmpName)+5));
+        sprintf( texName, "%s.rgb", tmpName );
+	}
+
+    fileNameExt = (char *)malloc(sizeof(char)*(strlen(fileName)+5));
+    sprintf( fileNameExt, "%s.pic", fileName ); 
+
+    if ( verbose >= 1 )
+        fprintf( outStream, "Looking for texture file: '%s'\n", fileNameExt );
+
+    // try to make conversion of file
+    int found_file = ( access( fileNameExt, F_OK ) == 0);
+    
+    if ( found_file )
+    {
+        if ( tex_path )
+        {
+            texNamePath = (char *)malloc(sizeof(char)*(strlen(tex_path) +
+                strlen(texName) + 2));
+
+            sprintf( texNamePath, "%s/%s", tex_path, texName );    
+
+            if ( texFile )
+                texFile << texNamePath << ": " << fileNameExt << "\n";
+
+            // make sure conversion doesn't already exist    
+            if ( (access( texNamePath, F_OK ) != 0)  && !texFile )    
+            {
+                char *command = (char *)malloc(sizeof(char)*
+                    (strlen(fileNameExt) + strlen(texNamePath) + 20));
+
+                sprintf( command, "image-resize -1 %s %s",  
+                    fileNameExt, texNamePath );
+
+                if ( verbose >=1 )
+                    fprintf( outStream, "executing %s\n", command );
+            
+                system( command );    
+
+                //free( command );
+            }
+            else
+                if ( verbose >=1 )
+                    fprintf( outStream, "%s already exists!\n", texNamePath );
+        }
+        else
+        {
+            if ( verbose >= 1 )
+            {
+                fprintf( outStream, "Warning: No texture path defined" );
+                fprintf( outStream, " - No automatic conversion performed\n" );
+            }
+        }
+    }
+    else
+    {
+        fprintf( outStream, "Warning: Couldn't find texture file: %s\n", 
+            fileNameExt );
+    }
+
+    //free( fileNameExt );
+
+    if (tex_path)
+        return( texNamePath );
+    else
+        return( texName );
+  }
+  else
+  {
+    fprintf( outStream, "Warning: ConvertTexture received NULL fileName\n" );
+    return( NULL );
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: FindClosestTriVert
+//       Access: Public
+//  Description: Given an egg vertex pool, map each vertex therein to 
+//                 a vertex within an array of SAA model vertices of
+//                 size numVert. Mapping is done by closest proximity.
+////////////////////////////////////////////////////////////////////
+int *soft2egg::
+FindClosestTriVert( EggVertexPool *vpool, SAA_DVector *vertices, int numVert )
+{
+    int    *vertMap = NULL; 
+    int     vpoolSize = vpool->NumVertices();
+    int     i,j;
+    float   thisDist;
+    float   closestDist;
+    int     closest;
+    
+
+    vertMap = (int *)malloc(sizeof(int)*vpoolSize);
+
+    // for each vertex in vpool
+    for ( i = 0; i < vpoolSize; i++ )
+    {
+        // find closest model vertex 
+        for ( j = 0; j < numVert-1; j++ ) 
+        {
+            // calculate distance
+            thisDist =    sqrtf( 
+                powf( vpool->Vertex(i)->position[0] - vertices[j].x , 2 ) + 
+                powf( vpool->Vertex(i)->position[1] - vertices[j].y , 2 ) + 
+                powf( vpool->Vertex(i)->position[2] - vertices[j].z , 2 ) ); 
+
+            // remember this if its the closest so far
+            if ( !j || ( thisDist < closestDist ) )
+            {
+                closest = j;
+                closestDist = thisDist;
+            }
+        } 
+        vertMap[i] = closest;
+
+        if ( verbose >= 2 )
+        {
+            fprintf( outStream, "mapping v %d of %d:( %f, %f, %f )\n", i, 
+                vpoolSize, vpool->Vertex(i)->position[0], 
+                vpool->Vertex(i)->position[1],
+                vpool->Vertex(i)->position[2] ); 
+            fprintf( outStream, "to cv %d of %d:( %f, %f, %f )\tdelta = %f\n",
+                closest, numVert-1, vertices[closest].x, vertices[closest].y, 
+                vertices[closest].z, closestDist );
+        }
+		}
+
+   	return( vertMap );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeIndexMap
+//       Access: Public
+//  Description: Given an array of indices that is a map from one
+//                 set of vertices to another, return an array that
+//                 performs the reverse mapping of the indices array 
+////////////////////////////////////////////////////////////////////
+int *soft2egg::
+MakeIndexMap( int *indices, int numIndices, int mapSize )
+{
+    int i, j;
+
+    // allocate map array
+    int *map = (int *)malloc(sizeof(int)*mapSize); 
+
+    if ( map != NULL )
+    {
+        for ( i = 0; i < mapSize; i++ )
+        {
+            j = 0;
+            int found = 0;
+            while( j < numIndices )
+            {
+                if ( indices[j] == i )
+                {
+                    map[i] = j;
+                    if ( verbose >= 2 )
+                        fprintf( outStream, "map[%d] = %d\n", i, map[i] );
+                    found = 1;
+                    break;
+                }
+                j++;
+            }
+            if ( !found) 
+            {
+                if ( verbose >= 2 )
+                    fprintf( outStream, "Warning: orphan vertex (%d)\n", i );
+                // default to -1 for now
+                map[i] = -1;
+            }
+        }
+    }
+    else
+        fprintf( outStream, "Not enough Memory for index Map...\n");
+    
+
+    return( map );
+}
+
+    
+////////////////////////////////////////////////////////////////////
+//     Function: findShapeVert
+//       Access: Public
+//  Description: given a vertex, find its corresponding shape vertex
+//                 and return its index.
+////////////////////////////////////////////////////////////////////
+int     soft2egg::
+findShapeVert( SAA_DVector vertex, SAA_DVector *vertices, int numVert )
+{
+    int i;
+    int found = 0;
+
+    for ( i = 0; i < numVert && !found ; i++ )
+    {
+        if ( ( vertex.x == vertices[i].x ) && 
+             ( vertex.y == vertices[i].y ) && 
+             ( vertex.z == vertices[i].z ) )
+        {
+            found = 1;
+
+            if ( verbose >= 2)
+                fprintf( outStream, "found shape vert at index %d\n", i );
+        }
+             
+    } 
+
+    if (!found )
+        i = -1;
+    else
+        i--;
+
+    return( i );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: LoadSoft
+//       Access: Public
+//  Description: Open the SI database and grab the scene & model info
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+LoadSoft()
+{
+    int      i;
+
+    if ( (scene_name == NULL && model_name == NULL) || database_name == NULL )
+    {
+        Usage();
+        exit( 1 );
+    }
+
+    if ((result = SAA_Init(rsrc_path, FALSE)) != SI_SUCCESS)
+    {
+        fprintf( outStream, "Error: Couldn't get resource path!\n");
+        exit( 1 );
+    }
+
+    if ((result = SAA_databaseLoad(database_name, &database)) != SI_SUCCESS)
+    {
+        fprintf( outStream, "Error: Couldn't load database!\n");
+        exit( 1 );
+    }
+
+    if ((result = SAA_sceneGetCurrent(&scene)) == SI_SUCCESS)
+    {
+        // load scene if present
+        if ( scene_name != NULL )
+        {
+            SAA_sceneLoad( &database, scene_name, &scene );
+
+            // if no egg filename specified, make up a name
+            if ( eggFileName == NULL )
+            {
+                eggFileName = (char *)malloc(sizeof(char)*
+                    (strlen( scene_name ) + 14 ));
+                sprintf( eggFileName, "%s", DepointellizeName(scene_name) );
+                if ( make_nurbs )
+                    strcat( eggFileName, "-nurb" );
+                strcat( eggFileName, "-mod.egg" );
+            }
+
+            // open an output file for the geometry if necessary 
+            if ( make_poly || make_nurbs )
+            {
+                unlink( eggFileName );
+                eggFile.open( eggFileName, ios::out, 0666 );
+
+                if ( !eggFile )
+                {
+                    fprintf( outStream, "Couldn't open output file: %s\n", 
+                        eggFileName ); 
+                    exit( 1 );
+                }
+            }
+
+            // open an output file for texture list if specified
+            if ( tex_filename != NULL )
+            {
+                unlink( tex_filename );
+                texFile.open( tex_filename, ios::out, 0666 );
+                
+                if ( !texFile )
+                {    
+                    fprintf( outStream, "Couldn't open output file: %s\n",
+                        tex_filename );
+                    exit( 1 );
+                }
+            }
+
+            if ( SAA_updatelistGet( &scene ) == SI_SUCCESS )
+            {
+                float time;
+
+                fprintf( outStream, "setting Scene to frame %d...\n", pose_frame );
+                //SAA_sceneSetPlayCtrlCurrentFrame( &scene, pose_frame );
+                SAA_frame2Seconds( &scene, pose_frame, &time );
+                SAA_updatelistEvalScene( &scene, time );
+                sginap( 100 );
+                SAA_updatelistEvalScene( &scene, time );
+                if ( make_pose )
+                    SAA_sceneFreeze( &scene );
+            } 
+
+            int            numModels;
+            SAA_Elem    *models;
+
+            SAA_sceneGetNbModels( &scene, &numModels ); 
+            fprintf( outStream, "Scene has %d model(s)...\n", numModels );
+        
+            if ( numModels )
+            {
+                // allocate array of models
+                models = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numModels);
+
+                if ( models != NULL )
+                {
+                    char *rootName = GetRootName( eggFileName );
+                    
+                
+                    if ( eggGroupName == NULL )    
+                        dart = _data.CreateGroup( NULL, rootName );
+                    else
+                        dart = _data.CreateGroup( NULL, eggGroupName );
+
+                    if (make_dart)
+                      dart->flags |= EF_DART;
+
+                    AnimGroup    *rootTable;
+
+                    rootTable = animData.CreateTable( NULL, eggFileName );    
+
+                    if ( eggGroupName == NULL )
+                        animRoot = animData.CreateBundle( rootTable, rootName );
+                    else
+                        animRoot = animData.CreateBundle( rootTable, 
+                            eggGroupName );
+
+                    // propagate commet to anim data
+                    animData.root_group.children.push_front(
+                        new EggComment( _commandLine ) );
+
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "made animRoot: %s\n", rootName );
+
+                    SAA_sceneGetModels( &scene, numModels, models );
+
+                    for ( i = 0; i < numModels; i++ )
+                    {
+                        int level;
+
+                        SAA_elementGetHierarchyLevel( &scene, &models[i], &level );
+                        if ( !level )
+                        {
+                            if ( verbose >= 1 )
+                                fprintf( outStream,
+                                    "\negging scene model[%d]\n", i );
+
+                            MakeEgg( dart, NULL, NULL,  &scene, &models[i] );
+                        }
+                    }
+
+            if ( make_poly || make_nurbs )
+            {
+                // generate soft skinning assignments if desired
+                //
+                //disabled 1/1/99 to streamline joint assignments.
+                // all joint assignments now done here. Hard & Soft.
+                //if ( make_soft)
+                {
+                    char        *name;
+                    char        *fullname;
+                    SAA_Boolean isSkeleton;
+
+                    // search through models and look for skeleton parts
+                    for ( i = 0; i < numModels; i++ )
+                    {
+                        SAA_modelIsSkeleton( &scene, &models[i], &isSkeleton );
+
+                        // get fullname for splitting files, but
+                        // only use it in file if requested
+                        fullname = GetFullName( &scene, &models[i] );
+                        if ( use_prefix )
+                            name = fullname;
+                        else
+                            name = GetName( &scene, &models[i] );
+
+                        // split
+                        if ( strstr( fullname, search_prefix ) != NULL )
+                        {
+                            // for every skel part: get soft skin info 
+                            if ( isSkeleton )
+                                MakeSoftSkin( &scene, &models[i], models,
+                                    numModels, name );
+                        }
+
+                        //free( name );
+                    }
+
+                    // make sure all vertices were assigned
+                    // via soft skinning - if not hard assign them
+                    for ( i = 0; i < numModels; i++ )
+                    {
+                        // get fullname for splitting files, but
+                        // only use it in file if requested
+                        fullname = GetFullName( &scene, &models[i] );
+                        if ( use_prefix )
+                            name = fullname;
+                        else
+                            name = GetName( &scene, &models[i] );
+
+                        // split
+                        if ( strstr( fullname, search_prefix ) != NULL )
+                            CleanUpSoftSkin( &scene, &models[i], name );
+
+                        //free( name );
+                    }
+
+                }
+
+
+                // put the skeleton data into the egg data
+                dart->StealChildren( *skeleton );
+
+                // make sure all elements have unique names
+                _data.UniquifyNames();
+    
+                // write out the geometry data if requested    
+                //if ( make_poly || make_nurbs )
+                //{
+                    eggFile << _data << "\n";
+                    fprintf( outStream, "\nwriting out %s...\n", eggFileName );
+                    eggFile.close();
+                }
+
+                // close texture list file if opened
+                if ( texFile )
+                    texFile.close();
+
+                // generate animation data if desired
+                if ( make_anim )
+                {
+                if ( animFileName == NULL )
+                {
+                    animFileName = (char *)malloc(sizeof(char)*
+                        (strlen(scene_name)+ 10 ));
+                    sprintf( animFileName, "%s", DepointellizeName(scene_name) );
+                    strcat( animFileName, "-chan.egg" );
+                }
+
+                unlink( animFileName );
+                animFile.open( animFileName, ios::out, 0666 );
+
+                if ( !animFile )
+                {
+                    fprintf( outStream, "Couldn't open output file: %s\n", 
+                        animFileName ); 
+                    exit( 1 );
+                }
+
+                int frame;
+                //int frameStep;
+                float time;
+
+                // get all the animation frame info if not specified
+                // on the command line
+                if (anim_start == -1000) 
+                    SAA_sceneGetPlayCtrlStartFrame( &scene, &anim_start );
+
+                if (anim_end == -1000)
+                    SAA_sceneGetPlayCtrlEndFrame( &scene, &anim_end );
+
+                //SAA_sceneGetPlayCtrlFrameStep( &scene, &frameStep );
+
+                fprintf( outStream, "\nframeStart = %d\n", anim_start );
+                fprintf( outStream, "frameEnd = %d\n", anim_end );
+                //fprintf( outStream, "frameStep = %d\n", frameStep );
+
+                // start at first frame and go to last
+                for ( frame = anim_start; frame <= anim_end; 
+                        frame += 1)
+                {
+                    SAA_frame2Seconds( &scene, frame, &time );
+                    SAA_updatelistEvalScene( &scene, time );
+										sginap( 100 );
+										SAA_updatelistEvalScene( &scene, time );
+                    fprintf( outStream, "\n> animating frame %d\n", frame );
+
+                    // for each model
+                    for ( i = 0; i < numModels; i++ )
+                    {
+                        char           *name;
+                        char           *fullname;
+                        SAA_Boolean     isSkeleton;
+                        SAA_ModelType     type;
+
+                        SAA_modelIsSkeleton( &scene, &models[i], &isSkeleton );
+
+                        // get fullname for splitting files, but
+                        // only use it in file if requested
+                        fullname = GetFullName( &scene, &models[i] );
+                        if ( use_prefix )
+                            name = fullname;
+                        else
+                            name = GetName( &scene, &models[i] );
+
+                        // split
+                        if ( strstr( fullname, search_prefix ) != NULL )
+                        {
+                            // make the morph table for this critter
+                            if ( make_morph )
+                            {
+                                MakeMorphTable( &scene, &models[i], models,
+                                    numModels, name, time );
+                            }
+                        }
+
+                        // find out what type of node we're dealing with
+                        result = SAA_modelGetType( &scene, &models[i], &type );
+
+                        int size;
+
+                        // check for uv texture animation
+                        SAA_elementGetUserDataSize( &scene, &models[i], 
+                            "TEX_OFFSETS", &size );
+
+                        // if so, update for this frame if desired
+                        if ( ( size != 0 ) && make_duv )    
+                            MakeTexAnim( &scene, &models[i], name );
+
+                        // if we have a skeleton or something that acts
+                        // like one - build anim tables
+                        if ( isSkeleton  ||
+                            ( strstr( name, "joint") != NULL ) )
+                                MakeAnimTable( &scene, &models[i], name ); 
+
+                        //free( name );
+                    }
+
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "\n" );
+                }
+
+                animFile << animData << "\n";
+                fprintf( outStream, "\nwriting out %s...\n", animFileName );
+                animFile.close();
+                }
+
+                //free( models );
+
+                }
+                else
+                    fprintf( outStream, "Error: Not enough Memory for models...\n");
+            }
+        }
+        // otherwise try to load a model
+        else if ( model_name != NULL )
+        {
+
+            if ( eggFileName == NULL )
+            {    
+                eggFileName = 
+                    (char *)malloc(sizeof(char)*(strlen( model_name )+13));
+                sprintf( eggFileName, "%s", DepointellizeName( model_name ) );
+
+                if ( make_nurbs )
+                    strcat( eggFileName, "-nurb" );
+                strcat( eggFileName, "-mod.egg" );
+            }
+
+            eggFile.open( eggFileName );    
+
+            if ( !eggFile )
+            {
+                fprintf( outStream, "Couldn't open output file: %s\n", 
+                    eggFileName ); 
+                exit( 1 );
+            }
+
+            if ((result = 
+                SAA_elementLoad(&database, &scene, model_name, &model))
+                == SI_SUCCESS)
+            {
+                fprintf( outStream, "Loading single model...\n");
+                MakeEgg( NULL, NULL, NULL,  &scene, &model );
+            }
+
+            eggFile << _data << "\n";
+        }
+    }
+
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeEgg
+//       Access: Public
+//  Description: Make egg geometry from a given model. This include
+//                 textures, tex coords, colors, normals, and joints. 
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+MakeEgg( EggGroup *parent, EggJoint *lastJoint, AnimGroup *lastAnim,
+            SAA_Scene *scene, SAA_Elem *model )
+{
+    char        *name;
+    char        *fullname;
+    SAA_ModelType type;
+    int         id = 0;
+    int         numShapes;
+    int         numTri;
+    int         numVert;
+    int         numTexLoc = 0;
+    int         numTexGlb = 0;
+    int         i, j;
+    float        matrix[4][4];
+    float        *uScale = NULL; 
+    float        *vScale = NULL;
+    float        *uOffset = NULL;
+    float        *vOffset = NULL;
+    SAA_Boolean    uv_swap = FALSE;
+    void        *relinfo;
+    SAA_SubElem *triangles = NULL;
+    SAA_Elem    *materials = NULL;
+    SAA_SubElem *cvertices = NULL;
+    SAA_DVector *cvertPos = NULL;
+    SAA_DVector *vertices = NULL;
+    SAA_DVector *normals = NULL;
+    int            *indices = NULL;
+    int            *indexMap = NULL;
+    int            *numTexTri = NULL;
+    SAA_Elem    *textures = NULL;
+    char        **texNameArray;
+    float        *uCoords = NULL;
+    float        *vCoords = NULL;
+    SAA_GeomType gtype = SAA_GEOM_ORIGINAL;
+    SAA_Boolean    visible;
+
+    /////////////////////////////////////////////////
+    // find out what type of node we're dealing with
+    /////////////////////////////////////////////////
+    result = SAA_modelGetType( scene, model, &type );
+
+    if ( verbose >= 1 )
+    {
+        if ( type == SAA_MNILL )
+            fprintf( outStream, "encountered null\n"); 
+        else if ( type == SAA_MPTCH )
+            fprintf( outStream, "encountered patch\n" );
+        else if ( type == SAA_MFACE )
+            fprintf( outStream, "encountered face\n" );
+        else if ( type == SAA_MSMSH )
+            fprintf( outStream, "encountered mesh\n" );
+        else if ( type == SAA_MJNT )
+            fprintf( outStream, "encountered joint\n" );
+        else if ( type == SAA_MSPLN )
+            fprintf( outStream, "encountered spline\n" );
+        else if ( type == SAA_MMETA )
+            fprintf( outStream, "encountered meta element\n" );
+        else if ( type == SAA_MBALL )
+            fprintf( outStream, "encountered metaball\n" );
+        else if ( type == SAA_MNCRV )
+            fprintf( outStream, "encountered nurb curve\n" );
+        else if ( type == SAA_MNSRF )
+            fprintf( outStream, "encountered nurbs surf\n" );
+        else 
+            fprintf( outStream, "encountered unknown type: %d\n", type );
+    }
+
+    /////////////////////////////
+    // Get the name of the model
+    /////////////////////////////
+
+    // Get the FULL name of the model
+    fullname = GetFullName( scene, model );
+
+    if ( use_prefix )
+    {
+        // Get the FULL name of the trim curve
+        name = fullname; 
+    }
+    else
+    {
+        // Get the name of the trim curve
+        name = GetName( scene, model );
+    }
+
+    if ( verbose >= 1 )
+        fprintf( outStream, "element name <%s>\n", name );
+
+    fflush( outStream );
+
+    // get the model's matrix    
+    SAA_modelGetMatrix( scene, model, SAA_COORDSYS_GLOBAL,  matrix );
+
+    if ( verbose >= 2 )
+    {
+        fprintf( outStream, "model matrix = %f %f %f %f\n", matrix[0][0],
+             matrix[0][1],  matrix[0][2],  matrix[0][3] ); 
+        fprintf( outStream, "model matrix = %f %f %f %f\n", matrix[1][0],
+             matrix[1][1],  matrix[1][2],  matrix[1][3] ); 
+        fprintf( outStream, "model matrix = %f %f %f %f\n", matrix[2][0],
+             matrix[2][1],  matrix[2][2],  matrix[2][3] ); 
+        fprintf( outStream, "model matrix = %f %f %f %f\n", matrix[3][0],
+             matrix[3][1],  matrix[3][2],  matrix[3][3] ); 
+    }
+    
+    ///////////////////////////////////////////////////////////////////////
+    // check to see if this is a branch we don't want to descend - this
+    // will prevent creating geometry for animation control structures
+    ///////////////////////////////////////////////////////////////////////
+    if ( (strstr( name, "con-" ) == NULL) && 
+         (strstr( name, "con_" ) == NULL) && 
+         (strstr( name, "fly_" ) == NULL) && 
+         (strstr( name, "fly-" ) == NULL) && 
+         (strstr( name, "camRIG" ) == NULL) &&
+         (strstr( name, "bars" ) == NULL) && 
+         // split
+         (strstr( fullname, search_prefix ) != NULL) )
+    {
+
+    // if making a pose - get deformed geometry
+    if ( make_pose )
+        gtype = SAA_GEOM_DEFORMED;
+        
+    // Get the number of key shapes
+    SAA_modelGetNbShapes( scene, model, &numShapes );
+    if ( verbose >= 1 )
+        fprintf( outStream, "MakeEgg: num shapes: %d\n", numShapes);
+
+    ///////////////////////////////////////////////////////////////////////
+    // if multiple key shapes exist create table entries for each
+    ///////////////////////////////////////////////////////////////////////
+    if ( (numShapes > 0) && make_morph )
+    {
+        has_morph = 1;
+
+        // make sure root morph table exists
+        if ( morphRoot == NULL )
+            morphRoot = animData.CreateTable( animRoot, "morph" ); 
+    
+        char   *tableName;
+
+        // create morph table entry for each key shape
+        // (start at second shape - as first is the original geometry)
+        for ( i = 1; i < numShapes; i++ )
+        {
+            tableName = MakeTableName( name, i );
+            SAnimTable *table = new SAnimTable( );     
+            table->name = tableName;
+            table->fps = anim_rate;
+            morphRoot->children.push_back( table );
+            if ( verbose >= 1 )
+                fprintf( outStream, "created table named: '%s'\n", tableName );    
+        }
+
+        //free( tableName );
+    }
+
+    SAA_modelGetNodeVisibility( scene, model, &visible ); 
+    if ( verbose >= 1 )
+        fprintf( outStream, "model visibility: %d\n", visible ); 
+    
+    ///////////////////////////////////////////////////////////////////////
+    // Only create egg polygon data if: the node is visible, and its not
+    // a NULL or a Joint, and we're outputing polys (or if we are outputing 
+    // NURBS and the model is a poly mesh or a face) 
+    ///////////////////////////////////////////////////////////////////////
+    if ( visible &&  
+         (type != SAA_MNILL) &&
+         (type != SAA_MJNT) && 
+         ((make_poly || 
+         (make_nurbs && ((type == SAA_MSMSH) || (type == SAA_MFACE )) )) 
+         || (!make_poly && !make_nurbs && make_duv && 
+            ((type == SAA_MSMSH) || (type == SAA_MFACE )) ))
+       )
+    {
+      // If the model is a NURBS in soft, set its step before tesselating
+      if ( type == SAA_MNSRF )
+        SAA_nurbsSurfaceSetStep( scene, model, nurbs_step, nurbs_step );
+
+      // If the model is a PATCH in soft, set its step before tesselating
+      else if ( type == SAA_MPTCH )
+        SAA_patchSetStep( scene, model, nurbs_step, nurbs_step );
+ 
+      // Get the number of triangles    
+      result = SAA_modelGetNbTriangles( scene, model, gtype, id, &numTri);
+      if ( verbose >= 1 )
+          fprintf( outStream, "triangles: %d\n", numTri);
+
+      if ( result != SI_SUCCESS )
+      {
+        if ( verbose >= 1 ) {
+            fprintf( outStream, 
+            "Error: couldn't get number of triangles!\n" );
+            fprintf( outStream, "\tbailing on model: '%s'\n", name );
+        }
+        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)
+      if ( isSkeleton || (strstr( name, "joint" ) != NULL) )
+      {
+          if ( verbose >= 1 )
+              fprintf( outStream, "animating Polys as joint!!!\n" );
+
+            MakeJoint( scene, lastJoint, lastAnim, model, name );
+      }
+    
+      // model is not a null and has no triangles!
+      if ( !numTri )
+      {
+        if ( verbose >= 1 )
+            fprintf( outStream, "no triangles!\n"); 
+      }
+      else 
+      {
+        // allocate array of triangles
+        triangles = (SAA_SubElem *)malloc(sizeof(SAA_SubElem)*numTri);
+        if ( triangles != NULL )
+        {
+            // triangulate model and read the triangles into array
+            SAA_modelGetTriangles( scene, model, gtype, id, numTri, triangles );
+        }
+        else
+            fprintf( outStream, "Not enough Memory for triangles...\n");
+
+        // allocate array of materials
+        materials = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numTri);
+        if ( materials != NULL )
+        {
+            // read each triangle's material into array  
+            SAA_triangleGetMaterials( scene, model, numTri, triangles, 
+                materials );
+        }
+        else
+            fprintf( outStream, "Not enough Memory for materials...\n");
+
+        // allocate array of textures per triangle
+        numTexTri = (int *)malloc(sizeof(int)*numTri);
+
+        // 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 )
+        {
+            // ASSUME only one texture per material
+            textures = (SAA_Elem *)malloc(sizeof(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] ); 
+            }
+
+            if ( verbose >= 1 )
+                fprintf( outStream, "numTexLoc = %d\n", numTexLoc);    
+        }
+        // if no local textures, try to get global textures
+        else
+        {
+            SAA_modelRelationGetT2DGlbNbElements( scene, model,
+                FALSE, &relinfo, &numTexGlb );
+
+            if ( numTexGlb )
+            {
+                // ASSUME only one texture per model
+                textures = (SAA_Elem *)malloc(sizeof(SAA_Elem));
+
+                // get the referenced texture
+                SAA_modelRelationGetT2DGlbElements( scene, model, 
+                    TEX_PER_MAT, textures ); 
+
+                if ( verbose >= 1 )
+                    fprintf( outStream, "numTexGlb = %d\n", numTexGlb);    
+            }
+        }
+
+        // allocate array of control vertices 
+        cvertices = (SAA_SubElem *)malloc(sizeof(SAA_SubElem)*numTri*3);
+        if ( cvertices != NULL )
+        {
+            // read each triangle's control vertices into array
+            SAA_triangleGetCtrlVertices( scene, model, gtype, id,
+                numTri, triangles, cvertices );
+
+            if ( verbose >= 2 )
+            {
+                cvertPos = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numTri*3);
+                SAA_ctrlVertexGetPositions(  scene, model, numTri*3, 
+                    cvertices, cvertPos);
+
+                for ( i=0; i < numTri*3; i++ )
+                {
+                    fprintf( outStream, "cvert[%d] = %f %f %f %f\n", i, 
+                        cvertPos[i].x, cvertPos[i].y, cvertPos[i].z, 
+                        cvertPos[i].w );
+                }
+            }
+        }
+        else
+            fprintf( outStream, "Not enough Memory for control vertices...\n");
+
+        // allocate array of control vertex indices
+        // this array maps from the redundant cvertices array into
+        // the unique vertices array (cvertices->vertices)
+        indices = (int *)malloc(sizeof(int)*numTri*3);
+        if ( indices != NULL )
+        {
+            for ( i=0; i < numTri*3; i++ )
+                indices[i] = 0;
+
+            SAA_ctrlVertexGetIndices( scene, model, numTri*3, 
+                cvertices, indices );
+       
+            if ( verbose >= 2 ) 
+                for ( i=0; i < numTri*3; i++ )
+                    fprintf( outStream, "indices[%d] = %d\n", i, indices[i] );
+        }
+        else
+            fprintf( outStream, "Not enough Memory for indices...\n");
+
+        // get number of UNIQUE vertices in model
+        SAA_modelGetNbTriVertices( scene, model, &numVert );
+
+        if ( verbose >= 2 )
+            fprintf( outStream, "num unique verts = %d\n", numVert );
+
+        //allocate array of vertices
+        vertices = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numVert);
+
+        // get the UNIQUE vertices of all triangles in model
+        SAA_modelGetTriVertices( scene, model, numVert, vertices );
+
+        if ( verbose >= 2 )
+        {
+            for ( i=0; i < numVert; i++ )
+            {
+                fprintf( outStream, "vertices[%d] = %f ", i, vertices[i].x );
+                fprintf( outStream, "%f %f %f\n", vertices[i].y, 
+                vertices[i].z, vertices[i].w );
+            }
+        }
+
+        // allocate indexMap array
+        // we contruct this array to map from the unique vertices
+        // array to the redundant cvertices array - it will save
+        // us from doing repetitive searches later
+        indexMap = MakeIndexMap( indices, numTri*3, numVert ); 
+
+        // allocate array of normals
+        normals = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numTri*3);
+        if ( normals != NULL )
+        {
+            // read each control vertex's normals into an array
+            SAA_ctrlVertexGetNormals( scene, model, numTri*3,
+                cvertices, normals );
+        }
+        else
+            fprintf( outStream, "Not enough Memory for normals...\n");
+
+        if ( verbose >= 2 )
+        {
+            for ( i=0; i<numTri*3; i++ )
+                fprintf( outStream, "normals[%d] = %f %f %f %f\n", i,
+                    normals[i].x, normals[i].y, normals[i].z, normals[i].w );
+        }
+
+        int uRepeat, vRepeat;
+
+        // make sure we have textures before we get t-coords
+        if ( numTexLoc )
+        {
+            // allocate arrays for u & v coords
+            uCoords = (float *)malloc(sizeof(float)*numTri*numTexLoc*3); 
+            vCoords = (float *)malloc(sizeof(float)*numTri*numTexLoc*3); 
+          
+            // read the u & v coords into the arrays
+            if ( uCoords != NULL && vCoords != NULL)
+            {
+              for ( i = 0; i < numTri*numTexLoc*3; i++ )
+                uCoords[i] = vCoords[i] = 0.0f;
+ 
+                SAA_ctrlVertexGetUVTxtCoords( scene, model, numTri*3,
+                    cvertices, numTexLoc*3, uCoords, vCoords );
+            }
+            else
+                fprintf( outStream, "Not enough Memory for texture coords...\n");
+
+            if ( verbose >= 2 )
+            {
+                for ( i=0; i<numTexLoc*3; i++ )
+                    fprintf( outStream, "texcoords[%d] = ( %f , %f )\n", i, 
+                        uCoords[i], vCoords[i] );
+            }
+
+            // allocate arrays of texture info
+            uScale = ( float *)malloc(sizeof(float)*numTri);
+            vScale = ( float *)malloc(sizeof(float)*numTri);
+            uOffset = ( float *)malloc(sizeof(float)*numTri);
+            vOffset = ( float *)malloc(sizeof(float)*numTri);
+            texNameArray = ( char **)malloc(sizeof(char *)*numTri);
+
+            for ( i = 0; i < numTri; i++ )
+            {
+                // initialize the array value
+                texNameArray[i] = NULL;
+
+                SAA_Boolean    valid = FALSE;
+                // check to see if texture is present
+                result = SAA_elementIsValid( scene, &textures[i], &valid );
+               
+                if ( result != SI_SUCCESS )
+                    fprintf( outStream, "SAA_elementIsValid failed!!!!\n" );
+ 
+                // texture present - get the name and uv info 
+                if ( valid )
+                {
+                    texNameArray[i] = ConvertTexture( scene, &textures[i] );
+                   
+                    if ( verbose >= 2 ) 
+                        fprintf( outStream, " tritex[%d] named: %s\n", i, 
+                            texNameArray[i] );
+
+                    SAA_texture2DGetUVSwap( scene, &textures[i], &uv_swap );
+
+                    if ( verbose >= 2 )
+                        if ( uv_swap == TRUE )
+                            fprintf( outStream, " 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] );
+
+                    if ( verbose >= 2 )
+                    {    
+                        fprintf(outStream, "tritex[%d] uScale: %f vScale: %f\n",                             i, uScale[i], vScale[i] );
+                        fprintf(outStream, " uOffset: %f vOffset: %f\n", 
+                            uOffset[i], vOffset[i] );
+                    }
+
+
+                    SAA_texture2DGetRepeats(  scene, &textures[i], &uRepeat,
+                        &vRepeat );
+
+                    if ( verbose >= 2 )
+                    {
+                        fprintf(outStream, "uRepeat = %d, vRepeat = %d\n",
+                            uRepeat, vRepeat );
+                    }
+                }
+                else 
+                {
+                    if ( verbose >= 2 ) 
+                    {
+                        fprintf( outStream, "Invalid texture...\n");
+                        fprintf( outStream, " tritex[%d] named: (null)\n", i );
+                    }
+                }
+            }
+
+            //debug
+            //for ( i = 0; i < numTri; i++ )
+            //{
+                //if ( texNameArray[i] != NULL )
+                    //fprintf( outStream, " tritex[%d] named: %s\n", i, 
+                        //texNameArray[i] );
+                //else
+                    //fprintf( outStream, " tritex[%d] named: (null)\n", i );
+            //}
+        }
+        // make sure we have textures before we get t-coords
+        else if ( numTexGlb )
+        {
+            SAA_Boolean    valid;
+
+            // check to see if texture is present
+            SAA_elementIsValid( scene, textures, &valid );
+                
+            // texture present - get the name and uv info 
+            if ( valid )
+            {
+                SAA_texture2DGetUVSwap( scene, textures, &uv_swap );
+
+                if ( verbose >= 1 )
+                    if ( uv_swap == TRUE )
+                        fprintf( outStream, " swapping u and v...\n" );
+
+                // allocate arrays for u & v coords
+                uCoords = (float *)malloc(sizeof(float)*numTri*numTexGlb*3); 
+                vCoords = (float *)malloc(sizeof(float)*numTri*numTexGlb*3); 
+
+                for ( i = 0; i < numTri*numTexGlb*3; i++ )
+                {
+                    uCoords[i] = vCoords[i] = 0.0f;
+                }                
+                    
+                // read the u & v coords into the arrays
+                if ( uCoords != NULL && vCoords != NULL)
+                {
+                    SAA_triCtrlVertexGetGlobalUVTxtCoords( scene, model, 
+                        numTri*3, cvertices, numTexGlb, textures, 
+                        uCoords, vCoords );
+                }
+                else
+                    fprintf( outStream, "Not enough Memory for texture coords...\n");
+
+                if ( verbose >= 2 )
+                  {
+                    for ( i=0; i<numTri*numTexGlb*3; i++ )
+                      fprintf( outStream, "texcoords[%d] = ( %f , %f )\n", i, 
+                               uCoords[i], vCoords[i] );
+                  }
+                
+                texNameArray = ( char **)malloc(sizeof(char *));
+                *texNameArray = ConvertTexture( scene, textures );
+
+                if ( verbose >= 1 )    
+                    fprintf( outStream, " global tex named: %s\n", 
+                        texNameArray );
+        
+                // allocate arrays of texture info
+                uScale = ( float *)malloc(sizeof(float));
+                vScale = ( float *)malloc(sizeof(float));
+                uOffset = ( float *)malloc(sizeof(float));
+                vOffset = ( float *)malloc(sizeof(float));
+
+                SAA_texture2DGetUScale( scene, textures, uScale );
+                SAA_texture2DGetVScale( scene, textures, vScale );
+                SAA_texture2DGetUOffset( scene, textures, uOffset );
+                SAA_texture2DGetVOffset( scene, textures, vOffset );
+
+                if ( verbose >= 1 ) 
+                {
+                    fprintf( outStream, " global tex uScale: %f vScale: %f\n", 
+                        *uScale, *vScale );
+                    fprintf( outStream, " uOffset: %f vOffset: %f\n", 
+                        *uOffset, *vOffset );
+                }
+
+                SAA_texture2DGetRepeats(  scene, textures, &uRepeat,
+                    &vRepeat );
+
+                if ( verbose >= 2 )
+                {
+                    fprintf(outStream, "uRepeat = %d, vRepeat = %d\n",
+                        uRepeat, vRepeat );
+                }
+            }
+            else fprintf( outStream, "Invalid texture...\n");
+        }
+
+        // make the egg vertex pool 
+        EggVertexPool *pool = _data.CreateVertexPool( parent, name );
+
+        for ( i = 0; i < numVert; i++ )
+        {
+            pfVec3    eggVert; 
+            pfVec3    eggNorm; 
+            
+            //convert to global coords
+            SAA_DVector local = vertices[i];
+            SAA_DVector global;
+
+            _VCT_X_MAT( global, local, matrix );
+
+            // set vertices array to reflect global coords
+            //vertices[i].x = global.x;
+            //vertices[i].y = global.y;
+            //vertices[i].z = global.z;
+
+            //eggVert.set( vertices[i].x, vertices[i].y, vertices[i].z );
+
+            // we'll preserve original verts for now
+            eggVert.set( global.x, global.y, global.z );
+
+            local = normals[indexMap[i]];
+
+            _VCT_X_MAT( global, local, matrix );
+
+            eggNorm.set( global.x, global.y, global.z );
+            eggNorm.normalize();
+
+            pool->AddVertex( eggVert, i );
+            pool->Vertex(i)->attrib.SetNormal( eggNorm );
+
+            // translate local uv's to global and add to vertex pool
+            if ( numTexLoc && (uCoords != NULL && vCoords !=NULL ))
+            {
+                float u, v;
+
+                if ( ignore_tex_offsets ) {
+                  u = uCoords[indexMap[i]];
+                  v = 1.0f - vCoords[indexMap[i]];
+                } else {
+                  u = (uCoords[indexMap[i]] - uOffset[indexMap[i]/3]) /
+                    uScale[indexMap[i]/3];
+                  
+                  v = 1.0f - ((vCoords[indexMap[i]] - vOffset[indexMap[i]/3]) /
+                              vScale[indexMap[i]/3]);
+                }
+                
+                if ( isNum(u) && isNum(v) )
+                { 
+                    if ( uv_swap == TRUE )
+                        pool->Vertex(i)->attrib.SetUV( v, u );
+                    else
+                        pool->Vertex(i)->attrib.SetUV( u, v );
+                }
+            }
+            else if ( numTexGlb && (uCoords != NULL && vCoords !=NULL ) )
+            {
+                float u, v;
+            
+                if ( ignore_tex_offsets ) {
+                  u = uCoords[indexMap[i]];
+                  v = 1.0f - vCoords[indexMap[i]];
+                } else {
+                  u = (uCoords[indexMap[i]] - *uOffset) / *uScale;
+                  v = 1.0f - (( vCoords[indexMap[i]] - *vOffset ) / *vScale);
+                }
+
+                if ( isNum(u) && isNum(v) )
+                { 
+                    if ( uv_swap == TRUE )
+                        pool->Vertex(i)->attrib.SetUV( v, u );
+                    else
+                        pool->Vertex(i)->attrib.SetUV( u, v );
+                }
+                        
+            }
+             
+            // if we've encountered textures and we desire duv anims
+            if (( numTexLoc || numTexGlb ) && make_duv )
+            {
+                int            numExp;
+                SAA_Elem   *tex;
+
+                // grab the current texture
+                if ( numTexLoc ) 
+                    tex = &textures[0];
+                else
+                    tex = textures;
+
+                // find how many expressions for this shape
+                SAA_elementGetNbExpressions( scene, tex, NULL, FALSE, 
+                    &numExp );
+
+                // if it has expressions we'll assume its animated
+                if ( numExp )
+                {
+                    // if animated object make base duv's, animtables
+                    // for the duv's and store the original offsets
+                    strstream uName, vName;
+
+                    // create duv target names
+                    uName << name << ".u" << ends;
+                    vName << name << ".v" << ends;
+
+                    // only create tables and store offsets
+                    // on a per model basis (not per vertex)
+                    if ( !i )
+                    {
+
+                        // make sure root morph table exists
+                        if ( morphRoot == NULL )
+                            morphRoot = animData.CreateTable( animRoot, 
+                                "morph" ); 
+
+                        // create morph table entry for each duv
+                        SAnimTable *uTable = new SAnimTable( );     
+                        uTable->name = uName.str();
+                        uTable->fps = anim_rate;
+                        morphRoot->children.push_back( uTable );
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "created duv table named: %s\n",                                uName.str() );    
+
+                        SAnimTable *vTable = new SAnimTable( );     
+                        vTable->name = vName.str();
+                        vTable->fps = anim_rate;
+                        morphRoot->children.push_back( vTable );
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "created duv table named: %s\n",                                vName.str() );    
+
+                        float    texOffsets[4];
+
+                        if ( numTexGlb )
+                        {
+                            texOffsets[0] = *uOffset;
+                            texOffsets[1] = *vOffset;
+                            texOffsets[2] = *uScale;
+                            texOffsets[3] = *vScale;
+                        }
+                        else
+                        {
+                            texOffsets[0] = uOffset[indexMap[i]/3];
+                            texOffsets[1] = vOffset[indexMap[i]/3];
+                            texOffsets[2] = uScale[indexMap[i]/3];
+                            texOffsets[3] = vScale[indexMap[i]/3];
+                        }
+
+                        // remember original texture offsets future reference
+                        SAA_elementSetUserData( scene, model, "TEX_OFFSETS", 
+                            sizeof( texOffsets ), TRUE, (void  **)&texOffsets );
+                    }
+
+                    EggMorphOffset *duvU;
+                    EggMorphOffset *duvV;
+
+                    // generate base duv's for this vertex
+                    duvU = new EggMorphOffset( uName.str(), 1.0 , 0.0 );    
+                    pool->Vertex(i)->attrib.uv_morphs.push_back( *duvU );
+            
+                    duvV = new EggMorphOffset( vName.str(), 0.0 , 1.0 );    
+                    pool->Vertex(i)->attrib.uv_morphs.push_back( *duvV );
+
+                } // if ( numExp )
+
+            } // if ( numTexLoc || numTexGlb )
+
+        } // for ( i = 0; i < numVert; i++ )
+
+        // if model has key shapes, generate vertex offsets
+        if ( has_morph && make_morph )
+            MakeVertexOffsets( scene, model, type, numShapes, numVert, 
+                vertices, matrix,  name );
+
+
+        // create vertex ref list for all polygons in the model
+        EggVertexRef *vref;
+
+        vref = new EggVertexRef( pool);
+        for ( i = 0; i < numVert; i++ )
+        {
+            //add each vert in pool to last joint for hard skinning
+            vref->indices.push_back( EggVertexIndex( i ) );
+        }
+
+        // hard assign poly geometry if no soft-skinning requested
+        //
+        //disabled 1/1/99 to streamline joint assignments.
+        // all hard-skinning now done in CleanUpSoftSkin.
+        //if ( !make_soft )
+        //{
+            //if ( lastJoint != NULL )
+            //{
+                //lastJoint->vrefs.AddUniqueNode( *vref );
+
+                //if ( verbose >= 1 )
+                    //fprintf( outStream, "hard-skinning %s (%d vertices)\n", 
+                        //name, i+1 );
+            //}
+        //}
+
+        // make an egg group to hold all triangles
+        EggGroup *group = _data.CreateGroup( parent, name);
+
+        // make this group the current parent
+        parent = group;
+
+        EggPolygon *poly = NULL;
+        EggColor *cref = NULL;
+        EggTexture *tref = NULL;
+
+        // for each triangle
+        for ( i = 0; i < numTri*3; i+=3 )
+        {
+            float    r,g,b,a;
+            pfVec4    color;
+
+            // make egg poly for each traingle and reference
+            // the appropriate vertex in the pool
+            poly = _data.CreatePolygon( group, pool );
+            poly->AddVertex(indices[i]);
+            poly->AddVertex(indices[i+1]);
+            poly->AddVertex(indices[i+2]);
+
+            // check for back face flag in model note info
+            char *modelNoteStr = GetModelNoteInfo( scene, model );
+
+            if ( modelNoteStr != NULL )
+            {
+                if ( strstr( modelNoteStr, "bface" ) != NULL )
+                    poly->flags |= EG_BFACE;
+            }
+            
+            // check to see if material is present
+            SAA_Boolean    valid;
+            SAA_elementIsValid( scene, &materials[i/3], &valid );
+
+            // material present - get the color 
+            if ( valid )
+            {
+                SAA_materialGetDiffuse( scene, &materials[i/3], &r, &g, &b );
+                SAA_materialGetTransparency( scene, &materials[i/3], &a );
+                color.set( r, g, b, 1.0f - a );
+            }
+            // no material - default to white
+            else
+                color.set( 1.0, 1.0, 1.0, 1.0 );
+
+            cref = _data.CreateColor(color);
+            poly->attrib.SetCRef(cref);
+
+            strstream uniqueTexName;
+
+            if (numTexLoc)
+            {
+                // polytex
+                if ( (texNameArray[i/3] != NULL) &&
+                        (strcmp(texNameArray[i/3], "NULL") != 0) )
+                {
+                    // append unique identifier to texname for
+                    // this particular object
+                    uniqueTexName << name << "-" 
+                        << RemovePathName(texNameArray[i/3]);
+
+                    tref = _data.CreateTexture( texNameArray[i/3], 
+                        uniqueTexName.str() ); 
+
+                    if ( verbose >= 1 )
+                        fprintf( outStream, " tritex[%d] named: %s\n", i/3, 
+                            texNameArray[i/3] );
+                }
+            }
+            else if ( numTexGlb )
+            {
+                if ( texNameArray != NULL )
+                {
+                    // append unique identifier to texname for
+                    // this particular object
+                    uniqueTexName << name << "-"
+                        << RemovePathName(*texNameArray);
+
+                    tref = _data.CreateTexture( *texNameArray, 
+                        uniqueTexName.str() );
+
+                    if ( verbose >= 1 )
+                        fprintf( outStream, " tritex named: %s\n",
+                             *texNameArray );
+                }
+            }
+
+            // set the clamp on the texture
+            if ( tref != NULL )
+            {
+                if ( uRepeat > 0 )
+                    tref->wrapu = EggTexture::WM_repeat;
+                else
+                    tref->wrapu = EggTexture::WM_clamp;
+
+                if ( vRepeat > 1 )
+                    tref->wrapv = EggTexture::WM_repeat;
+                else
+                    tref->wrapv = EggTexture::WM_clamp;
+
+                poly->attrib.SetTRef(tref);
+            }
+
+        }
+
+        // we're done - trash triangles...
+        SAA_modelClearTriangles( scene, model );
+
+        // free molloc'd memory
+        //free( triangles );
+        //free( materials );
+        //free( normals );
+        //free( cvertices );
+        //free( vertices );
+        //free( indices );
+        //free( indexMap );
+
+        // free these only if they were malloc'd for textures
+        if (numTexLoc || numTexGlb)
+        {
+            //free( textures );
+            //free( uCoords );
+            //free( vCoords );
+            //free( texNameArray );
+            //free( uScale );
+            //free( vScale );
+            //free( uOffset );
+            //free( vOffset );
+        }
+      }
+    }
+    else
+    {
+        ///////////////////////////////////////
+        // check to see if its a nurbs surface
+        ///////////////////////////////////////
+        if ( (type == SAA_MNSRF) && ( visible ) && (( make_nurbs ) 
+            || ( !make_nurbs && !make_poly &&  make_duv )) )
+        {
+            // check to see if NURBS is also skeleton...
+            SAA_Boolean isSkeleton = FALSE;
+
+            SAA_modelIsSkeleton( scene, model, &isSkeleton );
+
+            // check to see if this NURBS 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 );
+                geom_as_joint = 1;
+                if ( verbose >= 1 )
+                    fprintf( outStream, "animating NURBS as joint!!!\n" );
+            }
+
+            EggNurbsSurface    *eggNurbsSurf = new EggNurbsSurface( name );
+            int uDegree, vDegree;
+
+            // create nurbs representation of surface
+            SAA_nurbsSurfaceGetDegree( scene, model, &uDegree, &vDegree );
+            eggNurbsSurf->u_order = uDegree + 1;
+            eggNurbsSurf->v_order = vDegree + 1;
+            if ( verbose >= 1 )
+            {
+                fprintf( outStream, "nurbs degree: %d u, %d v\n", 
+                    uDegree, vDegree );
+                fprintf( outStream, "nurbs order: %d u, %d v\n", 
+                    uDegree + 1, vDegree + 1 );
+            }
+
+            SAA_Boolean    uClosed = FALSE;
+            SAA_Boolean    vClosed = FALSE;
+
+            SAA_nurbsSurfaceGetClosed( scene, model, &uClosed, &vClosed);    
+
+            if ( verbose >= 1 )
+            {    
+                if ( uClosed )
+                    fprintf( outStream, "nurbs is closed in u...\n");
+                if ( vClosed )
+                    fprintf( outStream, "nurbs is closed in v...\n");
+            }    
+            
+            int uRows, vRows;
+            SAA_nurbsSurfaceGetNbVertices( scene, model, &uRows, &vRows );
+            if ( verbose >= 1 )
+                fprintf( outStream, "nurbs vertices: %d u, %d v\n", 
+                    uRows, vRows );
+            
+            int uCurves, vCurves;
+            SAA_nurbsSurfaceGetNbCurves( scene, model, &uCurves, &vCurves );
+            if ( verbose >= 1 )
+                fprintf( outStream, "nurbs curves: %d u, %d v\n", 
+                    uCurves, vCurves );
+
+            if ( shift_textures )
+            {
+            if ( uClosed )
+                // shift starting point on NURBS surface for correct textures
+                SAA_nurbsSurfaceShiftParameterization( scene, model, -2, 0 );
+
+            if ( vClosed )
+                // shift starting point on NURBS surface for correct textures
+                SAA_nurbsSurfaceShiftParameterization( scene, model, 0, -2 );
+            }
+
+            SAA_nurbsSurfaceSetStep( scene, model, nurbs_step, nurbs_step );
+
+            // check for back face flag in model note info
+            char *modelNoteStr = GetModelNoteInfo( scene, model );
+
+            if ( modelNoteStr != NULL )
+            {
+                if ( strstr( modelNoteStr, "bface" ) != NULL )
+                    eggNurbsSurf->flags |= EG_BFACE;
+            }
+            
+            int numKnotsU, numKnotsV;
+            
+            SAA_nurbsSurfaceGetNbKnots( scene, model, &numKnotsU, &numKnotsV );
+            if ( verbose >= 1 )
+                fprintf( outStream, "nurbs knots: %d u, %d v\n", 
+                    numKnotsU, numKnotsV );
+
+            double *knotsU, *knotsV;    
+            knotsU = (double *)malloc(sizeof(double)*numKnotsU);
+            knotsV = (double *)malloc(sizeof(double)*numKnotsV);
+            SAA_nurbsSurfaceGetKnots( scene, model, gtype, 0, 
+                numKnotsU, numKnotsV, knotsU, knotsV );
+
+            if ( verbose >= 2 )
+                fprintf( outStream, "u knots:\n" );
+
+            AddKnots( eggNurbsSurf->u_knots, knotsU, numKnotsU, uClosed, uDegree ); 
+            if ( verbose >= 2 )
+                fprintf( outStream, "v knots:\n" );
+
+            AddKnots( eggNurbsSurf->v_knots, knotsV, numKnotsV, vClosed, vDegree); 
+
+            //free( knotsU );
+            //free( knotsV );
+
+            // set sub_div so we can see it in perfly
+            eggNurbsSurf->u_subdiv = (uRows-1)*nurbs_step;
+            eggNurbsSurf->v_subdiv = (vRows-1)*nurbs_step;
+
+            SAA_modelGetNbVertices( scene, model, &numVert );
+        
+            if ( verbose >= 2 )    
+                fprintf( outStream, "%d CV's\n", numVert ); 
+
+            // get the CV's
+            vertices = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numVert);
+            SAA_modelGetVertices( scene, model, gtype, 0,
+                numVert, vertices );
+
+            // create pool of NURBS vertices 
+            EggVertexPool *pool = _data.CreateVertexPool( parent, name );
+            eggNurbsSurf->SetVertexPool( pool );
+
+            // create vertex ref list for all cv's in the model
+            EggVertexRef *vref;
+
+            vref = new EggVertexRef( pool);
+
+            for ( int k = 0; k<numVert; k++ )
+            {
+                if ( verbose >= 2 )
+                {
+                    fprintf( outStream, "original cv[%d] = %f %f %f %f\n", k, 
+                        vertices[k].x, vertices[k].y, vertices[k].z, 
+                        vertices[k].w );
+                }
+
+                pfVec4  eggVert;
+
+                // convert to global coords
+                SAA_DVector global;
+
+                _VCT_X_MAT( global, vertices[k], matrix );
+
+                //preserve original weight
+                global.w = vertices[k].w;
+
+                // normalize coords to weight
+                global.x *= global.w;
+                global.y *= global.w;
+                global.z *= global.w;
+
+                // this code is commented out because I
+                // am no longer sending global data to
+                // the other routines (ie makevertexoffset)
+
+                // set vertices array to reflect global coords
+                //vertices[k].x = global.x;
+                //vertices[k].y = global.y;
+                //vertices[k].z = global.z;
+                //vertices[k].w = global.w;
+
+                //if ( verbose >= 2 )
+                //{
+                    //fprintf( outStream, "global cv[%d] = %f %f %f %f\n", k, 
+                        //vertices[k].x, vertices[k].y, vertices[k].z, 
+                        //vertices[k].w );
+                //}
+
+                //eggVert.set( vertices[k].x, vertices[k].y, vertices[k].z, 
+                    //vertices[k].w );
+
+                if ( verbose >= 2 )
+                {
+                    fprintf( outStream, "global cv[%d] = %f %f %f %f\n", k, 
+                        global.x, global.y, global.z, 
+                        global.w );
+                }
+
+                eggVert.set( global.x, global.y, global.z, 
+                    global.w );
+
+                // populate vertex pool
+                pool->AddVertex( eggVert, k );
+
+                // add vref's to NURBS info 
+                eggNurbsSurf->AddVertex( k ); 
+
+                //add each vert in pool to vref for hard skinning
+                vref->indices.push_back( EggVertexIndex( k ) );
+                
+                // check to see if the NURB is closed in u    
+                if ( uClosed )
+                {
+                    // add first uDegree verts to end of row
+                    if ( (k % uRows) == ( uRows - 1) )
+                    for ( int i = 0; i < uDegree; i++ ) 
+                    {
+                        // add vref's to NURBS info 
+                        eggNurbsSurf->AddVertex( i+((k/uRows)*uRows) ); 
+
+                        //add each vert to vref 
+                        vref->indices.push_back( 
+                            EggVertexIndex( i+((k/uRows)*uRows) ) );
+                    }
+                } 
+            }
+
+            // if hard skinned or this nurb is also a joint
+            //
+            //disabled 1/1/99 to streamline joint assignments.
+            // all hard skinning now done in CleanUpSoftSkin.
+            //if (!make_soft || geom_as_joint)
+            //{
+                //add the new cv references to the last 
+                //joint for hard skinning only 
+                //if ( lastJoint != NULL )
+                //{
+                    //lastJoint->vrefs.AddUniqueNode( *vref );
+                    //geom_as_joint = 0;    
+                    //if ( verbose >= 1 )    
+                        //fprintf( outStream, "Doing NURBS hard skinning...\n");
+                //}
+            //}
+
+            // check to see if the NURB is closed in v    
+            if ( vClosed && !uClosed )
+            {
+                // add first vDegree rows of verts to end of list
+                for ( int i = 0; i < vDegree*uRows; i++ ) 
+                    eggNurbsSurf->AddVertex( i ); 
+            }
+            // check to see if the NURB is closed in u and v    
+            else if ( vClosed && uClosed )
+            {
+                // add the first (degree) v verts and a few
+                // extra - for good measure
+                for ( i = 0; i < vDegree; i++ ) 
+                {
+                    // add first vDegree rows of verts to end of list
+                    for ( j = 0; j < uRows; j++ )
+                        eggNurbsSurf->AddVertex( j+(i*uRows) );
+
+                    // if u is closed to we have added uDegree
+                    // verts onto the ends of the rows - add them here too
+                    for ( k = 0; k < uDegree; k++ )
+                        eggNurbsSurf->AddVertex( k+(i*uRows)+((k/uRows)*uRows) );
+                }
+
+            }
+
+            // get the color of the NURBS surface
+            int numNurbMats;
+            EggColor *nurbCref;
+            pfVec4    nurbColor;
+
+            SAA_modelRelationGetMatNbElements( scene, model, FALSE, &relinfo,
+                &numNurbMats ); 
+
+            if ( verbose >= 1 )
+                fprintf( outStream, "nurbs surf has %d materials\n", 
+                    numNurbMats );
+
+            if ( numNurbMats )
+            {
+                float r,g,b,a;
+
+                materials = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numNurbMats);
+                
+                SAA_modelRelationGetMatElements( scene, model, relinfo, 
+                    numNurbMats, materials ); 
+
+                SAA_materialGetDiffuse( scene, &materials[0], &r, &g, &b );
+                SAA_materialGetTransparency( scene, &materials[0], &a );
+                nurbColor.set( r, g, b, 1.0f - a );
+                //nurbColor.set( r, g, b, 1.0 );
+
+                nurbCref = _data.CreateColor(nurbColor);
+                eggNurbsSurf->attrib.SetCRef(nurbCref);
+        
+                //get the texture of the NURBS surface from the material
+                int numNurbTexLoc = 0;
+                int numNurbTexGlb = 0;
+
+                // ASSUME only one texture per material
+                SAA_Elem nurbTex;
+
+                // 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 )
+                {
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "%s had %d local tex\n", name, 
+                            numNurbTexLoc );
+
+                    // get the referenced texture
+                    SAA_materialRelationGetT2DLocElements( scene, &materials[0],
+                        TEX_PER_MAT, &nurbTex ); 
+
+                }
+                // if no locals, try to get globals
+                else
+                {
+                    SAA_modelRelationGetT2DGlbNbElements( scene, model,
+                        FALSE, &relinfo, &numNurbTexGlb );
+
+                    if ( numNurbTexGlb )
+                    {
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "%s had %d global tex\n", name, 
+                                numNurbTexGlb );
+
+                            // get the referenced texture
+                            SAA_modelRelationGetT2DGlbElements( scene, 
+                                model, TEX_PER_MAT, &nurbTex ); 
+                        }
+                }
+
+                // add tex ref's if we found any textures
+                if ( numNurbTexLoc || numNurbTexGlb) 
+                {
+                    char    *texName = NULL;
+                    char    *uniqueTexName = NULL;
+                    EggTexture *tref;
+                    pfMatrix  nurbTexMat;
+
+
+                    // convert the texture to .rgb and adjust name 
+                    texName = ConvertTexture( scene, &nurbTex );
+
+                    // append unique identifier to texname for
+                    // this particular object
+                    uniqueTexName = (char *)malloc(sizeof(char)*
+                        (strlen(name)+strlen(texName)+3) );
+                    sprintf( uniqueTexName, "%s-%s", name, 
+                        RemovePathName(texName) );
+
+                    if ( verbose >= 1 )
+                    {
+                        fprintf( outStream, "creating tref %s\n", 
+                            uniqueTexName );
+                    }
+
+                    tref = _data.CreateTexture( texName, uniqueTexName );
+
+                    uScale = ( float *)malloc(sizeof(float));
+                    vScale = ( float *)malloc(sizeof(float));
+                    uOffset = ( float *)malloc(sizeof(float));
+                    vOffset = ( float *)malloc(sizeof(float));
+
+                    // get texture offset info
+                    SAA_texture2DGetUScale( scene, &nurbTex, uScale );
+                    SAA_texture2DGetVScale( scene, &nurbTex, vScale );
+                    SAA_texture2DGetUOffset( scene, &nurbTex, uOffset );
+                    SAA_texture2DGetVOffset( scene, &nurbTex, vOffset );
+                    SAA_texture2DGetUVSwap( scene, &nurbTex, &uv_swap );
+
+
+                    if ( verbose >= 1 )
+                    {
+                        fprintf( outStream, "nurbTex uScale: %f\n", *uScale );
+                        fprintf( outStream, "nurbTex vScale: %f\n", *vScale );
+                        fprintf( outStream, "nurbTex uOffset: %f\n", *uOffset );
+                        fprintf( outStream, "nurbTex vOffset: %f\n", *vOffset );
+                        if ( uv_swap )
+                            fprintf( outStream, "nurbTex u & v swapped!\n" );
+                        else
+                            fprintf( outStream, "nurbTex u & v NOT swapped\n" );
+                    }
+
+                    nurbTexMat.makeIdent();
+
+                    if ( !ignore_tex_offsets )
+                    {
+                        if ( uv_swap )
+                        {
+                            nurbTexMat[0][0] = 0.0f;
+                            nurbTexMat[1][1] = 0.0f;
+                            nurbTexMat[0][1] = 1 / *vScale;
+                            nurbTexMat[1][0] = 1 / *uScale;
+                            nurbTexMat[2][1] = -(*uOffset / *uScale);
+                            nurbTexMat[2][0] = -(*vOffset / *vScale);
+                        }
+                        else
+                        {
+                            nurbTexMat[0][0] = 1 / *uScale;
+                            nurbTexMat[1][1] = 1 / *vScale;
+                            nurbTexMat[2][0] = -(*uOffset / *uScale);
+                            nurbTexMat[2][1] = -(*vOffset / *vScale);
+                        }
+                    }
+
+
+    //call printMat
+    if ( verbose >= 2 )
+    {    
+    fprintf( outStream, "nurb tex matrix = %f %f %f %f\n", nurbTexMat[0][0],
+         nurbTexMat[0][1],  nurbTexMat[0][2],  nurbTexMat[0][3] ); 
+    fprintf( outStream, "nurb tex matrix = %f %f %f %f\n", nurbTexMat[1][0],
+         nurbTexMat[1][1],  nurbTexMat[1][2],  nurbTexMat[1][3] ); 
+    fprintf( outStream, "nurb tex matrix = %f %f %f %f\n", nurbTexMat[2][0],
+         nurbTexMat[2][1],  nurbTexMat[2][2],  nurbTexMat[2][3] ); 
+    fprintf( outStream, "nurb tex matrix = %f %f %f %f\n", nurbTexMat[3][0],
+         nurbTexMat[3][1],  nurbTexMat[3][2],  nurbTexMat[3][3] ); 
+    }
+
+
+                    tref->tex_mat = nurbTexMat;
+                    tref->flags |= EFT_TRANSFORM;
+
+                    eggNurbsSurf->attrib.SetTRef(tref);
+
+                }
+
+                // if we've encountered textures and we desire duv anims
+                if (( numNurbTexLoc || numNurbTexGlb ) && make_duv )
+                {
+                    int            numExp;
+
+                     // find how many expressions for this shape
+                    SAA_elementGetNbExpressions( scene, &nurbTex, NULL, FALSE, 
+                        &numExp );
+
+                    // if it has expressions we'll assume its animated
+                    if ( numExp )
+                    {
+                        if ( verbose > 1 )
+                            printf( "nurbTex has %d expressions...\n", numExp );
+
+                        // if animated object make base duv's, animtables
+                        // for the duv's and store the original offsets
+                        strstream uName, vName;
+
+                        // create duv target names
+                        uName << name << ".u" << ends;
+                        vName << name << ".v" << ends;
+
+                        // make sure root morph table exists
+                        if ( morphRoot == NULL )
+                            morphRoot = animData.CreateTable( animRoot, 
+                                "morph" ); 
+
+                        // create morph table entry for each duv
+                        SAnimTable *uTable = new SAnimTable( );     
+                        uTable->name = uName.str();
+                        uTable->fps = anim_rate;
+                        morphRoot->children.push_back( uTable );
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "created duv table named: %s\n",                                uName.str() );    
+
+                        SAnimTable *vTable = new SAnimTable( );     
+                        vTable->name = vName.str();
+                        vTable->fps = anim_rate;
+                        morphRoot->children.push_back( vTable );
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "created duv table named: %s\n",                                vName.str() );    
+
+                        float    texOffsets[4];
+
+                        texOffsets[0] = *uOffset;
+                        texOffsets[1] = *vOffset;
+                        texOffsets[2] = *uScale;
+                        texOffsets[3] = *vScale;
+
+                        // remember original texture offsets future reference
+                        SAA_elementSetUserData( scene, model, "TEX_OFFSETS", 
+                            sizeof( texOffsets ), TRUE, (void  **)&texOffsets );
+
+                        // create UV's and duv's for each vertex 
+                        for( i = 0; i < numVert; i++ )
+                        {
+                            pfVec2            tmpUV;
+                            EggMorphOffset *duvU;
+                            EggMorphOffset *duvV;
+
+                            //create uv's so we can store duv's
+                            eggNurbsSurf->CalcActualUV( i, tmpUV );
+                            pool->Vertex(i)->attrib.SetUV( tmpUV[0], tmpUV[1] );
+                            
+                            // generate base duv's for this vertex
+                            duvU = new EggMorphOffset(uName.str(), 1.0 , 0.0);    
+                            pool->Vertex(i)->attrib.uv_morphs.push_back(*duvU);
+                    
+                            duvV = new EggMorphOffset(vName.str(), 0.0 , 1.0);    
+                            pool->Vertex(i)->attrib.uv_morphs.push_back(*duvV);
+                        }
+
+                  } // if ( numExp )
+                } // if ( numTexLoc || numTexGlb )
+
+                //free( uScale );
+                //free( vScale );
+                //free( uOffset );
+                //free( vOffset );
+
+                //free( materials );
+            }
+            else
+            {
+                // no material present - default to white 
+                nurbColor.set( 1.0, 1.0, 1.0, 1.0 );
+            }
+
+            //////////////////////////////////////////
+            // check NURBS surface for trim curves
+            //////////////////////////////////////////
+            int     numTrims;
+            bool    isTrim = TRUE;
+            SAA_SubElem *trims;
+        
+            SAA_nurbsSurfaceGetNbTrimCurves( scene, model, SAA_TRIMTYPE_TRIM,
+                &numTrims );
+
+            if ( verbose >= 1 )
+                fprintf( outStream, "nurbs surf has %d trim curves\n", 
+                    numTrims );
+
+            if ( numTrims)
+            {
+                trims = (SAA_SubElem *)malloc(sizeof(SAA_SubElem)*numTrims);
+
+                if ( trims )
+                {
+                    SAA_nurbsSurfaceGetTrimCurves( scene, model, 
+                        gtype, 0, SAA_TRIMTYPE_TRIM, numTrims, 
+                        trims );
+
+                    MakeSurfaceCurve( scene, model, parent, eggNurbsSurf, 
+                        numTrims, trims, isTrim );
+                }
+
+                //free( trims );
+            }
+
+            //////////////////////////////////////////
+            // check NURBS surface for surface curves
+            //////////////////////////////////////////
+            isTrim = FALSE;
+
+            SAA_nurbsSurfaceGetNbTrimCurves( scene, model, 
+                SAA_TRIMTYPE_PROJECTION, &numTrims );
+
+            if ( verbose >= 1 )
+                fprintf( outStream, "nurbs surf has %d surface curves\n", 
+                    numTrims );
+
+            if ( numTrims)
+            {
+                trims = (SAA_SubElem *)malloc(sizeof(SAA_SubElem)*numTrims);
+
+                if ( trims )
+                {
+                    SAA_nurbsSurfaceGetTrimCurves( scene, model, 
+                        gtype, 0, SAA_TRIMTYPE_PROJECTION, 
+                        numTrims, trims );
+
+                    MakeSurfaceCurve( scene, model, parent, eggNurbsSurf, 
+                        numTrims, trims, isTrim );
+                }
+
+                //free( trims );
+            }
+
+            // push the NURBS into the egg data
+            parent->children.push_back( eggNurbsSurf );    
+
+            // if model has key shapes, generate vertex offsets
+            if ( has_morph && make_morph )
+                MakeVertexOffsets( scene, model, type, numShapes, numVert, 
+                    vertices, matrix, name );
+
+
+            //free( vertices );
+
+        }
+        /////////////////////////////////////
+        // check to see if its a NURBS curve
+        /////////////////////////////////////
+        else if ( (type == SAA_MNCRV) && ( visible ) && ( make_nurbs ) )
+        {
+            // ignore for now
+            // make the NURBS curve and push it into the egg data
+            //parent->children.push_back( MakeNurbsCurve( scene, model, parent, 
+                //matrix, name ) );
+           }
+        else if ( type == SAA_MJNT )
+        {
+            MakeJoint( scene, lastJoint, lastAnim, model, name );
+            if ( verbose >= 1 )
+                fprintf( outStream, "encountered IK joint: %s\n", name );
+        }
+        /////////////////////
+        // it must be a NULL
+        /////////////////////
+        else 
+        {
+            SAA_AlgorithmType    algo;
+        
+            SAA_modelGetAlgorithm( scene, model, &algo );
+            if ( verbose >= 1 )
+                fprintf( outStream, "null algorithm: %d\n", algo );
+
+            if ( algo == SAA_ALG_INV_KIN )
+            {
+                MakeJoint( scene, lastJoint, lastAnim,  model, name );
+                if ( verbose >= 1 )
+                    fprintf( outStream, "encountered IK root: %s\n", name );
+            }
+            else if ( algo == SAA_ALG_INV_KIN_LEAF )
+            {
+                MakeJoint( scene, lastJoint, lastAnim, model, name );
+                if ( verbose >= 1 )
+                    fprintf( outStream, "encountered IK leaf: %s\n", name );
+            }
+            else if ( algo == SAA_ALG_STANDARD )
+            {
+                SAA_Boolean isSkeleton = FALSE;
+
+                if ( verbose >= 1 )
+                    fprintf( outStream, "encountered Standard null: %s\n", name);
+
+                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 );
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "animating Standard null!!!\n" );
+                }
+                
+            }
+            else
+                if ( verbose >= 1 )
+                    fprintf( outStream, "encountered some other NULL: %d\n", 
+                        algo );
+        }
+    }
+
+
+    // check for children...
+    int        numChildren;
+    int        thisChild;
+    SAA_Elem *children;
+
+    SAA_modelGetNbChildren( scene, model, &numChildren );
+    if ( verbose >= 1 )
+        fprintf( outStream, "Model children: %d\n", numChildren );
+
+    if ( numChildren )
+    {
+        children = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numChildren);
+        SAA_modelGetChildren( scene, model, numChildren, children );
+        if ( children != NULL )
+        {
+            for ( thisChild = 0; thisChild < numChildren; thisChild++ )
+            {
+                if ( verbose >= 1 )
+                    fprintf( outStream, "\negging child %d...\n", thisChild);
+                MakeEgg( parent, lastJoint, lastAnim, scene, 
+                    &children[thisChild] );
+            }
+        }
+        else
+            fprintf( outStream, "Not enough Memory for children...\n");
+        //free( children );
+    }
+    fflush( outStream );
+  }
+  else
+    if ( verbose >= 1 )
+        fprintf( outStream, "Don't descend this branch!\n" );
+    
+  // we are done for the most part - start cleaning up memory
+  //free( name );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeSurfaceCurve
+//       Access: Public 
+//  Description: Given a scene and lists of u and v samples create a
+//                an egg NURBS curve of degree two from the samples 
+////////////////////////////////////////////////////////////////////
+void  soft2egg::
+MakeSurfaceCurve(  SAA_Scene *scene, SAA_Elem *model, EggGroup *parent, 
+    EggNurbsSurface *&nurbsSurf, int numTrims, SAA_SubElem *trims,
+    bool isTrim )
+{
+    int      i;
+    long      totalSamples = 0;
+    long    *numSamples;
+    double    *uSamples;
+    double    *vSamples;
+    SAA_Elem *trimCurves;
+    char    *name;
+
+    //get UV coord data
+    numSamples = (long *)malloc(sizeof(long)*numTrims);
+
+    SAA_surfaceCurveGetNbLinearSamples( scene, model,  numTrims, trims,
+        numSamples );
+
+    for ( i = 0; i < numTrims; i++ )
+    {
+        totalSamples += numSamples[i];
+        if ( verbose >= 2 )
+            fprintf( outStream, "numSamples[%d] = %d\n", i, numSamples[i] );
+    }
+
+    if ( verbose >= 2 )
+        fprintf( outStream, "total samples = %ld\n", totalSamples );
+
+    uSamples = (double *)malloc(sizeof(double)*totalSamples);
+    vSamples = (double *)malloc(sizeof(double)*totalSamples);
+
+    SAA_surfaceCurveGetLinearSamples( scene, model, numTrims, trims,
+        numSamples, uSamples, vSamples ); 
+
+    if ( verbose >= 2 )
+        for ( long li = 0; li < totalSamples; li++ )
+            fprintf( outStream, "master list cv[%ld] = %f, %f\n", li, 
+                uSamples[li], vSamples[li] ); 
+
+    trimCurves = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numTrims);
+
+    SAA_surfaceCurveExtract( scene, model, numTrims, trims, trimCurves );
+
+    // if it's a trim create a trim to assign trim curves to
+    EggNurbsSurface::Trim *eggTrim = new EggNurbsSurface::Trim();
+
+    // for each trim curve, make an egg curve and 
+    // add it to the trims of the NURBS surface
+    for ( i = 0; i < numTrims; i++ )
+    {
+        if ( use_prefix )
+        {
+            // Get the FULL name of the trim curve
+            name = GetFullName( scene, &trimCurves[i] );
+        }
+        else
+        {
+            // Get the name of the trim curve
+            name = GetName( scene, &trimCurves[i] );
+        }
+
+        if ( isTrim )
+        {
+            // add to trim list
+            EggNurbsSurface::Loop *eggLoop = new EggNurbsSurface::Loop();
+            eggLoop->push_back( MakeUVNurbsCurve( i, numSamples, uSamples, 
+                vSamples, parent, name ) );
+            eggTrim->push_back( *eggLoop );
+        }
+        else
+            // add to curve list
+            nurbsSurf->curves.push_back( MakeUVNurbsCurve( i, numSamples,                         uSamples, vSamples, parent, name ) );
+    } 
+
+    if ( isTrim )
+        // pus trim list onto trims list
+        nurbsSurf->trims.push_back( *eggTrim );
+
+    //free( name );
+    //free( trimCurves );
+    //free( uSamples );
+    //free( vSamples );
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeUVNurbsCurve
+//       Access: Public 
+//  Description: Given a scene and lists of u and v samples create a
+//                an egg NURBS curve of degree two from the samples 
+////////////////////////////////////////////////////////////////////
+EggNurbsCurve  *soft2egg::
+MakeUVNurbsCurve( int numCurve, long *numSamples, double *uSamples, 
+    double *vSamples, EggGroup *parent, char *name )
+{
+    EggNurbsCurve    *eggNurbsCurve = new EggNurbsCurve( name );
+
+    eggNurbsCurve->order = 2;
+
+
+    if ( verbose >= 2 )
+        fprintf( outStream, "nurbs UV curve %s:\n", name );
+
+    //set sub_div so we can see it in perfly
+    //eggNurbsCurve->subdiv = numSamples[numCurve]/4;
+    // perfly chokes on big numbers - keep it reasonable
+    eggNurbsCurve->subdiv = 150; 
+
+    //create pool of NURBS vertices 
+    EggVertexPool *pool = _data.CreateVertexPool( parent, name );
+    eggNurbsCurve->SetVertexPool( pool );
+
+    // calculate offset to this curve's samples
+    // in list of all curve samples
+    int offset = 0;
+
+    for ( int o = 0; o < numCurve; o++ )
+        offset += numSamples[o];
+    
+    for ( int k = 0; k<numSamples[numCurve]; k++ )
+    {
+        pfVec3  eggVert;
+
+        // index into the array of samples for this curve
+        eggVert.set( uSamples[k+offset], vSamples[k+offset], 1.0f );
+
+        if ( verbose >= 2 )
+            fprintf( outStream, "cv[%d] = %f %f %f\n", k, eggVert[0], 
+                eggVert[1], eggVert[2] ); 
+
+        //populate vertex pool
+        pool->AddVertex( eggVert, k );
+
+        //add vref's to NURBS info 
+        eggNurbsCurve->AddVertex( k ); 
+    }
+
+    // create numSamples[numCurve]+2 knots
+    eggNurbsCurve->knots.push_back( 0 );
+    for ( k = 0; k < numSamples[numCurve]; k++ )
+        eggNurbsCurve->knots.push_back( k  );
+    eggNurbsCurve->knots.push_back( numSamples[numCurve] - 1 );
+
+    //set color to bright green for now
+    EggColor *nurbCref;
+    pfVec4    nurbColor;
+
+    nurbColor.set( 0.5, 1.0, 0.5, 1.0 );
+    nurbCref = _data.CreateColor(nurbColor);
+    eggNurbsCurve->attrib.SetCRef(nurbCref);
+
+    return( eggNurbsCurve );
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeNurbsCurve
+//       Access: Public 
+//  Description: Given a scene and a NURBS curve model create the
+//                 the appropriate egg structures  
+////////////////////////////////////////////////////////////////////
+EggNurbsCurve  *soft2egg::
+MakeNurbsCurve( SAA_Scene *scene, SAA_Elem *model, EggGroup *parent, 
+    float matrix[4][4], char *name )
+{
+    EggNurbsCurve    *eggNurbsCurve = new EggNurbsCurve( name );
+    int degree;
+
+    if ( verbose >= 2 )
+        fprintf( outStream, "nurbs curve %s:\n", name );
+
+    //create nurbs representation of surface
+    SAA_nurbsCurveGetDegree( scene, model, &degree );
+    eggNurbsCurve->order = degree + 1;
+    if ( verbose >= 2 )
+        fprintf( outStream, "nurbs curve order: %d\n", degree + 1 );
+    
+    SAA_nurbsCurveSetStep( scene, model, nurbs_step );
+
+    SAA_Boolean    closed = FALSE;
+
+    SAA_nurbsCurveGetClosed( scene, model, &closed );    
+    if ( closed )
+        if ( verbose >= 2 )
+            fprintf( outStream, "nurbs curve is closed...\n");
+
+    int numKnots;
+    
+    SAA_nurbsCurveGetNbKnots( scene, model, &numKnots );
+    if ( verbose >= 2 )
+        fprintf( outStream, "nurbs curve knots: %d\n", numKnots );
+    double *knots;    
+    knots = (double *)malloc(sizeof(double)*numKnots);
+    SAA_nurbsCurveGetKnots( scene, model, SAA_GEOM_ORIGINAL, 0, 
+        numKnots, knots );
+
+    AddKnots( eggNurbsCurve->knots, knots, numKnots, closed, degree );
+
+    //free( knots );
+
+    int    numCV;
+
+    SAA_modelGetNbVertices( scene, model, &numCV );
+    if ( verbose >= 2 )
+        fprintf( outStream, "%d CV's (=? %d)\n", numCV, (numKnots-(degree+1)) );
+
+    //set sub_div so we can see it in perfly
+    eggNurbsCurve->subdiv = (numCV-1)*nurbs_step;
+
+    // get the CV's
+    SAA_DVector *cvArray;
+    cvArray = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numCV);
+    SAA_modelGetVertices( scene, model, SAA_GEOM_ORIGINAL, 0,
+        numCV, cvArray );
+
+    //create pool of NURBS vertices 
+    EggVertexPool *pool = _data.CreateVertexPool( parent, name );
+    eggNurbsCurve->SetVertexPool( pool );
+
+    for ( int k = 0; k<numCV; k++ )
+    {
+        if ( verbose >= 2 )
+            fprintf( outStream, "cv[%d] = %f %f %f %f\n", k, cvArray[k].x,
+                cvArray[k].y, cvArray[k].z, cvArray[k].w );
+
+        pfVec4  eggVert;
+
+        //convert to global coords
+        SAA_DVector local = cvArray[k];
+        SAA_DVector global;
+
+        _HVCT_X_MAT( global, local, matrix );
+
+        eggVert.set( global.x, global.y, global.z, global.w );
+
+        //populate vertex pool
+        pool->AddVertex( eggVert, k );
+
+        //add vref's to NURBS info 
+        eggNurbsCurve->AddVertex( k ); 
+    }
+
+    if ( closed )
+    {
+        // need to replicate first (degree) vertices
+        for ( k = 0; k < degree; k++ )
+        {
+            eggNurbsCurve->AddVertex( k );
+            if ( verbose >= 2 )
+                fprintf( outStream, "adding cv[%d] = %f %f %f %f\n", k, 
+                    cvArray[k].x, cvArray[k].y, cvArray[k].z, cvArray[k].w );
+        }
+    }
+
+    //free( cvArray );
+
+    //set color to bright green for now
+    EggColor *nurbCref;
+    pfVec4    nurbColor;
+
+    nurbColor.set( 0.5, 1.0, 0.5, 1.0 );
+    nurbCref = _data.CreateColor(nurbColor);
+    eggNurbsCurve->attrib.SetCRef(nurbCref);
+
+    return( eggNurbsCurve );
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: AddKnots
+//       Access: Public 
+//  Description: Given a parametric surface, and its knots, create
+//                 the appropriate egg structure by filling in Soft's
+//                 implicit knots and assigning the rest to eggKnots. 
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+AddKnots( perf_vector<double> &eggKnots, double *knots, int numKnots, 
+    SAA_Boolean closed, int degree ) 
+{
+    int k = 0;
+    double lastKnot = knots[0];
+    double    *newKnots;
+
+    // add initial implicit knot(s)
+    if ( closed ) 
+    {
+        int i = 0;
+        newKnots = (double *)malloc(sizeof(double)*degree);
+
+        // need to add (degree) number of knots 
+        for ( k = numKnots - 1; k >= numKnots - degree; k-- )
+        {
+            // we have to know these in order to calculate
+            // next knot value so hold them in temp array
+            newKnots[i] =  lastKnot - (knots[k] - knots[k-1]);
+            lastKnot = newKnots[i];
+            i++;
+        }
+        for ( k = degree - 1; k >= 0; k-- )
+        {
+            eggKnots.push_back( newKnots[k] );
+            if ( verbose >= 2 )
+                fprintf( outStream, "knots[%d] = %f\n", k, newKnots[k] ); 
+        }
+
+        //free( newKnots );
+    }
+    else
+    {
+        eggKnots.push_back( knots[k] );
+        if ( verbose >= 2 )
+            fprintf( outStream, "knots[%d] = %f\n", k, knots[k] ); 
+    }
+
+    // add the regular complement of knots
+    for (k = 0; k < numKnots; k++)
+    {
+        eggKnots.push_back( knots[k] );
+        if ( verbose >= 2 )
+            fprintf( outStream, "knots[%d] = %f\n", k+1, knots[k] ); 
+    }
+
+    lastKnot = knots[numKnots-1];
+
+    // add trailing implicit knots
+    if ( closed ) 
+    {
+
+        // need to add (degree) number of knots 
+        for ( k = 1; k <= degree; k++ )
+        {
+            eggKnots.push_back( lastKnot + (knots[k] - knots[k-1]) );
+            if ( verbose >= 2 )
+                fprintf( outStream, "knots[%d] = %f\n", k, 
+                    lastKnot + (knots[k] - knots[k-1]) ); 
+            lastKnot = lastKnot + (knots[k] - knots[k-1]);
+        }
+    }
+    else
+    {
+        eggKnots.push_back( knots[k-1] );
+        if ( verbose >= 2 )
+            fprintf( outStream, "knots[%d] = %f\n", k+1, knots[k-1] ); 
+    }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeJoint
+//       Access: Public 
+//  Description: Given a name, a parent and a model create a new 
+//                 a new EggJoint for that model. 
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+MakeJoint( SAA_Scene *scene, EggJoint *&lastJoint, AnimGroup *&lastAnim, 
+    SAA_Elem *model, char *name )
+{
+    float     matrix[4][4];
+    pfMatrix  Matrix;
+    EggJoint *joint;
+    SAA_Boolean    globalFlag = FALSE;
+    int    scale_joint = 0;
+
+
+    // this is a quick fix to make scaled skeletons possible
+    // if the parent contains the keyword "scale" make this joint
+    // a global root joint instead of a child...
+    if (lastJoint != NULL)
+    {
+        if ( strstr( lastJoint->name.Str(), "scale" ) != NULL )
+        {
+            scale_joint = 1;    
+            if ( verbose >= 1 )
+                fprintf( outStream, "scale joint flag set!\n" );
+        }
+    }
+
+    // if not root, flatten is false, and last joint had no scaling
+    // applied to it, then create joint in skeleton tree
+    if ( (lastJoint != NULL) && !flatten && !scale_joint )
+    {
+        if ( verbose >= 1 )
+        {
+            fprintf( outStream, "lastJoint = %s\n", lastJoint->name.Str() );
+            fprintf( outStream, "getting local transform\n" );
+        }
+
+        SAA_elementSetUserData( scene, model, "GLOBAL", sizeof( SAA_Boolean ),
+            TRUE, (void  **)&globalFlag );
+
+        // get the local matrix
+        SAA_modelGetMatrix( scene, model, SAA_COORDSYS_LOCAL,  matrix );
+
+        // make this into a pfMatrix
+        Matrix[0][0] = matrix[0][0];
+        Matrix[0][1] = matrix[0][1];
+        Matrix[0][2] = matrix[0][2];
+        Matrix[0][3] = matrix[0][3];
+        Matrix[1][0] = matrix[1][0];
+        Matrix[1][1] = matrix[1][1];
+        Matrix[1][2] = matrix[1][2];
+        Matrix[1][3] = matrix[1][3];
+        Matrix[2][0] = matrix[2][0];
+        Matrix[2][1] = matrix[2][1];
+        Matrix[2][2] = matrix[2][2];
+        Matrix[2][3] = matrix[2][3];
+        Matrix[3][0] = matrix[3][0];
+        Matrix[3][1] = matrix[3][1];
+        Matrix[3][2] = matrix[3][2];
+        Matrix[3][3] = matrix[3][3];
+
+        joint = _data.CreateJoint( lastJoint, name ); 
+        joint->transform = Matrix;
+    }
+    // if we already have a root attach this joint to it
+    else if (foundRoot)
+    {
+        if ( verbose >= 1 )
+            fprintf( outStream, "getting global transform\n" );
+
+        globalFlag = TRUE;
+
+        SAA_elementSetUserData( scene, model, "GLOBAL", sizeof( SAA_Boolean ),
+            TRUE, (void *)&globalFlag );
+
+        // get the global matrix
+        SAA_modelGetMatrix( scene, model, SAA_COORDSYS_GLOBAL,  matrix );
+
+        // make this into a pfMatrix
+        Matrix[0][0] = matrix[0][0];
+        Matrix[0][1] = matrix[0][1];
+        Matrix[0][2] = matrix[0][2];
+        Matrix[0][3] = matrix[0][3];
+        Matrix[1][0] = matrix[1][0];
+        Matrix[1][1] = matrix[1][1];
+        Matrix[1][2] = matrix[1][2];
+        Matrix[1][3] = matrix[1][3];
+        Matrix[2][0] = matrix[2][0];
+        Matrix[2][1] = matrix[2][1];
+        Matrix[2][2] = matrix[2][2];
+        Matrix[2][3] = matrix[2][3];
+        Matrix[3][0] = matrix[3][0];
+        Matrix[3][1] = matrix[3][1];
+        Matrix[3][2] = matrix[3][2];
+        Matrix[3][3] = matrix[3][3];
+
+        if ( verbose >= 1 )
+            fprintf( outStream, "attaching orphan chain to root\n" );
+
+        joint = _data.CreateJoint( rootJnt, name ); 
+        joint->transform = Matrix;
+        lastAnim = rootAnim;    
+    }
+    // if root, make a seperate tree for skeleton and
+    // create required Table for the Egg heirarchy
+    else 
+    {
+        if ( verbose >= 1 )
+            fprintf( outStream, "getting global transform\n" );
+
+        globalFlag = TRUE;
+
+        SAA_elementSetUserData( scene, model, "GLOBAL", sizeof( SAA_Boolean ),
+            TRUE, (void *)&globalFlag );
+
+        // get the global matrix
+        SAA_modelGetMatrix( scene, model, SAA_COORDSYS_GLOBAL,  matrix );
+
+        // make this into a pfMatrix
+        Matrix[0][0] = matrix[0][0];
+        Matrix[0][1] = matrix[0][1];
+        Matrix[0][2] = matrix[0][2];
+        Matrix[0][3] = matrix[0][3];
+        Matrix[1][0] = matrix[1][0];
+        Matrix[1][1] = matrix[1][1];
+        Matrix[1][2] = matrix[1][2];
+        Matrix[1][3] = matrix[1][3];
+        Matrix[2][0] = matrix[2][0];
+        Matrix[2][1] = matrix[2][1];
+        Matrix[2][2] = matrix[2][2];
+        Matrix[2][3] = matrix[2][3];
+        Matrix[3][0] = matrix[3][0];
+        Matrix[3][1] = matrix[3][1];
+        Matrix[3][2] = matrix[3][2];
+        Matrix[3][3] = matrix[3][3];
+
+        rootJnt = _data.CreateJoint( skeleton, "root" );
+        rootJnt->transform.makeIdent();
+        if ( verbose >= 1 )
+            fprintf( outStream, "setting skeleton root\n" );
+        rootJnt->flags |= EF_TRANSFORM;
+
+        joint = _data.CreateJoint( rootJnt, name ); 
+        joint->transform = Matrix;
+        foundRoot = TRUE;
+        if ( verbose >= 1 )
+            fprintf( outStream, "found first chain\n" );
+
+        // make skeleton table
+        AnimGroup *skeletonTable;
+        skeletonTable = animData.CreateTable( animRoot, "<skeleton>" ); 
+        rootAnim = animData.CreateTable( skeletonTable, "root" ); 
+        XfmSAnimTable *table = new XfmSAnimTable( );     
+        table->name = "xform";
+        table->fps = anim_rate;
+        rootAnim->children.push_back( table );
+        lastAnim = rootAnim;    
+    }
+
+    joint->flags |= EF_TRANSFORM;
+
+    //if ( make_anim)
+    //{
+        AnimGroup *anim = animData.CreateTable( lastAnim, name );
+        XfmSAnimTable *table = new XfmSAnimTable( );     
+        if ( verbose >= 1 )
+            fprintf( outStream, "created anim table: %s\n", "xform" );
+        table->name = "xform";
+        table->fps = anim_rate;
+        anim->children.push_back( table );
+        lastAnim = anim;
+    //}
+
+    // make this joint current parent of chain
+    lastJoint = joint;
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeSoftSkin
+//       Access: Public 
+//  Description: Given a skeleton part find its envelopes (if any)
+//                 get the vertices associated with the envelopes and
+//                 their weights and make vertex ref's for the joint 
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+MakeSoftSkin( SAA_Scene *scene, SAA_Elem *model, SAA_Elem *models,
+    int numModels, char *name )
+{
+    int            numEnv;
+    SAA_ModelType   type;
+    SAA_Elem    *envelopes;
+
+    if ( verbose >= 1 )
+        fprintf( outStream, "\n>found skeleton part( %s )!\n", name );
+
+    SAA_skeletonGetNbEnvelopes( scene, model, &numEnv );
+    
+    if ( numEnv )
+    {
+        // it's got envelopes - must be soft skinned
+        if ( verbose >= 1 )
+            fprintf( outStream, "numEnv = %d\n", numEnv );
+
+        // allocate envelope array
+        envelopes = ( SAA_Elem *)malloc( sizeof( SAA_Elem )*numEnv );
+
+        if ( envelopes != NULL )
+        {
+            int         thisEnv;
+            SAA_EnvType envType;
+            bool        hasEnvVertices = 0;
+
+            SAA_skeletonGetEnvelopes( scene, model, numEnv, envelopes );
+
+            for ( thisEnv = 0; thisEnv < numEnv; thisEnv++ )
+            {
+                if ( verbose >= 1 )
+                    fprintf( outStream, "env[%d]: ", thisEnv );
+
+                SAA_envelopeGetType( scene, &envelopes[thisEnv], &envType );
+
+                if ( envType == SAA_ENVTYPE_NONE )
+                {
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "envType = none\n" );
+                }
+                else if ( envType == SAA_ENVTYPE_FLXLCL )
+                {
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "envType = flexible, local\n" );
+                    hasEnvVertices = 1;
+                }
+                else if ( envType == SAA_ENVTYPE_FLXGLB )
+                {
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "envType = flexible, global\n" );
+                    hasEnvVertices = 1;
+                }
+                else if ( envType == SAA_ENVTYPE_RGDGLB )
+                {
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "envType = rigid, global\n" );
+                    hasEnvVertices = 1;
+                }
+                else
+                {
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "envType = unknown\n" );
+                }
+            }
+
+            if ( hasEnvVertices)
+            {
+                int            *numEnvVertices;
+                SAA_SubElem    *envVertices = NULL;
+
+                numEnvVertices = (int *)malloc(sizeof(int)*numEnv);
+
+                SAA_envelopeGetNbCtrlVertices( scene, model, numEnv,
+                    envelopes, numEnvVertices );
+
+                if ( numEnvVertices != NULL )
+                {
+                    int totalEnvVertices = 0;
+                    int    i,j,k;
+
+                    for( i = 0; i < numEnv; i++ )
+                    {
+                        totalEnvVertices += numEnvVertices[i];
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "numEnvVertices[%d] = %d\n", 
+                                i, numEnvVertices[i] );
+                    }
+
+
+                    if ( verbose >= 1 )
+                        fprintf( outStream, "total env verts = %d\n", 
+                            totalEnvVertices );    
+
+                    if ( totalEnvVertices )
+                    {
+                    envVertices = (SAA_SubElem *)malloc(sizeof(SAA_SubElem)*totalEnvVertices);
+
+                    if ( envVertices != NULL )
+                    {
+
+                        SAA_envelopeGetCtrlVertices( scene, model,
+                            numEnv, envelopes, numEnvVertices, envVertices);
+            
+                        // loop through for each envelope
+                        for ( i = 0; i < numEnv; i++ )
+                        {
+                            float *weights = NULL;
+                            int       vertArrayOffset = 0;
+
+                            if ( verbose >= 2 )
+                                fprintf( outStream, "\nenvelope[%d]:\n", i );
+
+                            weights = (float *)malloc(sizeof(float)*numEnvVertices[i]);
+
+                            if ( weights )
+                            {
+                            char *envName;
+                            int *vpoolMap = NULL;
+
+                            for ( j = 0; j < i; j++ )
+                                vertArrayOffset += numEnvVertices[j];
+
+                            if ( verbose >= 1 )
+                                fprintf( outStream, 
+                                    "envVertArray offset = %d\n", 
+                                    vertArrayOffset );
+
+                            // get the weights of the envelope vertices
+                            SAA_ctrlVertexGetEnvelopeWeights( 
+                                    scene, model, &envelopes[i], 
+                                    numEnvVertices[i], 
+                                    &envVertices[vertArrayOffset], weights ); 
+
+                            // Get the name of the envelope model
+                            if ( use_prefix )
+                            {
+                                // Get the FULL name of the envelope
+                                envName = GetFullName( scene, &envelopes[i] );
+                            }
+                            else
+                            {
+                                // Get the name of the envelope
+                                envName = GetName( scene, &envelopes[i] );
+                            }
+
+                            if ( verbose >= 1 )
+                                fprintf( outStream, "envelope name %s\n", envName );
+
+                            // find out if envelope geometry is poly or nurb
+                            //SAA_modelGetType( scene, 
+                                //FindModelByName( envName, scene, 
+                                    //models, numModels ), &type );
+
+                            SAA_modelGetType( scene, &envelopes[i], &type );
+			
+			    if ( verbose >= 1 )
+			    {
+                                fprintf( outStream, "envelope model type ");
+			
+				if ( type == SAA_MSMSH )
+					fprintf( outStream, "MESH\n" );
+				else if ( type == SAA_MNSRF )
+					fprintf( outStream, "NURBS\n" );
+				else
+					fprintf( outStream, "OTHER\n" );
+			   }
+
+                            int *envVtxIndices = NULL;
+                            envVtxIndices = (int *)malloc(sizeof(int)*numEnvVertices[i]);
+
+                            // Get the envelope vertex indices
+                            SAA_ctrlVertexGetIndices( scene, &envelopes[i],                                     numEnvVertices[i], 
+                                &envVertices[vertArrayOffset], envVtxIndices );
+
+                            // find out how many vertices the model has
+                            int modelNumVert;
+
+                            SAA_modelGetNbVertices( scene, &envelopes[i], &modelNumVert );
+                             
+                            SAA_DVector *modelVertices = NULL;
+                            modelVertices = (SAA_DVector *)malloc(sizeof(SAA_DVector)*modelNumVert);
+
+                            // get the model vertices
+                            SAA_modelGetVertices( scene, &envelopes[i],
+                                SAA_GEOM_ORIGINAL, 0, modelNumVert, 
+                                modelVertices );
+                            
+                            // create array of global model coords 
+                            SAA_DVector *globalModelVertices = NULL;
+                            globalModelVertices = (SAA_DVector *)malloc(sizeof(SAA_DVector)*modelNumVert);
+                            float        matrix[4][4];
+
+                            // tranform local model vert coords to global
+
+                            // first get the global matrix
+                            SAA_modelGetMatrix( scene, &envelopes[i],                                                 SAA_COORDSYS_GLOBAL,  matrix );
+
+                            // populate array of global model verts
+                            for ( j = 0; j < modelNumVert; j++ )
+                            {
+                                _VCT_X_MAT( globalModelVertices[j], 
+                                    modelVertices[j], matrix );
+                            }
+
+                            // find the egg vertex pool that corresponds
+                            // to this envelope model
+                            EggVertexPool *envPool = 
+                                (EggVertexPool *)(_data.pools.FindName( envName ));
+                            // If we are outputting triangles:
+                            // create an array that maps from a referenced
+                            // vertex in the envelope to a corresponding 
+                            // vertex in the egg vertex pool
+                            //if ( (type == SAA_MNSRF) && !make_nurbs ) 
+                            if ( !make_nurbs || (type == SAA_MSMSH) ) 
+                            {
+                                vpoolMap = FindClosestTriVert( envPool,
+                                    globalModelVertices, modelNumVert );
+                            }
+
+
+                            if ( envPool != NULL )
+                            {
+    
+                            // find the egg joint that corresponds to this model
+                            EggJoint *joint = 
+                                (EggJoint *)(skeleton->FindDescendent( name ));
+
+														// this doesn't seem to be necessary 4/7/99
+                            //EggJoint *parent = (EggJoint *)joint->parent;
+														//assert(parent->IsA(NT_EggJoint));
+
+                            // for every envelope vertex 
+                            for (j = 0; j < numEnvVertices[i]; j++)
+                            {
+                                double scaledWeight =  weights[j]/ 100.0f;
+
+                                // make sure its in legal range
+                                if (( envVtxIndices[j] < modelNumVert )
+                                    && ( envVtxIndices[j] >= 0 ))
+                                {
+                                  if ( (type == SAA_MNSRF) && make_nurbs ) 
+                                  { 
+                                    // assign all referenced control vertices
+                                    joint->AddVertex( envPool->Vertex(envVtxIndices[j]), scaledWeight );
+
+                                    if ( verbose >= 2 )
+                                        fprintf( outStream, 
+                                            "%d: adding vref to cv %d with weight %f\n", 
+                                            j, envVtxIndices[j], scaledWeight );
+
+                                    envPool->Vertex(envVtxIndices[j])->AddJoint( joint, scaledWeight );
+                                    // set flag to show this vertex has
+                                    // been assigned
+                                    envPool->Vertex(envVtxIndices[j])->multipleJoints = 1;
+                                  }
+                                  else
+                                  {    
+                                    //assign all the tri verts associated
+                                    // with this control vertex to joint 
+                                    for ( k = 0; k < envPool->NumVertices(); k++ )    
+                                    {
+                                      if ( vpoolMap[k] == envVtxIndices[j] ) 
+                                      {
+
+                                        // add each vert in pool to last 
+                                        // joint for soft skinning
+                                        joint->AddVertex(envPool->Vertex(k), 
+                                            scaledWeight);
+
+                                        if ( verbose >= 2 )
+                                            fprintf( outStream, 
+                                                "%d: adding vref from cv %d to vert %d with weight %f(vpool)\n", 
+                                                j, envVtxIndices[j], k, scaledWeight );
+
+                                        envPool->Vertex(k)->AddJoint( joint, scaledWeight );
+                                        // set flag to show this vertex has
+                                        // been assigned
+                                        envPool->Vertex(k)->multipleJoints = 1;
+                                      }
+                                    }
+                                 }
+                                }
+                                else
+                                    if ( verbose >= 2 )
+                                        fprintf( outStream, 
+                                            "%d: Omitted vref from cv %d with weight %f (out of range 0 to %d )\n",
+                                            j, envVtxIndices[j], scaledWeight, modelNumVert );
+                                    
+                            }
+
+                            }
+                            else 
+                                if ( verbose >= 2 )
+                                    fprintf( outStream, "Couldn't find vpool %s!\n", envName );
+
+                            //free( modelVertices ); 
+                            //free( globalModelVertices ); 
+                            //free( envVtxIndices );
+                            //free( envName );
+                            } //if (weights)
+                            //free( weights );
+
+                        } // for i
+
+                    } // if (envVertices != NULL)
+                    else
+                        fprintf( outStream, "Not enough memory for envelope vertices...\n");
+                    //free( envVertices );
+                    } // if (totalEnvVertices) 
+                    else
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "No envelope vertices present...\n");
+
+                    //free( numEnvVertices );
+
+            } // if (numEnvVertices != NULL)
+
+          } // if (hasEnvVertices)
+
+        } // if (envelopes != NULL)
+        else
+            fprintf( outStream, "Not enough memory for envelopes...\n" );
+
+        //free( envelopes );
+
+    } //if (numEnv)
+
+    else
+        if ( verbose >= 1 )
+            fprintf( outStream, "Skeleton member has no envelopes...\n" );
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: CleanUpSoftSkin
+//       Access: Public 
+//  Description: Given a model, make sure all its vertices have been 
+//                 soft assigned. If not hard assign to the last
+//                 joint we saw.
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+CleanUpSoftSkin( SAA_Scene *scene, SAA_Elem *model, char *name )
+{
+    static EggJoint *joint;
+    SAA_Elem        parent;
+    SAA_ModelType     type;
+    SAA_Boolean        skel;
+
+    /////////////////////////////////////////////////
+    // find out what type of node we're dealing with
+    /////////////////////////////////////////////////
+    SAA_modelGetType( scene, model, &type );
+
+    char   *parentName;
+    int        level;
+    SAA_Elem    *searchNode = model;
+
+    if ( verbose >= 1 )
+        fprintf( outStream, "\nCleaning up model %s\n", name );
+
+    // this step is weird - I think I want it here but it seems
+    // to break some models. Files like props-props_wh_cookietime.3-0 in
+    // /ful/rnd/pub/vrml/chip/chips_adventure/char/zone1/rooms/warehouse_final
+    // need to do the "if (skel)" bit.
+
+    // am I a skeleton too?
+    SAA_modelIsSkeleton( scene, model, &skel );
+
+    // if not look for the last skeleton part
+    if ( skel ) 
+        parentName = name;
+    else do 
+    {
+        SAA_elementGetHierarchyLevel( scene, searchNode, &level );
+
+        // make sure we don't try to get the root's parent
+        if ( level )
+        {
+            SAA_modelGetParent( scene, searchNode, &parent );
+
+            if ( use_prefix )
+            {
+                // Get the FULL name of the parent
+                parentName = GetFullName( scene, &parent );
+            }
+            else
+            {
+                // Get the name of the parent
+                parentName = GetName( scene, &parent );
+            }
+
+            SAA_modelGetType( scene, &parent, &type );
+
+            SAA_modelIsSkeleton( scene, &parent, &skel );
+
+            if ( verbose >= 1 )
+                fprintf( outStream, "model %s, level %d, type %d, skel %d\n",
+                    parentName, level, type, skel );
+
+            searchNode = &parent;
+        }
+        else
+        {
+            // we reached the root of the tree
+            parentName = NULL;
+            if ( verbose >= 1 )
+                fprintf( outStream, "at root of tree! level %d\n", level );
+            break;
+        }
+
+    // look until parent is a joint or acts like one
+    } while ( !skel && ( strstr( parentName,"joint") == NULL ));
+
+    EggJoint    *thisJoint = NULL;
+
+    if ( parentName != NULL )
+    {
+        if ( verbose >= 1 )
+        {
+            fprintf( outStream, "found model parent joint %s\n", parentName);
+            fprintf( outStream, "looking for joint %s\n", parentName );
+        }
+        thisJoint = (EggJoint *)(skeleton->FindDescendent( parentName ));
+    }
+    else
+        if ( verbose >= 1 )
+                fprintf( outStream, "Couldn't find parent joint!\n");
+
+    if ( thisJoint != NULL )
+    {
+        joint = thisJoint;
+        if ( verbose >= 1 )
+            fprintf( outStream, "setting joint to %s\n", parentName );
+
+        //find the vpool for this model
+        EggVertexPool *vPool = 
+            (EggVertexPool *)(_data.pools.FindName( name ));
+
+        if (vPool != NULL)
+        {
+            int i;
+            double membership;
+            int numVerts = vPool->NumVertices() ;
+
+
+            if ( verbose >= 1 )
+                fprintf( outStream, "found vpool %s w/ %d verts\n", 
+                name, numVerts );
+        
+            for ( i = 0; i < numVerts; i++ )     
+            {
+                if ( vPool->Vertex(i)->multipleJoints != 1 )
+                {
+                    if ( verbose >= 1 )
+                    {    
+                        fprintf( outStream, "vpool %s vert %d", name, i );
+                        fprintf( outStream, " not assigned!\n" );
+                    }
+
+                    // hard skin this vertex
+                    joint->AddVertex( vPool->Vertex(i), 1.0f );
+                }
+                else
+                {    
+                    membership = vPool->Vertex(i)->NetMembership();
+
+
+                    if ( verbose >= 1 )
+                    {
+                        fprintf( outStream, "vpool %s vert %d", name, 
+                            i );
+                        fprintf( outStream, " has membership %f\n", 
+                            membership );
+                    }
+
+                    if ( membership == 0 )
+                    {
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "adding full weight..\n" );
+
+                        // hard skin this vertex
+                        joint->AddVertex( vPool->Vertex(i), 1.0f );
+                    }
+                }
+            }
+        }
+        else
+            if ( verbose >= 1 )
+                fprintf( outStream, "couldn't find vpool %s\n", name );
+    }
+    else
+    {
+        if ( parentName != NULL )
+            if ( verbose >= 1 )
+                fprintf( outStream, "Couldn't find joint %s\n", parentName );
+    }
+}
+
+//////////////////////////////////////////////////////////////////////
+//     Function: MakeAnimTable
+//       Access: Public 
+//  Description: Given a scene and a skeleton part ,get all the 
+//                   position, rotation, and scale for the skeleton
+//                 part for this frame and write them out as Egg
+//                 animation tables. 
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+MakeAnimTable( SAA_Scene *scene, SAA_Elem *skeletonPart, char *name )
+{    
+
+    if ( skeletonPart != NULL )
+    {
+        float    i,j,k;
+        float    h,p,r;
+        float    x,y,z;
+        int         size;
+        SAA_Boolean globalFlag = FALSE;
+        SAA_Boolean bigEndian;
+
+        if ( verbose >= 1 )
+            fprintf( outStream, "\n\nanimating child %s\n", name );
+
+        SAA_elementGetUserDataSize( scene, skeletonPart, "GLOBAL", &size );
+        
+        if ( size != 0 )    
+            SAA_elementGetUserData( scene, skeletonPart, "GLOBAL", 
+                sizeof( SAA_Boolean), &bigEndian, (void *)&globalFlag );
+ 
+        if ( globalFlag )
+        {
+            if ( verbose >= 1 )
+                fprintf( outStream, " 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 );
+        }
+        else
+        {
+            if ( verbose >= 1 )
+                fprintf( outStream, "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 );
+        }
+
+
+        if ( verbose >= 2 )
+            fprintf( outStream, "\nanim data: %f %f %f\n\t%f %f %f\n\t%f %f %f\n",
+                i, j, k, h, p, r, x, y, z );  
+
+        // find the appropriate anim table for this skeleton part
+        AnimGroup     *thisGroup;
+        XfmSAnimTable *thisTable;
+
+        //find the anim table associated with this group
+        thisGroup = (AnimGroup *)(animRoot->FindDescendent( name ));
+        if ( verbose >= 2 )
+            fprintf( outStream, "\nlooking for anim group %s\n", name );
+        if ( thisGroup != NULL )
+        {
+            thisTable = (XfmSAnimTable *)(thisGroup->FindDescendent( "xform" ));
+
+            if ( thisTable != NULL )
+            {    
+                thisTable->sub_tables[0].AddElement( i );
+                thisTable->sub_tables[1].AddElement( j ); 
+                thisTable->sub_tables[2].AddElement( k ); 
+                thisTable->sub_tables[3].AddElement( p ); 
+                thisTable->sub_tables[4].AddElement( h ); 
+                thisTable->sub_tables[5].AddElement( r ); 
+                thisTable->sub_tables[6].AddElement( x ); 
+                thisTable->sub_tables[7].AddElement( y ); 
+                thisTable->sub_tables[8].AddElement( z ); 
+            }
+            else
+                fprintf( outStream, "Couldn't allocate anim table\n" );
+        }
+        else
+            if ( verbose >= 2 )
+                fprintf( outStream, "Couldn't find anim group  %s\n",  name );
+    }
+    else
+    {
+        if ( verbose >= 2 )
+            fprintf( outStream, "Cannot build anim table - no skeleton\n" );
+    }
+    
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeVertexOffsets
+//       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 soft2egg::
+MakeVertexOffsets( SAA_Scene *scene, SAA_Elem *model, SAA_ModelType type,
+    int numShapes, int numOrigVert, SAA_DVector *originalVerts, float
+    matrix[4][4], char *name )
+{
+    int i, j;
+    int offset;
+    int    numCV;
+    char *mTableName;
+    SAA_DVector *shapeVerts = NULL;
+    SAA_DVector *uniqueVerts = NULL;
+
+    if ( (type == SAA_MNSRF) && make_nurbs ) 
+        SAA_nurbsSurfaceSetStep( scene, model, nurbs_step, nurbs_step );
+
+    SAA_modelGetNbVertices( scene, model, &numCV );
+
+    // get the shape verts
+    uniqueVerts = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numCV);
+    SAA_modelGetVertices( scene, model, SAA_GEOM_ORIGINAL, 0,
+        numCV, uniqueVerts );
+
+    if ( verbose >= 2 )
+        fprintf( outStream, "%d CV's\n", numCV ); 
+
+    if ( verbose >= 2 )
+    {    
+        for ( i = 0; i < numCV; i++ )
+            fprintf( outStream, "uniqueVerts[%d] = %f %f %f %f\n", i, 
+                uniqueVerts[i].x, uniqueVerts[i].y, 
+                uniqueVerts[i].z,  uniqueVerts[i].w );
+    }
+
+    // iterate through for each key shape (except original)
+    for ( i = 1; i < numShapes; i++ )
+    {
+        mTableName = MakeTableName( name, i );
+
+        if ( verbose >= 1 )
+        {
+            fprintf( outStream, "\nMaking geometry offsets for %s...\n", 
+                mTableName );
+
+            if ( (type == SAA_MNSRF) && make_nurbs ) 
+                fprintf( outStream, "calculating NURBS morphs...\n" );
+            else 
+                fprintf( outStream, "calculating triangle morphs...\n" );
+        }
+
+        // get the shape verts
+        shapeVerts = (SAA_DVector *)malloc(sizeof(SAA_DVector)*numCV);
+        SAA_modelGetVertices( scene, model, SAA_GEOM_SHAPE, i+1,
+            numCV, shapeVerts );
+
+        if ( verbose >= 2 )
+        { 
+            for ( j=0; j < numCV; j++ )
+            {
+                fprintf( outStream, "shapeVerts[%d] = %f %f %f\n", j, 
+                    shapeVerts[j].x, shapeVerts[j].y, shapeVerts[j].z );
+            }
+        }
+
+        // find the appropriate vertex pool
+        EggVertexPool *vPool = 
+            (EggVertexPool *)(_data.pools.FindName( name ));
+
+        // for every original vertex, compare to the corresponding
+        // key shape vertex and see if a vertex offset is needed 
+        for ( j=0; j < numOrigVert; j++ )
+        {
+            double    dx, dy, dz;
+        
+            if ( (type == SAA_MNSRF) && make_nurbs )
+            {
+                //dx = shapeVerts[j].x - (originalVerts[j].x/originalVerts[j].w); 
+                //dy = shapeVerts[j].y - (originalVerts[j].y/originalVerts[j].w); 
+                //dz = shapeVerts[j].z - (originalVerts[j].z/originalVerts[j].w); 
+                dx = shapeVerts[j].x - originalVerts[j].x; 
+                dy = shapeVerts[j].y - originalVerts[j].y; 
+                dz = shapeVerts[j].z - originalVerts[j].z; 
+            }
+            else 
+            {
+                // we need to map from original vertices
+                // to triangle shape vertices here
+                offset = findShapeVert( originalVerts[j], uniqueVerts,
+                    numCV ); 
+
+                dx = shapeVerts[offset].x - originalVerts[j].x; 
+                dy = shapeVerts[offset].y - originalVerts[j].y; 
+                dz = shapeVerts[offset].z - originalVerts[j].z; 
+            }
+
+            if ( verbose >= 2 )
+            {
+                fprintf( outStream, "oVert[%d] = %f %f %f %f\n", j, 
+                    originalVerts[j].x, originalVerts[j].y, 
+                    originalVerts[j].z,  originalVerts[j].w );
+
+                if ( (type == SAA_MNSRF) && make_nurbs )
+                {
+                    fprintf( outStream, "global shapeVerts[%d] = %f %f %f %f\n",                        j, shapeVerts[j].x, shapeVerts[j].y, 
+                        shapeVerts[j].z, shapeVerts[j].w );
+                }
+                else
+                {
+                    fprintf( outStream, 
+                        "global shapeVerts[%d] = %f %f %f\n", offset, 
+                        shapeVerts[offset].x, 
+                        shapeVerts[offset].y, 
+                        shapeVerts[offset].z );
+                }
+
+                fprintf( outStream, "%d: dx = %f, dy = %f, dz = %f\n", j,
+                    dx, dy, dz );
+            }
+
+            // 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
+                    EggMorphOffset *dxyz = 
+                        new EggMorphOffset( mTableName, dx, dy, dz );
+
+                    EggVertex *eggVert;
+
+                    // get the appropriate egg vertex
+                    eggVert = vPool->Vertex(j);    
+
+                    // add the offset to the vertex
+                    eggVert->morphs.push_back( *dxyz );
+                }
+                else
+                    fprintf( outStream, "Error: couldn't find vertex pool %s\n", name ); 
+                
+            } // if total
+        } //for j
+    } //for i
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeMorphTable
+//       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 soft2egg::
+MakeMorphTable( SAA_Scene *scene, SAA_Elem *model, SAA_Elem *models,
+    int numModels, char *name, float time )
+{
+    int         numShapes;
+    SAA_AnimInterpType    type;
+
+    // Get the number of key shapes
+    SAA_modelGetNbShapes( scene, model, &numShapes );
+
+    if ( numShapes > 0 )
+    {
+        if ( verbose >= 1 )
+            fprintf( outStream, "MakeMorphTable: %s: num shapes: %d\n", 
+                name, numShapes);
+
+        SAA_modelGetShapeInterpolation( scene, model, &type );
+
+        if ( type == SAA_ANIM_LINEAR || type == SAA_ANIM_CARDINAL )
+        {
+            MakeLinearMorphTable( scene, model, numShapes, name, time );
+        }
+        else     // must be weighted...
+        {
+            // check first for expressions
+            MakeExpressionMorphTable( scene, model, models, numModels,
+                numShapes, name, time );
+        }
+
+    }
+    
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeLinearMorphTable
+//       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 soft2egg::
+MakeLinearMorphTable( SAA_Scene *scene, SAA_Elem *model, int numShapes,
+    char *name, float time )
+{    
+    int            i;
+    SAA_Elem     fcurve;
+    float        curveVal;
+    SAnimTable *thisTable;
+    char          *tableName;
+
+    if ( verbose >= 1 )
+        fprintf( outStream, "linear interp, getting fcurve\n" );
+
+    SAA_modelFcurveGetShape( scene, model, &fcurve );
+
+    SAA_fcurveEval( scene, &fcurve, time, &curveVal );    
+    
+    if ( verbose >= 2 )
+        fprintf( outStream, "at time %f, fcurve for %s = %f\n", time,
+            name, curveVal );
+
+    float nextVal = 0.0f;
+
+    // populate morph table values for this frame
+    for ( i = 1; i < numShapes; i++ )
+    {
+        // derive table name from the model name
+        tableName = MakeTableName( name, i );
+
+        if ( verbose >= 2 )
+            fprintf( outStream, "Linear: looking for table '%s'\n", tableName );
+
+        //find the morph table associated with this key shape
+        thisTable = (SAnimTable *)(morphRoot->FindDescendent( tableName ));
+
+        if ( thisTable != NULL )
+        {    
+            if ( i == (int)curveVal )
+            {
+                if ( curveVal - i == 0 )
+                {
+                    thisTable->AddElement( 1.0f ); 
+                    if ( verbose >= 2 )
+                        fprintf( outStream, "adding element 1.0f\n" );
+                }
+                else
+                {
+                    thisTable->AddElement( 1.0f - (curveVal - i) );
+                    nextVal = curveVal - i;
+                    if ( verbose >= 2 )
+                        fprintf( outStream, "adding element %f\n",                                                 1.0f - (curveVal - i) );
+                }
+            }
+            else
+            {
+                if ( nextVal )
+                {
+                    thisTable->AddElement( nextVal );
+                    nextVal = 0.0f;
+                    if ( verbose >= 2 )
+                        fprintf( outStream, "adding element %f\n", nextVal );
+                }
+                else
+                {
+                    thisTable->AddElement( 0.0f );
+                    if ( verbose >= 2 )
+                        fprintf( outStream, "adding element 0.0f\n" );
+                }
+            }
+
+            if ( verbose >= 2 )
+                fprintf( outStream, " to '%s'\n", tableName );
+        }
+        else
+            fprintf( outStream, "%d: Couldn't find table '%s'\n", 
+                i, tableName );
+    }    
+
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeWeightedMorphTable
+//       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 soft2egg::
+MakeWeightedMorphTable( SAA_Scene *scene, SAA_Elem *model, SAA_Elem *models, 
+    int numModels,  int numShapes, char *name, float time )
+{
+    SI_Error     result;
+    SAA_Elem    *weightCurves;
+    float         curveVal;
+    SAnimTable *thisTable;
+    char          *tableName;
+
+    // allocate array of weight curves (one for each shape)
+    weightCurves = ( SAA_Elem *)malloc( sizeof( 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;
+
+						if ( verbose >= 2 )
+								fprintf( outStream, "at time %f, weightCurve[%d] for %s = %f\n",                     time, i, name, curveVal );
+
+
+            // derive table name from the model name
+            tableName = MakeTableName( name, i );
+
+						// find and populate shape table
+            if ( verbose >= 2 )
+                fprintf( outStream, "Weight: looking for table '%s'\n", 
+								tableName );
+
+            //find the morph table associated with this key shape
+            thisTable = (SAnimTable *)(morphRoot->FindDescendent( tableName ));
+
+            if ( thisTable != NULL )
+            {    
+                thisTable->AddElement( curveVal ); 
+                if ( verbose >= 2 )
+                    fprintf( outStream, "adding element %f\n", curveVal );
+            }
+            else
+                fprintf( outStream, "%d: Couldn't find table '%s'\n", 
+                    i, tableName );
+        }
+    }
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeExpressionMorphTable
+//       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 soft2egg::
+MakeExpressionMorphTable( SAA_Scene *scene, SAA_Elem *model, SAA_Elem *models, 
+    int numModels,  int numShapes, char *name, float time )
+{    
+    int            j;
+    SAnimTable *thisTable;
+    char          *tableName;
+    char          *sliderName;
+    char        *track;
+    int            numExp;
+    SAA_Elem   *expressions;
+    float        expVal;
+    float        sliderVal;
+
+    // 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 );
+
+    if ( verbose >= 2 )
+        fprintf( outStream, "%s has %d RHS expressions\n", name, numExp );
+
+    if ( numExp )
+    {
+        // get the expressions for this shape
+        expressions = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numExp);    
+
+        if ( verbose >= 1 )
+            fprintf( outStream, "getting %d RHS expressions...\n", numExp );
+
+        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
+                thisTable = (SAnimTable *)
+                    (morphRoot->FindDescendent( tableName ));
+
+                if ( thisTable != NULL )
+                {    
+                    thisTable->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 
+        // no expression, use weight curves
+        MakeWeightedMorphTable( scene, model, models, numModels, 
+            numShapes, name, time );
+
+}
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: MakeTexAnim
+//       Access: Public 
+//  Description: Given a scene, a POLYGON model, and the name
+//                 of the that model, get the u and v offsets for
+//                 the current frame. 
+////////////////////////////////////////////////////////////////////
+void soft2egg::
+MakeTexAnim( SAA_Scene *scene, SAA_Elem *model, char *modelName )
+{
+    if ( verbose >= 1 )
+        fprintf( outStream, "\n\nmaking texture animation for %s...\n", 
+            modelName );
+
+    // get the color of the surface
+    int         numMats;
+    pfVec4        Color;
+    SAA_Elem    *materials;
+    void        *relinfo;
+
+    SAA_modelRelationGetMatNbElements( scene, model, FALSE, &relinfo,
+        &numMats ); 
+
+    if ( verbose >= 2 )    
+        fprintf( outStream, "surface has %d materials\n", numMats );
+
+    if ( numMats )
+    {
+        float r,g,b,a;
+
+        materials = (SAA_Elem *)malloc(sizeof(SAA_Elem)*numMats);
+        
+        SAA_modelRelationGetMatElements( scene, model, relinfo, 
+            numMats, materials ); 
+
+        SAA_materialGetDiffuse( scene, &materials[0], &r, &g, &b );
+        SAA_materialGetTransparency( scene, &materials[0], &a );
+        Color.set( r, g, b, 1.0f - a );
+
+        int numTexLoc = 0;
+        int numTexGlb = 0;
+
+        // ASSUME only one texture per material
+        SAA_Elem tex;
+
+        // find out how many local textures per surface
+        // ASSUME it only has one material
+        SAA_materialRelationGetT2DLocNbElements( scene, &materials[0],
+            FALSE, &relinfo, &numTexLoc );
+
+        // if present, get local textures
+        if ( numTexLoc )
+        {
+            if ( verbose >= 1 )
+                fprintf( outStream, "%s had %d local tex\n", modelName, 
+                    numTexLoc );
+
+            // get the referenced texture
+            SAA_materialRelationGetT2DLocElements( scene, &materials[0],
+                TEX_PER_MAT, &tex ); 
+
+        }
+        // if no locals, try to get globals
+        else
+        {
+            SAA_modelRelationGetT2DGlbNbElements( scene, model,
+                FALSE, &relinfo, &numTexGlb );
+
+            if ( numTexGlb )
+            {
+                if ( verbose >= 1 )
+                    fprintf( outStream, "%s had %d global tex\n", modelName, numTexGlb );
+
+                // get the referenced texture
+                SAA_modelRelationGetT2DGlbElements( scene, 
+                    model, TEX_PER_MAT, &tex ); 
+            }
+        }
+
+        // add tex ref's if we found any textures
+        if ( numTexLoc || numTexGlb) 
+        {
+            char    *fullTexName = NULL;
+            char    *texName = NULL;
+            char    *uniqueTexName = NULL;
+            int      texNameLen;
+
+            // get its name 
+            SAA_texture2DGetPicNameLength( scene, &tex, &texNameLen);
+            fullTexName = (char *)malloc(sizeof(char)*++texNameLen);
+            SAA_texture2DGetPicName( scene, &tex, texNameLen, 
+                fullTexName );
+
+            // append unique identifier to texname for
+            // this particular object
+            uniqueTexName = (char *)malloc(sizeof(char)*
+                (strlen(modelName)+strlen(texName)+3) );
+            sprintf( uniqueTexName, "%s-%s", modelName, texName );
+            if ( verbose >= 2 )
+                fprintf( outStream, "referencing tref %s\n", 
+                    uniqueTexName );
+
+            float uScale;
+            float vScale;
+            float uOffset;
+            float vOffset;
+            SAA_Boolean    uv_swap = FALSE;
+
+            // get texture offset info
+            SAA_texture2DGetUScale( scene, &tex, &uScale );
+            SAA_texture2DGetVScale( scene, &tex, &vScale );
+            SAA_texture2DGetUOffset( scene, &tex, &uOffset );
+            SAA_texture2DGetVOffset( scene, &tex, &vOffset );
+            SAA_texture2DGetUVSwap( scene, &tex, &uv_swap );
+
+
+            if ( verbose >= 2 )
+            {
+                fprintf( outStream, "tex uScale: %f\n", uScale );
+                fprintf( outStream, "tex vScale: %f\n", vScale );
+                fprintf( outStream, "tex uOffset: %f\n", uOffset );
+                fprintf( outStream, "tex vOffset: %f\n", vOffset );
+                if ( uv_swap )
+                    fprintf( outStream, "nurbTex u & v swapped!\n" );
+                else
+                    fprintf( outStream, "nurbTex u & v NOT swapped\n" );
+            }
+
+
+            // find the vpool for this model
+            EggVertexPool *vPool = 
+                (EggVertexPool *)(_data.pools.FindName( modelName ));
+
+            // if we found the pool
+            if ( vPool != NULL )
+            {
+                // generate duv's for model
+                float oldOffsets[4];
+                double u, v, du, dv;    
+                int        size;
+                SAA_Boolean bigEndian;
+
+                SAA_elementGetUserDataSize( scene, model, "TEX_OFFSETS",                                 &size );
+
+                if ( size != 0 )    
+                {
+                    // remember original texture offsets future reference
+                    SAA_elementGetUserData( scene, model, "TEX_OFFSETS", 
+                        size, &bigEndian, (void *)&oldOffsets );
+
+                    // get the original scales and offsets
+                    u = oldOffsets[0];
+                    v = oldOffsets[1];
+
+                    du = u - uOffset;
+                    dv = v - vOffset;
+
+                    if ( verbose >= 1 )
+                    {
+                        fprintf( outStream, "original u = %f, v = %f\n",
+                            u, v );
+                        fprintf( outStream, "u = %f, v = %f\n",
+                            uOffset, vOffset );
+                        fprintf( outStream, "du = %f, dv = %f\n",
+                            du, dv );
+                    }
+
+                    strstream uName, vName;
+
+                    // create duv target names
+                    uName << modelName << ".u" << ends;
+                    vName << modelName << ".v" << ends;
+
+                    // find the appropriate table to store the
+                    // duv animation info into
+                    SAnimTable *thisTable;
+
+                    //find the duv U table associated with this model
+                    thisTable = (SAnimTable *)(morphRoot->FindDescendent( 
+                        uName.str() ));
+
+                    if ( thisTable != NULL )
+                    {    
+                        thisTable->AddElement( du ); 
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "adding element %f to %s\n", 
+                                du, uName.str() );
+                    }
+                    else
+                        fprintf( outStream, "Couldn't find uTable %s\n", 
+                            uName.str() );
+
+                    //find the duv V table associated with this model
+                    thisTable = (SAnimTable *)(morphRoot->FindDescendent( 
+                        vName.str() ));
+
+                    if ( thisTable != NULL )
+                    {    
+                        thisTable->AddElement( dv ); 
+                        if ( verbose >= 1 )
+                            fprintf( outStream, "adding element %f to %s\n", 
+                                dv, uName.str() );
+                    }
+                    else
+                        fprintf( outStream, "Couldn't find vTable %s\n", 
+                            uName.str() );
+                }
+            }
+            else
+                if ( verbose >= 2 )
+                    fprintf( outStream, "Couldn't find vpool %s\n", modelName );
+            
+        }
+
+        //free( materials );
+
+    }
+
+}
+#endif
+
+////////////////////////////////////////////////////////////////////
+//     Function: Main
+//       Access: Private 
+//  Description: Instantiate converter and process a file
+////////////////////////////////////////////////////////////////////
+EXPCL_MISC SI_Error soft2egg(int argc, char *argv[]) {
+  // pass control to the c++ system
+  init_soft2egg(argc, argv);
+  return SI_SUCCESS;
+}
+#ifdef __cplusplus
+}
+#endif