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