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 */
_obj_canload(PM_PARAMS_CANLOAD)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( (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
SizeObjVertexData(TObjVertexData * vertexData,int reqEntries,int * entries,int * allocated)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
FreeObjVertexData(TObjVertexData * vertexData)210 static void FreeObjVertexData( TObjVertexData *vertexData )
211 {
212 if (vertexData != NULL)
213 {
214 free( (TObjVertexData *)vertexData );
215 }
216 }
217
_obj_mtl_load(picoModel_t * model)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 0
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
311 /* pointer to current shader must be valid */
312 if (curShader == NULL)
313 _obj_mtl_error_return;
314
315 /* get material's diffuse map name */
316 mapName = _pico_parse( p,0 );
317
318 /* validate map name */
319 if (mapName == NULL || !strlen(mapName))
320 {
321 _pico_printf( PICO_ERROR,"Missing material map name in MTL, line %d.",p->curLine);
322 _obj_mtl_error_return;
323 }
324 /* set shader map name */
325 PicoSetShaderMapName( shader,mapName );
326 }
327 /* dissolve factor (pseudo transparency 0..1) */
328 /* where 0 means 100% transparent and 1 means opaque */
329 else if (!_pico_stricmp(p->token,"d"))
330 {
331 picoByte_t *diffuse;
332 float value;
333
334
335 /* get dissolve factor */
336 if (!_pico_parse_float( p,&value ))
337 _obj_mtl_error_return;
338
339 /* set shader transparency */
340 PicoSetShaderTransparency( curShader,value );
341
342 /* get shader's diffuse color */
343 diffuse = PicoGetShaderDiffuseColor( curShader );
344
345 /* set diffuse alpha to transparency */
346 diffuse[ 3 ] = (picoByte_t)( value * 255.0 );
347
348 /* set shader's new diffuse color */
349 PicoSetShaderDiffuseColor( curShader,diffuse );
350 }
351 /* shininess (phong specular component) */
352 else if (!_pico_stricmp(p->token,"ns"))
353 {
354 /* remark:
355 * - well, this is some major obj spec fuckup once again. some
356 * apps store this in 0..1 range, others use 0..100 range,
357 * even others use 0..2048 range, and again others use the
358 * range 0..128, some even use 0..1000, 0..200, 400..700,
359 * honestly, what's up with the 3d app coders? happens when
360 * you smoke too much weed i guess. -sea
361 */
362 float value;
363
364 /* pointer to current shader must be valid */
365 if (curShader == NULL)
366 _obj_mtl_error_return;
367
368 /* get totally screwed up shininess (a random value in fact ;) */
369 if (!_pico_parse_float( p,&value ))
370 _obj_mtl_error_return;
371
372 /* okay, there is no way to set this correctly, so we simply */
373 /* try to guess a few ranges (most common ones i have seen) */
374
375 /* assume 0..2048 range */
376 if (value > 1000)
377 value = 128.0 * (value / 2048.0);
378 /* assume 0..1000 range */
379 else if (value > 200)
380 value = 128.0 * (value / 1000.0);
381 /* assume 0..200 range */
382 else if (value > 100)
383 value = 128.0 * (value / 200.0);
384 /* assume 0..100 range */
385 else if (value > 1)
386 value = 128.0 * (value / 100.0);
387 /* assume 0..1 range */
388 else {
389 value *= 128.0;
390 }
391 /* negative shininess is bad (yes, i have seen it...) */
392 if (value < 0.0) value = 0.0;
393
394 /* set the pico shininess value in range 0..127 */
395 /* geez, .obj is such a mess... */
396 PicoSetShaderShininess( curShader,value );
397 }
398 /* kol0r ambient (wut teh fuk does "ka" stand for?) */
399 else if (!_pico_stricmp(p->token,"ka"))
400 {
401 picoColor_t color;
402 picoVec3_t v;
403
404 /* pointer to current shader must be valid */
405 if (curShader == NULL)
406 _obj_mtl_error_return;
407
408 /* get color vector */
409 if (!_pico_parse_vec( p,v ))
410 _obj_mtl_error_return;
411
412 /* scale to byte range */
413 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
414 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
415 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
416 color[ 3 ] = (picoByte_t)( 255 );
417
418 /* set ambient color */
419 PicoSetShaderAmbientColor( curShader,color );
420 }
421 /* kol0r diffuse */
422 else if (!_pico_stricmp(p->token,"kd"))
423 {
424 picoColor_t color;
425 picoVec3_t v;
426
427 /* pointer to current shader must be valid */
428 if (curShader == NULL)
429 _obj_mtl_error_return;
430
431 /* get color vector */
432 if (!_pico_parse_vec( p,v ))
433 _obj_mtl_error_return;
434
435 /* scale to byte range */
436 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
437 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
438 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
439 color[ 3 ] = (picoByte_t)( 255 );
440
441 /* set diffuse color */
442 PicoSetShaderDiffuseColor( curShader,color );
443 }
444 /* kol0r specular */
445 else if (!_pico_stricmp(p->token,"ks"))
446 {
447 picoColor_t color;
448 picoVec3_t v;
449
450 /* pointer to current shader must be valid */
451 if (curShader == NULL)
452 _obj_mtl_error_return;
453
454 /* get color vector */
455 if (!_pico_parse_vec( p,v ))
456 _obj_mtl_error_return;
457
458 /* scale to byte range */
459 color[ 0 ] = (picoByte_t)( v[ 0 ] * 255 );
460 color[ 1 ] = (picoByte_t)( v[ 1 ] * 255 );
461 color[ 2 ] = (picoByte_t)( v[ 2 ] * 255 );
462 color[ 3 ] = (picoByte_t)( 255 );
463
464 /* set specular color */
465 PicoSetShaderSpecularColor( curShader,color );
466 }
467 #endif
468 /* skip rest of line */
469 _pico_parse_skip_rest( p );
470 }
471
472 /* free parser, file buffer, and file name */
473 _pico_free_parser( p );
474 _pico_free_file( mtlBuffer );
475 _pico_free( fileName );
476
477 /* return with success */
478 return 1;
479 }
480
481 /* _obj_load:
482 * loads a wavefront obj model file.
483 */
_obj_load(PM_PARAMS_LOAD)484 static picoModel_t *_obj_load( PM_PARAMS_LOAD )
485 {
486 TObjVertexData *vertexData = NULL;
487 picoModel_t *model;
488 picoSurface_t *curSurface = NULL;
489 picoParser_t *p;
490 int allocated;
491 int entries;
492 int numVerts = 0;
493 int numNormals = 0;
494 int numUVs = 0;
495 int curVertex = 0;
496 int curFace = 0;
497
498 /* helper */
499 #define _obj_error_return(m) \
500 { \
501 _pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine); \
502 _pico_free_parser( p ); \
503 FreeObjVertexData( vertexData ); \
504 PicoFreeModel( model ); \
505 return NULL; \
506 }
507 /* alllocate a new pico parser */
508 p = _pico_new_parser( (picoByte_t *)buffer,bufSize );
509 if (p == NULL) return NULL;
510
511 /* create a new pico model */
512 model = PicoNewModel();
513 if (model == NULL)
514 {
515 _pico_free_parser( p );
516 return NULL;
517 }
518 /* do model setup */
519 PicoSetModelFrameNum( model,frameNum );
520 PicoSetModelName( model,fileName );
521 PicoSetModelFileName( model,fileName );
522
523 /* try loading the materials; we don't handle the result */
524 #if 0
525 _obj_mtl_load( model );
526 #endif
527
528 /* parse obj line by line */
529 while( 1 )
530 {
531 /* get first token on line */
532 if (_pico_parse_first( p ) == NULL)
533 break;
534
535 /* skip empty lines */
536 if (p->token == NULL || !strlen( p->token ))
537 continue;
538
539 /* skip comment lines */
540 if (p->token[0] == '#')
541 {
542 _pico_parse_skip_rest( p );
543 continue;
544 }
545 /* vertex */
546 if (!_pico_stricmp(p->token,"v"))
547 {
548 TObjVertexData *data;
549 picoVec3_t v;
550
551 vertexData = SizeObjVertexData( vertexData,numVerts+1,&entries,&allocated );
552 if (vertexData == NULL)
553 _obj_error_return("Realloc of vertex data failed (1)");
554
555 data = &vertexData[ numVerts++ ];
556
557 /* get and copy vertex */
558 if (!_pico_parse_vec( p,v ))
559 _obj_error_return("Vertex parse error");
560
561 _pico_copy_vec( v,data->v );
562
563 #ifdef DEBUG_PM_OBJ_EX
564 printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
565 #endif
566 }
567 /* uv coord */
568 else if (!_pico_stricmp(p->token,"vt"))
569 {
570 TObjVertexData *data;
571 picoVec2_t coord;
572
573 vertexData = SizeObjVertexData( vertexData,numUVs+1,&entries,&allocated );
574 if (vertexData == NULL)
575 _obj_error_return("Realloc of vertex data failed (2)");
576
577 data = &vertexData[ numUVs++ ];
578
579 /* get and copy tex coord */
580 if (!_pico_parse_vec2( p,coord ))
581 _obj_error_return("UV coord parse error");
582
583 _pico_copy_vec2( coord,data->vt );
584
585 #ifdef DEBUG_PM_OBJ_EX
586 printf("TexCoord: u: %f v: %f\n",coord[0],coord[1]);
587 #endif
588 }
589 /* vertex normal */
590 else if (!_pico_stricmp(p->token,"vn"))
591 {
592 TObjVertexData *data;
593 picoVec3_t n;
594
595 vertexData = SizeObjVertexData( vertexData,numNormals+1,&entries,&allocated );
596 if (vertexData == NULL)
597 _obj_error_return("Realloc of vertex data failed (3)");
598
599 data = &vertexData[ numNormals++ ];
600
601 /* get and copy vertex normal */
602 if (!_pico_parse_vec( p,n ))
603 _obj_error_return("Vertex normal parse error");
604
605 _pico_copy_vec( n,data->vn );
606
607 #ifdef DEBUG_PM_OBJ_EX
608 printf("Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2]);
609 #endif
610 }
611 /* new group (for us this means a new surface) */
612 else if (!_pico_stricmp(p->token,"g"))
613 {
614 picoSurface_t *newSurface;
615 char *groupName;
616
617 /* get first group name (ignore 2nd,3rd,etc.) */
618 groupName = _pico_parse( p,0 );
619 if (groupName == NULL || !strlen(groupName))
620 {
621 /* some obj exporters feel like they don't need to */
622 /* supply a group name. so we gotta handle it here */
623 #if 1
624 strcpy( p->token,"default" );
625 groupName = p->token;
626 #else
627 _obj_error_return("Invalid or missing group name");
628 #endif
629 }
630 /* allocate a pico surface */
631 newSurface = PicoNewSurface( model );
632 if (newSurface == NULL)
633 _obj_error_return("Error allocating surface");
634
635 /* reset face index for surface */
636 curFace = 0;
637
638 /* set ptr to current surface */
639 curSurface = newSurface;
640
641 /* we use triangle meshes */
642 PicoSetSurfaceType( newSurface,PICO_TRIANGLES );
643
644 /* set surface name */
645 PicoSetSurfaceName( newSurface,groupName );
646
647 #ifdef DEBUG_PM_OBJ_EX
648 printf("Group: '%s'\n",groupName);
649 #endif
650 }
651 /* face (oh jesus, hopefully this will do the job right ;) */
652 else if (!_pico_stricmp(p->token,"f"))
653 {
654 /* okay, this is a mess. some 3d apps seem to try being unique, */
655 /* hello cinema4d & 3d exploration, feel good today?, and save */
656 /* this crap in tons of different formats. gah, those screwed */
657 /* coders. tho the wavefront obj standard defines exactly two */
658 /* ways of storing face information. so, i really won't support */
659 /* such stupid extravaganza here! */
660
661 picoVec3_t verts [ 4 ];
662 picoVec3_t normals[ 4 ];
663 picoVec2_t coords [ 4 ];
664
665 int iv [ 4 ], has_v;
666 int ivt[ 4 ], has_vt = 0;
667 int ivn[ 4 ], has_vn = 0;
668 int have_quad = 0;
669 int slashcount;
670 int doubleslash;
671 int i;
672
673 /* group defs *must* come before faces */
674 if (curSurface == NULL)
675 _obj_error_return("No group defined for faces");
676
677 #ifdef DEBUG_PM_OBJ_EX
678 printf("Face: ");
679 #endif
680 /* read vertex/uv/normal indices for the first three face */
681 /* vertices (cause we only support triangles) into 'i*[]' */
682 /* store the actual vertex/uv/normal data in three arrays */
683 /* called 'verts','coords' and 'normals'. */
684 for (i=0; i<4; i++)
685 {
686 char *str;
687
688 /* get next vertex index string (different */
689 /* formats are handled below) */
690 str = _pico_parse( p,0 );
691 if (str == NULL)
692 {
693 /* just break for quads */
694 if (i == 3) break;
695
696 /* error otherwise */
697 _obj_error_return("Face parse error");
698 }
699 /* if this is the fourth index string we're */
700 /* parsing we assume that we have a quad */
701 if (i == 3)
702 have_quad = 1;
703
704 /* get slash count once */
705 if (i == 0)
706 {
707 slashcount = _pico_strchcount( str,'/' );
708 doubleslash = strstr(str,"//") != NULL;
709 }
710 /* handle format 'v//vn' */
711 if (doubleslash && (slashcount == 2))
712 {
713 has_v = has_vn = 1;
714 sscanf( str,"%d//%d",&iv[ i ],&ivn[ i ] );
715 }
716 /* handle format 'v/vt/vn' */
717 else if (!doubleslash && (slashcount == 2))
718 {
719 has_v = has_vt = has_vn = 1;
720 sscanf( str,"%d/%d/%d",&iv[ i ],&ivt[ i ],&ivn[ i ] );
721 }
722 /* handle format 'v/vt' (non-standard fuckage) */
723 else if (!doubleslash && (slashcount == 1))
724 {
725 has_v = has_vt = 1;
726 sscanf( str,"%d/%d",&iv[ i ],&ivt[ i ] );
727 }
728 /* else assume face format 'v' */
729 /* (must have been invented by some bored granny) */
730 else {
731 /* get single vertex index */
732 has_v = 1;
733 iv[ i ] = atoi( str );
734
735 /* either invalid face format or out of range */
736 if (iv[ i ] == 0)
737 _obj_error_return("Invalid face format");
738 }
739 /* fix useless back references */
740 /* todo: check if this works as it is supposed to */
741
742 /* assign new indices */
743 if (iv [ i ] < 0) iv [ i ] = (numVerts - iv [ i ]);
744 if (ivt[ i ] < 0) ivt[ i ] = (numUVs - ivt[ i ]);
745 if (ivn[ i ] < 0) ivn[ i ] = (numNormals - ivn[ i ]);
746
747 /* validate indices */
748 /* - commented out. index range checks will trigger
749 if (iv [ i ] < 1) iv [ i ] = 1;
750 if (ivt[ i ] < 1) ivt[ i ] = 1;
751 if (ivn[ i ] < 1) ivn[ i ] = 1;
752 */
753 /* set vertex origin */
754 if (has_v)
755 {
756 /* check vertex index range */
757 if (iv[ i ] < 1 || iv[ i ] > numVerts)
758 _obj_error_return("Vertex index out of range");
759
760 /* get vertex data */
761 verts[ i ][ 0 ] = vertexData[ iv[ i ] - 1 ].v[ 0 ];
762 verts[ i ][ 1 ] = vertexData[ iv[ i ] - 1 ].v[ 1 ];
763 verts[ i ][ 2 ] = vertexData[ iv[ i ] - 1 ].v[ 2 ];
764 }
765 /* set vertex normal */
766 if (has_vn)
767 {
768 /* check normal index range */
769 if (ivn[ i ] < 1 || ivn[ i ] > numNormals)
770 _obj_error_return("Normal index out of range");
771
772 /* get normal data */
773 normals[ i ][ 0 ] = vertexData[ ivn[ i ] - 1 ].vn[ 0 ];
774 normals[ i ][ 1 ] = vertexData[ ivn[ i ] - 1 ].vn[ 1 ];
775 normals[ i ][ 2 ] = vertexData[ ivn[ i ] - 1 ].vn[ 2 ];
776 }
777 /* set texture coordinate */
778 if (has_vt)
779 {
780 /* check uv index range */
781 if (ivt[ i ] < 1 || ivt[ i ] > numUVs)
782 _obj_error_return("UV coord index out of range");
783
784 /* get uv coord data */
785 coords[ i ][ 0 ] = vertexData[ ivt[ i ] - 1 ].vt[ 0 ];
786 coords[ i ][ 1 ] = vertexData[ ivt[ i ] - 1 ].vt[ 1 ];
787 coords[ i ][ 1 ] = -coords[ i ][ 1 ];
788 }
789 #ifdef DEBUG_PM_OBJ_EX
790 printf("(%4d",iv[ i ]);
791 if (has_vt) printf(" %4d",ivt[ i ]);
792 if (has_vn) printf(" %4d",ivn[ i ]);
793 printf(") ");
794 #endif
795 }
796 #ifdef DEBUG_PM_OBJ_EX
797 printf("\n");
798 #endif
799 /* now that we have extracted all the indices and have */
800 /* read the actual data we need to assign all the crap */
801 /* to our current pico surface */
802 if (has_v)
803 {
804 int max = 3;
805 if (have_quad) max = 4;
806
807 /* assign all surface information */
808 for (i=0; i<max; i++)
809 {
810 /*if( has_v )*/ PicoSetSurfaceXYZ ( curSurface, (curVertex + i), verts [ i ] );
811 /*if( has_vt )*/ PicoSetSurfaceST ( curSurface,0,(curVertex + i), coords [ i ] );
812 /*if( has_vn )*/ PicoSetSurfaceNormal( curSurface, (curVertex + i), normals[ i ] );
813 }
814 /* add our triangle (A B C) */
815 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 2),(picoIndex_t)( curVertex + 0 ) );
816 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 1),(picoIndex_t)( curVertex + 1 ) );
817 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 0),(picoIndex_t)( curVertex + 2 ) );
818 curFace++;
819
820 /* if we don't have a simple triangle, but a quad... */
821 if (have_quad)
822 {
823 /* we have to add another triangle (2nd half of quad which is A C D) */
824 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 2),(picoIndex_t)( curVertex + 0 ) );
825 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 1),(picoIndex_t)( curVertex + 2 ) );
826 PicoSetSurfaceIndex( curSurface,(curFace * 3 + 0),(picoIndex_t)( curVertex + 3 ) );
827 curFace++;
828 }
829 /* increase vertex count */
830 curVertex += max;
831 }
832 }
833 /* skip unparsed rest of line and continue */
834 _pico_parse_skip_rest( p );
835 }
836 /* free memory used by temporary vertexdata */
837 FreeObjVertexData( vertexData );
838
839 /* return allocated pico model */
840 return model;
841 // return NULL;
842 }
843
844 /* pico file format module definition */
845 const picoModule_t picoModuleOBJ =
846 {
847 "0.6-b", /* module version string */
848 "Wavefront ASCII", /* module display name */
849 "seaw0lf", /* author's name */
850 "2002 seaw0lf", /* module copyright */
851 {
852 "obj",NULL,NULL,NULL /* default extensions to use */
853 },
854 _obj_canload, /* validation routine */
855 _obj_load, /* load routine */
856 NULL, /* save validation routine */
857 NULL /* save routine */
858 };
859