]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/picomodel/pm_obj.c
uncrustify! now the code is only ugly on the *inside*
[xonotic/netradiant.git] / libs / picomodel / pm_obj.c
1 /* -----------------------------------------------------------------------------
2
3    PicoModel Library
4
5    Copyright (c) 2002, Randy Reddig & seaw0lf
6    All rights reserved.
7
8    Redistribution and use in source and binary forms, with or without modification,
9    are permitted provided that the following conditions are met:
10
11    Redistributions of source code must retain the above copyright notice, this list
12    of conditions and the following disclaimer.
13
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.
17
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.
21
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.
32
33    ----------------------------------------------------------------------------- */
34
35
36
37 /* marker */
38 #define PM_OBJ_C
39
40 /* dependencies */
41 #include "picointernal.h"
42
43 /* disable warnings */
44 #ifdef _WIN32
45 #pragma warning( disable:4100 )         /* unref param */
46 #endif
47
48 /* todo:
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
53  */
54 /* uncomment when debugging this module */
55 /* #define DEBUG_PM_OBJ */
56 /* #define DEBUG_PM_OBJ_EX */
57
58 /* this holds temporary vertex data read by parser */
59 typedef struct SObjVertexData
60 {
61         picoVec3_t v;           /* geometric vertices */
62         picoVec2_t vt;          /* texture vertices */
63         picoVec3_t vn;          /* vertex normals (optional) */
64 }
65 TObjVertexData;
66
67 /* _obj_canload:
68  *  validates a wavefront obj model file.
69  */
70 static int _obj_canload( PM_PARAMS_CANLOAD ){
71         picoParser_t *p;
72
73         /* check data length */
74         if ( bufSize < 30 ) {
75                 return PICO_PMV_ERROR_SIZE;
76         }
77
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 ) {
82                 return PICO_PMV_OK;
83         }
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 */
87
88         /* alllocate a new pico parser */
89         p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
90         if ( p == NULL ) {
91                 return PICO_PMV_ERROR_MEMORY;
92         }
93
94         /* parse obj head line by line for type check */
95         while ( 1 )
96         {
97                 /* get first token on line */
98                 if ( _pico_parse_first( p ) == NULL ) {
99                         break;
100                 }
101
102                 /* we only parse the first few lines, say 80 */
103                 if ( p->curLine > 80 ) {
104                         break;
105                 }
106
107                 /* skip empty lines */
108                 if ( p->token == NULL || !strlen( p->token ) ) {
109                         continue;
110                 }
111
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 );
119
120                         /* seems to be a valid wavefront obj */
121                         return PICO_PMV_OK;
122                 }
123                 /* skip rest of line */
124                 _pico_parse_skip_rest( p );
125         }
126         /* free the pico parser thing */
127         _pico_free_parser( p );
128
129         /* doesn't really look like an obj to us */
130         return PICO_PMV_ERROR;
131 }
132
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.
137  */
138 #define SIZE_OBJ_STEP  4096
139
140 static TObjVertexData *SizeObjVertexData(
141         TObjVertexData *vertexData, int reqEntries,
142         int *entries, int *allocated ){
143         int newAllocated;
144
145         /* sanity checks */
146         if ( reqEntries < 1 ) {
147                 return NULL;
148         }
149         if ( entries == NULL || allocated == NULL ) {
150                 return NULL; /* must have */
151
152         }
153         /* no need to grow yet */
154         if ( vertexData && ( reqEntries < *allocated ) ) {
155                 *entries = reqEntries;
156                 return vertexData;
157         }
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;
163
164                 /* throw out an extended debug message */
165 #ifdef DEBUG_PM_OBJ_EX
166                 printf( "SizeObjVertexData: allocate (%d entries)\n",
167                                 newAllocated );
168 #endif
169                 /* first time allocation */
170                 vertexData = (TObjVertexData *)
171                                          _pico_alloc( sizeof( TObjVertexData ) * newAllocated );
172
173                 /* allocation failed */
174                 if ( vertexData == NULL ) {
175                         return NULL;
176                 }
177
178                 /* allocation succeeded */
179                 *allocated = newAllocated;
180                 *entries   = reqEntries;
181                 return vertexData;
182         }
183         /* given vertex data ptr needs to be resized */
184         if ( reqEntries == *allocated ) {
185                 newAllocated = ( *allocated + SIZE_OBJ_STEP );
186
187                 /* throw out an extended debug message */
188 #ifdef DEBUG_PM_OBJ_EX
189                 printf( "SizeObjVertexData: reallocate (%d entries)\n",
190                                 newAllocated );
191 #endif
192                 /* try to reallocate */
193                 vertexData = (TObjVertexData *)
194                                          _pico_realloc( (void *)&vertexData,
195                                                                         sizeof( TObjVertexData ) * ( *allocated ),
196                                                                         sizeof( TObjVertexData ) * ( newAllocated ) );
197
198                 /* reallocation failed */
199                 if ( vertexData == NULL ) {
200                         return NULL;
201                 }
202
203                 /* reallocation succeeded */
204                 *allocated = newAllocated;
205                 *entries   = reqEntries;
206                 return vertexData;
207         }
208         /* we're b0rked when we reach this */
209         return NULL;
210 }
211
212 static void FreeObjVertexData( TObjVertexData *vertexData ){
213         if ( vertexData != NULL ) {
214                 free( (TObjVertexData *)vertexData );
215         }
216 }
217
218 #if 0
219 static int _obj_mtl_load( picoModel_t *model ){
220         //picoShader_t *curShader = NULL;
221         picoParser_t *p;
222         picoByte_t   *mtlBuffer;
223         int mtlBufSize;
224         char         *fileName;
225
226         /* sanity checks */
227         if ( model == NULL || model->fileName == NULL ) {
228                 return 0;
229         }
230
231         /* skip if we have a zero length model file name */
232         if ( !strlen( model->fileName ) ) {
233                 return 0;
234         }
235
236         /* helper */
237         #define _obj_mtl_error_return \
238         { \
239                 _pico_free_parser( p ); \
240                 _pico_free_file( mtlBuffer ); \
241                 _pico_free( fileName ); \
242                 return 0; \
243         }
244         /* alloc copy of model file name */
245         fileName = _pico_clone_alloc( model->fileName,-1 );
246         if ( fileName == NULL ) {
247                 return 0;
248         }
249
250         /* change extension of model file to .mtl */
251         _pico_setfext( fileName, "mtl" );
252
253         /* load .mtl file contents */
254         _pico_load_file( fileName,&mtlBuffer,&mtlBufSize );
255
256         /* check result */
257         if ( mtlBufSize == 0 ) {
258                 return 1;                       /* file is empty: no error */
259         }
260         if ( mtlBufSize  < 0 ) {
261                 return 0;                       /* load failed: error */
262
263         }
264         /* create a new pico parser */
265         p = _pico_new_parser( mtlBuffer, mtlBufSize );
266         if ( p == NULL ) {
267                 _obj_mtl_error_return;
268         }
269
270         /* doo teh .mtl parse */
271         while ( 1 )
272         {
273                 /* get next token in material file */
274                 if ( _pico_parse( p,1 ) == NULL ) {
275                         break;
276                 }
277 #if 0
278
279                 /* skip empty lines */
280                 if ( p->token == NULL || !strlen( p->token ) ) {
281                         continue;
282                 }
283
284                 /* skip comment lines */
285                 if ( p->token[0] == '#' ) {
286                         _pico_parse_skip_rest( p );
287                         continue;
288                 }
289                 /* new material */
290                 if ( !_pico_stricmp( p->token,"newmtl" ) ) {
291                         picoShader_t *shader;
292                         char *name;
293
294                         /* get material name */
295                         name = _pico_parse( p,0 );
296
297                         /* validate material name */
298                         if ( name == NULL || !strlen( name ) ) {
299                                 _pico_printf( PICO_ERROR,"Missing material name in MTL, line %d.",p->curLine );
300                                 _obj_mtl_error_return;
301                         }
302                         /* create a new pico shader */
303                         shader = PicoNewShader( model );
304                         if ( shader == NULL ) {
305                                 _obj_mtl_error_return;
306                         }
307
308                         /* set shader name */
309                         PicoSetShaderName( shader,name );
310
311                         /* assign pointer to current shader */
312                         curShader = shader;
313                 }
314                 /* diffuse map name */
315                 else if ( !_pico_stricmp( p->token,"map_kd" ) ) {
316                         char *mapName;
317
318                         /* pointer to current shader must be valid */
319                         if ( curShader == NULL ) {
320                                 _obj_mtl_error_return;
321                         }
322
323                         /* get material's diffuse map name */
324                         mapName = _pico_parse( p,0 );
325
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;
330                         }
331                         /* set shader map name */
332                         PicoSetShaderMapName( shader,mapName );
333                 }
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" ) ) {
337                         picoByte_t *diffuse;
338                         float value;
339
340
341                         /* get dissolve factor */
342                         if ( !_pico_parse_float( p,&value ) ) {
343                                 _obj_mtl_error_return;
344                         }
345
346                         /* set shader transparency */
347                         PicoSetShaderTransparency( curShader,value );
348
349                         /* get shader's diffuse color */
350                         diffuse = PicoGetShaderDiffuseColor( curShader );
351
352                         /* set diffuse alpha to transparency */
353                         diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
354
355                         /* set shader's new diffuse color */
356                         PicoSetShaderDiffuseColor( curShader,diffuse );
357                 }
358                 /* shininess (phong specular component) */
359                 else if ( !_pico_stricmp( p->token,"ns" ) ) {
360                         /* remark:
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
367                          */
368                         float value;
369
370                         /* pointer to current shader must be valid */
371                         if ( curShader == NULL ) {
372                                 _obj_mtl_error_return;
373                         }
374
375                         /* get totally screwed up shininess (a random value in fact ;) */
376                         if ( !_pico_parse_float( p,&value ) ) {
377                                 _obj_mtl_error_return;
378                         }
379
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) */
382
383                         /* assume 0..2048 range */
384                         if ( value > 1000 ) {
385                                 value = 128.0 * ( value / 2048.0 );
386                         }
387                         /* assume 0..1000 range */
388                         else if ( value > 200 ) {
389                                 value = 128.0 * ( value / 1000.0 );
390                         }
391                         /* assume 0..200 range */
392                         else if ( value > 100 ) {
393                                 value = 128.0 * ( value / 200.0 );
394                         }
395                         /* assume 0..100 range */
396                         else if ( value > 1 ) {
397                                 value = 128.0 * ( value / 100.0 );
398                         }
399                         /* assume 0..1 range */
400                         else {
401                                 value *= 128.0;
402                         }
403                         /* negative shininess is bad (yes, i have seen it...) */
404                         if ( value < 0.0 ) {
405                                 value = 0.0;
406                         }
407
408                         /* set the pico shininess value in range 0..127 */
409                         /* geez, .obj is such a mess... */
410                         PicoSetShaderShininess( curShader,value );
411                 }
412                 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
413                 else if ( !_pico_stricmp( p->token,"ka" ) ) {
414                         picoColor_t color;
415                         picoVec3_t v;
416
417                         /* pointer to current shader must be valid */
418                         if ( curShader == NULL ) {
419                                 _obj_mtl_error_return;
420                         }
421
422                         /* get color vector */
423                         if ( !_pico_parse_vec( p,v ) ) {
424                                 _obj_mtl_error_return;
425                         }
426
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 );
432
433                         /* set ambient color */
434                         PicoSetShaderAmbientColor( curShader,color );
435                 }
436                 /* kol0r diffuse */
437                 else if ( !_pico_stricmp( p->token,"kd" ) ) {
438                         picoColor_t color;
439                         picoVec3_t v;
440
441                         /* pointer to current shader must be valid */
442                         if ( curShader == NULL ) {
443                                 _obj_mtl_error_return;
444                         }
445
446                         /* get color vector */
447                         if ( !_pico_parse_vec( p,v ) ) {
448                                 _obj_mtl_error_return;
449                         }
450
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 );
456
457                         /* set diffuse color */
458                         PicoSetShaderDiffuseColor( curShader,color );
459                 }
460                 /* kol0r specular */
461                 else if ( !_pico_stricmp( p->token,"ks" ) ) {
462                         picoColor_t color;
463                         picoVec3_t v;
464
465                         /* pointer to current shader must be valid */
466                         if ( curShader == NULL ) {
467                                 _obj_mtl_error_return;
468                         }
469
470                         /* get color vector */
471                         if ( !_pico_parse_vec( p,v ) ) {
472                                 _obj_mtl_error_return;
473                         }
474
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 );
480
481                         /* set specular color */
482                         PicoSetShaderSpecularColor( curShader,color );
483                 }
484 #endif
485                 /* skip rest of line */
486                 _pico_parse_skip_rest( p );
487         }
488
489         /* free parser, file buffer, and file name */
490         _pico_free_parser( p );
491         _pico_free_file( mtlBuffer );
492         _pico_free( fileName );
493
494         /* return with success */
495         return 1;
496 }
497 #endif
498
499 /* _obj_load:
500  *  loads a wavefront obj model file.
501  */
502 static picoModel_t *_obj_load( PM_PARAMS_LOAD ){
503         TObjVertexData *vertexData  = NULL;
504         picoModel_t    *model;
505         picoSurface_t  *curSurface  = NULL;
506         picoParser_t   *p;
507         int allocated;
508         int entries;
509         int numVerts    = 0;
510         int numNormals  = 0;
511         int numUVs      = 0;
512         int curVertex   = 0;
513         int curFace     = 0;
514
515         /* helper */
516         #define _obj_error_return( m ) \
517         { \
518                 _pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine ); \
519                 _pico_free_parser( p ); \
520                 FreeObjVertexData( vertexData ); \
521                 PicoFreeModel( model ); \
522                 return NULL; \
523         }
524         /* alllocate a new pico parser */
525         p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
526         if ( p == NULL ) {
527                 return NULL;
528         }
529
530         /* create a new pico model */
531         model = PicoNewModel();
532         if ( model == NULL ) {
533                 _pico_free_parser( p );
534                 return NULL;
535         }
536         /* do model setup */
537         PicoSetModelFrameNum( model,frameNum );
538         PicoSetModelName( model,fileName );
539         PicoSetModelFileName( model,fileName );
540
541         /* try loading the materials; we don't handle the result */
542 #if 0
543         _obj_mtl_load( model );
544 #endif
545
546         /* parse obj line by line */
547         while ( 1 )
548         {
549                 /* get first token on line */
550                 if ( _pico_parse_first( p ) == NULL ) {
551                         break;
552                 }
553
554                 /* skip empty lines */
555                 if ( p->token == NULL || !strlen( p->token ) ) {
556                         continue;
557                 }
558
559                 /* skip comment lines */
560                 if ( p->token[0] == '#' ) {
561                         _pico_parse_skip_rest( p );
562                         continue;
563                 }
564                 /* vertex */
565                 if ( !_pico_stricmp( p->token,"v" ) ) {
566                         TObjVertexData *data;
567                         picoVec3_t v;
568
569                         vertexData = SizeObjVertexData( vertexData,numVerts + 1,&entries,&allocated );
570                         if ( vertexData == NULL ) {
571                                 _obj_error_return( "Realloc of vertex data failed (1)" );
572                         }
573
574                         data = &vertexData[ numVerts++ ];
575
576                         /* get and copy vertex */
577                         if ( !_pico_parse_vec( p,v ) ) {
578                                 _obj_error_return( "Vertex parse error" );
579                         }
580
581                         _pico_copy_vec( v,data->v );
582
583 #ifdef DEBUG_PM_OBJ_EX
584                         printf( "Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2] );
585 #endif
586                 }
587                 /* uv coord */
588                 else if ( !_pico_stricmp( p->token,"vt" ) ) {
589                         TObjVertexData *data;
590                         picoVec2_t coord;
591
592                         vertexData = SizeObjVertexData( vertexData,numUVs + 1,&entries,&allocated );
593                         if ( vertexData == NULL ) {
594                                 _obj_error_return( "Realloc of vertex data failed (2)" );
595                         }
596
597                         data = &vertexData[ numUVs++ ];
598
599                         /* get and copy tex coord */
600                         if ( !_pico_parse_vec2( p,coord ) ) {
601                                 _obj_error_return( "UV coord parse error" );
602                         }
603
604                         _pico_copy_vec2( coord,data->vt );
605
606 #ifdef DEBUG_PM_OBJ_EX
607                         printf( "TexCoord: u: %f v: %f\n",coord[0],coord[1] );
608 #endif
609                 }
610                 /* vertex normal */
611                 else if ( !_pico_stricmp( p->token,"vn" ) ) {
612                         TObjVertexData *data;
613                         picoVec3_t n;
614
615                         vertexData = SizeObjVertexData( vertexData,numNormals + 1,&entries,&allocated );
616                         if ( vertexData == NULL ) {
617                                 _obj_error_return( "Realloc of vertex data failed (3)" );
618                         }
619
620                         data = &vertexData[ numNormals++ ];
621
622                         /* get and copy vertex normal */
623                         if ( !_pico_parse_vec( p,n ) ) {
624                                 _obj_error_return( "Vertex normal parse error" );
625                         }
626
627                         _pico_copy_vec( n,data->vn );
628
629 #ifdef DEBUG_PM_OBJ_EX
630                         printf( "Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2] );
631 #endif
632                 }
633                 /* new group (for us this means a new surface) */
634                 else if ( !_pico_stricmp( p->token,"g" ) ) {
635                         picoSurface_t *newSurface;
636                         char *groupName;
637
638                         /* get first group name (ignore 2nd,3rd,etc.) */
639                         groupName = _pico_parse( p,0 );
640                         if ( groupName == NULL || !strlen( groupName ) ) {
641                                 /* some obj exporters feel like they don't need to */
642                                 /* supply a group name. so we gotta handle it here */
643 #if 1
644                                 strcpy( p->token,"default" );
645                                 groupName = p->token;
646 #else
647                                 _obj_error_return( "Invalid or missing group name" );
648 #endif
649                         }
650                         /* allocate a pico surface */
651                         newSurface = PicoNewSurface( model );
652                         if ( newSurface == NULL ) {
653                                 _obj_error_return( "Error allocating surface" );
654                         }
655
656                         /* reset face index for surface */
657                         curFace = 0;
658
659                         /* set ptr to current surface */
660                         curSurface = newSurface;
661
662                         /* we use triangle meshes */
663                         PicoSetSurfaceType( newSurface,PICO_TRIANGLES );
664
665                         /* set surface name */
666                         PicoSetSurfaceName( newSurface,groupName );
667
668 #ifdef DEBUG_PM_OBJ_EX
669                         printf( "Group: '%s'\n",groupName );
670 #endif
671                 }
672                 /* face (oh jesus, hopefully this will do the job right ;) */
673                 else if ( !_pico_stricmp( p->token,"f" ) ) {
674                         /* okay, this is a mess. some 3d apps seem to try being unique, */
675                         /* hello cinema4d & 3d exploration, feel good today?, and save */
676                         /* this crap in tons of different formats. gah, those screwed */
677                         /* coders. tho the wavefront obj standard defines exactly two */
678                         /* ways of storing face information. so, i really won't support */
679                         /* such stupid extravaganza here! */
680
681                         picoVec3_t verts  [ 4 ];
682                         picoVec3_t normals[ 4 ];
683                         picoVec2_t coords [ 4 ];
684
685                         int iv [ 4 ], has_v;
686                         int ivt[ 4 ], has_vt = 0;
687                         int ivn[ 4 ], has_vn = 0;
688                         int have_quad = 0;
689                         int slashcount;
690                         int doubleslash;
691                         int i;
692
693                         /* group defs *must* come before faces */
694                         if ( curSurface == NULL ) {
695                                 _obj_error_return( "No group defined for faces" );
696                         }
697
698 #ifdef DEBUG_PM_OBJ_EX
699                         printf( "Face: " );
700 #endif
701                         /* read vertex/uv/normal indices for the first three face */
702                         /* vertices (cause we only support triangles) into 'i*[]' */
703                         /* store the actual vertex/uv/normal data in three arrays */
704                         /* called 'verts','coords' and 'normals'. */
705                         for ( i = 0; i < 4; i++ )
706                         {
707                                 char *str;
708
709                                 /* get next vertex index string (different */
710                                 /* formats are handled below) */
711                                 str = _pico_parse( p,0 );
712                                 if ( str == NULL ) {
713                                         /* just break for quads */
714                                         if ( i == 3 ) {
715                                                 break;
716                                         }
717
718                                         /* error otherwise */
719                                         _obj_error_return( "Face parse error" );
720                                 }
721                                 /* if this is the fourth index string we're */
722                                 /* parsing we assume that we have a quad */
723                                 if ( i == 3 ) {
724                                         have_quad = 1;
725                                 }
726
727                                 /* get slash count once */
728                                 if ( i == 0 ) {
729                                         slashcount  = _pico_strchcount( str,'/' );
730                                         doubleslash =  strstr( str,"//" ) != NULL;
731                                 }
732                                 /* handle format 'v//vn' */
733                                 if ( doubleslash && ( slashcount == 2 ) ) {
734                                         has_v = has_vn = 1;
735                                         sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
736                                 }
737                                 /* handle format 'v/vt/vn' */
738                                 else if ( !doubleslash && ( slashcount == 2 ) ) {
739                                         has_v = has_vt = has_vn = 1;
740                                         sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
741                                 }
742                                 /* handle format 'v/vt' (non-standard fuckage) */
743                                 else if ( !doubleslash && ( slashcount == 1 ) ) {
744                                         has_v = has_vt = 1;
745                                         sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
746                                 }
747                                 /* else assume face format 'v' */
748                                 /* (must have been invented by some bored granny) */
749                                 else {
750                                         /* get single vertex index */
751                                         has_v = 1;
752                                         iv[ i ] = atoi( str );
753
754                                         /* either invalid face format or out of range */
755                                         if ( iv[ i ] == 0 ) {
756                                                 _obj_error_return( "Invalid face format" );
757                                         }
758                                 }
759                                 /* fix useless back references */
760                                 /* todo: check if this works as it is supposed to */
761
762                                 /* assign new indices */
763                                 if ( iv [ i ] < 0 ) {
764                                         iv [ i ] = ( numVerts   - iv [ i ] );
765                                 }
766                                 if ( ivt[ i ] < 0 ) {
767                                         ivt[ i ] = ( numUVs     - ivt[ i ] );
768                                 }
769                                 if ( ivn[ i ] < 0 ) {
770                                         ivn[ i ] = ( numNormals - ivn[ i ] );
771                                 }
772
773                                 /* validate indices */
774                                 /* - commented out. index range checks will trigger
775                                    if (iv [ i ] < 1) iv [ i ] = 1;
776                                    if (ivt[ i ] < 1) ivt[ i ] = 1;
777                                    if (ivn[ i ] < 1) ivn[ i ] = 1;
778                                  */
779                                 /* set vertex origin */
780                                 if ( has_v ) {
781                                         /* check vertex index range */
782                                         if ( iv[ i ] < 1 || iv[ i ] > numVerts ) {
783                                                 _obj_error_return( "Vertex index out of range" );
784                                         }
785
786                                         /* get vertex data */
787                                         verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
788                                         verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
789                                         verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
790                                 }
791                                 /* set vertex normal */
792                                 if ( has_vn ) {
793                                         /* check normal index range */
794                                         if ( ivn[ i ] < 1 || ivn[ i ] > numNormals ) {
795                                                 _obj_error_return( "Normal index out of range" );
796                                         }
797
798                                         /* get normal data */
799                                         normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
800                                         normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
801                                         normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
802                                 }
803                                 /* set texture coordinate */
804                                 if ( has_vt ) {
805                                         /* check uv index range */
806                                         if ( ivt[ i ] < 1 || ivt[ i ] > numUVs ) {
807                                                 _obj_error_return( "UV coord index out of range" );
808                                         }
809
810                                         /* get uv coord data */
811                                         coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
812                                         coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
813                                         coords[ i ][ 1 ] = -coords[ i ][ 1 ];
814                                 }
815 #ifdef DEBUG_PM_OBJ_EX
816                                 printf( "(%4d",iv[ i ] );
817                                 if ( has_vt ) {
818                                         printf( " %4d",ivt[ i ] );
819                                 }
820                                 if ( has_vn ) {
821                                         printf( " %4d",ivn[ i ] );
822                                 }
823                                 printf( ") " );
824 #endif
825                         }
826 #ifdef DEBUG_PM_OBJ_EX
827                         printf( "\n" );
828 #endif
829                         /* now that we have extracted all the indices and have */
830                         /* read the actual data we need to assign all the crap */
831                         /* to our current pico surface */
832                         if ( has_v ) {
833                                 int max = 3;
834                                 if ( have_quad ) {
835                                         max = 4;
836                                 }
837
838                                 /* assign all surface information */
839                                 for ( i = 0; i < max; i++ )
840                                 {
841                                         /*if( has_v  )*/ PicoSetSurfaceXYZ( curSurface,  ( curVertex + i ), verts  [ i ] );
842                                         /*if( has_vt )*/ PicoSetSurfaceST( curSurface,0,( curVertex + i ), coords [ i ] );
843                                         /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface,  ( curVertex + i ), normals[ i ] );
844                                 }
845                                 /* add our triangle (A B C) */
846                                 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
847                                 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 1 ) );
848                                 PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 2 ) );
849                                 curFace++;
850
851                                 /* if we don't have a simple triangle, but a quad... */
852                                 if ( have_quad ) {
853                                         /* we have to add another triangle (2nd half of quad which is A C D) */
854                                         PicoSetSurfaceIndex( curSurface,( curFace * 3 + 2 ),(picoIndex_t)( curVertex + 0 ) );
855                                         PicoSetSurfaceIndex( curSurface,( curFace * 3 + 1 ),(picoIndex_t)( curVertex + 2 ) );
856                                         PicoSetSurfaceIndex( curSurface,( curFace * 3 + 0 ),(picoIndex_t)( curVertex + 3 ) );
857                                         curFace++;
858                                 }
859                                 /* increase vertex count */
860                                 curVertex += max;
861                         }
862                 }
863                 /* skip unparsed rest of line and continue */
864                 _pico_parse_skip_rest( p );
865         }
866         /* free memory used by temporary vertexdata */
867         FreeObjVertexData( vertexData );
868
869         /* return allocated pico model */
870         return model;
871 //      return NULL;
872 }
873
874 /* pico file format module definition */
875 const picoModule_t picoModuleOBJ =
876 {
877         "0.6-b",                    /* module version string */
878         "Wavefront ASCII",          /* module display name */
879         "seaw0lf",                  /* author's name */
880         "2002 seaw0lf",             /* module copyright */
881         {
882                 "obj",NULL,NULL,NULL    /* default extensions to use */
883         },
884         _obj_canload,               /* validation routine */
885         _obj_load,                  /* load routine */
886         NULL,                       /* save validation routine */
887         NULL                        /* save routine */
888 };