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"
38 /* disable warnings */
40 #pragma warning( disable:4100 ) /* unref param */
44 * - '_obj_load' code crashes in a weird way after
45 * '_obj_mtl_load' for a few .mtl files
46 * - process 'mtllib' rather than using <model>.mtl
47 * - handle 'usemtl' statements
49 /* uncomment when debugging this module */
50 /* #define DEBUG_PM_OBJ */
51 /* #define DEBUG_PM_OBJ_EX */
53 /* this holds temporary vertex data read by parser */
54 typedef struct SObjVertexData
56 picoVec3_t v; /* geometric vertices */
57 picoVec2_t vt; /* texture vertices */
58 picoVec3_t vn; /* vertex normals (optional) */
63 * validates a wavefront obj model file.
65 static int _obj_canload( PM_PARAMS_CANLOAD ){
68 /* check data length */
70 return PICO_PMV_ERROR_SIZE;
73 /* first check file extension. we have to do this for objs */
74 /* cause there is no good way to identify the contents */
75 if ( _pico_stristr( fileName,".obj" ) != NULL ||
76 _pico_stristr( fileName,".wf" ) != NULL ) {
79 /* if the extension check failed we parse through the first */
80 /* few lines in file and look for common keywords often */
81 /* appearing at the beginning of wavefront objects */
83 /* alllocate a new pico parser */
84 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
86 return PICO_PMV_ERROR_MEMORY;
89 /* parse obj head line by line for type check */
92 /* get first token on line */
93 if ( _pico_parse_first( p ) == NULL ) {
97 /* we only parse the first few lines, say 80 */
98 if ( p->curLine > 80 ) {
102 /* skip empty lines */
103 if ( p->token == NULL || !strlen( p->token ) ) {
107 /* material library keywords are teh good */
108 if ( !_pico_stricmp( p->token,"usemtl" ) ||
109 !_pico_stricmp( p->token,"mtllib" ) ||
110 !_pico_stricmp( p->token,"g" ) ||
111 !_pico_stricmp( p->token,"v" ) ) { /* v,g bit fishy, but uh... */
112 /* free the pico parser thing */
113 _pico_free_parser( p );
115 /* seems to be a valid wavefront obj */
118 /* skip rest of line */
119 _pico_parse_skip_rest( p );
121 /* free the pico parser thing */
122 _pico_free_parser( p );
124 /* doesn't really look like an obj to us */
125 return PICO_PMV_ERROR;
128 /* SizeObjVertexData:
129 * This pretty piece of 'alloc ahead' code dynamically
130 * allocates - and reallocates as soon as required -
131 * my vertex data array in even steps.
133 const int SIZE_OBJ_STEP = 4096;
135 static TObjVertexData *SizeObjVertexData(
136 TObjVertexData *vertexData, int reqEntries,
137 int *entries, int *allocated ){
141 if ( reqEntries < 1 ) {
144 if ( entries == NULL || allocated == NULL ) {
145 return NULL; /* must have */
148 /* no need to grow yet */
149 if ( vertexData && ( reqEntries < *allocated ) ) {
150 *entries = reqEntries;
153 /* given vertex data ptr not allocated yet */
154 if ( vertexData == NULL ) {
155 /* how many entries to allocate */
156 newAllocated = ( reqEntries > SIZE_OBJ_STEP ) ?
157 reqEntries : SIZE_OBJ_STEP;
159 /* throw out an extended debug message */
160 #ifdef DEBUG_PM_OBJ_EX
161 printf( "SizeObjVertexData: allocate (%d entries)\n",
164 /* first time allocation */
165 vertexData = (TObjVertexData *)
166 _pico_alloc( sizeof( TObjVertexData ) * newAllocated );
168 /* allocation failed */
169 if ( vertexData == NULL ) {
173 /* allocation succeeded */
174 *allocated = newAllocated;
175 *entries = reqEntries;
178 /* given vertex data ptr needs to be resized */
179 if ( reqEntries == *allocated ) {
180 newAllocated = ( *allocated + SIZE_OBJ_STEP );
182 /* throw out an extended debug message */
183 #ifdef DEBUG_PM_OBJ_EX
184 printf( "SizeObjVertexData: reallocate (%d entries)\n",
187 /* try to reallocate */
188 vertexData = (TObjVertexData *)
189 _pico_realloc( (void *)&vertexData,
190 sizeof( TObjVertexData ) * ( *allocated ),
191 sizeof( TObjVertexData ) * ( newAllocated ) );
193 /* reallocation failed */
194 if ( vertexData == NULL ) {
198 /* reallocation succeeded */
199 *allocated = newAllocated;
200 *entries = reqEntries;
203 /* we're b0rked when we reach this */
207 static void FreeObjVertexData( TObjVertexData *vertexData ){
208 if ( vertexData != NULL ) {
209 free( (TObjVertexData *)vertexData );
213 static int _obj_mtl_load( picoModel_t *model ){
214 picoShader_t *curShader = NULL;
216 picoByte_t *mtlBuffer;
221 if ( model == NULL || model->fileName == NULL ) {
225 /* skip if we have a zero length model file name */
226 if ( !strlen( model->fileName ) ) {
231 #define _obj_mtl_error_return \
233 _pico_free_parser( p ); \
234 _pico_free_file( mtlBuffer ); \
235 _pico_free( fileName ); \
238 /* alloc copy of model file name */
239 fileName = _pico_clone_alloc( model->fileName );
240 if ( fileName == NULL ) {
244 /* change extension of model file to .mtl */
245 _pico_setfext( fileName, "mtl" );
247 /* load .mtl file contents */
248 _pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
251 if ( mtlBufSize == 0 ) {
252 return 1; /* file is empty: no error */
254 if ( mtlBufSize < 0 ) {
255 return 0; /* load failed: error */
258 /* create a new pico parser */
259 p = _pico_new_parser( mtlBuffer, mtlBufSize );
261 _obj_mtl_error_return;
264 /* doo teh .mtl parse */
267 /* get next token in material file */
268 if ( _pico_parse( p,1 ) == NULL ) {
273 /* skip empty lines */
274 if ( p->token == NULL || !strlen( p->token ) ) {
278 /* skip comment lines */
279 if ( p->token[0] == '#' ) {
280 _pico_parse_skip_rest( p );
284 if ( !_pico_stricmp( p->token,"newmtl" ) ) {
285 picoShader_t *shader;
288 /* get material name */
289 name = _pico_parse( p,0 );
291 /* validate material name */
292 if ( name == NULL || !strlen( name ) ) {
293 _pico_printf( PICO_ERROR,"Missing material name in MTL, line %d.",p->curLine );
294 _obj_mtl_error_return;
296 /* create a new pico shader */
297 shader = PicoNewShader( model );
298 if ( shader == NULL ) {
299 _obj_mtl_error_return;
302 /* set shader name */
303 PicoSetShaderName( shader,name );
305 /* assign pointer to current shader */
308 /* diffuse map name */
309 else if ( !_pico_stricmp( p->token,"map_kd" ) ) {
311 picoShader_t *shader;
313 /* pointer to current shader must be valid */
314 if ( curShader == NULL ) {
315 _obj_mtl_error_return;
318 /* get material's diffuse map name */
319 mapName = _pico_parse( p,0 );
321 /* validate map name */
322 if ( mapName == NULL || !strlen( mapName ) ) {
323 _pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine );
324 _obj_mtl_error_return;
326 /* create a new pico shader */
327 shader = PicoNewShader( model );
328 if ( shader == NULL ) {
329 _obj_mtl_error_return;
331 /* set shader map name */
332 PicoSetShaderMapName( shader,mapName );
334 /* dissolve factor (pseudo transparency 0..1) */
335 /* where 0 means 100% transparent and 1 means opaque */
336 else if ( !_pico_stricmp( p->token,"d" ) ) {
341 /* get dissolve factor */
342 if ( !_pico_parse_float( p,&value ) ) {
343 _obj_mtl_error_return;
346 /* set shader transparency */
347 PicoSetShaderTransparency( curShader,value );
349 /* get shader's diffuse color */
350 diffuse = PicoGetShaderDiffuseColor( curShader );
352 /* set diffuse alpha to transparency */
353 diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
355 /* set shader's new diffuse color */
356 PicoSetShaderDiffuseColor( curShader,diffuse );
358 /* shininess (phong specular component) */
359 else if ( !_pico_stricmp( p->token,"ns" ) ) {
361 * - well, this is some major obj spec fuckup once again. some
362 * apps store this in 0..1 range, others use 0..100 range,
363 * even others use 0..2048 range, and again others use the
364 * range 0..128, some even use 0..1000, 0..200, 400..700,
365 * honestly, what's up with the 3d app coders? happens when
366 * you smoke too much weed i guess. -sea
370 /* pointer to current shader must be valid */
371 if ( curShader == NULL ) {
372 _obj_mtl_error_return;
375 /* get totally screwed up shininess (a random value in fact ;) */
376 if ( !_pico_parse_float( p,&value ) ) {
377 _obj_mtl_error_return;
380 /* okay, there is no way to set this correctly, so we simply */
381 /* try to guess a few ranges (most common ones i have seen) */
383 /* assume 0..2048 range */
384 if ( value > 1000 ) {
385 value = 128.0 * ( value / 2048.0 );
387 /* assume 0..1000 range */
388 else if ( value > 200 ) {
389 value = 128.0 * ( value / 1000.0 );
391 /* assume 0..200 range */
392 else if ( value > 100 ) {
393 value = 128.0 * ( value / 200.0 );
395 /* assume 0..100 range */
396 else if ( value > 1 ) {
397 value = 128.0 * ( value / 100.0 );
399 /* assume 0..1 range */
403 /* negative shininess is bad (yes, i have seen it...) */
408 /* set the pico shininess value in range 0..127 */
409 /* geez, .obj is such a mess... */
410 PicoSetShaderShininess( curShader,value );
412 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
413 else if ( !_pico_stricmp( p->token,"ka" ) ) {
417 /* pointer to current shader must be valid */
418 if ( curShader == NULL ) {
419 _obj_mtl_error_return;
422 /* get color vector */
423 if ( !_pico_parse_vec( p,v ) ) {
424 _obj_mtl_error_return;
427 /* scale to byte range */
428 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
429 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
430 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
431 color[ 3 ] = (picoByte_t)( 255 );
433 /* set ambient color */
434 PicoSetShaderAmbientColor( curShader,color );
437 else if ( !_pico_stricmp( p->token,"kd" ) ) {
441 /* pointer to current shader must be valid */
442 if ( curShader == NULL ) {
443 _obj_mtl_error_return;
446 /* get color vector */
447 if ( !_pico_parse_vec( p,v ) ) {
448 _obj_mtl_error_return;
451 /* scale to byte range */
452 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
453 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
454 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
455 color[ 3 ] = (picoByte_t)( 255 );
457 /* set diffuse color */
458 PicoSetShaderDiffuseColor( curShader,color );
461 else if ( !_pico_stricmp( p->token,"ks" ) ) {
465 /* pointer to current shader must be valid */
466 if ( curShader == NULL ) {
467 _obj_mtl_error_return;
470 /* get color vector */
471 if ( !_pico_parse_vec( p,v ) ) {
472 _obj_mtl_error_return;
475 /* scale to byte range */
476 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
477 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
478 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
479 color[ 3 ] = (picoByte_t)( 255 );
481 /* set specular color */
482 PicoSetShaderSpecularColor( curShader,color );
485 /* skip rest of line */
486 _pico_parse_skip_rest( p );
489 /* free parser, file buffer, and file name */
490 _pico_free_parser( p );
491 _pico_free_file( mtlBuffer );
492 _pico_free( fileName );
494 /* return with success */
499 * loads a wavefront obj model file.
501 static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
502 TObjVertexData *vertexData = NULL;
504 picoSurface_t *curSurface = NULL;
514 int autoGroupNumber = 0;
515 char autoGroupNameBuf[64];
517 #define AUTO_GROUPNAME( namebuf ) \
518 sprintf( namebuf, "__autogroup_%d", autoGroupNumber++ )
519 #define NEW_SURFACE( name ) \
521 picoSurface_t *newSurface; \
522 /* allocate a pico surface */ \
523 newSurface = PicoNewSurface( model ); \
524 if ( newSurface == NULL ) { \
525 _obj_error_return( "Error allocating surface" ); } \
526 /* reset face 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, line %d.",m,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; we don't handle the result */
567 _obj_mtl_load( model );
570 /* parse obj line by line */
573 /* get first token on line */
574 if ( _pico_parse_first( p ) == NULL ) {
578 /* skip empty lines */
579 if ( p->token == NULL || !strlen( p->token ) ) {
583 /* skip comment lines */
584 if ( p->token[0] == '#' ) {
585 _pico_parse_skip_rest( p );
589 if ( !_pico_stricmp( p->token,"v" ) ) {
590 TObjVertexData *data;
593 vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
594 if ( vertexData == NULL ) {
595 _obj_error_return( "Realloc of vertex data failed (1)" );
598 data = &vertexData[ numVerts++ ];
600 /* get and copy vertex */
601 if ( !_pico_parse_vec( p,v ) ) {
602 _obj_error_return( "Vertex parse error" );
605 _pico_copy_vec( v,data->v );
607 #ifdef DEBUG_PM_OBJ_EX
608 printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
612 else if ( !_pico_stricmp( p->token,"vt" ) ) {
613 TObjVertexData *data;
616 vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
617 if ( vertexData == NULL ) {
618 _obj_error_return( "Realloc of vertex data failed (2)" );
621 data = &vertexData[ numUVs++ ];
623 /* get and copy tex coord */
624 if ( !_pico_parse_vec2( p,coord ) ) {
625 _obj_error_return( "UV coord parse error" );
628 _pico_copy_vec2( coord,data->vt );
630 #ifdef DEBUG_PM_OBJ_EX
631 printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
635 else if ( !_pico_stricmp( p->token,"vn" ) ) {
636 TObjVertexData *data;
639 vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
640 if ( vertexData == NULL ) {
641 _obj_error_return( "Realloc of vertex data failed (3)" );
644 data = &vertexData[ numNormals++ ];
646 /* get and copy vertex normal */
647 if ( !_pico_parse_vec( p,n ) ) {
648 _obj_error_return( "Vertex normal parse error" );
651 _pico_copy_vec( n,data->vn );
653 #ifdef DEBUG_PM_OBJ_EX
654 printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
657 /* new group (for us this means a new surface) */
658 else if ( !_pico_stricmp( p->token,"g" ) ) {
661 /* get first group name (ignore 2nd,3rd,etc.) */
662 groupName = _pico_parse( p,0 );
663 if ( groupName == NULL || !strlen( groupName ) ) {
664 /* some obj exporters feel like they don't need to */
665 /* supply a group name. so we gotta handle it here */
667 strcpy( p->token,"default" );
668 groupName = p->token;
670 _obj_error_return( "Invalid or missing group name" );
674 if ( curFace == 0 && curSurface != NULL ) {
675 PicoSetSurfaceName( curSurface,groupName );
679 NEW_SURFACE( groupName );
682 #ifdef DEBUG_PM_OBJ_EX
683 printf( "Group: '%s'\n",groupName );
686 /* face (oh jesus, hopefully this will do the job right ;) */
687 else if ( !_pico_stricmp( p->token,"f" ) ) {
688 /* okay, this is a mess. some 3d apps seem to try being unique, */
689 /* hello cinema4d & 3d exploration, feel good today?, and save */
690 /* this crap in tons of different formats. gah, those screwed */
691 /* coders. tho the wavefront obj standard defines exactly two */
692 /* ways of storing face information. so, i really won't support */
693 /* such stupid extravaganza here! */
695 picoVec3_t verts [ 4 ];
696 picoVec3_t normals[ 4 ];
697 picoVec2_t coords [ 4 ];
700 int ivt[ 4 ], has_vt = 0;
701 int ivn[ 4 ], has_vn = 0;
707 if ( curSurface == NULL ) {
708 _pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ, line %d.",p->curLine );
709 AUTO_GROUPNAME( autoGroupNameBuf );
710 NEW_SURFACE( autoGroupNameBuf );
713 /* group defs *must* come before faces */
714 if ( curSurface == NULL ) {
715 _obj_error_return( "No group defined for faces" );
718 #ifdef DEBUG_PM_OBJ_EX
721 /* read vertex/uv/normal indices for the first three face */
722 /* vertices (cause we only support triangles) into 'i*[]' */
723 /* store the actual vertex/uv/normal data in three arrays */
724 /* called 'verts','coords' and 'normals'. */
725 for ( i = 0; i < 4; i++ )
729 /* get next vertex index string (different */
730 /* formats are handled below) */
731 str = _pico_parse( p,0 );
733 /* just break for quads */
738 /* error otherwise */
739 _obj_error_return( "Face parse error" );
741 /* if this is the fourth index string we're */
742 /* parsing we assume that we have a quad */
747 /* get slash count once */
749 slashcount = _pico_strchcount( str,'/' );
750 doubleslash = strstr( str,"//" ) != NULL;
752 /* handle format 'v//vn' */
753 if ( doubleslash && ( slashcount == 2 ) ) {
755 sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
757 /* handle format 'v/vt/vn' */
758 else if ( !doubleslash && ( slashcount == 2 ) ) {
759 has_v = has_vt = has_vn = 1;
760 sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
762 /* handle format 'v/vt' (non-standard fuckage) */
763 else if ( !doubleslash && ( slashcount == 1 ) ) {
765 sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
767 /* else assume face format 'v' */
768 /* (must have been invented by some bored granny) */
770 /* get single vertex index */
772 iv[ i ] = atoi( str );
774 /* either invalid face format or out of range */
775 if ( iv[ i ] == 0 ) {
776 _obj_error_return( "Invalid face format" );
779 /* fix useless back references */
780 /* todo: check if this works as it is supposed to */
782 /* assign new indices */
783 if ( iv [ i ] < 0 ) {
784 iv [ i ] = ( numVerts - iv [ i ] );
786 if ( ivt[ i ] < 0 ) {
787 ivt[ i ] = ( numUVs - ivt[ i ] );
789 if ( ivn[ i ] < 0 ) {
790 ivn[ i ] = ( numNormals - ivn[ i ] );
793 /* validate indices */
794 /* - commented out. index range checks will trigger
795 if (iv [ i ] < 1) iv [ i ] = 1;
796 if (ivt[ i ] < 1) ivt[ i ] = 1;
797 if (ivn[ i ] < 1) ivn[ i ] = 1;
799 /* set vertex origin */
801 /* check vertex index range */
802 if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
803 _obj_error_return( "Vertex index out of range" );
806 /* get vertex data */
807 verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
808 verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
809 verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
811 /* set vertex normal */
813 /* check normal index range */
814 if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
815 _obj_error_return( "Normal index out of range" );
818 /* get normal data */
819 normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
820 normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
821 normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
823 /* set texture coordinate */
825 /* check uv index range */
826 if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
827 _obj_error_return( "UV coord index out of range" );
830 /* get uv coord data */
831 coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
832 coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
833 coords[ i ][ 1 ] = -coords[ i ][ 1 ];
835 #ifdef DEBUG_PM_OBJ_EX
836 printf( "(%4d",iv[ i ] );
838 printf( " %4d",ivt[ i ] );
841 printf( " %4d",ivn[ i ] );
846 #ifdef DEBUG_PM_OBJ_EX
849 /* now that we have extracted all the indices and have */
850 /* read the actual data we need to assign all the crap */
851 /* to our current pico surface */
858 /* assign all surface information */
859 for ( i = 0; i < max; i++ )
861 /*if( has_v )*/ PicoSetSurfaceXYZ( curSurface, ( curVertex + i ), verts [ i ] );
862 /*if( has_vt )*/ PicoSetSurfaceST( curSurface,0,( curVertex + i ), coords [ i ] );
863 /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, ( curVertex + i ), normals[ i ] );
865 /* add our triangle (A B C) */
866 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
867 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 1 ) );
868 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 2 ) );
871 /* if we don't have a simple triangle, but a quad... */
873 /* we have to add another triangle (2nd half of quad which is A C D) */
874 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
875 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 2 ) );
876 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 3 ) );
879 /* increase vertex count */
883 else if ( !_pico_stricmp( p->token,"usemtl" ) ) {
884 picoShader_t *shader;
887 /* get material name */
888 name = _pico_parse( p,0 );
890 if ( curFace != 0 || curSurface == NULL ) {
891 _pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ, line %d.",p->curLine );
892 AUTO_GROUPNAME( autoGroupNameBuf );
893 NEW_SURFACE( autoGroupNameBuf );
896 /* validate material name */
897 if ( name == NULL || !strlen( name ) ) {
898 _pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine );
902 shader = PicoFindShader( model, name, 1 );
903 if ( shader == NULL ) {
904 _pico_printf( PICO_WARNING,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine );
906 /* create a new pico shader */
907 shader = PicoNewShader( model );
908 if ( shader != NULL ) {
909 PicoSetShaderName( shader,name );
910 PicoSetShaderMapName( shader,name );
911 PicoSetSurfaceShader( curSurface, shader );
916 PicoSetSurfaceShader( curSurface, shader );
920 /* skip unparsed rest of line and continue */
921 _pico_parse_skip_rest( p );
923 /* free memory used by temporary vertexdata */
924 FreeObjVertexData( vertexData );
926 /* return allocated pico model */
931 /* pico file format module definition */
932 const picoModule_t picoModuleOBJ =
934 "0.6-b", /* module version string */
935 "Wavefront ASCII", /* module display name */
936 "seaw0lf", /* author's name */
937 "2002 seaw0lf", /* module copyright */
939 "obj",NULL,NULL,NULL /* default extensions to use */
941 _obj_canload, /* validation routine */
942 _obj_load, /* load routine */
943 NULL, /* save validation routine */
944 NULL /* save routine */