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_print_ok _pico_printf( PICO_NORMAL, "PICO: loading %s...OK\n", fileName )
233 #define _obj_mtl_print_fail _pico_printf( PICO_WARNING, "PICO: loading %s...FAIL\n", fileName )
234 #define _obj_mtl_error_return \
236 _obj_mtl_print_fail; \
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 ) {
248 /* change extension of model file to .mtl */
249 _pico_setfext( fileName, "mtl" );
251 /* load .mtl file contents */
252 _pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
255 if ( mtlBufSize == 0 ) {
257 return 1; /* file is empty: no error */
259 if ( mtlBufSize < 0 ) {
261 return 0; /* load failed: error */
264 /* create a new pico parser */
265 p = _pico_new_parser( mtlBuffer, mtlBufSize );
267 _obj_mtl_error_return;
270 /* doo teh .mtl parse */
273 /* get next token in material file */
274 if ( _pico_parse( p,1 ) == NULL ) {
278 /* skip empty lines */
279 if ( p->token == NULL || !strlen( p->token ) ) {
283 /* skip comment lines */
284 if ( p->token[0] == '#' ) {
285 _pico_parse_skip_rest( p );
289 if ( !_pico_stricmp( p->token,"newmtl" ) ) {
290 picoShader_t *shader;
293 /* get material name */
294 name = _pico_parse( p,0 );
296 /* validate material name */
297 if ( name == NULL || !strlen( name ) ) {
298 _pico_printf( PICO_ERROR,"Missing material name in MTL %s, line %d.",fileName,p->curLine );
299 _obj_mtl_error_return;
301 /* create a new pico shader */
302 shader = PicoNewShader( model );
303 if ( shader == NULL ) {
304 _obj_mtl_error_return;
307 /* set shader name */
308 PicoSetShaderName( shader,name );
310 /* assign pointer to current shader */
313 /* diffuse map name */
314 else if ( !_pico_stricmp( p->token,"map_kd" ) ) {
317 /* pointer to current shader must be valid */
318 if ( curShader == NULL ) {
319 _obj_mtl_error_return;
322 /* get material's diffuse map name */
323 mapName = _pico_parse( p,0 );
325 /* validate map name */
326 if ( mapName == NULL || !strlen( mapName ) ) {
327 _pico_printf( PICO_ERROR,"Missing material map name in MTL %s, line %d.",fileName,p->curLine );
328 _obj_mtl_error_return;
330 /* set shader map name */
331 PicoSetShaderMapName( curShader, mapName );
333 /* dissolve factor (pseudo transparency 0..1) */
334 /* where 0 means 100% transparent and 1 means opaque */
335 else if ( !_pico_stricmp( p->token,"d" ) ) {
340 /* get dissolve factor */
341 if ( !_pico_parse_float( p,&value ) ) {
342 _obj_mtl_error_return;
345 /* set shader transparency */
346 PicoSetShaderTransparency( curShader,value );
348 /* get shader's diffuse color */
349 diffuse = PicoGetShaderDiffuseColor( curShader );
351 /* set diffuse alpha to transparency */
352 diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
354 /* set shader's new diffuse color */
355 PicoSetShaderDiffuseColor( curShader,diffuse );
357 /* shininess (phong specular component) */
358 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;
374 /* get totally screwed up shininess (a random value in fact ;) */
375 if ( !_pico_parse_float( p,&value ) ) {
376 _obj_mtl_error_return;
379 /* okay, there is no way to set this correctly, so we simply */
380 /* try to guess a few ranges (most common ones i have seen) */
382 /* assume 0..2048 range */
383 if ( value > 1000 ) {
384 value = 128.0 * ( value / 2048.0 );
386 /* assume 0..1000 range */
387 else if ( value > 200 ) {
388 value = 128.0 * ( value / 1000.0 );
390 /* assume 0..200 range */
391 else if ( value > 100 ) {
392 value = 128.0 * ( value / 200.0 );
394 /* assume 0..100 range */
395 else if ( value > 1 ) {
396 value = 128.0 * ( value / 100.0 );
398 /* assume 0..1 range */
402 /* negative shininess is bad (yes, i have seen it...) */
407 /* set the pico shininess value in range 0..127 */
408 /* geez, .obj is such a mess... */
409 PicoSetShaderShininess( curShader,value );
411 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
412 else if ( !_pico_stricmp( p->token,"ka" ) ) {
416 /* pointer to current shader must be valid */
417 if ( curShader == NULL ) {
418 _obj_mtl_error_return;
421 /* get color vector */
422 if ( !_pico_parse_vec( p,v ) ) {
423 _obj_mtl_error_return;
426 /* scale to byte range */
427 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
428 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
429 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
430 color[ 3 ] = (picoByte_t)( 255 );
432 /* set ambient color */
433 PicoSetShaderAmbientColor( curShader,color );
436 else if ( !_pico_stricmp( p->token,"kd" ) ) {
440 /* pointer to current shader must be valid */
441 if ( curShader == NULL ) {
442 _obj_mtl_error_return;
445 /* get color vector */
446 if ( !_pico_parse_vec( p,v ) ) {
447 _obj_mtl_error_return;
450 /* scale to byte range */
451 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
452 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
453 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
454 color[ 3 ] = (picoByte_t)( 255 );
456 /* set diffuse color */
457 PicoSetShaderDiffuseColor( curShader,color );
460 else if ( !_pico_stricmp( p->token,"ks" ) ) {
464 /* pointer to current shader must be valid */
465 if ( curShader == NULL ) {
466 _obj_mtl_error_return;
469 /* get color vector */
470 if ( !_pico_parse_vec( p,v ) ) {
471 _obj_mtl_error_return;
474 /* scale to byte range */
475 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
476 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
477 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
478 color[ 3 ] = (picoByte_t)( 255 );
480 /* set specular color */
481 PicoSetShaderSpecularColor( curShader,color );
483 /* skip rest of line */
484 _pico_parse_skip_rest( p );
487 /* free parser, file buffer, and file name */
489 _pico_free_parser( p );
490 _pico_free_file( mtlBuffer );
491 _pico_free( fileName );
493 /* return with success */
498 * loads a wavefront obj model file.
500 static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
501 TObjVertexData *vertexData = NULL;
503 picoSurface_t *curSurface = NULL;
513 int autoGroupNumber = 0;
514 char autoGroupNameBuf[64];
516 #define AUTO_GROUPNAME( namebuf ) \
517 sprintf( namebuf, "__autogroup_%d", autoGroupNumber++ )
518 #define NEW_SURFACE( name ) \
520 picoSurface_t *newSurface; \
521 /* allocate a pico surface */ \
522 newSurface = PicoNewSurface( model ); \
523 if ( newSurface == NULL ) { \
524 _obj_error_return( "Error allocating surface" ); } \
525 /* reset face index and vertex index for surface */ \
528 /* if we can, assign the previous shader to this surface */ \
529 if ( curSurface ) { \
530 PicoSetSurfaceShader( newSurface, curSurface->shader ); } \
531 /* set ptr to current surface */ \
532 curSurface = newSurface; \
533 /* we use triangle meshes */ \
534 PicoSetSurfaceType( newSurface,PICO_TRIANGLES ); \
535 /* set surface name */ \
536 PicoSetSurfaceName( newSurface,name ); \
540 #define _obj_error_return( m ) \
542 _pico_printf( PICO_ERROR, "%s in OBJ %s, line %d.", m, model->fileName, p->curLine ); \
543 _pico_free_parser( p ); \
544 FreeObjVertexData( vertexData ); \
545 PicoFreeModel( model ); \
548 /* alllocate a new pico parser */
549 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
554 /* create a new pico model */
555 model = PicoNewModel();
556 if ( model == NULL ) {
557 _pico_free_parser( p );
561 PicoSetModelFrameNum( model,frameNum );
562 PicoSetModelName( model,fileName );
563 PicoSetModelFileName( model,fileName );
565 /* try loading the materials */
566 _obj_mtl_load( model );
568 /* parse obj line by line */
571 /* get first token on line */
572 if ( _pico_parse_first( p ) == NULL ) {
576 /* skip empty lines */
577 if ( p->token == NULL || !strlen( p->token ) ) {
581 /* skip comment lines */
582 if ( p->token[0] == '#' ) {
583 _pico_parse_skip_rest( p );
587 if ( !_pico_stricmp( p->token,"v" ) ) {
588 TObjVertexData *data;
591 vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
592 if ( vertexData == NULL ) {
593 _obj_error_return( "Realloc of vertex data failed (1)" );
596 data = &vertexData[ numVerts++ ];
598 /* get and copy vertex */
599 if ( !_pico_parse_vec( p,v ) ) {
600 _obj_error_return( "Vertex parse error" );
603 _pico_copy_vec( v,data->v );
605 #ifdef DEBUG_PM_OBJ_EX
606 printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
610 else if ( !_pico_stricmp( p->token,"vt" ) ) {
611 TObjVertexData *data;
614 vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
615 if ( vertexData == NULL ) {
616 _obj_error_return( "Realloc of vertex data failed (2)" );
619 data = &vertexData[ numUVs++ ];
621 /* get and copy tex coord */
622 if ( !_pico_parse_vec2( p,coord ) ) {
623 _obj_error_return( "UV coord parse error" );
626 _pico_copy_vec2( coord,data->vt );
628 #ifdef DEBUG_PM_OBJ_EX
629 printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
633 else if ( !_pico_stricmp( p->token,"vn" ) ) {
634 TObjVertexData *data;
637 vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
638 if ( vertexData == NULL ) {
639 _obj_error_return( "Realloc of vertex data failed (3)" );
642 data = &vertexData[ numNormals++ ];
644 /* get and copy vertex normal */
645 if ( !_pico_parse_vec( p,n ) ) {
646 _obj_error_return( "Vertex normal parse error" );
649 _pico_copy_vec( n,data->vn );
651 #ifdef DEBUG_PM_OBJ_EX
652 printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
655 /* new group (for us this means a new surface) */
656 else if ( !_pico_stricmp( p->token,"g" ) ) {
659 /* get first group name (ignore 2nd,3rd,etc.) */
660 groupName = _pico_parse( p,0 );
661 if ( groupName == NULL || !strlen( groupName ) ) {
662 /* some obj exporters feel like they don't need to */
663 /* supply a group name. so we gotta handle it here */
665 strcpy( p->token,"default" );
666 groupName = p->token;
668 _obj_error_return( "Invalid or missing group name" );
672 if ( curFace == 0 && curSurface != NULL ) {
673 PicoSetSurfaceName( curSurface,groupName );
677 NEW_SURFACE( groupName );
680 #ifdef DEBUG_PM_OBJ_EX
681 printf( "Group: '%s'\n",groupName );
684 /* face (oh jesus, hopefully this will do the job right ;) */
685 else if ( !_pico_stricmp( p->token,"f" ) ) {
686 /* okay, this is a mess. some 3d apps seem to try being unique, */
687 /* hello cinema4d & 3d exploration, feel good today?, and save */
688 /* this crap in tons of different formats. gah, those screwed */
689 /* coders. tho the wavefront obj standard defines exactly two */
690 /* ways of storing face information. so, i really won't support */
691 /* such stupid extravaganza here! */
693 picoVec3_t verts [ 4 ];
694 picoVec3_t normals[ 4 ];
695 picoVec2_t coords [ 4 ];
698 int ivt[ 4 ], has_vt = 0;
699 int ivn[ 4 ], has_vn = 0;
705 if ( curSurface == NULL ) {
706 _pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ %s, line %d.",model->fileName,p->curLine );
707 AUTO_GROUPNAME( autoGroupNameBuf );
708 NEW_SURFACE( autoGroupNameBuf );
711 /* group defs *must* come before faces */
712 if ( curSurface == NULL ) {
713 _obj_error_return( "No group defined for faces" );
716 #ifdef DEBUG_PM_OBJ_EX
719 /* read vertex/uv/normal indices for the first three face */
720 /* vertices (cause we only support triangles) into 'i*[]' */
721 /* store the actual vertex/uv/normal data in three arrays */
722 /* called 'verts','coords' and 'normals'. */
723 for ( i = 0; i < 4; i++ )
727 /* get next vertex index string (different */
728 /* formats are handled below) */
729 str = _pico_parse( p,0 );
731 /* just break for quads */
736 /* error otherwise */
737 _obj_error_return( "Face parse error" );
739 /* if this is the fourth index string we're */
740 /* parsing we assume that we have a quad */
745 /* get slash count once */
747 slashcount = _pico_strchcount( str,'/' );
748 doubleslash = strstr( str,"//" ) != NULL;
750 /* handle format 'v//vn' */
751 if ( doubleslash && ( slashcount == 2 ) ) {
753 sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
755 /* handle format 'v/vt/vn' */
756 else if ( !doubleslash && ( slashcount == 2 ) ) {
757 has_v = has_vt = has_vn = 1;
758 sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
760 /* handle format 'v/vt' (non-standard fuckage) */
761 else if ( !doubleslash && ( slashcount == 1 ) ) {
763 sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
765 /* else assume face format 'v' */
766 /* (must have been invented by some bored granny) */
768 /* get single vertex index */
770 iv[ i ] = atoi( str );
772 /* either invalid face format or out of range */
773 if ( iv[ i ] == 0 ) {
774 _obj_error_return( "Invalid face format" );
777 /* fix useless back references */
778 /* todo: check if this works as it is supposed to */
780 /* assign new indices */
781 if ( iv [ i ] < 0 ) {
782 iv [ i ] = ( numVerts - iv [ i ] );
784 if ( ivt[ i ] < 0 ) {
785 ivt[ i ] = ( numUVs - ivt[ i ] );
787 if ( ivn[ i ] < 0 ) {
788 ivn[ i ] = ( numNormals - ivn[ i ] );
791 /* validate indices */
792 /* - commented out. index range checks will trigger
793 if (iv [ i ] < 1) iv [ i ] = 1;
794 if (ivt[ i ] < 1) ivt[ i ] = 1;
795 if (ivn[ i ] < 1) ivn[ i ] = 1;
797 /* set vertex origin */
799 /* check vertex index range */
800 if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
801 _obj_error_return( "Vertex index out of range" );
804 /* get vertex data */
805 verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
806 verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
807 verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
809 /* set vertex normal */
811 /* check normal index range */
812 if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
813 _obj_error_return( "Normal index out of range" );
816 /* get normal data */
817 normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
818 normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
819 normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
821 /* set texture coordinate */
823 /* check uv index range */
824 if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
825 _obj_error_return( "UV coord index out of range" );
828 /* get uv coord data */
829 coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
830 coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
831 coords[ i ][ 1 ] = -coords[ i ][ 1 ];
833 #ifdef DEBUG_PM_OBJ_EX
834 printf( "(%4d",iv[ i ] );
836 printf( " %4d",ivt[ i ] );
839 printf( " %4d",ivn[ i ] );
844 #ifdef DEBUG_PM_OBJ_EX
847 /* now that we have extracted all the indices and have */
848 /* read the actual data we need to assign all the crap */
849 /* to our current pico surface */
856 /* assign all surface information */
857 for ( i = 0; i < max; i++ )
859 /*if( has_v )*/ PicoSetSurfaceXYZ( curSurface, ( curVertex + i ), verts [ i ] );
860 /*if( has_vt )*/ PicoSetSurfaceST( curSurface,0,( curVertex + i ), coords [ i ] );
861 /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, ( curVertex + i ), normals[ i ] );
863 /* add our triangle (A B C) */
864 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
865 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 1 ) );
866 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 2 ) );
869 /* if we don't have a simple triangle, but a quad... */
871 /* we have to add another triangle (2nd half of quad which is A C D) */
872 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
873 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 2 ) );
874 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 3 ) );
877 /* increase vertex count */
881 else if ( !_pico_stricmp( p->token,"usemtl" ) ) {
882 picoShader_t *shader;
885 /* get material name */
886 name = _pico_parse( p,0 );
888 if ( curFace != 0 || curSurface == NULL ) {
889 _pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ %s, line %d.",model->fileName,p->curLine );
890 AUTO_GROUPNAME( autoGroupNameBuf );
891 NEW_SURFACE( autoGroupNameBuf );
894 /* validate material name */
895 if ( name == NULL || !strlen( name ) ) {
896 _pico_printf( PICO_ERROR,"Missing material name in OBJ %s, line %d.",model->fileName,p->curLine );
900 shader = PicoFindShader( model, name, 1 );
901 if ( shader == NULL ) {
902 _pico_printf( PICO_WARNING, "Undefined material name \"%s\" in OBJ, line %d. Making a default shader.", name, p->curLine );
904 /* create a new pico shader */
905 shader = PicoNewShader( model );
906 if ( shader != NULL ) {
907 PicoSetShaderName( shader,name );
908 PicoSetShaderMapName( shader,name );
909 PicoSetSurfaceShader( curSurface, shader );
914 PicoSetSurfaceShader( curSurface, shader );
918 /* skip unparsed rest of line and continue */
919 _pico_parse_skip_rest( p );
922 /* free memory used by temporary vertexdata */
923 FreeObjVertexData( vertexData );
925 /* return allocated pico model */
930 /* pico file format module definition */
931 const picoModule_t picoModuleOBJ =
933 "0.6-b", /* module version string */
934 "Wavefront ASCII", /* module display name */
935 "seaw0lf", /* author's name */
936 "2002 seaw0lf", /* module copyright */
938 "obj",NULL,NULL,NULL /* default extensions to use */
940 _obj_canload, /* validation routine */
941 _obj_load, /* load routine */
942 NULL, /* save validation routine */
943 NULL /* save routine */