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 ){
73 /* check data length */
75 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 ) {
84 /* if the extension check failed we parse through the first */
85 /* few lines in file and look for common keywords often */
86 /* appearing at the beginning of wavefront objects */
88 /* alllocate a new pico parser */
89 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
91 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 ) {
102 /* we only parse the first few lines, say 80 */
103 if ( p->curLine > 80 ) {
107 /* skip empty lines */
108 if ( p->token == NULL || !strlen( p->token ) ) {
112 /* material library keywords are teh good */
113 if ( !_pico_stricmp( p->token,"usemtl" ) ||
114 !_pico_stricmp( p->token,"mtllib" ) ||
115 !_pico_stricmp( p->token,"g" ) ||
116 !_pico_stricmp( p->token,"v" ) ) { /* v,g bit fishy, but uh... */
117 /* free the pico parser thing */
118 _pico_free_parser( p );
120 /* seems to be a valid wavefront obj */
123 /* skip rest of line */
124 _pico_parse_skip_rest( p );
126 /* free the pico parser thing */
127 _pico_free_parser( p );
129 /* doesn't really look like an obj to us */
130 return PICO_PMV_ERROR;
133 /* SizeObjVertexData:
134 * This pretty piece of 'alloc ahead' code dynamically
135 * allocates - and reallocates as soon as required -
136 * my vertex data array in even steps.
138 #define SIZE_OBJ_STEP 4096
140 static TObjVertexData *SizeObjVertexData(
141 TObjVertexData *vertexData, int reqEntries,
142 int *entries, int *allocated ){
146 if ( reqEntries < 1 ) {
149 if ( entries == NULL || allocated == NULL ) {
150 return NULL; /* must have */
153 /* no need to grow yet */
154 if ( vertexData && ( reqEntries < *allocated ) ) {
155 *entries = reqEntries;
158 /* given vertex data ptr not allocated yet */
159 if ( vertexData == NULL ) {
160 /* how many entries to allocate */
161 newAllocated = ( reqEntries > SIZE_OBJ_STEP ) ?
162 reqEntries : SIZE_OBJ_STEP;
164 /* throw out an extended debug message */
165 #ifdef DEBUG_PM_OBJ_EX
166 printf( "SizeObjVertexData: allocate (%d entries)\n",
169 /* first time allocation */
170 vertexData = (TObjVertexData *)
171 _pico_alloc( sizeof( TObjVertexData ) * newAllocated );
173 /* allocation failed */
174 if ( vertexData == NULL ) {
178 /* allocation succeeded */
179 *allocated = newAllocated;
180 *entries = reqEntries;
183 /* given vertex data ptr needs to be resized */
184 if ( reqEntries == *allocated ) {
185 newAllocated = ( *allocated + SIZE_OBJ_STEP );
187 /* throw out an extended debug message */
188 #ifdef DEBUG_PM_OBJ_EX
189 printf( "SizeObjVertexData: reallocate (%d entries)\n",
192 /* try to reallocate */
193 vertexData = (TObjVertexData *)
194 _pico_realloc( (void *)&vertexData,
195 sizeof( TObjVertexData ) * ( *allocated ),
196 sizeof( TObjVertexData ) * ( newAllocated ) );
198 /* reallocation failed */
199 if ( vertexData == NULL ) {
203 /* reallocation succeeded */
204 *allocated = newAllocated;
205 *entries = reqEntries;
208 /* we're b0rked when we reach this */
212 static void FreeObjVertexData( TObjVertexData *vertexData ){
213 if ( vertexData != NULL ) {
214 free( (TObjVertexData *)vertexData );
218 static int _obj_mtl_load( picoModel_t *model ){
219 picoShader_t *curShader = NULL;
221 picoByte_t *mtlBuffer;
226 if ( model == NULL || model->fileName == NULL ) {
230 /* skip if we have a zero length model file name */
231 if ( !strlen( model->fileName ) ) {
236 #define _obj_mtl_error_return \
238 _pico_free_parser( p ); \
239 _pico_free_file( mtlBuffer ); \
240 _pico_free( fileName ); \
243 /* alloc copy of model file name */
244 fileName = _pico_clone_alloc( model->fileName );
245 if ( fileName == NULL ) {
249 /* change extension of model file to .mtl */
250 _pico_setfext( fileName, "mtl" );
252 /* load .mtl file contents */
253 _pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
256 if ( mtlBufSize == 0 ) {
257 return 1; /* file is empty: no error */
259 if ( mtlBufSize < 0 ) {
260 return 0; /* load failed: error */
263 /* create a new pico parser */
264 p = _pico_new_parser( mtlBuffer, mtlBufSize );
266 _obj_mtl_error_return;
269 /* doo teh .mtl parse */
272 /* get next token in material file */
273 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, line %d.",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" ) ) {
316 picoShader_t *shader;
318 /* pointer to current shader must be valid */
319 if ( curShader == NULL ) {
320 _obj_mtl_error_return;
323 /* get material's diffuse map name */
324 mapName = _pico_parse( p,0 );
326 /* validate map name */
327 if ( mapName == NULL || !strlen( mapName ) ) {
328 _pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine );
329 _obj_mtl_error_return;
331 /* create a new pico shader */
332 shader = PicoNewShader( model );
333 if ( shader == NULL ) {
334 _obj_mtl_error_return;
336 /* set shader map name */
337 PicoSetShaderMapName( shader,mapName );
339 /* dissolve factor (pseudo transparency 0..1) */
340 /* where 0 means 100% transparent and 1 means opaque */
341 else if ( !_pico_stricmp( p->token,"d" ) ) {
346 /* get dissolve factor */
347 if ( !_pico_parse_float( p,&value ) ) {
348 _obj_mtl_error_return;
351 /* set shader transparency */
352 PicoSetShaderTransparency( curShader,value );
354 /* get shader's diffuse color */
355 diffuse = PicoGetShaderDiffuseColor( curShader );
357 /* set diffuse alpha to transparency */
358 diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
360 /* set shader's new diffuse color */
361 PicoSetShaderDiffuseColor( curShader,diffuse );
363 /* shininess (phong specular component) */
364 else if ( !_pico_stricmp( p->token,"ns" ) ) {
366 * - well, this is some major obj spec fuckup once again. some
367 * apps store this in 0..1 range, others use 0..100 range,
368 * even others use 0..2048 range, and again others use the
369 * range 0..128, some even use 0..1000, 0..200, 400..700,
370 * honestly, what's up with the 3d app coders? happens when
371 * you smoke too much weed i guess. -sea
375 /* pointer to current shader must be valid */
376 if ( curShader == NULL ) {
377 _obj_mtl_error_return;
380 /* get totally screwed up shininess (a random value in fact ;) */
381 if ( !_pico_parse_float( p,&value ) ) {
382 _obj_mtl_error_return;
385 /* okay, there is no way to set this correctly, so we simply */
386 /* try to guess a few ranges (most common ones i have seen) */
388 /* assume 0..2048 range */
389 if ( value > 1000 ) {
390 value = 128.0 * ( value / 2048.0 );
392 /* assume 0..1000 range */
393 else if ( value > 200 ) {
394 value = 128.0 * ( value / 1000.0 );
396 /* assume 0..200 range */
397 else if ( value > 100 ) {
398 value = 128.0 * ( value / 200.0 );
400 /* assume 0..100 range */
401 else if ( value > 1 ) {
402 value = 128.0 * ( value / 100.0 );
404 /* assume 0..1 range */
408 /* negative shininess is bad (yes, i have seen it...) */
413 /* set the pico shininess value in range 0..127 */
414 /* geez, .obj is such a mess... */
415 PicoSetShaderShininess( curShader,value );
417 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
418 else if ( !_pico_stricmp( p->token,"ka" ) ) {
422 /* pointer to current shader must be valid */
423 if ( curShader == NULL ) {
424 _obj_mtl_error_return;
427 /* get color vector */
428 if ( !_pico_parse_vec( p,v ) ) {
429 _obj_mtl_error_return;
432 /* scale to byte range */
433 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
434 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
435 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
436 color[ 3 ] = (picoByte_t)( 255 );
438 /* set ambient color */
439 PicoSetShaderAmbientColor( curShader,color );
442 else if ( !_pico_stricmp( p->token,"kd" ) ) {
446 /* pointer to current shader must be valid */
447 if ( curShader == NULL ) {
448 _obj_mtl_error_return;
451 /* get color vector */
452 if ( !_pico_parse_vec( p,v ) ) {
453 _obj_mtl_error_return;
456 /* scale to byte range */
457 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
458 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
459 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
460 color[ 3 ] = (picoByte_t)( 255 );
462 /* set diffuse color */
463 PicoSetShaderDiffuseColor( curShader,color );
466 else if ( !_pico_stricmp( p->token,"ks" ) ) {
470 /* pointer to current shader must be valid */
471 if ( curShader == NULL ) {
472 _obj_mtl_error_return;
475 /* get color vector */
476 if ( !_pico_parse_vec( p,v ) ) {
477 _obj_mtl_error_return;
480 /* scale to byte range */
481 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
482 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
483 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
484 color[ 3 ] = (picoByte_t)( 255 );
486 /* set specular color */
487 PicoSetShaderSpecularColor( curShader,color );
490 /* skip rest of line */
491 _pico_parse_skip_rest( p );
494 /* free parser, file buffer, and file name */
495 _pico_free_parser( p );
496 _pico_free_file( mtlBuffer );
497 _pico_free( fileName );
499 /* return with success */
504 * loads a wavefront obj model file.
506 static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
507 TObjVertexData *vertexData = NULL;
509 picoSurface_t *curSurface = NULL;
519 int autoGroupNumber = 0;
520 char autoGroupNameBuf[64];
522 #define AUTO_GROUPNAME( namebuf ) \
523 sprintf( namebuf, "__autogroup_%d", autoGroupNumber++ )
524 #define NEW_SURFACE( name ) \
526 picoSurface_t *newSurface; \
527 /* allocate a pico surface */ \
528 newSurface = PicoNewSurface( model ); \
529 if ( newSurface == NULL ) { \
530 _obj_error_return( "Error allocating surface" ); } \
531 /* reset face index for surface */ \
533 /* if we can, assign the previous shader to this surface */ \
534 if ( curSurface ) { \
535 PicoSetSurfaceShader( newSurface, curSurface->shader ); } \
536 /* set ptr to current surface */ \
537 curSurface = newSurface; \
538 /* we use triangle meshes */ \
539 PicoSetSurfaceType( newSurface,PICO_TRIANGLES ); \
540 /* set surface name */ \
541 PicoSetSurfaceName( newSurface,name ); \
545 #define _obj_error_return( m ) \
547 _pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine ); \
548 _pico_free_parser( p ); \
549 FreeObjVertexData( vertexData ); \
550 PicoFreeModel( model ); \
553 /* alllocate a new pico parser */
554 p = _pico_new_parser( (const picoByte_t *)buffer,bufSize );
559 /* create a new pico model */
560 model = PicoNewModel();
561 if ( model == NULL ) {
562 _pico_free_parser( p );
566 PicoSetModelFrameNum( model,frameNum );
567 PicoSetModelName( model,fileName );
568 PicoSetModelFileName( model,fileName );
570 /* try loading the materials; we don't handle the result */
572 _obj_mtl_load( model );
575 /* parse obj line by line */
578 /* get first token on line */
579 if ( _pico_parse_first( p ) == NULL ) {
583 /* skip empty lines */
584 if ( p->token == NULL || !strlen( p->token ) ) {
588 /* skip comment lines */
589 if ( p->token[0] == '#' ) {
590 _pico_parse_skip_rest( p );
594 if ( !_pico_stricmp( p->token,"v" ) ) {
595 TObjVertexData *data;
598 vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
599 if ( vertexData == NULL ) {
600 _obj_error_return( "Realloc of vertex data failed (1)" );
603 data = &vertexData[ numVerts++ ];
605 /* get and copy vertex */
606 if ( !_pico_parse_vec( p,v ) ) {
607 _obj_error_return( "Vertex parse error" );
610 _pico_copy_vec( v,data->v );
612 #ifdef DEBUG_PM_OBJ_EX
613 printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
617 else if ( !_pico_stricmp( p->token,"vt" ) ) {
618 TObjVertexData *data;
621 vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
622 if ( vertexData == NULL ) {
623 _obj_error_return( "Realloc of vertex data failed (2)" );
626 data = &vertexData[ numUVs++ ];
628 /* get and copy tex coord */
629 if ( !_pico_parse_vec2( p,coord ) ) {
630 _obj_error_return( "UV coord parse error" );
633 _pico_copy_vec2( coord,data->vt );
635 #ifdef DEBUG_PM_OBJ_EX
636 printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
640 else if ( !_pico_stricmp( p->token,"vn" ) ) {
641 TObjVertexData *data;
644 vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
645 if ( vertexData == NULL ) {
646 _obj_error_return( "Realloc of vertex data failed (3)" );
649 data = &vertexData[ numNormals++ ];
651 /* get and copy vertex normal */
652 if ( !_pico_parse_vec( p,n ) ) {
653 _obj_error_return( "Vertex normal parse error" );
656 _pico_copy_vec( n,data->vn );
658 #ifdef DEBUG_PM_OBJ_EX
659 printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
662 /* new group (for us this means a new surface) */
663 else if ( !_pico_stricmp( p->token,"g" ) ) {
666 /* get first group name (ignore 2nd,3rd,etc.) */
667 groupName = _pico_parse( p,0 );
668 if ( groupName == NULL || !strlen( groupName ) ) {
669 /* some obj exporters feel like they don't need to */
670 /* supply a group name. so we gotta handle it here */
672 strcpy( p->token,"default" );
673 groupName = p->token;
675 _obj_error_return( "Invalid or missing group name" );
679 if ( curFace == 0 && curSurface != NULL ) {
680 PicoSetSurfaceName( curSurface,groupName );
684 NEW_SURFACE( groupName );
687 #ifdef DEBUG_PM_OBJ_EX
688 printf( "Group: '%s'\n",groupName );
691 /* face (oh jesus, hopefully this will do the job right ;) */
692 else if ( !_pico_stricmp( p->token,"f" ) ) {
693 /* okay, this is a mess. some 3d apps seem to try being unique, */
694 /* hello cinema4d & 3d exploration, feel good today?, and save */
695 /* this crap in tons of different formats. gah, those screwed */
696 /* coders. tho the wavefront obj standard defines exactly two */
697 /* ways of storing face information. so, i really won't support */
698 /* such stupid extravaganza here! */
700 picoVec3_t verts [ 4 ];
701 picoVec3_t normals[ 4 ];
702 picoVec2_t coords [ 4 ];
705 int ivt[ 4 ], has_vt = 0;
706 int ivn[ 4 ], has_vn = 0;
712 if ( curSurface == NULL ) {
713 _pico_printf( PICO_WARNING,"No group defined for faces, so creating an autoSurface in OBJ, line %d.",p->curLine );
714 AUTO_GROUPNAME( autoGroupNameBuf );
715 NEW_SURFACE( autoGroupNameBuf );
718 /* group defs *must* come before faces */
719 if ( curSurface == NULL ) {
720 _obj_error_return( "No group defined for faces" );
723 #ifdef DEBUG_PM_OBJ_EX
726 /* read vertex/uv/normal indices for the first three face */
727 /* vertices (cause we only support triangles) into 'i*[]' */
728 /* store the actual vertex/uv/normal data in three arrays */
729 /* called 'verts','coords' and 'normals'. */
730 for ( i = 0; i < 4; i++ )
734 /* get next vertex index string (different */
735 /* formats are handled below) */
736 str = _pico_parse( p,0 );
738 /* just break for quads */
743 /* error otherwise */
744 _obj_error_return( "Face parse error" );
746 /* if this is the fourth index string we're */
747 /* parsing we assume that we have a quad */
752 /* get slash count once */
754 slashcount = _pico_strchcount( str,'/' );
755 doubleslash = strstr( str,"//" ) != NULL;
757 /* handle format 'v//vn' */
758 if ( doubleslash && ( slashcount == 2 ) ) {
760 sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
762 /* handle format 'v/vt/vn' */
763 else if ( !doubleslash && ( slashcount == 2 ) ) {
764 has_v = has_vt = has_vn = 1;
765 sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
767 /* handle format 'v/vt' (non-standard fuckage) */
768 else if ( !doubleslash && ( slashcount == 1 ) ) {
770 sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
772 /* else assume face format 'v' */
773 /* (must have been invented by some bored granny) */
775 /* get single vertex index */
777 iv[ i ] = atoi( str );
779 /* either invalid face format or out of range */
780 if ( iv[ i ] == 0 ) {
781 _obj_error_return( "Invalid face format" );
784 /* fix useless back references */
785 /* todo: check if this works as it is supposed to */
787 /* assign new indices */
788 if ( iv [ i ] < 0 ) {
789 iv [ i ] = ( numVerts - iv [ i ] );
791 if ( ivt[ i ] < 0 ) {
792 ivt[ i ] = ( numUVs - ivt[ i ] );
794 if ( ivn[ i ] < 0 ) {
795 ivn[ i ] = ( numNormals - ivn[ i ] );
798 /* validate indices */
799 /* - commented out. index range checks will trigger
800 if (iv [ i ] < 1) iv [ i ] = 1;
801 if (ivt[ i ] < 1) ivt[ i ] = 1;
802 if (ivn[ i ] < 1) ivn[ i ] = 1;
804 /* set vertex origin */
806 /* check vertex index range */
807 if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
808 _obj_error_return( "Vertex index out of range" );
811 /* get vertex data */
812 verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
813 verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
814 verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
816 /* set vertex normal */
818 /* check normal index range */
819 if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
820 _obj_error_return( "Normal index out of range" );
823 /* get normal data */
824 normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
825 normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
826 normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
828 /* set texture coordinate */
830 /* check uv index range */
831 if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
832 _obj_error_return( "UV coord index out of range" );
835 /* get uv coord data */
836 coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
837 coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
838 coords[ i ][ 1 ] = -coords[ i ][ 1 ];
840 #ifdef DEBUG_PM_OBJ_EX
841 printf( "(%4d",iv[ i ] );
843 printf( " %4d",ivt[ i ] );
846 printf( " %4d",ivn[ i ] );
851 #ifdef DEBUG_PM_OBJ_EX
854 /* now that we have extracted all the indices and have */
855 /* read the actual data we need to assign all the crap */
856 /* to our current pico surface */
863 /* assign all surface information */
864 for ( i = 0; i < max; i++ )
866 /*if( has_v )*/ PicoSetSurfaceXYZ( curSurface, ( curVertex + i ), verts [ i ] );
867 /*if( has_vt )*/ PicoSetSurfaceST( curSurface,0,( curVertex + i ), coords [ i ] );
868 /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, ( curVertex + i ), normals[ i ] );
870 /* add our triangle (A B C) */
871 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
872 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 1 ) );
873 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 2 ) );
876 /* if we don't have a simple triangle, but a quad... */
878 /* we have to add another triangle (2nd half of quad which is A C D) */
879 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
880 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 2 ) );
881 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 3 ) );
884 /* increase vertex count */
888 else if ( !_pico_stricmp( p->token,"usemtl" ) ) {
889 picoShader_t *shader;
892 /* get material name */
893 name = _pico_parse( p,0 );
895 if ( curFace != 0 || curSurface == NULL ) {
896 _pico_printf( PICO_WARNING,"No group defined for usemtl, so creating an autoSurface in OBJ, line %d.",p->curLine );
897 AUTO_GROUPNAME( autoGroupNameBuf );
898 NEW_SURFACE( autoGroupNameBuf );
901 /* validate material name */
902 if ( name == NULL || !strlen( name ) ) {
903 _pico_printf( PICO_ERROR,"Missing material name in OBJ, line %d.",p->curLine );
907 shader = PicoFindShader( model, name, 1 );
908 if ( shader == NULL ) {
909 _pico_printf( PICO_WARNING,"Undefined material name in OBJ, line %d. Making a default shader.",p->curLine );
911 /* create a new pico shader */
912 shader = PicoNewShader( model );
913 if ( shader != NULL ) {
914 PicoSetShaderName( shader,name );
915 PicoSetShaderMapName( shader,name );
916 PicoSetSurfaceShader( curSurface, shader );
921 PicoSetSurfaceShader( curSurface, shader );
925 /* skip unparsed rest of line and continue */
926 _pico_parse_skip_rest( p );
928 /* free memory used by temporary vertexdata */
929 FreeObjVertexData( vertexData );
931 /* return allocated pico model */
936 /* pico file format module definition */
937 const picoModule_t picoModuleOBJ =
939 "0.6-b", /* module version string */
940 "Wavefront ASCII", /* module display name */
941 "seaw0lf", /* author's name */
942 "2002 seaw0lf", /* module copyright */
944 "obj",NULL,NULL,NULL /* default extensions to use */
946 _obj_canload, /* validation routine */
947 _obj_load, /* load routine */
948 NULL, /* save validation routine */
949 NULL /* save routine */