]> git.xonotic.org Git - xonotic/netradiant.git/blobdiff - libs/picomodel/pm_ase.c
fix some strncpy use
[xonotic/netradiant.git] / libs / picomodel / pm_ase.c
index e8c5751d80aa54bc2fe653e1c1b860c9df8be66f..101a7d2d23413cc11c84ffc19fc41fd6109111aa 100644 (file)
-/* -----------------------------------------------------------------------------\r
-\r
-PicoModel Library\r
-\r
-Copyright (c) 2002, Randy Reddig & seaw0lf\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification,\r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list\r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other aseMaterialList provided with the distribution.\r
-\r
-Neither the names of the copyright holders nor the names of its contributors may\r
-be used to endorse or promote products derived from this software without\r
-specific prior written permission.\r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND\r
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\r
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR\r
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON\r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS\r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
-\r
------------------------------------------------------------------------------ */\r
-\r
-\r
-/* marker */\r
-#define PM_ASE_C\r
-\r
-/* uncomment when debugging this module */\r
-//#define DEBUG_PM_ASE \r
-//#define DEBUG_PM_ASE_EX\r
-\r
-\r
-/* dependencies */\r
-#include "picointernal.h"\r
-\r
-#ifdef DEBUG_PM_ASE\r
-#include "time.h"\r
-#endif\r
-\r
-/* plain white */\r
-static picoColor_t white = { 255, 255, 255, 255 };\r
-\r
-/* jhefty - multi-subobject material support */\r
-\r
-/* Material/SubMaterial management */\r
-/* A material should have 1..n submaterials assigned to it */\r
-\r
-typedef struct aseSubMaterial_s\r
-{\r
-       struct aseSubMaterial_s* next;\r
-       int subMtlId;\r
-       picoShader_t* shader;\r
-       \r
-} aseSubMaterial_t;\r
-\r
-typedef struct aseMaterial_s\r
-{\r
-       struct aseMaterial_s* next;\r
-       struct aseSubMaterial_s* subMtls;\r
-       int mtlId;              \r
-} aseMaterial_t;\r
-\r
-/* Material/SubMaterial management functions */\r
-static aseMaterial_t* _ase_get_material ( aseMaterial_t* list , int mtlIdParent )\r
-{\r
-       aseMaterial_t* mtl = list;\r
-\r
-       while ( mtl )\r
-       {\r
-               if ( mtlIdParent == mtl->mtlId )\r
-               {\r
-                       break;\r
-               }\r
-               mtl = mtl->next;\r
-       }\r
-       return mtl;\r
-}\r
-\r
-static aseSubMaterial_t* _ase_get_submaterial ( aseMaterial_t* list, int  mtlIdParent , int subMtlId )\r
-{\r
-       aseMaterial_t* parent = _ase_get_material ( list , mtlIdParent );\r
-       aseSubMaterial_t* subMtl = NULL;\r
-\r
-       if ( !parent )\r
-       {\r
-               _pico_printf ( PICO_ERROR , "No ASE material exists with id %i\n" , mtlIdParent );\r
-               return NULL;\r
-       }\r
-\r
-       subMtl = parent->subMtls;\r
-       while ( subMtl )\r
-       {\r
-               if ( subMtlId == subMtl->subMtlId )\r
-               {\r
-                       break;\r
-               }\r
-               subMtl = subMtl->next;\r
-       }\r
-       return subMtl;\r
-}\r
-\r
-static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent )\r
-{\r
-       aseMaterial_t *mtl = _pico_calloc( 1, sizeof( aseMaterial_t ) );\r
-       mtl->mtlId = mtlIdParent;\r
-       mtl->subMtls = NULL;\r
-       mtl->next = *list;\r
-       *list = mtl;\r
-\r
-       return mtl;\r
-}\r
-\r
-static aseSubMaterial_t* _ase_add_submaterial( aseMaterial_t **list, int mtlIdParent, int subMtlId, picoShader_t* shader )\r
-{\r
-       aseMaterial_t *parent = _ase_get_material( *list,  mtlIdParent );\r
-       aseSubMaterial_t *subMtl = _pico_calloc( 1, sizeof ( aseSubMaterial_t ) );\r
-\r
-       if ( !parent )\r
-       {\r
-               parent = _ase_add_material ( list , mtlIdParent );\r
-       }\r
-\r
-       subMtl->shader = shader;\r
-       subMtl->subMtlId = subMtlId;\r
-       subMtl->next = parent->subMtls;\r
-       parent->subMtls = subMtl;\r
-\r
-       return subMtl;\r
-}\r
-\r
-static void _ase_free_materials( aseMaterial_t **list )\r
-{\r
-       aseMaterial_t* mtl = *list;\r
-       aseSubMaterial_t* subMtl = NULL;\r
-\r
-       aseMaterial_t* mtlTemp = NULL;\r
-       aseSubMaterial_t* subMtlTemp = NULL;\r
-\r
-       while ( mtl )\r
-       {\r
-               subMtl = mtl->subMtls;\r
-               while ( subMtl )\r
-               {\r
-                       subMtlTemp = subMtl->next;\r
-                       _pico_free ( subMtl );\r
-                       subMtl = subMtlTemp;\r
-               }\r
-               mtlTemp = mtl->next;\r
-               _pico_free ( mtl );\r
-               mtl = mtlTemp;\r
-       }\r
-       (*list) = NULL;\r
-}\r
-\r
-#ifdef DEBUG_PM_ASE\r
-static void _ase_print_materials( aseMaterial_t *list )\r
-{\r
-       aseMaterial_t* mtl = list;\r
-       aseSubMaterial_t* subMtl = NULL;\r
-\r
-       while ( mtl )\r
-       {\r
-               _pico_printf ( PICO_NORMAL ,  "ASE Material %i" , mtl->mtlId );\r
-               subMtl = mtl->subMtls;\r
-               while ( subMtl )\r
-               {\r
-                       _pico_printf ( PICO_NORMAL ,  " -- ASE SubMaterial %i - %s\n" , subMtl->subMtlId , subMtl->shader->name );\r
-                       subMtl = subMtl->next;\r
-               }\r
-               mtl = mtl->next;\r
-       }\r
-}\r
-#endif //DEBUG_PM_ASE\r
-\r
-/* ASE Face management */\r
-/* These are used to keep an association between a submaterial and a face definition */\r
-/* They are kept in parallel with the current picoSurface, */\r
-/* and are used by _ase_submit_triangles to lookup the proper material/submaterial IDs */\r
-typedef struct aseFace_s\r
-{\r
-       struct aseFace_s* next;\r
-       int mtlId;\r
-       int subMtlId;\r
-       int index[9];\r
-} aseFace_t;\r
-\r
-/* ASE Face management functions */\r
-void _ase_add_face( aseFace_t **list, aseFace_t **tail, aseFace_t *newFace )\r
-{\r
-       aseFace_t* face = *list;\r
-       aseFace_t* tempFace = NULL;\r
-\r
-       /* insert as head of list */\r
-       if ( !(*list) )\r
-       {\r
-               *list = newFace;\r
-       }\r
-       else\r
-       {\r
-               (*tail)->next = newFace;\r
-       }       \r
-\r
-       *tail = newFace;\r
-       newFace->next = NULL;\r
-       \r
-       //tag the color indices so we can detect them and apply the default color to them\r
-       newFace->index[6] = -1;\r
-       newFace->index[7] = -1;\r
-       newFace->index[8] = -1;\r
-}\r
-\r
-aseFace_t* _ase_get_face_for_index( aseFace_t *list, int index )\r
-{\r
-       int counter = 0;\r
-       aseFace_t* face = list;\r
-\r
-       while ( counter < index )\r
-       {\r
-               face = face->next;\r
-               counter++;\r
-       }\r
-       return face;\r
-}\r
-static void _ase_free_faces (aseFace_t** list, aseFace_t** tail )\r
-{\r
-       aseFace_t* face = *list;\r
-       aseFace_t* tempFace = NULL;\r
-\r
-       while ( face )\r
-       {\r
-               tempFace = face->next;\r
-               _pico_free ( face );\r
-               face = tempFace;\r
-       }\r
-\r
-       (*list) = NULL;\r
-       (*tail) = NULL;\r
-}\r
-\r
-/* todo:\r
- * - apply material specific uv offsets to uv coordinates\r
- */\r
-\r
-/* _ase_canload:\r
- *  validates a 3dsmax ase model file.\r
- */\r
-static int _ase_canload( PM_PARAMS_CANLOAD )\r
-{\r
-       picoParser_t *p;\r
-       \r
-       \r
-       /* quick data length validation */\r
-       if( bufSize < 80 )\r
-               return PICO_PMV_ERROR_SIZE;\r
-       \r
-       /* keep the friggin compiler happy */\r
-       *fileName = *fileName;\r
-       \r
-       /* create pico parser */\r
-       p = _pico_new_parser( (picoByte_t*) buffer, bufSize );\r
-       if( p == NULL )\r
-               return PICO_PMV_ERROR_MEMORY;\r
-       \r
-       /* get first token */\r
-       if( _pico_parse_first( p ) == NULL)\r
-       {\r
-               return PICO_PMV_ERROR_IDENT;\r
-       }\r
-       \r
-       /* check first token */\r
-       if( _pico_stricmp( p->token, "*3dsmax_asciiexport" ) )\r
-       {\r
-               _pico_free_parser( p );\r
-               return PICO_PMV_ERROR_IDENT;\r
-       }\r
-       \r
-       /* free the pico parser object */\r
-       _pico_free_parser( p );\r
-       \r
-       /* file seems to be a valid ase file */\r
-       return PICO_PMV_OK;\r
-}\r
-\r
-\r
-\r
-/* _ase_submit_triangles - jhefty\r
- use the surface and the current face list to look up material/submaterial IDs\r
- and submit them to the model for proper processing\r
-\r
-The following still holds from ydnar's _ase_make_surface:\r
- indexes 0 1 2 = vert indexes\r
- indexes 3 4 5 = st indexes\r
- indexes 6 7 8 = color indexes (new)\r
-*/\r
-\r
-static void _ase_submit_triangles ( picoSurface_t* surface , picoModel_t* model , aseMaterial_t* materials , aseFace_t* faces )\r
-{\r
-       aseFace_t* face;\r
-       aseSubMaterial_t* subMtl;\r
-       picoVec3_t* xyz[3];\r
-       picoVec3_t* normal[3];\r
-       picoVec2_t* st[3];\r
-       picoColor_t* color[3];\r
-       int i;\r
-\r
-       face = faces;\r
-       while ( face != NULL )\r
-       {\r
-               /* look up the shader for the material/submaterial pair */\r
-               subMtl = _ase_get_submaterial( materials, face->mtlId, face->subMtlId );\r
-               if( subMtl == NULL )\r
-               {\r
-                       /* ydnar: trying default submaterial */\r
-                       subMtl = _ase_get_submaterial( materials, face->mtlId, 0 );\r
-                       if( subMtl == NULL )\r
-                       {\r
-                               _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", face->mtlId, face->subMtlId );\r
-                               return;\r
-                       }\r
-               }\r
-               \r
-               /* we pull the data from the surface using the facelist data */\r
-               for ( i = 0 ; i < 3 ; i ++ )\r
-               {\r
-                       xyz[i]    = (picoVec3_t*) PicoGetSurfaceXYZ   ( surface, face->index[ i ] );\r
-                       normal[i] = (picoVec3_t*) PicoGetSurfaceNormal( surface, face->index[ i ] );                                                            \r
-                       st[i]     = (picoVec2_t*) PicoGetSurfaceST    ( surface, 0, face->index[ i + 3 ] );\r
-                       \r
-                       if ( face->index [ i + 6] >= 0 )\r
-                       {\r
-                               color[i]  = (picoColor_t*)PicoGetSurfaceColor ( surface, 0, face->index[ i + 6 ] );\r
-                       }\r
-                       else\r
-                       {\r
-                               color[i] = &white;\r
-                       }\r
-                       \r
-               }\r
-\r
-               /* submit the triangle to the model */\r
-               PicoAddTriangleToModel ( model , xyz , normal , 1 , st , 1 , color , subMtl->shader );\r
-\r
-               /* advance to the next face */\r
-               face = face->next;              \r
-       }       \r
-}\r
-\r
-/* _ase_load:\r
- *  loads a 3dsmax ase model file.\r
-*/\r
-static picoModel_t *_ase_load( PM_PARAMS_LOAD )\r
-{\r
-       picoModel_t    *model;\r
-       picoSurface_t  *surface = NULL;\r
-       picoParser_t   *p;\r
-       char                    lastNodeName[ 1024 ];\r
-\r
-       aseFace_t* faces = NULL;\r
-       aseFace_t* facesTail = NULL;\r
-       aseMaterial_t* materials = NULL;\r
-\r
-#ifdef DEBUG_PM_ASE\r
-       clock_t start, finish;\r
-       double elapsed;\r
-       start = clock();\r
-#endif\r
-\r
-       /* helper */\r
-       #define _ase_error_return(m) \\r
-       { \\r
-               _pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine); \\r
-               _pico_free_parser( p ); \\r
-               PicoFreeModel( model ); \\r
-               return NULL; \\r
-       }\r
-       /* create a new pico parser */\r
-       p = _pico_new_parser( (picoByte_t *)buffer,bufSize );\r
-       if (p == NULL) return NULL;\r
-\r
-       /* create a new pico model */\r
-       model = PicoNewModel();\r
-       if (model == NULL)\r
-       {\r
-               _pico_free_parser( p );\r
-               return NULL;\r
-       }\r
-       /* do model setup */\r
-       PicoSetModelFrameNum( model, frameNum );\r
-       PicoSetModelName( model, fileName );\r
-       PicoSetModelFileName( model, fileName );\r
-\r
-       /* initialize some stuff */\r
-       memset( lastNodeName,0,sizeof(lastNodeName) );\r
-\r
-       /* parse ase model file */\r
-       while( 1 )\r
-       {\r
-               /* get first token on line */\r
-               if (_pico_parse_first( p ) == NULL)\r
-                       break;\r
-\r
-               /* we just skip empty lines */\r
-               if (p->token == NULL || !strlen( p->token ))\r
-                       continue;\r
-\r
-               /* we skip invalid ase statements */\r
-               if (p->token[0] != '*' && p->token[0] != '{' && p->token[0] != '}')\r
-               {\r
-                       _pico_parse_skip_rest( p );\r
-                       continue;\r
-               }\r
-               /* remember node name */\r
-               if (!_pico_stricmp(p->token,"*node_name"))\r
-               {\r
-                       /* read node name */\r
-                       char *ptr = _pico_parse( p,0 );\r
-                       if (ptr == NULL)\r
-                               _ase_error_return("Node name parse error");\r
-\r
-                       /* remember node name */\r
-                       strncpy( lastNodeName,ptr,sizeof(lastNodeName) );\r
-               }\r
-               /* model mesh (originally contained within geomobject) */\r
-               else if (!_pico_stricmp(p->token,"*mesh"))\r
-               {\r
-                       /* finish existing surface */\r
-                       //_ase_make_surface( model, &surface );\r
-                       _ase_submit_triangles (surface, model ,materials,faces);\r
-                       _ase_free_faces (&faces,&facesTail);\r
-                                               \r
-                       /* allocate new pico surface */\r
-                       surface = PicoNewSurface( NULL );\r
-                       if (surface == NULL)\r
-                       {\r
-                               PicoFreeModel( model );\r
-                               return NULL;\r
-                       }\r
-               }\r
-               /* mesh material reference. this usually comes at the end of */\r
-               /* geomobjects after the mesh blocks. we must assume that the */\r
-               /* new mesh was already created so all we can do here is assign */\r
-               /* the material reference id (shader index) now. */\r
-               else if (!_pico_stricmp(p->token,"*material_ref"))\r
-               {\r
-                       int mtlId;\r
-                       aseFace_t* face;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               _ase_error_return("Missing mesh for material reference");\r
-\r
-                       /* get the material ref (0..n) */\r
-                       if (!_pico_parse_int( p,&mtlId) )\r
-                               _ase_error_return("Missing material reference ID");\r
-\r
-                       /* fix up all of the aseFaceList in the surface to point to the parent material */\r
-                       /* we've already saved off their subMtl */\r
-                       face = faces;\r
-                       while ( face != NULL )\r
-                       {\r
-                               face->mtlId = mtlId;\r
-                               face = face->next;\r
-                       }\r
-               }\r
-               /* model mesh vertex */\r
-               else if (!_pico_stricmp(p->token,"*mesh_vertex"))\r
-               {\r
-                       picoVec3_t      v;\r
-                       int                     index;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-\r
-                       /* get vertex data (orig: index +y -x +z) */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("Vertex parse error");\r
-                       if (!_pico_parse_vec( p,v ))\r
-                               _ase_error_return("Vertex parse error");\r
-\r
-                       /* set vertex */\r
-                       PicoSetSurfaceXYZ( surface,index,v );\r
-               }\r
-               /* model mesh vertex normal */\r
-               else if (!_pico_stricmp(p->token,"*mesh_vertexnormal"))\r
-               {\r
-                       picoVec3_t      v;\r
-                       int                     index;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-\r
-                       /* get vertex data (orig: index +y -x +z) */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("Vertex parse error");\r
-                       if (!_pico_parse_vec( p,v ))\r
-                               _ase_error_return("Vertex parse error");\r
-\r
-                       /* set vertex */\r
-                       PicoSetSurfaceNormal( surface,index,v );\r
-               }\r
-               /* model mesh face */\r
-               else if (!_pico_stricmp(p->token,"*mesh_face"))\r
-               {\r
-                       picoIndex_t indexes[3];\r
-                       int                     index;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-\r
-                       /* get face index */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* get 1st vertex index */\r
-                       _pico_parse( p,0 );\r
-                       if (!_pico_parse_int( p,&indexes[0] ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* get 2nd vertex index */\r
-                       _pico_parse( p,0 );\r
-                       if (!_pico_parse_int( p,&indexes[1] ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* get 3rd vertex index */\r
-                       _pico_parse( p,0 );\r
-                       if (!_pico_parse_int( p,&indexes[2] ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* set face indexes (note interleaved offset!) */\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 0), indexes[2] );\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 1), indexes[1] );\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 2), indexes[0] );\r
-                       \r
-                       /* parse to the subMaterial ID */\r
-                       while ( 1 )\r
-                       {\r
-                               _pico_parse (p,0);\r
-                               if (!_pico_stricmp (p->token,"*MESH_MTLID" ))\r
-                               {\r
-                                       aseFace_t* newFace;\r
-                                       int subMtlId;\r
-                                       \r
-                                       _pico_parse_int ( p , &subMtlId );\r
-                                       newFace = _pico_calloc ( 1 , sizeof ( aseFace_t ));\r
-                                       \r
-                                       /* we fix up the mtlId later when we parse the material_ref */\r
-                                       newFace->mtlId = 0;     \r
-                                       newFace->subMtlId = subMtlId;\r
-                                       newFace->index[0] = indexes[2];\r
-                                       newFace->index[1] = indexes[1];\r
-                                       newFace->index[2] = indexes[0];\r
-\r
-                                       _ase_add_face ( &faces,&facesTail,newFace );\r
-                                       break;\r
-                               }\r
-                       }\r
-                       \r
-               }\r
-               /* model texture vertex */\r
-               else if (!_pico_stricmp(p->token,"*mesh_tvert"))\r
-               {\r
-                       picoVec2_t      uv;\r
-                       int                     index;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-\r
-                       /* get uv vertex index */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("UV vertex parse error");\r
-\r
-                       /* get uv vertex s */\r
-                       if (!_pico_parse_float( p,&uv[0] ))\r
-                               _ase_error_return("UV vertex parse error");\r
-\r
-                       /* get uv vertex t */\r
-                       if (!_pico_parse_float( p,&uv[1] ))\r
-                               _ase_error_return("UV vertex parse error");\r
-                       \r
-                       /* ydnar: invert t */\r
-                       uv[ 1 ] = 1.0f - uv[ 1 ];\r
-                       \r
-                       /* set texture vertex */\r
-                       PicoSetSurfaceST( surface,0,index,uv );\r
-               }\r
-               /* ydnar: model mesh texture face */\r
-               else if( !_pico_stricmp( p->token, "*mesh_tface" ) )\r
-               {\r
-                       picoIndex_t indexes[3];\r
-                       int                     index;\r
-                       aseFace_t* face;\r
-                       \r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-                       \r
-                       /* get face index */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("Texture face parse error");\r
-                       \r
-                       /* get 1st vertex index */\r
-                       if (!_pico_parse_int( p,&indexes[0] ))\r
-                               _ase_error_return("Texture face parse error");\r
-                       \r
-                       /* get 2nd vertex index */\r
-                       if (!_pico_parse_int( p,&indexes[1] ))\r
-                               _ase_error_return("Texture face parse error");\r
-                       \r
-                       /* get 3rd vertex index */\r
-                       if (!_pico_parse_int( p,&indexes[2] ))\r
-                               _ase_error_return("Texture face parse error");\r
-                       \r
-                       /* set face indexes (note interleaved offset!) */\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 3), indexes[2] );\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 4), indexes[1] );\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 5), indexes[0] );\r
-\r
-                       face = _ase_get_face_for_index(faces,index);\r
-                       face->index[3] = indexes[2];\r
-                       face->index[4] = indexes[1];\r
-                       face->index[5] = indexes[0];\r
-               }\r
-               /* model color vertex */\r
-               else if (!_pico_stricmp(p->token,"*mesh_vertcol"))\r
-               {\r
-                       picoColor_t     color;\r
-                       int                     index;\r
-                       float           colorInput;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-\r
-                       /* get color vertex index */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("UV vertex parse error");\r
-\r
-                       /* get R component */\r
-                       if (!_pico_parse_float( p,&colorInput ))\r
-                               _ase_error_return("color vertex parse error");\r
-                       color[0] = (picoByte_t)(colorInput * 255);\r
-\r
-                       /* get G component */\r
-                       if (!_pico_parse_float( p,&colorInput ))\r
-                               _ase_error_return("color vertex parse error");\r
-                       color[1] = (picoByte_t)(colorInput * 255);\r
-\r
-                       /* get B component */\r
-                       if (!_pico_parse_float( p,&colorInput ))\r
-                               _ase_error_return("color vertex parse error");\r
-                       color[2] = (picoByte_t)(colorInput * 255);\r
-                       \r
-                       /* leave alpha alone since we don't get any data from the ASE format */\r
-                       color[3] = 255;\r
-\r
-                       /* set texture vertex */\r
-                       PicoSetSurfaceColor( surface,0,index,color );\r
-               }\r
-               /* model color face */\r
-               else if (!_pico_stricmp(p->token,"*mesh_cface"))\r
-               {\r
-                       picoIndex_t indexes[3];\r
-                       int                     index;\r
-                       aseFace_t*  face;\r
-\r
-                       /* we must have a valid surface */\r
-                       if( surface == NULL )\r
-                               continue;\r
-\r
-                       /* get face index */\r
-                       if (!_pico_parse_int( p,&index ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* get 1st cvertex index */\r
-                       //                      _pico_parse( p,0 );\r
-                       if (!_pico_parse_int( p,&indexes[0] ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* get 2nd cvertex index */\r
-                       //                      _pico_parse( p,0 );\r
-                       if (!_pico_parse_int( p,&indexes[1] ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* get 3rd cvertex index */\r
-                       //                      _pico_parse( p,0 );\r
-                       if (!_pico_parse_int( p,&indexes[2] ))\r
-                               _ase_error_return("Face parse error");\r
-\r
-                       /* set face indexes (note interleaved offset!) */\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 6), indexes[2] );\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 7), indexes[1] );\r
-                       PicoSetSurfaceIndex( surface, (index * 9 + 8), indexes[0] );\r
-\r
-                       face = _ase_get_face_for_index(faces,index);\r
-                       face->index[6] = indexes[2];\r
-                       face->index[7] = indexes[1];\r
-                       face->index[8] = indexes[0];\r
-               }\r
-               /* model material */\r
-               else if( !_pico_stricmp( p->token, "*material" ) )\r
-               {\r
-                       aseSubMaterial_t*       subMaterial = NULL;\r
-                       picoShader_t            *shader;\r
-                       int                                     level = 1, index;\r
-                       char                            materialName[ 1024 ];\r
-                       float                           transValue = 0.0f, shineValue = 1.0f;\r
-                       picoColor_t                     ambientColor, diffuseColor, specularColor;\r
-                       char                            *mapname = NULL;\r
-                       int                                     subMtlId, subMaterialLevel = -1;\r
-                       \r
-                       \r
-                       /* get material index */\r
-                       _pico_parse_int( p,&index );\r
-                       \r
-                       /* check brace */\r
-                       if (!_pico_parse_check(p,1,"{"))\r
-                               _ase_error_return("Material missing opening brace");\r
-                       \r
-                       /* parse material block */\r
-                       while( 1 )\r
-                       {\r
-                               /* get next token */\r
-                               if (_pico_parse(p,1) == NULL) break;\r
-                               if (!strlen(p->token)) continue;\r
-\r
-                               /* handle levels */\r
-                               if (p->token[0] == '{') level++;\r
-                               if (p->token[0] == '}') level--;\r
-                               if (!level) break;\r
-\r
-                               if( level == subMaterialLevel )\r
-                               {\r
-                                       /* set material name */\r
-                                       PicoSetShaderName( shader, materialName);\r
-\r
-                                       /* set shader's transparency */\r
-                                       PicoSetShaderTransparency( shader,transValue );\r
-\r
-                                       /* set shader's ambient color */\r
-                                       PicoSetShaderAmbientColor( shader,ambientColor );\r
-\r
-                                       /* set diffuse alpha to transparency */\r
-                                       diffuseColor[3] = (picoByte_t)( transValue * 255.0 );\r
-\r
-                                       /* set shader's diffuse color */\r
-                                       PicoSetShaderDiffuseColor( shader,diffuseColor );\r
-\r
-                                       /* set shader's specular color */\r
-                                       PicoSetShaderSpecularColor( shader,specularColor );\r
-\r
-                                       /* set shader's shininess */\r
-                                       PicoSetShaderShininess( shader,shineValue );\r
-\r
-                                       /* set material map name */\r
-                                       PicoSetShaderMapName( shader, mapname );\r
-\r
-                                       subMaterial = _ase_add_submaterial( &materials, index, subMtlId, shader );\r
-                                       subMaterialLevel = -1;\r
-                               }\r
-\r
-                               /* parse submaterial index */\r
-                               if (!_pico_stricmp(p->token,"*submaterial"))\r
-                               {                                                                                       \r
-                                       /* allocate new pico shader */\r
-                                       _pico_parse_int( p , &subMtlId );\r
-\r
-                                       shader = PicoNewShader( model );\r
-                                       if (shader == NULL)\r
-                                       {\r
-                                               PicoFreeModel( model );\r
-                                               return NULL;\r
-                                       }                       \r
-                                       subMaterialLevel = level;\r
-                               }\r
-                               /* parse material name */\r
-                               else if (!_pico_stricmp(p->token,"*material_name"))\r
-                               {\r
-                                       char* name = _pico_parse(p,0);\r
-                                       if ( name == NULL)\r
-                                               _ase_error_return("Missing material name");\r
-                                       \r
-                                       strcpy ( materialName , name );\r
-                                       /* skip rest and continue with next token */\r
-                                       _pico_parse_skip_rest( p );\r
-                                       continue;\r
-                               }\r
-                               /* parse material transparency */\r
-                               else if (!_pico_stricmp(p->token,"*material_transparency"))\r
-                               {\r
-                                       /* get transparency value from ase */\r
-                                       if (!_pico_parse_float( p,&transValue ))\r
-                                               _ase_error_return("Material transparency parse error");\r
-\r
-                                       /* skip rest and continue with next token */\r
-                                       _pico_parse_skip_rest( p );\r
-                                       continue;\r
-                               }\r
-                               /* parse material shininess */\r
-                               else if (!_pico_stricmp(p->token,"*material_shine"))\r
-                               {\r
-                                       /* remark:\r
-                                        * - not sure but instead of '*material_shine' i might\r
-                                        *   need to use '*material_shinestrength' */\r
-\r
-                                       /* get shine value from ase */\r
-                                       if (!_pico_parse_float( p,&shineValue ))\r
-                                               _ase_error_return("Material shine parse error");\r
-\r
-                                       /* scale ase shine range 0..1 to pico range 0..127 */\r
-                                       shineValue *= 128.0;\r
-\r
-                                       /* skip rest and continue with next token */\r
-                                       _pico_parse_skip_rest( p );\r
-                                       continue;\r
-                               }\r
-                               /* parse ambient material color */\r
-                               else if (!_pico_stricmp(p->token,"*material_ambient"))\r
-                               {\r
-                                       picoVec3_t  vec;\r
-                                       /* get r,g,b float values from ase */\r
-                                       if (!_pico_parse_vec( p,vec ))\r
-                                               _ase_error_return("Material color parse error");\r
-\r
-                                       /* setup 0..255 range color values */\r
-                                       ambientColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );\r
-                                       ambientColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );\r
-                                       ambientColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );\r
-                                       ambientColor[ 3 ] = (int)( 255 );\r
-\r
-                                       /* skip rest and continue with next token */\r
-                                       _pico_parse_skip_rest( p );\r
-                                       continue;\r
-                               }\r
-                               /* parse diffuse material color */\r
-                               else if (!_pico_stricmp(p->token,"*material_diffuse"))\r
-                               {\r
-                                       picoVec3_t  vec;\r
-\r
-                                       /* get r,g,b float values from ase */\r
-                                       if (!_pico_parse_vec( p,vec ))\r
-                                               _ase_error_return("Material color parse error");\r
-\r
-                                       /* setup 0..255 range color */\r
-                                       diffuseColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );\r
-                                       diffuseColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );\r
-                                       diffuseColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );\r
-                                       diffuseColor[ 3 ] = (int)( 255 );\r
-\r
-                                       /* skip rest and continue with next token */\r
-                                       _pico_parse_skip_rest( p );\r
-                                       continue;\r
-                               }\r
-                               /* parse specular material color */\r
-                               else if (!_pico_stricmp(p->token,"*material_specular"))\r
-                               {\r
-                                       picoVec3_t  vec;\r
-\r
-                                       /* get r,g,b float values from ase */\r
-                                       if (!_pico_parse_vec( p,vec ))\r
-                                               _ase_error_return("Material color parse error");\r
-\r
-                                       /* setup 0..255 range color */\r
-                                       specularColor[ 0 ] = (int)( vec[ 0 ] * 255 );\r
-                                       specularColor[ 1 ] = (int)( vec[ 1 ] * 255 );\r
-                                       specularColor[ 2 ] = (int)( vec[ 2 ] * 255 );\r
-                                       specularColor[ 3 ] = (int)( 255 );\r
-\r
-                                       /* skip rest and continue with next token */\r
-                                       _pico_parse_skip_rest( p );\r
-                                       continue;\r
-                               }\r
-                               /* material diffuse map */\r
-                               else if (!_pico_stricmp(p->token,"*map_diffuse") )\r
-                               {\r
-                                       int sublevel = 0;\r
-\r
-                                       /* parse material block */\r
-                                       while( 1 )\r
-                                       {\r
-                                               /* get next token */\r
-                                               if (_pico_parse(p,1) == NULL) break;\r
-                                               if (!strlen(p->token)) continue;\r
-                                               \r
-                                               /* handle levels */\r
-                                               if (p->token[0] == '{') sublevel++;\r
-                                               if (p->token[0] == '}') sublevel--;\r
-                                               if (!sublevel) break;\r
-                                               \r
-                                               /* parse diffuse map bitmap */\r
-                                               if (!_pico_stricmp(p->token,"*bitmap"))\r
-                                               {\r
-                                                       char* name = _pico_parse(p,0);\r
-                                                       if (name == NULL)\r
-                                                               _ase_error_return("Missing material map bitmap name");\r
-                                                       mapname = _pico_alloc ( strlen ( name ) + 1 );\r
-                                                       strcpy ( mapname, name );\r
-                                                       /* skip rest and continue with next token */\r
-                                                       _pico_parse_skip_rest( p );\r
-                                                       continue;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               /* end map_diffuse block */\r
-                       }\r
-                       /* end material block */\r
-\r
-                       if( subMaterial == NULL )\r
-                       {\r
-                               /* allocate new pico shader */\r
-                               shader = PicoNewShader( model );\r
-                               if (shader == NULL)\r
-                               {\r
-                                       PicoFreeModel( model );\r
-                                       return NULL;\r
-                               }\r
-\r
-                               /* set material name */\r
-                               PicoSetShaderName( shader,materialName );\r
-\r
-                               /* set shader's transparency */\r
-                               PicoSetShaderTransparency( shader,transValue );\r
-\r
-                               /* set shader's ambient color */\r
-                               PicoSetShaderAmbientColor( shader,ambientColor );\r
-\r
-                               /* set diffuse alpha to transparency */\r
-                               diffuseColor[3] = (picoByte_t)( transValue * 255.0 );\r
-\r
-                               /* set shader's diffuse color */\r
-                               PicoSetShaderDiffuseColor( shader,diffuseColor );\r
-\r
-                               /* set shader's specular color */\r
-                               PicoSetShaderSpecularColor( shader,specularColor );\r
-\r
-                               /* set shader's shininess */\r
-                               PicoSetShaderShininess( shader,shineValue );\r
-\r
-                               /* set material map name */\r
-                               PicoSetShaderMapName( shader, mapname );\r
-\r
-                               /* this is just a material with 1 submaterial */\r
-                               subMaterial = _ase_add_submaterial( &materials, index, 0, shader );\r
-                       }\r
-                       \r
-                       /* ydnar: free mapname */\r
-                       if( mapname != NULL )\r
-                               _pico_free( mapname );\r
-               }       // !_pico_stricmp ( "*material" )\r
-\r
-               /* skip unparsed rest of line and continue */\r
-               _pico_parse_skip_rest( p );\r
-       }\r
-       \r
-       /* ydnar: finish existing surface */\r
-//     _ase_make_surface( model, &surface );\r
-       _ase_submit_triangles (surface, model ,materials,faces);\r
-       _ase_free_faces (&faces,&facesTail);\r
-\r
-#ifdef DEBUG_PM_ASE\r
-       _ase_print_materials(materials);\r
-       finish = clock();\r
-       elapsed = (double)(finish - start) / CLOCKS_PER_SEC;\r
-       _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s)\n", elapsed );\r
-#endif //DEBUG_PM_ASE\r
-\r
-       _ase_free_materials(&materials);\r
-\r
-       /* return allocated pico model */\r
-       return model;\r
-}\r
-\r
-/* pico file format module definition */\r
-const picoModule_t picoModuleASE =\r
-{\r
-       "1.0",                                  /* module version string */\r
-       "Autodesk 3DSMAX ASCII",        /* module display name */\r
-       "Jared Hefty, seaw0lf",                                 /* author's name */\r
-       "2003 Jared Hefty, 2002 seaw0lf",                               /* module copyright */\r
-       {\r
-               "ase",NULL,NULL,NULL    /* default extensions to use */\r
-       },\r
-       _ase_canload,                           /* validation routine */\r
-       _ase_load,                                      /* load routine */\r
-        NULL,                                          /* save validation routine */\r
-        NULL                                           /* save routine */\r
-};\r
+/* -----------------------------------------------------------------------------
+
+   PicoModel Library
+
+   Copyright (c) 2002, Randy Reddig & seaw0lf
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without modification,
+   are permitted provided that the following conditions are met:
+
+   Redistributions of source code must retain the above copyright notice, this list
+   of conditions and the following disclaimer.
+
+   Redistributions in binary form must reproduce the above copyright notice, this
+   list of conditions and the following disclaimer in the documentation and/or
+   other aseMaterialList provided with the distribution.
+
+   Neither the names of the copyright holders nor the names of its contributors may
+   be used to endorse or promote products derived from this software without
+   specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+   DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+   ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+   ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   ----------------------------------------------------------------------------- */
+
+/* uncomment when debugging this module */
+//#define DEBUG_PM_ASE
+//#define DEBUG_PM_ASE_EX
+
+
+/* dependencies */
+#include "picointernal.h"
+
+#ifdef DEBUG_PM_ASE
+#include "time.h"
+#endif
+
+/* jhefty - multi-subobject material support */
+
+/* Material/SubMaterial management */
+/* A material should have 1..n submaterials assigned to it */
+
+typedef struct aseSubMaterial_s
+{
+       struct aseSubMaterial_s* next;
+       int subMtlId;
+       picoShader_t* shader;
+
+} aseSubMaterial_t;
+
+typedef struct aseMaterial_s
+{
+       struct aseMaterial_s* next;
+       struct aseSubMaterial_s* subMtls;
+       int mtlId;
+} aseMaterial_t;
+
+/* Material/SubMaterial management functions */
+static aseMaterial_t* _ase_get_material( aseMaterial_t* list, int mtlIdParent ){
+       aseMaterial_t* mtl = list;
+
+       while ( mtl )
+       {
+               if ( mtlIdParent == mtl->mtlId ) {
+                       break;
+               }
+               mtl = mtl->next;
+       }
+       return mtl;
+}
+
+static aseSubMaterial_t* _ase_get_submaterial( aseMaterial_t* list, int mtlIdParent, int subMtlId ){
+       aseMaterial_t* parent = _ase_get_material( list, mtlIdParent );
+       aseSubMaterial_t* subMtl = NULL;
+
+       if ( !parent ) {
+               _pico_printf( PICO_ERROR, "No ASE material exists with id %i\n", mtlIdParent );
+               return NULL;
+       }
+
+       subMtl = parent->subMtls;
+       while ( subMtl )
+       {
+               if ( subMtlId == subMtl->subMtlId ) {
+                       break;
+               }
+               subMtl = subMtl->next;
+       }
+       return subMtl;
+}
+
+aseSubMaterial_t* _ase_get_submaterial_or_default( aseMaterial_t* materials, int mtlIdParent, int subMtlId ){
+       aseSubMaterial_t* subMtl = _ase_get_submaterial( materials, mtlIdParent, subMtlId );
+       if ( subMtl != NULL ) {
+               return subMtl;
+       }
+
+       /* ydnar: trying default submaterial */
+       subMtl = _ase_get_submaterial( materials, mtlIdParent, 0 );
+       if ( subMtl != NULL ) {
+               return subMtl;
+       }
+
+       _pico_printf( PICO_ERROR, "Could not find material/submaterial for id %d/%d\n", mtlIdParent, subMtlId );
+       return NULL;
+}
+
+
+
+
+static aseMaterial_t* _ase_add_material( aseMaterial_t **list, int mtlIdParent ){
+       aseMaterial_t *mtl = _pico_calloc( 1, sizeof( aseMaterial_t ) );
+       mtl->mtlId = mtlIdParent;
+       mtl->subMtls = NULL;
+       mtl->next = *list;
+       *list = mtl;
+
+       return mtl;
+}
+
+static aseSubMaterial_t* _ase_add_submaterial( aseMaterial_t **list, int mtlIdParent, int subMtlId, picoShader_t* shader ){
+       aseMaterial_t *parent = _ase_get_material( *list,  mtlIdParent );
+       aseSubMaterial_t *subMtl = _pico_calloc( 1, sizeof( aseSubMaterial_t ) );
+
+       if ( !parent ) {
+               parent = _ase_add_material( list, mtlIdParent );
+       }
+
+       subMtl->shader = shader;
+       subMtl->subMtlId = subMtlId;
+       subMtl->next = parent->subMtls;
+       parent->subMtls = subMtl;
+
+       return subMtl;
+}
+
+static void _ase_free_materials( aseMaterial_t **list ){
+       aseMaterial_t* mtl = *list;
+       aseSubMaterial_t* subMtl = NULL;
+
+       aseMaterial_t* mtlTemp = NULL;
+       aseSubMaterial_t* subMtlTemp = NULL;
+
+       while ( mtl )
+       {
+               subMtl = mtl->subMtls;
+               while ( subMtl )
+               {
+                       subMtlTemp = subMtl->next;
+                       _pico_free( subMtl );
+                       subMtl = subMtlTemp;
+               }
+               mtlTemp = mtl->next;
+               _pico_free( mtl );
+               mtl = mtlTemp;
+       }
+       ( *list ) = NULL;
+}
+
+#ifdef DEBUG_PM_ASE
+static void _ase_print_materials( aseMaterial_t *list ){
+       aseMaterial_t* mtl = list;
+       aseSubMaterial_t* subMtl = NULL;
+
+       while ( mtl )
+       {
+               _pico_printf( PICO_NORMAL,  "ASE Material %i", mtl->mtlId );
+               subMtl = mtl->subMtls;
+               while ( subMtl )
+               {
+                       _pico_printf( PICO_NORMAL,  " -- ASE SubMaterial %i - %s\n", subMtl->subMtlId, subMtl->shader->name );
+                       subMtl = subMtl->next;
+               }
+               mtl = mtl->next;
+       }
+}
+#endif //DEBUG_PM_ASE
+
+/* todo:
+ * - apply material specific uv offsets to uv coordinates
+ */
+
+/* _ase_canload:
+ *  validates a 3dsmax ase model file.
+ */
+static int _ase_canload( PM_PARAMS_CANLOAD ){
+       picoParser_t *p;
+
+
+       /* quick data length validation */
+       if ( bufSize < 80 ) {
+               return PICO_PMV_ERROR_SIZE;
+       }
+
+       /* create pico parser */
+       p = _pico_new_parser( (const picoByte_t*) buffer, bufSize );
+       if ( p == NULL ) {
+               return PICO_PMV_ERROR_MEMORY;
+       }
+
+       /* get first token */
+       if ( _pico_parse_first( p ) == NULL ) {
+               return PICO_PMV_ERROR_IDENT;
+       }
+
+       /* check first token */
+       if ( _pico_stricmp( p->token, "*3dsmax_asciiexport" ) ) {
+               _pico_free_parser( p );
+               return PICO_PMV_ERROR_IDENT;
+       }
+
+       /* free the pico parser object */
+       _pico_free_parser( p );
+
+       /* file seems to be a valid ase file */
+       return PICO_PMV_OK;
+}
+
+typedef struct aseVertex_s aseVertex_t;
+struct aseVertex_s
+{
+       picoVec3_t xyz;
+       picoVec3_t normal;
+       picoIndex_t id;
+};
+
+typedef struct aseTexCoord_s aseTexCoord_t;
+struct aseTexCoord_s
+{
+       picoVec2_t texcoord;
+};
+
+typedef struct aseColor_s aseColor_t;
+struct aseColor_s
+{
+       picoColor_t color;
+};
+
+typedef struct aseFace_s aseFace_t;
+struct aseFace_s
+{
+       picoIndex_t indices[9];
+       picoIndex_t smoothingGroup;
+       picoIndex_t materialId;
+       picoIndex_t subMaterialId;
+};
+typedef aseFace_t* aseFacesIter_t;
+
+picoSurface_t* PicoModelFindOrAddSurface( picoModel_t *model, picoShader_t* shader ){
+       /* see if a surface already has the shader */
+       int i = 0;
+       for ( ; i < model->numSurfaces ; i++ )
+       {
+               picoSurface_t* workSurface = model->surface[i];
+               if ( workSurface->shader == shader ) {
+                       return workSurface;
+               }
+       }
+
+       /* no surface uses this shader yet, so create a new surface */
+
+       {
+               /* create a new surface in the model for the unique shader */
+               picoSurface_t* workSurface = PicoNewSurface( model );
+               if ( !workSurface ) {
+                       _pico_printf( PICO_ERROR, "Could not allocate a new surface!\n" );
+                       return 0;
+               }
+
+               /* do surface setup */
+               PicoSetSurfaceType( workSurface, PICO_TRIANGLES );
+               PicoSetSurfaceName( workSurface, shader->name );
+               PicoSetSurfaceShader( workSurface, shader );
+
+               return workSurface;
+       }
+}
+
+/* _ase_submit_triangles - jhefty
+   use the surface and the current face list to look up material/submaterial IDs
+   and submit them to the model for proper processing
+
+   The following still holds from ydnar's _ase_make_surface:
+   indexes 0 1 2 = vert indexes
+   indexes 3 4 5 = st indexes
+   indexes 6 7 8 = color indexes (new)
+ */
+
+#if 0
+typedef picoIndex_t* picoIndexIter_t;
+
+typedef struct aseUniqueIndices_s aseUniqueIndices_t;
+struct aseUniqueIndices_s
+{
+       picoIndex_t* data;
+       picoIndex_t* last;
+
+       aseFace_t* faces;
+};
+
+size_t aseUniqueIndices_size( aseUniqueIndices_t* self ){
+       return self->last - self->data;
+}
+
+void aseUniqueIndices_reserve( aseUniqueIndices_t* self, picoIndex_t size ){
+       self->data = self->last = (picoIndex_t*)_pico_calloc( size, sizeof( picoIndex_t ) );
+}
+
+void aseUniqueIndices_clear( aseUniqueIndices_t* self ){
+       _pico_free( self->data );
+}
+
+void aseUniqueIndices_pushBack( aseUniqueIndices_t* self, picoIndex_t index ){
+       *self->last++ = index;
+}
+
+picoIndex_t aseFaces_getVertexIndex( aseFace_t* faces, picoIndex_t index ){
+       return faces[index / 3].indices[index % 3];
+}
+
+picoIndex_t aseFaces_getTexCoordIndex( aseFace_t* faces, picoIndex_t index ){
+       return faces[index / 3].indices[( index % 3 ) + 3];
+}
+
+picoIndex_t aseFaces_getColorIndex( aseFace_t* faces, picoIndex_t index ){
+       return faces[index / 3].indices[( index % 3 ) + 6];
+}
+
+int aseUniqueIndex_equal( aseFace_t* faces, picoIndex_t index, picoIndex_t other ){
+       return aseFaces_getVertexIndex( faces, index ) == aseFaces_getVertexIndex( faces, other )
+                  && aseFaces_getTexCoordIndex( faces, index ) == aseFaces_getTexCoordIndex( faces, other )
+                  && aseFaces_getColorIndex( faces, index ) == aseFaces_getColorIndex( faces, other );
+}
+
+picoIndex_t aseUniqueIndices_insertUniqueVertex( aseUniqueIndices_t* self, picoIndex_t index ){
+       picoIndexIter_t i = self->data;
+       for (; i != self->last; ++i )
+       {
+               picoIndex_t other = (picoIndex_t)( i - self->data );
+               if ( aseUniqueIndex_equal( self->faces, index, other ) ) {
+                       return other;
+               }
+       }
+
+       aseUniqueIndices_pushBack( self, index );
+       return (picoIndex_t)( aseUniqueIndices_size( self ) - 1 );
+}
+
+static void _ase_submit_triangles_unshared( picoModel_t* model, aseMaterial_t* materials, aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces, int meshHasNormals ){
+       aseFacesIter_t i = faces, end = faces + numFaces;
+
+       aseUniqueIndices_t indices;
+       aseUniqueIndices_t remap;
+       aseUniqueIndices_reserve( &indices, numFaces * 3 );
+       aseUniqueIndices_reserve( &remap, numFaces * 3 );
+       indices.faces = faces;
+
+       for (; i != end; ++i )
+       {
+               /* look up the shader for the material/submaterial pair */
+               aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, ( *i ).materialId, ( *i ).subMaterialId );
+               if ( subMtl == NULL ) {
+                       return;
+               }
+
+               {
+                       picoSurface_t* surface = PicoModelFindOrAddSurface( model, subMtl->shader );
+                       int j;
+                       /* we pull the data from the vertex, color and texcoord arrays using the face index data */
+                       for ( j = 0 ; j < 3 ; j++ )
+                       {
+                               picoIndex_t index = (picoIndex_t)( ( ( i - faces ) * 3 ) + j );
+                               picoIndex_t size = (picoIndex_t)aseUniqueIndices_size( &indices );
+                               picoIndex_t unique = aseUniqueIndices_insertUniqueVertex( &indices, index );
+
+                               picoIndex_t numVertexes = PicoGetSurfaceNumVertexes( surface );
+                               picoIndex_t numIndexes = PicoGetSurfaceNumIndexes( surface );
+
+                               aseUniqueIndices_pushBack( &remap, numIndexes );
+
+                               PicoSetSurfaceIndex( surface, numIndexes, remap.data[unique] );
+
+                               if ( unique == size ) {
+                                       PicoSetSurfaceXYZ( surface, numVertexes, vertices[( *i ).indices[j]].xyz );
+                                       PicoSetSurfaceNormal( surface, numVertexes, vertices[( *i ).indices[j]].normal );
+                                       PicoSetSurfaceST( surface, 0, numVertexes, texcoords[( *i ).indices[j + 3]].texcoord );
+
+                                       if ( ( *i ).indices[j + 6] >= 0 ) {
+                                               PicoSetSurfaceColor( surface, 0, numVertexes, colors[( *i ).indices[j + 6]].color );
+                                       }
+                                       else
+                                       {
+                                               PicoSetSurfaceColor( surface, 0, numVertexes, picoColor_white );
+                                       }
+
+                                       PicoSetSurfaceSmoothingGroup( surface, numVertexes, ( vertices[( *i ).indices[j]].id * ( 1 << 16 ) ) + ( *i ).smoothingGroup );
+                               }
+                       }
+               }
+       }
+
+       aseUniqueIndices_clear( &indices );
+       aseUniqueIndices_clear( &remap );
+}
+
+#endif
+
+static void _ase_submit_triangles( picoModel_t* model, aseMaterial_t* materials, aseVertex_t* vertices, aseTexCoord_t* texcoords, aseColor_t* colors, aseFace_t* faces, int numFaces, const char *name ){
+       aseFacesIter_t i = faces, end = faces + numFaces;
+       for (; i != end; ++i )
+       {
+               /* look up the shader for the material/submaterial pair */
+               aseSubMaterial_t* subMtl = _ase_get_submaterial_or_default( materials, ( *i ).materialId, ( *i ).subMaterialId );
+               if ( subMtl == NULL ) {
+                       return;
+               }
+
+               {
+                       picoVec3_t* xyz[3];
+                       picoVec3_t* normal[3];
+                       picoVec2_t* st[3];
+                       const picoColor_t* color[3];
+                       picoIndex_t smooth[3];
+                       int j;
+                       /* we pull the data from the vertex, color and texcoord arrays using the face index data */
+                       for ( j = 0 ; j < 3 ; j++ )
+                       {
+                               xyz[j]    = &vertices[( *i ).indices[j]].xyz;
+                               normal[j] = &vertices[( *i ).indices[j]].normal;
+                               st[j]     = &texcoords[( *i ).indices[j + 3]].texcoord;
+
+                               if ( colors != NULL && ( *i ).indices[j + 6] >= 0 ) {
+                                       color[j] = &colors[( *i ).indices[j + 6]].color;
+                               }
+                               else
+                               {
+                                       color[j] = &picoColor_white;
+                               }
+
+                               smooth[j] = ( vertices[( *i ).indices[j]].id * ( 1 << 16 ) ) + ( *i ).smoothingGroup; /* don't merge vertices */
+
+                       }
+
+                       /* submit the triangle to the model */
+                       PicoAddTriangleToModel( model, xyz, normal, 1, st, 1, color, subMtl->shader, name, smooth );
+               }
+       }
+}
+
+
+/* _ase_load:
+ *  loads a 3dsmax ase model file.
+ */
+static picoModel_t *_ase_load( PM_PARAMS_LOAD ){
+       picoModel_t    *model;
+       picoParser_t   *p;
+       char lastNodeName[ 1024 ];
+
+       aseVertex_t* vertices = NULL;
+       aseTexCoord_t* texcoords = NULL;
+       aseColor_t* colors = NULL;
+       aseFace_t* faces = NULL;
+       int numVertices = 0;
+       int numFaces = 0;
+       int numTextureVertices = 0;
+       int numTextureVertexFaces = 0;
+       int numColorVertices = 0;
+       int numColorVertexFaces = 0;
+       int vertexId = 0;
+
+       aseMaterial_t* materials = NULL;
+
+#ifdef DEBUG_PM_ASE
+       clock_t start, finish;
+       double elapsed;
+       start = clock();
+#endif
+
+       /* helper */
+       #define _ase_error_return( m ) \
+       { \
+               _pico_printf( PICO_ERROR,"%s in ASE, line %d.",m,p->curLine ); \
+               _pico_free_parser( p ); \
+               PicoFreeModel( model ); \
+               return NULL; \
+       }
+       /* create a new pico parser */
+       p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
+       if ( p == NULL ) {
+               return NULL;
+       }
+
+       /* create a new pico model */
+       model = PicoNewModel();
+       if ( model == NULL ) {
+               _pico_free_parser( p );
+               return NULL;
+       }
+       /* do model setup */
+       PicoSetModelFrameNum( model, frameNum );
+       PicoSetModelName( model, fileName );
+       PicoSetModelFileName( model, fileName );
+
+       /* initialize some stuff */
+       memset( lastNodeName, 0, sizeof( lastNodeName ) );
+
+       /* parse ase model file */
+       while ( 1 )
+       {
+               /* get first token on line */
+               if ( _pico_parse_first( p ) == NULL ) {
+                       break;
+               }
+
+               /* we just skip empty lines */
+               if ( p->token == NULL || !strlen( p->token ) ) {
+                       continue;
+               }
+
+               /* we skip invalid ase statements */
+               if ( p->token[0] != '*' && p->token[0] != '{' && p->token[0] != '}' ) {
+                       _pico_parse_skip_rest( p );
+                       continue;
+               }
+               /* remember node name */
+               if ( !_pico_stricmp( p->token,"*node_name" ) ) {
+                       /* read node name */
+                       char *ptr = _pico_parse( p,0 );
+                       if ( ptr == NULL ) {
+                               _ase_error_return( "Node name parse error" );
+                       }
+
+                       /* remember node name */
+                       strncpy( lastNodeName, ptr, sizeof( lastNodeName ) - 1 );
+               }
+               /* model mesh (originally contained within geomobject) */
+               else if ( !_pico_stricmp( p->token,"*mesh" ) ) {
+                       /* finish existing surface */
+                       _ase_submit_triangles( model, materials, vertices, texcoords, colors, faces, numFaces, lastNodeName );
+                       _pico_free( faces );
+                       _pico_free( vertices );
+                       _pico_free( texcoords );
+                       _pico_free( colors );
+               }
+               else if ( !_pico_stricmp( p->token,"*mesh_numvertex" ) ) {
+                       if ( !_pico_parse_int( p, &numVertices ) ) {
+                               _ase_error_return( "Missing MESH_NUMVERTEX value" );
+                       }
+
+                       vertices = _pico_calloc( numVertices, sizeof( aseVertex_t ) );
+               }
+               else if ( !_pico_stricmp( p->token,"*mesh_numfaces" ) ) {
+                       if ( !_pico_parse_int( p, &numFaces ) ) {
+                               _ase_error_return( "Missing MESH_NUMFACES value" );
+                       }
+
+                       faces = _pico_calloc( numFaces, sizeof( aseFace_t ) );
+               }
+               else if ( !_pico_stricmp( p->token,"*mesh_numtvertex" ) ) {
+                       if ( !_pico_parse_int( p, &numTextureVertices ) ) {
+                               _ase_error_return( "Missing MESH_NUMTVERTEX value" );
+                       }
+
+                       texcoords = _pico_calloc( numTextureVertices, sizeof( aseTexCoord_t ) );
+               }
+               else if ( !_pico_stricmp( p->token,"*mesh_numtvfaces" ) ) {
+                       if ( !_pico_parse_int( p, &numTextureVertexFaces ) ) {
+                               _ase_error_return( "Missing MESH_NUMTVFACES value" );
+                       }
+               }
+               else if ( !_pico_stricmp( p->token,"*mesh_numcvertex" ) ) {
+                       if ( !_pico_parse_int( p, &numColorVertices ) ) {
+                               _ase_error_return( "Missing MESH_NUMCVERTEX value" );
+                       }
+
+                       colors = _pico_calloc( numColorVertices, sizeof( aseColor_t ) );
+                       memset( colors, 255, numColorVertices * sizeof( aseColor_t ) ); /* ydnar: force colors to white initially */
+               }
+               else if ( !_pico_stricmp( p->token,"*mesh_numcvfaces" ) ) {
+                       if ( !_pico_parse_int( p, &numColorVertexFaces ) ) {
+                               _ase_error_return( "Missing MESH_NUMCVFACES value" );
+                       }
+               }
+               /* mesh material reference. this usually comes at the end of */
+               /* geomobjects after the mesh blocks. we must assume that the */
+               /* new mesh was already created so all we can do here is assign */
+               /* the material reference id (shader index) now. */
+               else if ( !_pico_stricmp( p->token,"*material_ref" ) ) {
+                       int mtlId;
+
+                       /* get the material ref (0..n) */
+                       if ( !_pico_parse_int( p,&mtlId ) ) {
+                               _ase_error_return( "Missing material reference ID" );
+                       }
+
+                       {
+                               int i = 0;
+                               /* fix up all of the aseFaceList in the surface to point to the parent material */
+                               /* we've already saved off their subMtl */
+                               for (; i < numFaces; ++i )
+                               {
+                                       faces[i].materialId = mtlId;
+                               }
+                       }
+               }
+               /* model mesh vertex */
+               else if ( !_pico_stricmp( p->token,"*mesh_vertex" ) ) {
+                       int index;
+
+                       if ( numVertices == 0 ) {
+                               _ase_error_return( "Vertex parse error" );
+                       }
+
+                       /* get vertex data (orig: index +y -x +z) */
+                       if ( !_pico_parse_int( p,&index ) ) {
+                               _ase_error_return( "Vertex parse error" );
+                       }
+                       if ( !_pico_parse_vec( p,vertices[index].xyz ) ) {
+                               _ase_error_return( "Vertex parse error" );
+                       }
+
+                       vertices[index].id = vertexId++;
+               }
+               /* model mesh vertex normal */
+               else if ( !_pico_stricmp( p->token,"*mesh_vertexnormal" ) ) {
+                       int index;
+
+                       if ( numVertices == 0 ) {
+                               _ase_error_return( "Vertex parse error" );
+                       }
+
+                       /* get vertex data (orig: index +y -x +z) */
+                       if ( !_pico_parse_int( p,&index ) ) {
+                               _ase_error_return( "Vertex parse error" );
+                       }
+                       if ( !_pico_parse_vec( p,vertices[index].normal ) ) {
+                               _ase_error_return( "Vertex parse error" );
+                       }
+               }
+               /* model mesh face */
+               else if ( !_pico_stricmp( p->token,"*mesh_face" ) ) {
+                       picoIndex_t indexes[3];
+                       int index;
+
+                       if ( numFaces == 0 ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get face index */
+                       if ( !_pico_parse_int( p,&index ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get 1st vertex index */
+                       _pico_parse( p,0 );
+                       if ( !_pico_parse_int( p,&indexes[0] ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get 2nd vertex index */
+                       _pico_parse( p,0 );
+                       if ( !_pico_parse_int( p,&indexes[1] ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get 3rd vertex index */
+                       _pico_parse( p,0 );
+                       if ( !_pico_parse_int( p,&indexes[2] ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* parse to the subMaterial ID */
+                       while ( 1 )
+                       {
+                               if ( !_pico_parse( p,0 ) ) { /* EOL */
+                                       break;
+                               }
+                               if ( !_pico_stricmp( p->token,"*MESH_SMOOTHING" ) ) {
+                                       _pico_parse_int( p, &faces[index].smoothingGroup );
+                               }
+                               if ( !_pico_stricmp( p->token,"*MESH_MTLID" ) ) {
+                                       _pico_parse_int( p, &faces[index].subMaterialId );
+                               }
+                       }
+
+                       faces[index].materialId = 0;
+                       faces[index].indices[0] = indexes[2];
+                       faces[index].indices[1] = indexes[1];
+                       faces[index].indices[2] = indexes[0];
+               }
+               /* model texture vertex */
+               else if ( !_pico_stricmp( p->token,"*mesh_tvert" ) ) {
+                       int index;
+
+                       if ( numVertices == 0 ) {
+                               _ase_error_return( "Texture Vertex parse error" );
+                       }
+
+                       /* get uv vertex index */
+                       if ( !_pico_parse_int( p,&index ) || index >= numTextureVertices ) {
+                               _ase_error_return( "Texture vertex parse error" );
+                       }
+
+                       /* get uv vertex s */
+                       if ( !_pico_parse_float( p,&texcoords[index].texcoord[0] ) ) {
+                               _ase_error_return( "Texture vertex parse error" );
+                       }
+
+                       /* get uv vertex t */
+                       if ( !_pico_parse_float( p,&texcoords[index].texcoord[1] ) ) {
+                               _ase_error_return( "Texture vertex parse error" );
+                       }
+
+                       /* ydnar: invert t */
+                       texcoords[index].texcoord[ 1 ] = 1.0f - texcoords[index].texcoord[ 1 ];
+               }
+               /* ydnar: model mesh texture face */
+               else if ( !_pico_stricmp( p->token, "*mesh_tface" ) ) {
+                       picoIndex_t indexes[3];
+                       int index;
+
+                       if ( numFaces == 0 ) {
+                               _ase_error_return( "Texture face parse error" );
+                       }
+
+                       /* get face index */
+                       if ( !_pico_parse_int( p,&index ) ) {
+                               _ase_error_return( "Texture face parse error" );
+                       }
+
+                       /* get 1st vertex index */
+                       if ( !_pico_parse_int( p,&indexes[0] ) ) {
+                               _ase_error_return( "Texture face parse error" );
+                       }
+
+                       /* get 2nd vertex index */
+                       if ( !_pico_parse_int( p,&indexes[1] ) ) {
+                               _ase_error_return( "Texture face parse error" );
+                       }
+
+                       /* get 3rd vertex index */
+                       if ( !_pico_parse_int( p,&indexes[2] ) ) {
+                               _ase_error_return( "Texture face parse error" );
+                       }
+
+                       faces[index].indices[3] = indexes[2];
+                       faces[index].indices[4] = indexes[1];
+                       faces[index].indices[5] = indexes[0];
+               }
+               /* model color vertex */
+               else if ( !_pico_stricmp( p->token,"*mesh_vertcol" ) ) {
+                       int index;
+                       float colorInput;
+
+                       if ( numVertices == 0 ) {
+                               _ase_error_return( "Color Vertex parse error" );
+                       }
+
+                       /* get color vertex index */
+                       if ( !_pico_parse_int( p,&index ) ) {
+                               _ase_error_return( "Color vertex parse error" );
+                       }
+
+                       /* get R component */
+                       if ( !_pico_parse_float( p,&colorInput ) ) {
+                               _ase_error_return( "Color vertex parse error" );
+                       }
+                       colors[index].color[0] = (picoByte_t)( colorInput * 255 );
+
+                       /* get G component */
+                       if ( !_pico_parse_float( p,&colorInput ) ) {
+                               _ase_error_return( "Color vertex parse error" );
+                       }
+                       colors[index].color[1] = (picoByte_t)( colorInput * 255 );
+
+                       /* get B component */
+                       if ( !_pico_parse_float( p,&colorInput ) ) {
+                               _ase_error_return( "Color vertex parse error" );
+                       }
+                       colors[index].color[2] = (picoByte_t)( colorInput * 255 );
+
+                       /* leave alpha alone since we don't get any data from the ASE format */
+                       colors[index].color[3] = 255;
+               }
+               /* model color face */
+               else if ( !_pico_stricmp( p->token,"*mesh_cface" ) ) {
+                       picoIndex_t indexes[3];
+                       int index;
+
+                       if ( numFaces == 0 ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get face index */
+                       if ( !_pico_parse_int( p,&index ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get 1st cvertex index */
+                       //                      _pico_parse( p,0 );
+                       if ( !_pico_parse_int( p,&indexes[0] ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get 2nd cvertex index */
+                       //                      _pico_parse( p,0 );
+                       if ( !_pico_parse_int( p,&indexes[1] ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       /* get 3rd cvertex index */
+                       //                      _pico_parse( p,0 );
+                       if ( !_pico_parse_int( p,&indexes[2] ) ) {
+                               _ase_error_return( "Face parse error" );
+                       }
+
+                       faces[index].indices[6] = indexes[2];
+                       faces[index].indices[7] = indexes[1];
+                       faces[index].indices[8] = indexes[0];
+               }
+               /* model material */
+               else if ( !_pico_stricmp( p->token, "*material" ) ) {
+                       aseSubMaterial_t*   subMaterial = NULL;
+                       picoShader_t        *shader = NULL;
+                       int level = 1, index;
+                       char materialName[ 1024 ];
+                       float transValue = 0.0f, shineValue = 1.0f;
+                       picoColor_t ambientColor, diffuseColor, specularColor;
+                       char                *mapname = NULL;
+                       int subMtlId, subMaterialLevel = -1;
+
+
+                       /* get material index */
+                       _pico_parse_int( p,&index );
+
+                       /* check brace */
+                       if ( !_pico_parse_check( p,1,"{" ) ) {
+                               _ase_error_return( "Material missing opening brace" );
+                       }
+
+                       /* parse material block */
+                       while ( 1 )
+                       {
+                               /* get next token */
+                               if ( _pico_parse( p,1 ) == NULL ) {
+                                       break;
+                               }
+                               if ( !strlen( p->token ) ) {
+                                       continue;
+                               }
+
+                               /* handle levels */
+                               if ( p->token[0] == '{' ) {
+                                       level++;
+                               }
+                               if ( p->token[0] == '}' ) {
+                                       level--;
+                               }
+                               if ( !level ) {
+                                       break;
+                               }
+
+                               if ( level == subMaterialLevel ) {
+                                       /* set material name */
+                                       _pico_first_token( materialName );
+                                       _pico_unixify( materialName );
+                                       PicoSetShaderName( shader, materialName );
+
+                                       /* set shader's transparency */
+                                       PicoSetShaderTransparency( shader,transValue );
+
+                                       /* set shader's ambient color */
+                                       PicoSetShaderAmbientColor( shader,ambientColor );
+
+                                       /* set diffuse alpha to transparency */
+                                       diffuseColor[3] = (picoByte_t)( transValue * 255.0 );
+
+                                       /* set shader's diffuse color */
+                                       PicoSetShaderDiffuseColor( shader,diffuseColor );
+
+                                       /* set shader's specular color */
+                                       PicoSetShaderSpecularColor( shader,specularColor );
+
+                                       /* set shader's shininess */
+                                       PicoSetShaderShininess( shader,shineValue );
+
+                                       /* set material map name */
+                                       PicoSetShaderMapName( shader, mapname );
+
+                                       subMaterial = _ase_add_submaterial( &materials, index, subMtlId, shader );
+                                       subMaterialLevel = -1;
+                               }
+
+                               /* parse submaterial index */
+                               if ( !_pico_stricmp( p->token,"*submaterial" ) ) {
+                                       /* allocate new pico shader */
+                                       _pico_parse_int( p, &subMtlId );
+
+                                       shader = PicoNewShader( model );
+                                       if ( shader == NULL ) {
+                                               PicoFreeModel( model );
+                                               return NULL;
+                                       }
+                                       subMaterialLevel = level;
+                               }
+                               /* parse material name */
+                               else if ( !_pico_stricmp( p->token,"*material_name" ) ) {
+                                       char* name = _pico_parse( p,0 );
+                                       if ( name == NULL ) {
+                                               _ase_error_return( "Missing material name" );
+                                       }
+
+                                       strcpy( materialName, name );
+                                       /* skip rest and continue with next token */
+                                       _pico_parse_skip_rest( p );
+                                       continue;
+                               }
+                               /* parse material transparency */
+                               else if ( !_pico_stricmp( p->token,"*material_transparency" ) ) {
+                                       /* get transparency value from ase */
+                                       if ( !_pico_parse_float( p,&transValue ) ) {
+                                               _ase_error_return( "Material transparency parse error" );
+                                       }
+
+                                       /* skip rest and continue with next token */
+                                       _pico_parse_skip_rest( p );
+                                       continue;
+                               }
+                               /* parse material shininess */
+                               else if ( !_pico_stricmp( p->token,"*material_shine" ) ) {
+                                       /* remark:
+                                        * - not sure but instead of '*material_shine' i might
+                                        *   need to use '*material_shinestrength' */
+
+                                       /* get shine value from ase */
+                                       if ( !_pico_parse_float( p,&shineValue ) ) {
+                                               _ase_error_return( "Material shine parse error" );
+                                       }
+
+                                       /* scale ase shine range 0..1 to pico range 0..127 */
+                                       shineValue *= 128.0;
+
+                                       /* skip rest and continue with next token */
+                                       _pico_parse_skip_rest( p );
+                                       continue;
+                               }
+                               /* parse ambient material color */
+                               else if ( !_pico_stricmp( p->token,"*material_ambient" ) ) {
+                                       picoVec3_t vec;
+                                       /* get r,g,b float values from ase */
+                                       if ( !_pico_parse_vec( p,vec ) ) {
+                                               _ase_error_return( "Material color parse error" );
+                                       }
+
+                                       /* setup 0..255 range color values */
+                                       ambientColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );
+                                       ambientColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );
+                                       ambientColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );
+                                       ambientColor[ 3 ] = (int)( 255 );
+
+                                       /* skip rest and continue with next token */
+                                       _pico_parse_skip_rest( p );
+                                       continue;
+                               }
+                               /* parse diffuse material color */
+                               else if ( !_pico_stricmp( p->token,"*material_diffuse" ) ) {
+                                       picoVec3_t vec;
+
+                                       /* get r,g,b float values from ase */
+                                       if ( !_pico_parse_vec( p,vec ) ) {
+                                               _ase_error_return( "Material color parse error" );
+                                       }
+
+                                       /* setup 0..255 range color */
+                                       diffuseColor[ 0 ] = (int)( vec[ 0 ] * 255.0 );
+                                       diffuseColor[ 1 ] = (int)( vec[ 1 ] * 255.0 );
+                                       diffuseColor[ 2 ] = (int)( vec[ 2 ] * 255.0 );
+                                       diffuseColor[ 3 ] = (int)( 255 );
+
+                                       /* skip rest and continue with next token */
+                                       _pico_parse_skip_rest( p );
+                                       continue;
+                               }
+                               /* parse specular material color */
+                               else if ( !_pico_stricmp( p->token,"*material_specular" ) ) {
+                                       picoVec3_t vec;
+
+                                       /* get r,g,b float values from ase */
+                                       if ( !_pico_parse_vec( p,vec ) ) {
+                                               _ase_error_return( "Material color parse error" );
+                                       }
+
+                                       /* setup 0..255 range color */
+                                       specularColor[ 0 ] = (int)( vec[ 0 ] * 255 );
+                                       specularColor[ 1 ] = (int)( vec[ 1 ] * 255 );
+                                       specularColor[ 2 ] = (int)( vec[ 2 ] * 255 );
+                                       specularColor[ 3 ] = (int)( 255 );
+
+                                       /* skip rest and continue with next token */
+                                       _pico_parse_skip_rest( p );
+                                       continue;
+                               }
+                               /* material diffuse map */
+                               else if ( !_pico_stricmp( p->token,"*map_diffuse" ) ) {
+                                       int sublevel = 0;
+
+                                       /* parse material block */
+                                       while ( 1 )
+                                       {
+                                               /* get next token */
+                                               if ( _pico_parse( p,1 ) == NULL ) {
+                                                       break;
+                                               }
+                                               if ( !strlen( p->token ) ) {
+                                                       continue;
+                                               }
+
+                                               /* handle levels */
+                                               if ( p->token[0] == '{' ) {
+                                                       sublevel++;
+                                               }
+                                               if ( p->token[0] == '}' ) {
+                                                       sublevel--;
+                                               }
+                                               if ( !sublevel ) {
+                                                       break;
+                                               }
+
+                                               /* parse diffuse map bitmap */
+                                               if ( !_pico_stricmp( p->token,"*bitmap" ) ) {
+                                                       char* name = _pico_parse( p,0 );
+                                                       if ( name == NULL ) {
+                                                               _ase_error_return( "Missing material map bitmap name" );
+                                                       }
+                                                       mapname = _pico_alloc( strlen( name ) + 1 );
+                                                       strcpy( mapname, name );
+                                                       /* skip rest and continue with next token */
+                                                       _pico_parse_skip_rest( p );
+                                                       continue;
+                                               }
+                                       }
+                               }
+                               /* end map_diffuse block */
+                       }
+                       /* end material block */
+
+                       if ( subMaterial == NULL ) {
+                               /* allocate new pico shader */
+                               shader = PicoNewShader( model );
+                               if ( shader == NULL ) {
+                                       PicoFreeModel( model );
+                                       return NULL;
+                               }
+
+                               /* set material name */
+                               _pico_unixify( materialName );
+                               PicoSetShaderName( shader,materialName );
+
+                               /* set shader's transparency */
+                               PicoSetShaderTransparency( shader,transValue );
+
+                               /* set shader's ambient color */
+                               PicoSetShaderAmbientColor( shader,ambientColor );
+
+                               /* set diffuse alpha to transparency */
+                               diffuseColor[3] = (picoByte_t)( transValue * 255.0 );
+
+                               /* set shader's diffuse color */
+                               PicoSetShaderDiffuseColor( shader,diffuseColor );
+
+                               /* set shader's specular color */
+                               PicoSetShaderSpecularColor( shader,specularColor );
+
+                               /* set shader's shininess */
+                               PicoSetShaderShininess( shader,shineValue );
+
+                               /* set material map name */
+                               PicoSetShaderMapName( shader, mapname );
+
+                               /* this is just a material with 1 submaterial */
+                               subMaterial = _ase_add_submaterial( &materials, index, 0, shader );
+                       }
+
+                       /* ydnar: free mapname */
+                       if ( mapname != NULL ) {
+                               _pico_free( mapname );
+                       }
+               }   // !_pico_stricmp ( "*material" )
+
+               /* skip unparsed rest of line and continue */
+               _pico_parse_skip_rest( p );
+       }
+
+       /* ydnar: finish existing surface */
+       _ase_submit_triangles( model, materials, vertices, texcoords, colors, faces, numFaces, lastNodeName );
+       _pico_free( faces );
+       _pico_free( vertices );
+       _pico_free( texcoords );
+       _pico_free( colors );
+
+#ifdef DEBUG_PM_ASE
+       _ase_print_materials( materials );
+       finish = clock();
+       elapsed = (double)( finish - start ) / CLOCKS_PER_SEC;
+       _pico_printf( PICO_NORMAL, "Loaded model in in %-.2f second(s)\n", elapsed );
+#endif //DEBUG_PM_ASE
+
+       _ase_free_materials( &materials );
+
+       _pico_free_parser( p );
+
+       /* return allocated pico model */
+       return model;
+}
+
+/* pico file format module definition */
+const picoModule_t picoModuleASE =
+{
+       "1.0",                  /* module version string */
+       "Autodesk 3DSMAX ASCII",    /* module display name */
+       "Jared Hefty, seaw0lf",                 /* author's name */
+       "2003 Jared Hefty, 2002 seaw0lf",               /* module copyright */
+       {
+               "ase",NULL,NULL,NULL    /* default extensions to use */
+       },
+       _ase_canload,               /* validation routine */
+       _ase_load,                  /* load routine */
+       NULL,                       /* save validation routine */
+       NULL                        /* save routine */
+};