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 ----------------------------------------------------------------------------- */
36 #include "picointernal.h"
37 #include "globaldefs.h"
39 /* disable warnings */
40 #if GDEF_COMPILER_MSVC
41 #pragma warning( disable:4100 ) /* unref param */
45 * - '_obj_load' code crashes in a weird way after
46 * '_obj_mtl_load' for a few .mtl files
47 * - process 'mtllib' rather than using <model>.mtl
48 * - handle 'usemtl' statements
50 /* uncomment when debugging this module */
51 /* #define DEBUG_PM_OBJ */
52 /* #define DEBUG_PM_OBJ_EX */
54 /* this holds temporary vertex data read by parser */
55 typedef struct SObjVertexData
57 picoVec3_t v; /* geometric vertices */
58 picoVec2_t vt; /* texture vertices */
59 picoVec3_t vn; /* vertex normals (optional) */
64 * validates a wavefront obj model file.
66 static int _obj_canload( PM_PARAMS_CANLOAD ){
69 /* check data length */
71 return PICO_PMV_ERROR_SIZE;
74 /* first check file extension. we have to do this for objs */
75 /* cause there is no good way to identify the contents */
76 if ( _pico_stristr( fileName,".obj" ) != NULL ||
77 _pico_stristr( fileName,".wf" ) != NULL ) {
80 /* if the extension check failed we parse through the first */
81 /* few lines in file and look for common keywords often */
82 /* appearing at the beginning of wavefront objects */
84 /* alllocate a new pico parser */
85 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
87 return PICO_PMV_ERROR_MEMORY;
90 /* parse obj head line by line for type check */
93 /* get first token on line */
94 if ( _pico_parse_first( p ) == NULL ) {
98 /* we only parse the first few lines, say 80 */
99 if ( p->curLine > 80 ) {
103 /* skip empty lines */
104 if ( p->token == NULL || !strlen( p->token ) ) {
108 /* material library keywords are teh good */
109 if ( !_pico_stricmp( p->token,"usemtl" ) ||
110 !_pico_stricmp( p->token,"mtllib" ) ||
111 !_pico_stricmp( p->token,"g" ) ||
112 !_pico_stricmp( p->token,"v" ) ) { /* v,g bit fishy, but uh... */
113 /* free the pico parser thing */
114 _pico_free_parser( p );
116 /* seems to be a valid wavefront obj */
119 /* skip rest of line */
120 _pico_parse_skip_rest( p );
122 /* free the pico parser thing */
123 _pico_free_parser( p );
125 /* doesn't really look like an obj to us */
126 return PICO_PMV_ERROR;
129 /* SizeObjVertexData:
130 * This pretty piece of 'alloc ahead' code dynamically
131 * allocates - and reallocates as soon as required -
132 * my vertex data array in even steps.
134 const int SIZE_OBJ_STEP = 4096;
136 static TObjVertexData *SizeObjVertexData(
137 TObjVertexData *vertexData, int reqEntries,
138 int *entries, int *allocated ){
142 if ( reqEntries < 1 ) {
145 if ( entries == NULL || allocated == NULL ) {
146 return NULL; /* must have */
149 /* no need to grow yet */
150 if ( vertexData && ( reqEntries < *allocated ) ) {
151 *entries = reqEntries;
154 /* given vertex data ptr not allocated yet */
155 if ( vertexData == NULL ) {
156 /* how many entries to allocate */
157 newAllocated = ( reqEntries > SIZE_OBJ_STEP ) ?
158 reqEntries : SIZE_OBJ_STEP;
160 /* throw out an extended debug message */
161 #ifdef DEBUG_PM_OBJ_EX
162 printf( "SizeObjVertexData: allocate (%d entries)\n",
165 /* first time allocation */
166 vertexData = (TObjVertexData *)
167 _pico_alloc( sizeof( TObjVertexData ) * newAllocated );
169 /* allocation failed */
170 if ( vertexData == NULL ) {
174 /* allocation succeeded */
175 *allocated = newAllocated;
176 *entries = reqEntries;
179 /* given vertex data ptr needs to be resized */
180 if ( reqEntries == *allocated ) {
181 newAllocated = ( *allocated + SIZE_OBJ_STEP );
183 /* throw out an extended debug message */
184 #ifdef DEBUG_PM_OBJ_EX
185 printf( "SizeObjVertexData: reallocate (%d entries)\n",
188 /* try to reallocate */
189 vertexData = (TObjVertexData *)
190 _pico_realloc( (void *)&vertexData,
191 sizeof( TObjVertexData ) * ( *allocated ),
192 sizeof( TObjVertexData ) * ( newAllocated ) );
194 /* reallocation failed */
195 if ( vertexData == NULL ) {
199 /* reallocation succeeded */
200 *allocated = newAllocated;
201 *entries = reqEntries;
204 /* we're b0rked when we reach this */
208 static void FreeObjVertexData( TObjVertexData *vertexData ){
209 if ( vertexData != NULL ) {
210 free( (TObjVertexData *)vertexData );
214 static int _obj_mtl_load( picoModel_t *model ){
215 picoShader_t *curShader = NULL;
217 picoByte_t *mtlBuffer;
222 if ( model == NULL || model->fileName == NULL ) {
226 /* skip if we have a zero length model file name */
227 if ( !strlen( model->fileName ) ) {
232 #define _obj_mtl_error_return \
234 _pico_free_parser( p ); \
235 _pico_free_file( mtlBuffer ); \
236 _pico_free( fileName ); \
239 /* alloc copy of model file name */
240 fileName = _pico_clone_alloc( model->fileName );
241 if ( fileName == NULL ) {
245 /* change extension of model file to .mtl */
246 _pico_setfext( fileName, "mtl" );
248 /* load .mtl file contents */
249 _pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
252 if ( mtlBufSize == 0 ) {
253 return 1; /* file is empty: no error */
255 if ( mtlBufSize < 0 ) {
256 return 0; /* load failed: error */
259 /* create a new pico parser */
260 p = _pico_new_parser( mtlBuffer, mtlBufSize );
262 _obj_mtl_error_return;
265 /* doo teh .mtl parse */
268 /* get next token in material file */
269 if ( _pico_parse( p,1 ) == NULL ) {
274 /* skip empty lines */
275 if ( p->token == NULL || !strlen( p->token ) ) {
279 /* skip comment lines */
280 if ( p->token[0] == '#' ) {
281 _pico_parse_skip_rest( p );
285 if ( !_pico_stricmp( p->token,"newmtl" ) ) {
286 picoShader_t *shader;
289 /* get material name */
290 name = _pico_parse( p,0 );
292 /* validate material name */
293 if ( name == NULL || !strlen( name ) ) {
294 _pico_printf( PICO_ERROR,"Missing material name in MTL %s, line %d.",fileName,p->curLine );
295 _obj_mtl_error_return;
297 /* create a new pico shader */
298 shader = PicoNewShader( model );
299 if ( shader == NULL ) {
300 _obj_mtl_error_return;
303 /* set shader name */
304 PicoSetShaderName( shader,name );
306 /* assign pointer to current shader */
309 /* diffuse map name */
310 else if ( !_pico_stricmp( p->token,"map_kd" ) ) {
312 picoShader_t *shader;
314 /* pointer to current shader must be valid */
315 if ( curShader == NULL ) {
316 _obj_mtl_error_return;
319 /* get material's diffuse map name */
320 mapName = _pico_parse( p,0 );
322 /* validate map name */
323 if ( mapName == NULL || !strlen( mapName ) ) {
324 _pico_printf( PICO_ERROR,"Missing material map name in MTL %s, line %d.",fileName,p->curLine );
325 _obj_mtl_error_return;
327 /* create a new pico shader */
328 shader = PicoNewShader( model );
329 if ( shader == NULL ) {
330 _obj_mtl_error_return;
332 /* set shader map name */
333 PicoSetShaderMapName( shader,mapName );
335 /* dissolve factor (pseudo transparency 0..1) */
336 /* where 0 means 100% transparent and 1 means opaque */
337 else if ( !_pico_stricmp( p->token,"d" ) ) {
342 /* get dissolve factor */
343 if ( !_pico_parse_float( p,&value ) ) {
344 _obj_mtl_error_return;
347 /* set shader transparency */
348 PicoSetShaderTransparency( curShader,value );
350 /* get shader's diffuse color */
351 diffuse = PicoGetShaderDiffuseColor( curShader );
353 /* set diffuse alpha to transparency */
354 diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
356 /* set shader's new diffuse color */
357 PicoSetShaderDiffuseColor( curShader,diffuse );
359 /* shininess (phong specular component) */
360 else if ( !_pico_stricmp( p->token,"ns" ) ) {
362 * - well, this is some major obj spec fuckup once again. some
363 * apps store this in 0..1 range, others use 0..100 range,
364 * even others use 0..2048 range, and again others use the
365 * range 0..128, some even use 0..1000, 0..200, 400..700,
366 * honestly, what's up with the 3d app coders? happens when
367 * you smoke too much weed i guess. -sea
371 /* pointer to current shader must be valid */
372 if ( curShader == NULL ) {
373 _obj_mtl_error_return;
376 /* get totally screwed up shininess (a random value in fact ;) */
377 if ( !_pico_parse_float( p,&value ) ) {
378 _obj_mtl_error_return;
381 /* okay, there is no way to set this correctly, so we simply */
382 /* try to guess a few ranges (most common ones i have seen) */
384 /* assume 0..2048 range */
385 if ( value > 1000 ) {
386 value = 128.0 * ( value / 2048.0 );
388 /* assume 0..1000 range */
389 else if ( value > 200 ) {
390 value = 128.0 * ( value / 1000.0 );
392 /* assume 0..200 range */
393 else if ( value > 100 ) {
394 value = 128.0 * ( value / 200.0 );
396 /* assume 0..100 range */
397 else if ( value > 1 ) {
398 value = 128.0 * ( value / 100.0 );
400 /* assume 0..1 range */
404 /* negative shininess is bad (yes, i have seen it...) */
409 /* set the pico shininess value in range 0..127 */
410 /* geez, .obj is such a mess... */
411 PicoSetShaderShininess( curShader,value );
413 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
414 else if ( !_pico_stricmp( p->token,"ka" ) ) {
418 /* pointer to current shader must be valid */
419 if ( curShader == NULL ) {
420 _obj_mtl_error_return;
423 /* get color vector */
424 if ( !_pico_parse_vec( p,v ) ) {
425 _obj_mtl_error_return;
428 /* scale to byte range */
429 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
430 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
431 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
432 color[ 3 ] = (picoByte_t)( 255 );
434 /* set ambient color */
435 PicoSetShaderAmbientColor( curShader,color );
438 else if ( !_pico_stricmp( p->token,"kd" ) ) {
442 /* pointer to current shader must be valid */
443 if ( curShader == NULL ) {
444 _obj_mtl_error_return;
447 /* get color vector */
448 if ( !_pico_parse_vec( p,v ) ) {
449 _obj_mtl_error_return;
452 /* scale to byte range */
453 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
454 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
455 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
456 color[ 3 ] = (picoByte_t)( 255 );
458 /* set diffuse color */
459 PicoSetShaderDiffuseColor( curShader,color );
462 else if ( !_pico_stricmp( p->token,"ks" ) ) {
466 /* pointer to current shader must be valid */
467 if ( curShader == NULL ) {
468 _obj_mtl_error_return;
471 /* get color vector */
472 if ( !_pico_parse_vec( p,v ) ) {
473 _obj_mtl_error_return;
476 /* scale to byte range */
477 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
478 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
479 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
480 color[ 3 ] = (picoByte_t)( 255 );
482 /* set specular color */
483 PicoSetShaderSpecularColor( curShader,color );
486 /* skip rest of line */
487 _pico_parse_skip_rest( p );
490 /* free parser, file buffer, and file name */
491 _pico_free_parser( p );
492 _pico_free_file( mtlBuffer );
493 _pico_free( fileName );
495 /* return with success */
500 * loads a wavefront obj model file.
502 static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
503 TObjVertexData *vertexData = NULL;
505 picoSurface_t *curSurface = NULL;
515 int autoGroupNumber = 0;
516 char autoGroupNameBuf[64];
518 #define AUTO_GROUPNAME( namebuf ) \
519 sprintf( namebuf, "__autogroup_%d", autoGroupNumber++ )
520 #define NEW_SURFACE( name ) \
522 picoSurface_t *newSurface; \
523 /* allocate a pico surface */ \
524 newSurface = PicoNewSurface( model ); \
525 if ( newSurface == NULL ) { \
526 _obj_error_return( "Error allocating surface" ); } \
527 /* reset face index and vertex index for surface */ \
530 /* if we can, assign the previous shader to this surface */ \
531 if ( curSurface ) { \
532 PicoSetSurfaceShader( newSurface, curSurface->shader ); } \
533 /* set ptr to current surface */ \
534 curSurface = newSurface; \
535 /* we use triangle meshes */ \
536 PicoSetSurfaceType( newSurface,PICO_TRIANGLES ); \
537 /* set surface name */ \
538 PicoSetSurfaceName( newSurface,name ); \
542 #define _obj_error_return( m ) \
544 _pico_printf( PICO_ERROR, "%s in OBJ %s, line %d.", m, model->fileName, p->curLine ); \
545 _pico_free_parser( p ); \
546 FreeObjVertexData( vertexData ); \
547 PicoFreeModel( model ); \
550 /* alllocate a new pico parser */
551 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
556 /* create a new pico model */
557 model = PicoNewModel();
558 if ( model == NULL ) {
559 _pico_free_parser( p );
563 PicoSetModelFrameNum( model,frameNum );
564 PicoSetModelName( model,fileName );
565 PicoSetModelFileName( model,fileName );
567 /* try loading the materials; we don't handle the result */
569 _obj_mtl_load( model );
572 /* parse obj line by line */
575 /* get first token on line */
576 if ( _pico_parse_first( p ) == NULL ) {
580 /* skip empty lines */
581 if ( p->token == NULL || !strlen( p->token ) ) {
585 /* skip comment lines */
586 if ( p->token[0] == '#' ) {
587 _pico_parse_skip_rest( p );
591 if ( !_pico_stricmp( p->token,"v" ) ) {
592 TObjVertexData *data;
595 vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
596 if ( vertexData == NULL ) {
597 _obj_error_return( "Realloc of vertex data failed (1)" );
600 data = &vertexData[ numVerts++ ];
602 /* get and copy vertex */
603 if ( !_pico_parse_vec( p,v ) ) {
604 _obj_error_return( "Vertex parse error" );
607 _pico_copy_vec( v,data->v );
609 #ifdef DEBUG_PM_OBJ_EX
610 printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
614 else if ( !_pico_stricmp( p->token,"vt" ) ) {
615 TObjVertexData *data;
618 vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
619 if ( vertexData == NULL ) {
620 _obj_error_return( "Realloc of vertex data failed (2)" );
623 data = &vertexData[ numUVs++ ];
625 /* get and copy tex coord */
626 if ( !_pico_parse_vec2( p,coord ) ) {
627 _obj_error_return( "UV coord parse error" );
630 _pico_copy_vec2( coord,data->vt );
632 #ifdef DEBUG_PM_OBJ_EX
633 printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
637 else if ( !_pico_stricmp( p->token,"vn" ) ) {
638 TObjVertexData *data;
641 vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
642 if ( vertexData == NULL ) {
643 _obj_error_return( "Realloc of vertex data failed (3)" );
646 data = &vertexData[ numNormals++ ];
648 /* get and copy vertex normal */
649 if ( !_pico_parse_vec( p,n ) ) {
650 _obj_error_return( "Vertex normal parse error" );
653 _pico_copy_vec( n,data->vn );
655 #ifdef DEBUG_PM_OBJ_EX
656 printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
659 /* new group (for us this means a new surface) */
660 else if ( !_pico_stricmp( p->token,"g" ) ) {
663 /* get first group name (ignore 2nd,3rd,etc.) */
664 groupName = _pico_parse( p,0 );
665 if ( groupName == NULL || !strlen( groupName ) ) {
666 /* some obj exporters feel like they don't need to */
667 /* supply a group name. so we gotta handle it here */
669 strcpy( p->token,"default" );
670 groupName = p->token;
672 _obj_error_return( "Invalid or missing group name" );
676 if ( curFace == 0 && curSurface != NULL ) {
677 PicoSetSurfaceName( curSurface,groupName );
681 NEW_SURFACE( groupName );
684 #ifdef DEBUG_PM_OBJ_EX
685 printf( "Group: '%s'\n",groupName );
688 /* face (oh jesus, hopefully this will do the job right ;) */
689 else if ( !_pico_stricmp( p->token,"f" ) ) {
690 /* okay, this is a mess. some 3d apps seem to try being unique, */
691 /* hello cinema4d & 3d exploration, feel good today?, and save */
692 /* this crap in tons of different formats. gah, those screwed */
693 /* coders. tho the wavefront obj standard defines exactly two */
694 /* ways of storing face information. so, i really won't support */
695 /* such stupid extravaganza here! */
697 picoVec3_t verts [ 4 ];
698 picoVec3_t normals[ 4 ];
699 picoVec2_t coords [ 4 ];
702 int ivt[ 4 ], has_vt = 0;
703 int ivn[ 4 ], has_vn = 0;
709 if ( curSurface == NULL ) {
710 _pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ %s, line %d.",model->fileName,p->curLine );
711 AUTO_GROUPNAME( autoGroupNameBuf );
712 NEW_SURFACE( autoGroupNameBuf );
715 /* group defs *must* come before faces */
716 if ( curSurface == NULL ) {
717 _obj_error_return( "No group defined for faces" );
720 #ifdef DEBUG_PM_OBJ_EX
723 /* read vertex/uv/normal indices for the first three face */
724 /* vertices (cause we only support triangles) into 'i*[]' */
725 /* store the actual vertex/uv/normal data in three arrays */
726 /* called 'verts','coords' and 'normals'. */
727 for ( i = 0; i < 4; i++ )
731 /* get next vertex index string (different */
732 /* formats are handled below) */
733 str = _pico_parse( p,0 );
735 /* just break for quads */
740 /* error otherwise */
741 _obj_error_return( "Face parse error" );
743 /* if this is the fourth index string we're */
744 /* parsing we assume that we have a quad */
749 /* get slash count once */
751 slashcount = _pico_strchcount( str,'/' );
752 doubleslash = strstr( str,"//" ) != NULL;
754 /* handle format 'v//vn' */
755 if ( doubleslash && ( slashcount == 2 ) ) {
757 sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
759 /* handle format 'v/vt/vn' */
760 else if ( !doubleslash && ( slashcount == 2 ) ) {
761 has_v = has_vt = has_vn = 1;
762 sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
764 /* handle format 'v/vt' (non-standard fuckage) */
765 else if ( !doubleslash && ( slashcount == 1 ) ) {
767 sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
769 /* else assume face format 'v' */
770 /* (must have been invented by some bored granny) */
772 /* get single vertex index */
774 iv[ i ] = atoi( str );
776 /* either invalid face format or out of range */
777 if ( iv[ i ] == 0 ) {
778 _obj_error_return( "Invalid face format" );
781 /* fix useless back references */
782 /* todo: check if this works as it is supposed to */
784 /* assign new indices */
785 if ( iv [ i ] < 0 ) {
786 iv [ i ] = ( numVerts - iv [ i ] );
788 if ( ivt[ i ] < 0 ) {
789 ivt[ i ] = ( numUVs - ivt[ i ] );
791 if ( ivn[ i ] < 0 ) {
792 ivn[ i ] = ( numNormals - ivn[ i ] );
795 /* validate indices */
796 /* - commented out. index range checks will trigger
797 if (iv [ i ] < 1) iv [ i ] = 1;
798 if (ivt[ i ] < 1) ivt[ i ] = 1;
799 if (ivn[ i ] < 1) ivn[ i ] = 1;
801 /* set vertex origin */
803 /* check vertex index range */
804 if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
805 _obj_error_return( "Vertex index out of range" );
808 /* get vertex data */
809 verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
810 verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
811 verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
813 /* set vertex normal */
815 /* check normal index range */
816 if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
817 _obj_error_return( "Normal index out of range" );
820 /* get normal data */
821 normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
822 normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
823 normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
825 /* set texture coordinate */
827 /* check uv index range */
828 if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
829 _obj_error_return( "UV coord index out of range" );
832 /* get uv coord data */
833 coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
834 coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
835 coords[ i ][ 1 ] = -coords[ i ][ 1 ];
837 #ifdef DEBUG_PM_OBJ_EX
838 printf( "(%4d",iv[ i ] );
840 printf( " %4d",ivt[ i ] );
843 printf( " %4d",ivn[ i ] );
848 #ifdef DEBUG_PM_OBJ_EX
851 /* now that we have extracted all the indices and have */
852 /* read the actual data we need to assign all the crap */
853 /* to our current pico surface */
860 /* assign all surface information */
861 for ( i = 0; i < max; i++ )
863 /*if( has_v )*/ PicoSetSurfaceXYZ( curSurface, ( curVertex + i ), verts [ i ] );
864 /*if( has_vt )*/ PicoSetSurfaceST( curSurface,0,( curVertex + i ), coords [ i ] );
865 /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, ( curVertex + i ), normals[ i ] );
867 /* add our triangle (A B C) */
868 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
869 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 1 ) );
870 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 2 ) );
873 /* if we don't have a simple triangle, but a quad... */
875 /* we have to add another triangle (2nd half of quad which is A C D) */
876 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
877 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 2 ) );
878 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 3 ) );
881 /* increase vertex count */
885 else if ( !_pico_stricmp( p->token,"usemtl" ) ) {
886 picoShader_t *shader;
889 /* get material name */
890 name = _pico_parse( p,0 );
892 if ( curFace != 0 || curSurface == NULL ) {
893 _pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ %s, line %d.",model->fileName,p->curLine );
894 AUTO_GROUPNAME( autoGroupNameBuf );
895 NEW_SURFACE( autoGroupNameBuf );
898 /* validate material name */
899 if ( name == NULL || !strlen( name ) ) {
900 _pico_printf( PICO_ERROR,"Missing material name in OBJ %s, line %d.",model->fileName,p->curLine );
904 shader = PicoFindShader( model, name, 1 );
905 if ( shader == NULL ) {
906 _pico_printf( PICO_WARNING,"Undefined material name \"%s\" in OBJ %s, line %d. Making a default shader.",name,model->fileName,p->curLine );
908 /* create a new pico shader */
909 shader = PicoNewShader( model );
910 if ( shader != NULL ) {
911 PicoSetShaderName( shader,name );
912 PicoSetShaderMapName( shader,name );
913 PicoSetSurfaceShader( curSurface, shader );
918 PicoSetSurfaceShader( curSurface, shader );
922 /* skip unparsed rest of line and continue */
923 _pico_parse_skip_rest( p );
925 /* free memory used by temporary vertexdata */
926 FreeObjVertexData( vertexData );
928 /* return allocated pico model */
933 /* pico file format module definition */
934 const picoModule_t picoModuleOBJ =
936 "0.6-b", /* module version string */
937 "Wavefront ASCII", /* module display name */
938 "seaw0lf", /* author's name */
939 "2002 seaw0lf", /* module copyright */
941 "obj",NULL,NULL,NULL /* default extensions to use */
943 _obj_canload, /* validation routine */
944 _obj_load, /* load routine */
945 NULL, /* save validation routine */
946 NULL /* save routine */