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