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