1 /* -----------------------------------------------------------------------------
5 Copyright (c) 2002, Randy Reddig & seaw0lf
8 Redistribution and use in source and binary forms, with or without modification,
9 are permitted provided that the following conditions are met:
11 Redistributions of source code must retain the above copyright notice, this list
12 of conditions and the following disclaimer.
14 Redistributions in binary form must reproduce the above copyright notice, this
15 list of conditions and the following disclaimer in the documentation and/or
16 other materials provided with the distribution.
18 Neither the names of the copyright holders nor the names of its contributors may
19 be used to endorse or promote products derived from this software without
20 specific prior written permission.
22 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
23 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
24 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
25 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
26 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
27 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
29 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 ----------------------------------------------------------------------------- */
41 #include "picointernal.h"
43 /* disable warnings */
45 #pragma warning( disable:4100 ) /* unref param */
49 * - '_obj_load' code crashes in a weird way after
50 * '_obj_mtl_load' for a few .mtl files
51 * - process 'mtllib' rather than using <model>.mtl
52 * - handle 'usemtl' statements
54 /* uncomment when debugging this module */
55 /* #define DEBUG_PM_OBJ */
56 /* #define DEBUG_PM_OBJ_EX */
58 /* this holds temporary vertex data read by parser */
59 typedef struct SObjVertexData
61 picoVec3_t v; /* geometric vertices */
62 picoVec2_t vt; /* texture vertices */
63 picoVec3_t vn; /* vertex normals (optional) */
68 * validates a wavefront obj model file.
70 static int _obj_canload( PM_PARAMS_CANLOAD )
74 /* check data length */
76 return PICO_PMV_ERROR_SIZE;
78 /* first check file extension. we have to do this for objs */
79 /* cause there is no good way to identify the contents */
80 if (_pico_stristr(fileName,".obj") != NULL ||
81 _pico_stristr(fileName,".wf" ) != NULL)
85 /* if the extension check failed we parse through the first */
86 /* few lines in file and look for common keywords often */
87 /* appearing at the beginning of wavefront objects */
89 /* alllocate a new pico parser */
90 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
92 return PICO_PMV_ERROR_MEMORY;
94 /* parse obj head line by line for type check */
97 /* get first token on line */
98 if (_pico_parse_first( p ) == NULL)
101 /* we only parse the first few lines, say 80 */
105 /* skip empty lines */
106 if (p->token == NULL || !strlen( p->token ))
109 /* material library keywords are teh good */
110 if (!_pico_stricmp(p->token,"usemtl") ||
111 !_pico_stricmp(p->token,"mtllib") ||
112 !_pico_stricmp(p->token,"g") ||
113 !_pico_stricmp(p->token,"v")) /* v,g bit fishy, but uh... */
115 /* free the pico parser thing */
116 _pico_free_parser( p );
118 /* seems to be a valid wavefront obj */
121 /* skip rest of line */
122 _pico_parse_skip_rest( p );
124 /* free the pico parser thing */
125 _pico_free_parser( p );
127 /* doesn't really look like an obj to us */
128 return PICO_PMV_ERROR;
131 /* SizeObjVertexData:
132 * This pretty piece of 'alloc ahead' code dynamically
133 * allocates - and reallocates as soon as required -
134 * my vertex data array in even steps.
136 #define SIZE_OBJ_STEP 4096
138 static TObjVertexData *SizeObjVertexData(
139 TObjVertexData *vertexData, int reqEntries,
140 int *entries, int *allocated)
147 if (entries == NULL || allocated == NULL)
148 return NULL; /* must have */
150 /* no need to grow yet */
151 if (vertexData && (reqEntries < *allocated))
153 *entries = reqEntries;
156 /* given vertex data ptr not allocated yet */
157 if (vertexData == NULL)
159 /* how many entries to allocate */
160 newAllocated = (reqEntries > SIZE_OBJ_STEP) ?
161 reqEntries : SIZE_OBJ_STEP;
163 /* throw out an extended debug message */
164 #ifdef DEBUG_PM_OBJ_EX
165 printf("SizeObjVertexData: allocate (%d entries)\n",
168 /* first time allocation */
169 vertexData = (TObjVertexData *)
170 _pico_alloc( sizeof(TObjVertexData) * newAllocated );
172 /* allocation failed */
173 if (vertexData == NULL)
176 /* allocation succeeded */
177 *allocated = newAllocated;
178 *entries = reqEntries;
181 /* given vertex data ptr needs to be resized */
182 if (reqEntries == *allocated)
184 newAllocated = (*allocated + SIZE_OBJ_STEP);
186 /* throw out an extended debug message */
187 #ifdef DEBUG_PM_OBJ_EX
188 printf("SizeObjVertexData: reallocate (%d entries)\n",
191 /* try to reallocate */
192 vertexData = (TObjVertexData *)
193 _pico_realloc( (void *)&vertexData,
194 sizeof(TObjVertexData) * (*allocated),
195 sizeof(TObjVertexData) * (newAllocated));
197 /* reallocation failed */
198 if (vertexData == NULL)
201 /* reallocation succeeded */
202 *allocated = newAllocated;
203 *entries = reqEntries;
206 /* we're b0rked when we reach this */
210 static void FreeObjVertexData( TObjVertexData *vertexData )
212 if (vertexData != NULL)
214 free( (TObjVertexData *)vertexData );
218 static int _obj_mtl_load( picoModel_t *model )
220 picoShader_t *curShader = NULL;
222 picoByte_t *mtlBuffer;
227 if( model == NULL || model->fileName == NULL )
230 /* skip if we have a zero length model file name */
231 if (!strlen( model->fileName ))
235 #define _obj_mtl_error_return \
237 _pico_free_parser( p ); \
238 _pico_free_file( mtlBuffer ); \
239 _pico_free( fileName ); \
242 /* alloc copy of model file name */
243 fileName = _pico_clone_alloc( model->fileName );
244 if (fileName == NULL)
247 /* change extension of model file to .mtl */
248 _pico_setfext( fileName, "mtl" );
250 /* load .mtl file contents */
251 _pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
254 if (mtlBufSize == 0) return 1; /* file is empty: no error */
255 if (mtlBufSize < 0) return 0; /* load failed: error */
257 /* create a new pico parser */
258 p = _pico_new_parser( mtlBuffer, mtlBufSize );
260 _obj_mtl_error_return;
262 /* doo teh .mtl parse */
265 /* get next token in material file */
266 if (_pico_parse( p,1 ) == NULL)
270 /* skip empty lines */
271 if (p->token == NULL || !strlen( p->token ))
274 /* skip comment lines */
275 if (p->token[0] == '#')
277 _pico_parse_skip_rest( p );
281 if (!_pico_stricmp(p->token,"newmtl"))
283 picoShader_t *shader;
286 /* get material name */
287 name = _pico_parse( p,0 );
289 /* validate material name */
290 if (name == NULL || !strlen(name))
292 _pico_printf( PICO_ERROR,"Missing material name in MTL, line %d.",p->curLine);
293 _obj_mtl_error_return;
295 /* create a new pico shader */
296 shader = PicoNewShader( model );
298 _obj_mtl_error_return;
300 /* set shader name */
301 PicoSetShaderName( shader,name );
303 /* assign pointer to current shader */
306 /* diffuse map name */
307 else if (!_pico_stricmp(p->token,"map_kd"))
310 picoShader_t *shader;
312 /* pointer to current shader must be valid */
313 if (curShader == NULL)
314 _obj_mtl_error_return;
316 /* get material's diffuse map name */
317 mapName = _pico_parse( p,0 );
319 /* validate map name */
320 if (mapName == NULL || !strlen(mapName))
322 _pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
323 _obj_mtl_error_return;
325 /* create a new pico shader */
326 shader = PicoNewShader( model );
328 _obj_mtl_error_return;
329 /* set shader map name */
330 PicoSetShaderMapName( shader,mapName );
332 /* dissolve factor (pseudo transparency 0..1) */
333 /* where 0 means 100% transparent and 1 means opaque */
334 else if (!_pico_stricmp(p->token,"d"))
340 /* get dissolve factor */
341 if (!_pico_parse_float( p,&value ))
342 _obj_mtl_error_return;
344 /* set shader transparency */
345 PicoSetShaderTransparency( curShader,value );
347 /* get shader's diffuse color */
348 diffuse = PicoGetShaderDiffuseColor( curShader );
350 /* set diffuse alpha to transparency */
351 diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
353 /* set shader's new diffuse color */
354 PicoSetShaderDiffuseColor( curShader,diffuse );
356 /* shininess (phong specular component) */
357 else if (!_pico_stricmp(p->token,"ns"))
360 * - well, this is some major obj spec fuckup once again. some
361 * apps store this in 0..1 range, others use 0..100 range,
362 * even others use 0..2048 range, and again others use the
363 * range 0..128, some even use 0..1000, 0..200, 400..700,
364 * honestly, what's up with the 3d app coders? happens when
365 * you smoke too much weed i guess. -sea
369 /* pointer to current shader must be valid */
370 if (curShader == NULL)
371 _obj_mtl_error_return;
373 /* get totally screwed up shininess (a random value in fact ;) */
374 if (!_pico_parse_float( p,&value ))
375 _obj_mtl_error_return;
377 /* okay, there is no way to set this correctly, so we simply */
378 /* try to guess a few ranges (most common ones i have seen) */
380 /* assume 0..2048 range */
382 value = 128.0 * (value / 2048.0);
383 /* assume 0..1000 range */
384 else if (value > 200)
385 value = 128.0 * (value / 1000.0);
386 /* assume 0..200 range */
387 else if (value > 100)
388 value = 128.0 * (value / 200.0);
389 /* assume 0..100 range */
391 value = 128.0 * (value / 100.0);
392 /* assume 0..1 range */
396 /* negative shininess is bad (yes, i have seen it...) */
397 if (value < 0.0) value = 0.0;
399 /* set the pico shininess value in range 0..127 */
400 /* geez, .obj is such a mess... */
401 PicoSetShaderShininess( curShader,value );
403 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
404 else if (!_pico_stricmp(p->token,"ka"))
409 /* pointer to current shader must be valid */
410 if (curShader == NULL)
411 _obj_mtl_error_return;
413 /* get color vector */
414 if (!_pico_parse_vec( p,v ))
415 _obj_mtl_error_return;
417 /* scale to byte range */
418 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
419 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
420 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
421 color[ 3 ] = (picoByte_t)( 255 );
423 /* set ambient color */
424 PicoSetShaderAmbientColor( curShader,color );
427 else if (!_pico_stricmp(p->token,"kd"))
432 /* pointer to current shader must be valid */
433 if (curShader == NULL)
434 _obj_mtl_error_return;
436 /* get color vector */
437 if (!_pico_parse_vec( p,v ))
438 _obj_mtl_error_return;
440 /* scale to byte range */
441 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
442 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
443 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
444 color[ 3 ] = (picoByte_t)( 255 );
446 /* set diffuse color */
447 PicoSetShaderDiffuseColor( curShader,color );
450 else if (!_pico_stricmp(p->token,"ks"))
455 /* pointer to current shader must be valid */
456 if (curShader == NULL)
457 _obj_mtl_error_return;
459 /* get color vector */
460 if (!_pico_parse_vec( p,v ))
461 _obj_mtl_error_return;
463 /* scale to byte range */
464 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
465 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
466 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
467 color[ 3 ] = (picoByte_t)( 255 );
469 /* set specular color */
470 PicoSetShaderSpecularColor( curShader,color );
473 /* skip rest of line */
474 _pico_parse_skip_rest( p );
477 /* free parser, file buffer, and file name */
478 _pico_free_parser( p );
479 _pico_free_file( mtlBuffer );
480 _pico_free( fileName );
482 /* return with success */
487 * loads a wavefront obj model file.
489 static picoModel_t *_obj_load( PM_PARAMS_LOAD )
491 TObjVertexData *vertexData = NULL;
493 picoSurface_t *curSurface = NULL;
503 int autoGroupNumber = 0;
504 char autoGroupNameBuf[64];
506 #define AUTO_GROUPNAME(namebuf) \
507 sprintf(namebuf, "__autogroup_%d", autoGroupNumber++)
508 #define NEW_SURFACE(name) \
510 picoSurface_t *newSurface; \
511 /* allocate a pico surface */ \
512 newSurface = PicoNewSurface( model ); \
513 if (newSurface == NULL) \
514 _obj_error_return("Error allocating surface"); \
515 /* reset face index for surface */ \
517 /* if we can, assign the previous shader to this surface */ \
519 PicoSetSurfaceShader(newSurface, curSurface->shader); \
520 /* set ptr to current surface */ \
521 curSurface = newSurface; \
522 /* we use triangle meshes */ \
523 PicoSetSurfaceType( newSurface,PICO_TRIANGLES ); \
524 /* set surface name */ \
525 PicoSetSurfaceName( newSurface,name ); \
529 #define _obj_error_return(m) \
531 _pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine); \
532 _pico_free_parser( p ); \
533 FreeObjVertexData( vertexData ); \
534 PicoFreeModel( model ); \
537 /* alllocate a new pico parser */
538 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
539 if (p == NULL) return NULL;
541 /* create a new pico model */
542 model = PicoNewModel();
545 _pico_free_parser( p );
549 PicoSetModelFrameNum( model,frameNum );
550 PicoSetModelName( model,fileName );
551 PicoSetModelFileName( model,fileName );
553 /* try loading the materials; we don't handle the result */
555 _obj_mtl_load( model );
558 /* parse obj line by line */
561 /* get first token on line */
562 if (_pico_parse_first( p ) == NULL)
565 /* skip empty lines */
566 if (p->token == NULL || !strlen( p->token ))
569 /* skip comment lines */
570 if (p->token[0] == '#')
572 _pico_parse_skip_rest( p );
576 if (!_pico_stricmp(p->token,"v"))
578 TObjVertexData *data;
581 vertexData = SizeObjVertexData( vertexData,numVerts+1,&entries,&allocated );
582 if (vertexData == NULL)
583 _obj_error_return("Realloc of vertex data failed (1)");
585 data = &vertexData[ numVerts++ ];
587 /* get and copy vertex */
588 if (!_pico_parse_vec( p,v ))
589 _obj_error_return("Vertex parse error");
591 _pico_copy_vec( v,data->v );
593 #ifdef DEBUG_PM_OBJ_EX
594 printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
598 else if (!_pico_stricmp(p->token,"vt"))
600 TObjVertexData *data;
603 vertexData = SizeObjVertexData( vertexData,numUVs+1,&entries,&allocated );
604 if (vertexData == NULL)
605 _obj_error_return("Realloc of vertex data failed (2)");
607 data = &vertexData[ numUVs++ ];
609 /* get and copy tex coord */
610 if (!_pico_parse_vec2( p,coord ))
611 _obj_error_return("UV coord parse error");
613 _pico_copy_vec2( coord,data->vt );
615 #ifdef DEBUG_PM_OBJ_EX
616 printf("TexCoord: u: %f v: %f\n",coord[0],coord[1]);
620 else if (!_pico_stricmp(p->token,"vn"))
622 TObjVertexData *data;
625 vertexData = SizeObjVertexData( vertexData,numNormals+1,&entries,&allocated );
626 if (vertexData == NULL)
627 _obj_error_return("Realloc of vertex data failed (3)");
629 data = &vertexData[ numNormals++ ];
631 /* get and copy vertex normal */
632 if (!_pico_parse_vec( p,n ))
633 _obj_error_return("Vertex normal parse error");
635 _pico_copy_vec( n,data->vn );
637 #ifdef DEBUG_PM_OBJ_EX
638 printf("Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2]);
641 /* new group (for us this means a new surface) */
642 else if (!_pico_stricmp(p->token,"g"))
646 /* get first group name (ignore 2nd,3rd,etc.) */
647 groupName = _pico_parse( p,0 );
648 if (groupName == NULL || !strlen(groupName))
650 /* some obj exporters feel like they don't need to */
651 /* supply a group name. so we gotta handle it here */
653 strcpy( p->token,"default" );
654 groupName = p->token;
656 _obj_error_return("Invalid or missing group name");
662 PicoSetSurfaceName( curSurface,groupName );
666 NEW_SURFACE(groupName);
669 #ifdef DEBUG_PM_OBJ_EX
670 printf("Group: '%s'\n",groupName);
673 /* face (oh jesus, hopefully this will do the job right ;) */
674 else if (!_pico_stricmp(p->token,"f"))
676 /* okay, this is a mess. some 3d apps seem to try being unique, */
677 /* hello cinema4d & 3d exploration, feel good today?, and save */
678 /* this crap in tons of different formats. gah, those screwed */
679 /* coders. tho the wavefront obj standard defines exactly two */
680 /* ways of storing face information. so, i really won't support */
681 /* such stupid extravaganza here! */
683 picoVec3_t verts [ 4 ];
684 picoVec3_t normals[ 4 ];
685 picoVec2_t coords [ 4 ];
688 int ivt[ 4 ], has_vt = 0;
689 int ivn[ 4 ], has_vn = 0;
695 if(curSurface == NULL)
697 _pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ, line %d.",p->curLine);
698 AUTO_GROUPNAME(autoGroupNameBuf);
699 NEW_SURFACE(autoGroupNameBuf);
702 /* group defs *must* come before faces */
703 if (curSurface == NULL)
704 _obj_error_return("No group defined for faces");
706 #ifdef DEBUG_PM_OBJ_EX
709 /* read vertex/uv/normal indices for the first three face */
710 /* vertices (cause we only support triangles) into 'i*[]' */
711 /* store the actual vertex/uv/normal data in three arrays */
712 /* called 'verts','coords' and 'normals'. */
717 /* get next vertex index string (different */
718 /* formats are handled below) */
719 str = _pico_parse( p,0 );
722 /* just break for quads */
725 /* error otherwise */
726 _obj_error_return("Face parse error");
728 /* if this is the fourth index string we're */
729 /* parsing we assume that we have a quad */
733 /* get slash count once */
736 slashcount = _pico_strchcount( str,'/' );
737 doubleslash = strstr(str,"//") != NULL;
739 /* handle format 'v//vn' */
740 if (doubleslash && (slashcount == 2))
743 sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
745 /* handle format 'v/vt/vn' */
746 else if (!doubleslash && (slashcount == 2))
748 has_v = has_vt = has_vn = 1;
749 sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
751 /* handle format 'v/vt' (non-standard fuckage) */
752 else if (!doubleslash && (slashcount == 1))
755 sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
757 /* else assume face format 'v' */
758 /* (must have been invented by some bored granny) */
760 /* get single vertex index */
762 iv[ i ] = atoi( str );
764 /* either invalid face format or out of range */
766 _obj_error_return("Invalid face format");
768 /* fix useless back references */
769 /* todo: check if this works as it is supposed to */
771 /* assign new indices */
772 if (iv [ i ] < 0) iv [ i ] = (numVerts - iv [ i ]);
773 if (ivt[ i ] < 0) ivt[ i ] = (numUVs - ivt[ i ]);
774 if (ivn[ i ] < 0) ivn[ i ] = (numNormals - ivn[ i ]);
776 /* validate indices */
777 /* - commented out. index range checks will trigger
778 if (iv [ i ] < 1) iv [ i ] = 1;
779 if (ivt[ i ] < 1) ivt[ i ] = 1;
780 if (ivn[ i ] < 1) ivn[ i ] = 1;
782 /* set vertex origin */
785 /* check vertex index range */
786 if (iv[ i ] < 1 || iv[ i ] > numVerts)
787 _obj_error_return("Vertex index out of range");
789 /* get vertex data */
790 verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
791 verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
792 verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
794 /* set vertex normal */
797 /* check normal index range */
798 if (ivn[ i ] < 1 || ivn[ i ] > numNormals)
799 _obj_error_return("Normal index out of range");
801 /* get normal data */
802 normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
803 normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
804 normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
806 /* set texture coordinate */
809 /* check uv index range */
810 if (ivt[ i ] < 1 || ivt[ i ] > numUVs)
811 _obj_error_return("UV coord index out of range");
813 /* get uv coord data */
814 coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
815 coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
816 coords[ i ][ 1 ] = -coords[ i ][ 1 ];
818 #ifdef DEBUG_PM_OBJ_EX
819 printf("(%4d",iv[ i ]);
820 if (has_vt) printf(" %4d",ivt[ i ]);
821 if (has_vn) printf(" %4d",ivn[ i ]);
825 #ifdef DEBUG_PM_OBJ_EX
828 /* now that we have extracted all the indices and have */
829 /* read the actual data we need to assign all the crap */
830 /* to our current pico surface */
834 if (have_quad) max = 4;
836 /* assign all surface information */
837 for (i=0; i<max; i++)
839 /*if( has_v )*/ PicoSetSurfaceXYZ ( curSurface, (curVertex + i), verts [ i ] );
840 /*if( has_vt )*/ PicoSetSurfaceST ( curSurface,0,(curVertex + i), coords [ i ] );
841 /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, (curVertex + i), normals[ i ] );
843 /* add our triangle (A B C) */
844 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 2),(picoIndex_t)( curVertex + 0 ) );
845 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 1),(picoIndex_t)( curVertex + 1 ) );
846 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 0),(picoIndex_t)( curVertex + 2 ) );
849 /* if we don't have a simple triangle, but a quad... */
852 /* we have to add another triangle (2nd half of quad which is A C D) */
853 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 2),(picoIndex_t)( curVertex + 0 ) );
854 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 1),(picoIndex_t)( curVertex + 2 ) );
855 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 0),(picoIndex_t)( curVertex + 3 ) );
858 /* increase vertex count */
862 else if (!_pico_stricmp(p->token,"usemtl"))
864 picoShader_t *shader;
867 /* get material name */
868 name = _pico_parse( p,0 );
870 if(curFace != 0 || curSurface == NULL)
872 _pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ, line %d.",p->curLine);
873 AUTO_GROUPNAME(autoGroupNameBuf);
874 NEW_SURFACE(autoGroupNameBuf);
877 /* validate material name */
878 if (name == NULL || !strlen(name))
880 _pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine);
884 shader = PicoFindShader( model, name, 1 );
887 _pico_printf( PICO_WARNING,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine);
889 /* create a new pico shader */
890 shader = PicoNewShader( model );
893 PicoSetShaderName( shader,name );
894 PicoSetShaderMapName( shader,name );
895 PicoSetSurfaceShader( curSurface, shader );
900 PicoSetSurfaceShader( curSurface, shader );
904 /* skip unparsed rest of line and continue */
905 _pico_parse_skip_rest( p );
907 /* free memory used by temporary vertexdata */
908 FreeObjVertexData( vertexData );
910 /* return allocated pico model */
915 /* pico file format module definition */
916 const picoModule_t picoModuleOBJ =
918 "0.6-b", /* module version string */
919 "Wavefront ASCII", /* module display name */
920 "seaw0lf", /* author's name */
921 "2002 seaw0lf", /* module copyright */
923 "obj",NULL,NULL,NULL /* default extensions to use */
925 _obj_canload, /* validation routine */
926 _obj_load, /* load routine */
927 NULL, /* save validation routine */
928 NULL /* save routine */