Exemple #1
0
/**
 * @brief loads a wavefront obj model file.
 */
static picoModel_t *_obj_load (PM_PARAMS_LOAD)
{
	TObjVertexData *vertexData = NULL;
	picoModel_t *model;
	picoSurface_t *curSurface = NULL;
	picoParser_t *p;
	int allocated;
	int entries;
	int numVerts = 0;
	int numNormals = 0;
	int numUVs = 0;
	int curVertex = 0;
	int curFace = 0;
	picoShader_t *shader;

	/* helper */
#define _obj_error_return(m) \
	{ \
		_pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine); \
		_pico_free_parser( p ); \
		FreeObjVertexData( vertexData ); \
		PicoFreeModel( model ); \
		return NULL; \
	}
	/* alllocate a new pico parser */
	p = _pico_new_parser((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);

	/* try loading the materials; we don't handle the result */
	shader = _obj_default_shader(model);
#if 0
	shader = _obj_mtl_load(model);
#endif

	/* parse obj line by line */
	while (1) {
		/* get first token on line */
		if (_pico_parse_first(p) == NULL)
			break;

		/* skip empty lines */
		if (p->token == NULL || !strlen(p->token))
			continue;

		/* skip comment lines */
		if (p->token[0] == '#') {
			_pico_parse_skip_rest(p);
			continue;
		}
		/* vertex */
		if (!_pico_stricmp(p->token, "v")) {
			TObjVertexData *data;
			picoVec3_t v;

			vertexData = SizeObjVertexData(vertexData, numVerts + 1, &entries, &allocated);
			if (vertexData == NULL)
				_obj_error_return("Realloc of vertex data failed (1)");

			data = &vertexData[numVerts++];

			/* get and copy vertex */
			if (!_pico_parse_vec(p, v))
				_obj_error_return("Vertex parse error");

			_pico_copy_vec(v, data->v);

#ifdef DEBUG_PM_OBJ_EX
			printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
#endif
		}
		/* uv coord */
		else if (!_pico_stricmp(p->token, "vt")) {
			TObjVertexData *data;
			picoVec2_t coord;

			vertexData = SizeObjVertexData(vertexData, numUVs + 1, &entries, &allocated);
			if (vertexData == NULL)
				_obj_error_return("Realloc of vertex data failed (2)");

			data = &vertexData[numUVs++];

			/* get and copy tex coord */
			if (!_pico_parse_vec2(p, coord))
				_obj_error_return("UV coord parse error");

			_pico_copy_vec2(coord, data->vt);

#ifdef DEBUG_PM_OBJ_EX
			printf("TexCoord: u: %f v: %f\n",coord[0],coord[1]);
#endif
		}
		/* vertex normal */
		else if (!_pico_stricmp(p->token, "vn")) {
			TObjVertexData *data;
			picoVec3_t n;

			vertexData = SizeObjVertexData(vertexData, numNormals + 1, &entries, &allocated);
			if (vertexData == NULL)
				_obj_error_return("Realloc of vertex data failed (3)");

			data = &vertexData[numNormals++];

			/* get and copy vertex normal */
			if (!_pico_parse_vec(p, n))
				_obj_error_return("Vertex normal parse error");

			_pico_copy_vec(n, data->vn);

#ifdef DEBUG_PM_OBJ_EX
			printf("Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2]);
#endif
		}
		/* new group (for us this means a new surface) */
		else if (!_pico_stricmp(p->token, "g")) {
			picoSurface_t *newSurface;
			char *groupName;

			/* get first group name (ignore 2nd,3rd,etc.) */
			groupName = _pico_parse(p, 0);
			if (groupName == NULL || !strlen(groupName)) {
				/* some obj exporters feel like they don't need to */
				/* supply a group name. so we gotta handle it here */
#if 1
				strcpy(p->token, "default");
				groupName = p->token;
#else
				_obj_error_return("Invalid or missing group name");
#endif
			}
			/* allocate a pico surface */
			newSurface = PicoNewSurface(model);
			if (newSurface == NULL)
				_obj_error_return("Error allocating surface");

			/* reset face index for surface */
			curFace = 0;

			/* set ptr to current surface */
			curSurface = newSurface;

			/* we use triangle meshes */
			PicoSetSurfaceType(newSurface, PICO_TRIANGLES);

			/* set surface name */
			PicoSetSurfaceName(newSurface, groupName);

			/* associate current surface with newly created shader */
			PicoSetSurfaceShader(newSurface, shader);

#ifdef DEBUG_PM_OBJ_EX
			printf("Group: '%s'\n",groupName);
#endif
		}
		/* face (oh jesus, hopefully this will do the job right ;) */
		else if (!_pico_stricmp(p->token, "f")) {
			/* okay, this is a mess. some 3d apps seem to try being unique, */
			/* hello cinema4d & 3d exploration, feel good today?, and save */
			/* this crap in tons of different formats. gah, those screwed */
			/* coders. tho the wavefront obj standard defines exactly two */
			/* ways of storing face information. so, i really won't support */
			/* such stupid extravaganza here! */

			picoVec3_t verts[4];
			picoVec3_t normals[4];
			picoVec2_t coords[4];

			int iv[4], has_v;
			int ivt[4], has_vt = 0;
			int ivn[4], has_vn = 0;
			int have_quad = 0;
			int slashcount;
			int doubleslash;
			int i;

			/* group defs *must* come before faces */
			if (curSurface == NULL)
				_obj_error_return("No group defined for faces");

#ifdef DEBUG_PM_OBJ_EX
			printf("Face: ");
#endif
			/* read vertex/uv/normal indices for the first three face */
			/* vertices (cause we only support triangles) into 'i*[]' */
			/* store the actual vertex/uv/normal data in three arrays */
			/* called 'verts','coords' and 'normals'. */
			for (i = 0; i < 4; i++) {
				char *str;

				/* get next vertex index string (different */
				/* formats are handled below) */
				str = _pico_parse(p, 0);
				if (str == NULL) {
					/* just break for quads */
					if (i == 3)
						break;

					/* error otherwise */
					_obj_error_return("Face parse error");
				}
				/* if this is the fourth index string we're */
				/* parsing we assume that we have a quad */
				if (i == 3)
					have_quad = 1;

				/* get slash count once */
				if (i == 0) {
					slashcount = _pico_strchcount(str, '/');
					doubleslash = strstr(str, "//") != NULL;
				}
				/* handle format 'v//vn' */
				if (doubleslash && (slashcount == 2)) {
					has_v = has_vn = 1;
					sscanf(str, "%d//%d", &iv[i], &ivn[i]);
				}
				/* handle format 'v/vt/vn' */
				else if (!doubleslash && (slashcount == 2)) {
					has_v = has_vt = has_vn = 1;
					sscanf(str, "%d/%d/%d", &iv[i], &ivt[i], &ivn[i]);
				}
				/* handle format 'v/vt' (non-standard fuckage) */
				else if (!doubleslash && (slashcount == 1)) {
					has_v = has_vt = 1;
					sscanf(str, "%d/%d", &iv[i], &ivt[i]);
				}
				/* else assume face format 'v' */
				/* (must have been invented by some bored granny) */
				else {
					/* get single vertex index */
					has_v = 1;
					iv[i] = atoi(str);

					/* either invalid face format or out of range */
					if (iv[i] == 0)
						_obj_error_return("Invalid face format");
				}
				/* fix useless back references */
				/* todo: check if this works as it is supposed to */

				/* assign new indices */
				if (iv[i] < 0)
					iv[i] = (numVerts - iv[i]);
				if (ivt[i] < 0)
					ivt[i] = (numUVs - ivt[i]);
				if (ivn[i] < 0)
					ivn[i] = (numNormals - ivn[i]);

				/* validate indices */
				/* - commented out. index range checks will trigger
				 if (iv [ i ] < 1) iv [ i ] = 1;
				 if (ivt[ i ] < 1) ivt[ i ] = 1;
				 if (ivn[ i ] < 1) ivn[ i ] = 1;
				 */
				/* set vertex origin */
				if (has_v) {
					/* check vertex index range */
					if (iv[i] < 1 || iv[i] > numVerts)
						_obj_error_return("Vertex index out of range");

					/* get vertex data */
					verts[i][0] = vertexData[iv[i] - 1].v[0];
					verts[i][1] = vertexData[iv[i] - 1].v[1];
					verts[i][2] = vertexData[iv[i] - 1].v[2];
				}
				/* set vertex normal */
				if (has_vn) {
					/* check normal index range */
					if (ivn[i] < 1 || ivn[i] > numNormals)
						_obj_error_return("Normal index out of range");

					/* get normal data */
					normals[i][0] = vertexData[ivn[i] - 1].vn[0];
					normals[i][1] = vertexData[ivn[i] - 1].vn[1];
					normals[i][2] = vertexData[ivn[i] - 1].vn[2];
				}
				/* set texture coordinate */
				if (has_vt) {
					/* check uv index range */
					if (ivt[i] < 1 || ivt[i] > numUVs)
						_obj_error_return("UV coord index out of range");

					/* get uv coord data */
					coords[i][0] = vertexData[ivt[i] - 1].vt[0];
					coords[i][1] = vertexData[ivt[i] - 1].vt[1];
					coords[i][1] = -coords[i][1];
				}
#ifdef DEBUG_PM_OBJ_EX
				printf("(%4d",iv[ i ]);
				if (has_vt) printf(" %4d",ivt[ i ]);
				if (has_vn) printf(" %4d",ivn[ i ]);
				printf(") ");
#endif
			}
#ifdef DEBUG_PM_OBJ_EX
			printf("\n");
#endif
			/* now that we have extracted all the indices and have
			 * read the actual data we need to assign all the crap
			 * to our current pico surface */
			if (has_v) {
				int max = 3;
				if (have_quad)
					max = 4;

				/* assign all surface information */
				for (i = 0; i < max; i++) {
					PicoSetSurfaceXYZ(curSurface, (curVertex + i), verts[i]);
					PicoSetSurfaceST(curSurface, 0, (curVertex + i), coords[i]);
					PicoSetSurfaceNormal(curSurface, (curVertex + i), normals[i]);
				}
				/* add our triangle (A B C) */
				PicoSetSurfaceIndex(curSurface, (curFace * 3 + 2), (picoIndex_t) (curVertex + 0));
				PicoSetSurfaceIndex(curSurface, (curFace * 3 + 1), (picoIndex_t) (curVertex + 1));
				PicoSetSurfaceIndex(curSurface, (curFace * 3 + 0), (picoIndex_t) (curVertex + 2));
				curFace++;

				/* if we don't have a simple triangle, but a quad... */
				if (have_quad) {
					/* we have to add another triangle (2nd half of quad which is A C D) */
					PicoSetSurfaceIndex(curSurface, (curFace * 3 + 2), (picoIndex_t) (curVertex + 0));
					PicoSetSurfaceIndex(curSurface, (curFace * 3 + 1), (picoIndex_t) (curVertex + 2));
					PicoSetSurfaceIndex(curSurface, (curFace * 3 + 0), (picoIndex_t) (curVertex + 3));
					curFace++;
				}

				/* associate current surface with newly created shader */
				PicoSetSurfaceShader(curSurface, shader);

				/* increase vertex count */
				curVertex += max;
			}
		}
		/* skip unparsed rest of line and continue */
		_pico_parse_skip_rest(p);
	}
	/* free memory used by temporary vertexdata */
	FreeObjVertexData(vertexData);

	/* return allocated pico model */
	return model;
}
Exemple #2
0
/* _obj_load:
 *  loads a wavefront obj model file.
 */
static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
	TObjVertexData *vertexData  = NULL;
	picoModel_t    *model;
	picoSurface_t  *curSurface  = NULL;
	picoParser_t   *p;
	int allocated = 0;
	int entries;
	int numVerts    = 0;
	int numNormals  = 0;
	int numUVs      = 0;
	int curVertex   = 0;
	int curFace     = 0;

	int autoGroupNumber = 0;
	char autoGroupNameBuf[64];

#define AUTO_GROUPNAME( namebuf ) \
	sprintf( namebuf, "__autogroup_%d", autoGroupNumber++ )
#define NEW_SURFACE( name )	\
	{ \
		picoSurface_t *newSurface; \
		/* allocate a pico surface */ \
		newSurface = PicoNewSurface( model ); \
		if ( newSurface == NULL ) {	\
			_obj_error_return( "Error allocating surface" ); } \
		/* reset face index for surface */ \
		curFace = 0; \
		curVertex = 0; \
		/* if we can, assign the previous shader to this surface */	\
		if ( curSurface ) {	\
			PicoSetSurfaceShader( newSurface, curSurface->shader ); } \
		/* set ptr to current surface */ \
		curSurface = newSurface; \
		/* we use triangle meshes */ \
		PicoSetSurfaceType( newSurface,PICO_TRIANGLES ); \
		/* set surface name */ \
		PicoSetSurfaceName( newSurface,name ); \
	}

	/* helper */
#define _obj_error_return( m ) \
	{ \
		_pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine ); \
		_pico_free_parser( p );	\
		FreeObjVertexData( vertexData ); \
		PicoFreeModel( model );	\
		return NULL; \
	}
	/* allocate 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 );

	/* try loading the materials */
	_obj_mtl_load( model );

	/* parse obj line by line */
	while ( 1 )
	{
		/* get first token on line */
		if ( _pico_parse_first( p ) == NULL ) {
			break;
		}

		/* skip empty lines */
		if ( p->token == NULL || !strlen( p->token ) ) {
			continue;
		}

		/* skip comment lines */
		if ( p->token[0] == '#' ) {
			_pico_parse_skip_rest( p );
			continue;
		}
		/* vertex */
		if ( !_pico_stricmp( p->token,"v" ) ) {
			TObjVertexData *data;
			picoVec3_t v;

			vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
			if ( vertexData == NULL ) {
				_obj_error_return( "Realloc of vertex data failed (1)" );
			}

			data = &vertexData[ numVerts++ ];

			/* get and copy vertex */
			if ( !_pico_parse_vec( p,v ) ) {
				_obj_error_return( "Vertex parse error" );
			}

			_pico_copy_vec( v,data->v );

#ifdef DEBUG_PM_OBJ_EX
			printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
#endif
		}
		/* uv coord */
		else if ( !_pico_stricmp( p->token,"vt" ) ) {
			TObjVertexData *data;
			picoVec2_t coord;

			vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
			if ( vertexData == NULL ) {
				_obj_error_return( "Realloc of vertex data failed (2)" );
			}

			data = &vertexData[ numUVs++ ];

			/* get and copy tex coord */
			if ( !_pico_parse_vec2( p,coord ) ) {
				_obj_error_return( "UV coord parse error" );
			}

			_pico_copy_vec2( coord,data->vt );

#ifdef DEBUG_PM_OBJ_EX
			printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
#endif
		}
		/* vertex normal */
		else if ( !_pico_stricmp( p->token,"vn" ) ) {
			TObjVertexData *data;
			picoVec3_t n;

			vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
			if ( vertexData == NULL ) {
				_obj_error_return( "Realloc of vertex data failed (3)" );
			}

			data = &vertexData[ numNormals++ ];

			/* get and copy vertex normal */
			if ( !_pico_parse_vec( p,n ) ) {
				_obj_error_return( "Vertex normal parse error" );
			}

			_pico_copy_vec( n,data->vn );

#ifdef DEBUG_PM_OBJ_EX
			printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
#endif
		}
		/* new group (for us this means a new surface) */
		else if ( !_pico_stricmp( p->token,"g" ) ) {
			char *groupName;

			/* get first group name (ignore 2nd,3rd,etc.) */
			groupName = _pico_parse( p,0 );
			if ( groupName == NULL || !strlen( groupName ) ) {
				/* some obj exporters feel like they don't need to */
				/* supply a group name. so we gotta handle it here */
#if 1
				strcpy( p->token,"default" );
				groupName = p->token;
#else
				_obj_error_return( "Invalid or missing group name" );
#endif
			}

			if ( curFace == 0 && curSurface != NULL ) {
				PicoSetSurfaceName( curSurface,groupName );
			}
			else
			{
				NEW_SURFACE( groupName );
			}

#ifdef DEBUG_PM_OBJ_EX
			printf( "Group: '%s'\n",groupName );
#endif
		}
		/* face (oh jesus, hopefully this will do the job right ;) */
		else if ( !_pico_stricmp( p->token,"f" ) ) {
			/* okay, this is a mess. some 3d apps seem to try being unique, */
			/* hello cinema4d & 3d exploration, feel good today?, and save */
			/* this crap in tons of different formats. gah, those screwed */
			/* coders. tho the wavefront obj standard defines exactly two */
			/* ways of storing face information. so, i really won't support */
			/* such stupid extravaganza here! */
			const int numPointsMax = 128;
			picoVec3_t verts  [ numPointsMax ];
			picoVec3_t normals[ numPointsMax ];
			picoVec2_t coords [ numPointsMax ];

			int iv [ numPointsMax ], has_v;
			int ivt[ numPointsMax ], has_vt = 0;
			int ivn[ numPointsMax ], has_vn = 0;
			int slashcount = 0;
			int doubleslash = 0;
			int i;

			if ( curSurface == NULL ) {
				_pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ, line %d.",p->curLine );
				AUTO_GROUPNAME( autoGroupNameBuf );
				NEW_SURFACE( autoGroupNameBuf );
			}

			/* group defs *must* come before faces */
			if ( curSurface == NULL ) {
				_obj_error_return( "No group defined for faces" );
			}

#ifdef DEBUG_PM_OBJ_EX
			printf( "Face: " );
#endif
			/* read vertex/uv/normal indices for the first three face */
			/* vertices (cause we only support triangles) into 'i*[]' */
			/* store the actual vertex/uv/normal data in three arrays */
			/* called 'verts','coords' and 'normals'. */
			for ( i = 0; i < numPointsMax; i++ )
			{
				char *str;

				/* get next vertex index string (different */
				/* formats are handled below) */
				str = _pico_parse( p,0 );
				if ( str == NULL ) {
					/* got nuff points */
					if ( i >= 3 ) {
						break;
					}

					/* error otherwise */
					_obj_error_return( "Face parse error" );
				}

				/* get slash count once */
				if ( i == 0 ) {
					slashcount  = _pico_strchcount( str,'/' );
					doubleslash =  strstr( str,"//" ) != NULL;
				}
				/* handle format 'v//vn' */
				if ( doubleslash && ( slashcount == 2 ) ) {
					has_v = has_vn = 1;
					sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
				}
				/* handle format 'v/vt/vn' */
				else if ( !doubleslash && ( slashcount == 2 ) ) {
					has_v = has_vt = has_vn = 1;
					sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
				}
				/* handle format 'v/vt' (non-standard fuckage) */
				else if ( !doubleslash && ( slashcount == 1 ) ) {
					has_v = has_vt = 1;
					sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
				}
				/* else assume face format 'v' */
				/* (must have been invented by some bored granny) */
				else {
					/* get single vertex index */
					has_v = 1;
					iv[ i ] = atoi( str );

					/* either invalid face format or out of range */
					if ( iv[ i ] == 0 ) {
						_obj_error_return( "Invalid face format" );
					}
				}
				/* fix useless back references */
				/* assign new indices */
				if ( iv [ i ] < 0 ) {
					iv [ i ] = ( numVerts   + iv [ i ] + 1 );
				}
				if ( ivt[ i ] < 0 ) {
					ivt[ i ] = ( numUVs     + ivt[ i ] + 1 );
				}
				if ( ivn[ i ] < 0 ) {
					ivn[ i ] = ( numNormals + ivn[ i ] + 1 );
				}

				/* validate indices */
				/* - commented out. index range checks will trigger
				   if (iv [ i ] < 1) iv [ i ] = 1;
				   if (ivt[ i ] < 1) ivt[ i ] = 1;
				   if (ivn[ i ] < 1) ivn[ i ] = 1;
				 */
				/* set vertex origin */
				if ( has_v ) {
					/* check vertex index range */
					if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
						_obj_error_return( "Vertex index out of range" );
					}

					/* get vertex data */
					verts[ i ][ 0 ] =  vertexData[ iv[ i ] - 1 ].v[ 0 ];
					verts[ i ][ 1 ] = -vertexData[ iv[ i ] - 1 ].v[ 2 ];
					verts[ i ][ 2 ] =  vertexData[ iv[ i ] - 1 ].v[ 1 ];
				}
				/* set vertex normal */
				if ( has_vn ) {
					/* check normal index range */
					if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
						_obj_error_return( "Normal index out of range" );
					}

					/* get normal data */
					normals[ i ][ 0 ] =  vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
					normals[ i ][ 1 ] = -vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
					normals[ i ][ 2 ] =  vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
				}
				/* set texture coordinate */
				if ( has_vt ) {
					/* check uv index range */
					if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
						_obj_error_return( "UV coord index out of range" );
					}

					/* get uv coord data */
					coords[ i ][ 0 ] =  vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
					coords[ i ][ 1 ] = -vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
				}
#ifdef DEBUG_PM_OBJ_EX
				printf( "(%4d",iv[ i ] );
				if ( has_vt ) {
					printf( " %4d",ivt[ i ] );
				}
				if ( has_vn ) {
					printf( " %4d",ivn[ i ] );
				}
				printf( ") " );
#endif
			}
#ifdef DEBUG_PM_OBJ_EX
			printf( "\n" );
#endif
			/* now that we have extracted all the indices and have */
			/* read the actual data we need to assign all the crap */
			/* to our current pico surface */
			if ( has_v ) {
				const int numPoints = i;

				/* assign all surface information */
				for ( i = 0; i < numPoints; i++ )
				{
					/*if( has_v  )*/ PicoSetSurfaceXYZ( curSurface,  ( curVertex + i ), verts  [ i ] );
					/*if( has_vt )*/ PicoSetSurfaceST( curSurface, 0, ( curVertex + i ), coords [ i ] );
					/*if( has_vn )*/ PicoSetSurfaceNormal( curSurface,  ( curVertex + i ), normals[ i ] );
					if( curSurface && curSurface->shader )
						PicoSetSurfaceColor( curSurface, 0, ( curVertex + i ), curSurface->shader->diffuseColor );
				}
				/* add triangles */
				for ( i = 1; i < numPoints - 1; ++i )
				{
					PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
					PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + i ) );
					PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + i + 1 ) );
					curFace++;
				}
				/* increase vertex count */
				curVertex += numPoints;
			}
		}
		else if ( !_pico_stricmp( p->token,"usemtl" ) ) {
			picoShader_t *shader;
			char *name;

			/* get material name */
			name = _pico_parse( p,0 );

			if ( curFace != 0 || curSurface == NULL ) {
				_pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ, line %d.",p->curLine );
				AUTO_GROUPNAME( autoGroupNameBuf );
				NEW_SURFACE( autoGroupNameBuf );
			}

			/* validate material name */
			if ( name == NULL || !strlen( name ) ) {
				_pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine );
			}
			else
			{
				shader = PicoFindShader( model, name, 1 );
				if ( shader == NULL ) {
					_pico_printf( PICO_WARNING, "Undefined material name \"%s\" in OBJ, line %d. Making a default shader.", name, p->curLine );

					/* create a new pico shader */
					shader = PicoNewShader( model );
					if ( shader != NULL ) {
						PicoSetShaderName( shader,name );
						PicoSetShaderMapName( shader,name );
						PicoSetSurfaceShader( curSurface, shader );
					}
				}
				else
				{
					PicoSetSurfaceShader( curSurface, shader );
				}
			}
		}
		/* skip unparsed rest of line and continue */
		_pico_parse_skip_rest( p );
	}

	/* free memory used by temporary vertexdata */
	FreeObjVertexData( vertexData );

	/* return allocated pico model */
	return model;
//	return NULL;
}