--- /dev/null
+/* -----------------------------------------------------------------------------
+
+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.
+
+----------------------------------------------------------------------------- */
+
+
+/* marker */
+#define PM_ASE_C
+
+/* 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
+
+/* plain white */
+static picoColor_t white = { 255, 255, 255, 255 };
+
+/* 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, 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 )
+{
+ 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];
+ 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] = &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, smooth );
+ }
+ }
+}
+
+static void shadername_convert(char* shaderName)
+{
+ /* unix-style path separators */
+ char* s = shaderName;
+ for(; *s != '\0'; ++s)
+ {
+ if(*s == '\\')
+ {
+ *s = '/';
+ }
+ }
+}
+
+
+/* _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) );
+ }
+ /* 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);
+ _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 );
+ shadername_convert(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 */
+ shadername_convert(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 );
+
+ /* extract shadername from bitmap path */
+ if(mapname != NULL)
+ {
+ char* p = mapname;
+
+ /* convert to shader-name format */
+ shadername_convert(mapname);
+ {
+ /* remove extension */
+ char* last_period = strrchr(p, '.');
+ if(last_period != NULL)
+ {
+ *last_period = '\0';
+ }
+ }
+
+ /* find shader path */
+ for(; *p != '\0'; ++p)
+ {
+ if(_pico_strnicmp(p, "models/", 7) == 0 || _pico_strnicmp(p, "textures/", 9) == 0)
+ {
+ break;
+ }
+ }
+
+ if(*p != '\0')
+ {
+ /* set material name */
+ PicoSetShaderName( shader,p );
+ }
+ }
+
+ /* 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);
+ _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 */
+};