pm_obj.c

Go to the documentation of this file.
00001 /* -----------------------------------------------------------------------------
00002 
00003  PicoModel Library
00004 
00005  Copyright (c) 2002, Randy Reddig & seaw0lf
00006  All rights reserved.
00007 
00008  Redistribution and use in source and binary forms, with or without modification,
00009  are permitted provided that the following conditions are met:
00010 
00011  Redistributions of source code must retain the above copyright notice, this list
00012  of conditions and the following disclaimer.
00013 
00014  Redistributions in binary form must reproduce the above copyright notice, this
00015  list of conditions and the following disclaimer in the documentation and/or
00016  other materials provided with the distribution.
00017 
00018  Neither the names of the copyright holders nor the names of its contributors may
00019  be used to endorse or promote products derived from this software without
00020  specific prior written permission.
00021 
00022  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00023  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00024  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
00025  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
00026  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
00027  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00028  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
00029  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
00030  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00031  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00032 
00033  ----------------------------------------------------------------------------- */
00034 
00035 /* marker */
00036 #define PM_OBJ_C
00037 
00038 /* dependencies */
00039 #include "picointernal.h"
00040 
00041 /* todo:
00042  * - '_obj_load' code crashes in a weird way after
00043  *   '_obj_mtl_load' for a few .mtl files
00044  * - process 'mtllib' rather than using <model>.mtl
00045  * - handle 'usemtl' statements
00046  */
00047 /* uncomment when debugging this module */
00048 /* #define DEBUG_PM_OBJ */
00049 /* #define DEBUG_PM_OBJ_EX */
00050 
00052 typedef struct SObjVertexData
00053 {
00054     picoVec3_t v; 
00055     picoVec2_t vt; 
00056     picoVec3_t vn; 
00057 } TObjVertexData;
00058 
00062 static int _obj_canload (PM_PARAMS_CANLOAD)
00063 {
00064     picoParser_t *p;
00065 
00066     /* check data length */
00067     if (bufSize < 30)
00068         return PICO_PMV_ERROR_SIZE;
00069 
00070     /* first check file extension. we have to do this for objs
00071      * cause there is no good way to identify the contents */
00072     if (_pico_stristr(fileName, ".obj") != NULL || _pico_stristr(fileName, ".wf") != NULL) {
00073         return PICO_PMV_OK;
00074     }
00075     /* if the extension check failed we parse through the first
00076      * few lines in file and look for common keywords often
00077      * appearing at the beginning of wavefront objects */
00078 
00079     /* allocate a new pico parser */
00080     p = _pico_new_parser((picoByte_t *) buffer, bufSize);
00081     if (p == NULL)
00082         return PICO_PMV_ERROR_MEMORY;
00083 
00084     /* parse obj head line by line for type check */
00085     while (1) {
00086         /* get first token on line */
00087         if (_pico_parse_first(p) == NULL)
00088             break;
00089 
00090         /* we only parse the first few lines, say 80 */
00091         if (p->curLine > 80)
00092             break;
00093 
00094         /* skip empty lines */
00095         if (p->token == NULL || !strlen(p->token))
00096             continue;
00097 
00098         /* material library keywords are teh good */
00099         if (!_pico_stricmp(p->token, "usemtl") || !_pico_stricmp(p->token, "mtllib") || !_pico_stricmp(p->token, "g")
00100                 ||
00101                 !_pico_stricmp(p->token, "v")) /* v,g bit fishy, but uh... */
00102         {
00103             /* free the pico parser thing */
00104             _pico_free_parser(p);
00105 
00106             /* seems to be a valid wavefront obj */
00107             return PICO_PMV_OK;
00108         }
00109         /* skip rest of line */
00110         _pico_parse_skip_rest(p);
00111     }
00112     /* free the pico parser thing */
00113     _pico_free_parser(p);
00114 
00115     /* doesn't really look like an obj to us */
00116     return PICO_PMV_ERROR;
00117 }
00118 
00119 #define SIZE_OBJ_STEP  4096
00120 
00125 static TObjVertexData *SizeObjVertexData (TObjVertexData *vertexData, int reqEntries, int *entries, int *allocated)
00126 {
00127     int newAllocated;
00128 
00129     /* sanity checks */
00130     if (reqEntries < 1)
00131         return NULL;
00132     if (entries == NULL || allocated == NULL)
00133         return NULL; /* must have */
00134 
00135     /* no need to grow yet */
00136     if (vertexData && (reqEntries < *allocated)) {
00137         *entries = reqEntries;
00138         return vertexData;
00139     }
00140     /* given vertex data ptr not allocated yet */
00141     if (vertexData == NULL) {
00142         /* how many entries to allocate */
00143         newAllocated = (reqEntries > SIZE_OBJ_STEP) ? reqEntries : SIZE_OBJ_STEP;
00144 
00145         /* throw out an extended debug message */
00146 #ifdef DEBUG_PM_OBJ_EX
00147         printf("SizeObjVertexData: allocate (%d entries)\n",
00148                 newAllocated);
00149 #endif
00150         /* first time allocation */
00151         vertexData = (TObjVertexData *) _pico_alloc(sizeof(TObjVertexData) * newAllocated);
00152 
00153         /* allocation failed */
00154         if (vertexData == NULL)
00155             return NULL;
00156 
00157         /* allocation succeeded */
00158         *allocated = newAllocated;
00159         *entries = reqEntries;
00160         return vertexData;
00161     }
00162     /* given vertex data ptr needs to be resized */
00163     if (reqEntries == *allocated) {
00164         newAllocated = (*allocated + SIZE_OBJ_STEP);
00165 
00166         /* throw out an extended debug message */
00167 #ifdef DEBUG_PM_OBJ_EX
00168         printf("SizeObjVertexData: reallocate (%d entries)\n",
00169                 newAllocated);
00170 #endif
00171         /* try to reallocate */
00172         vertexData = (TObjVertexData *) _pico_realloc((void *) &vertexData, sizeof(TObjVertexData) * (*allocated),
00173                 sizeof(TObjVertexData) * (newAllocated));
00174 
00175         /* reallocation failed */
00176         if (vertexData == NULL)
00177             return NULL;
00178 
00179         /* reallocation succeeded */
00180         *allocated = newAllocated;
00181         *entries = reqEntries;
00182         return vertexData;
00183     }
00184     /* we're b0rked when we reach this */
00185     return NULL;
00186 }
00187 
00188 static void FreeObjVertexData (TObjVertexData *vertexData)
00189 {
00190     if (vertexData != NULL) {
00191         free((TObjVertexData *) vertexData);
00192     }
00193 }
00194 
00195 static picoShader_t *_obj_default_shader (picoModel_t *model)
00196 {
00197     picoShader_t *picoShader = PicoNewShader(model);
00198     char *skinname = _pico_clone_alloc(model->fileName);
00199     _pico_setfext(skinname, "");
00200 
00201     PicoSetShaderName(picoShader, skinname);
00202 
00203     _pico_free(skinname);
00204 
00205     return picoShader;
00206 }
00207 
00208 #if 0
00209 static int _obj_mtl_load (picoModel_t *model)
00210 {
00211     picoParser_t *p;
00212     picoByte_t *mtlBuffer;
00213     int mtlBufSize;
00214     char *fileName;
00215 
00216     /* sanity checks */
00217     if (model == NULL || model->fileName == NULL)
00218         return 0;
00219 
00220     /* skip if we have a zero length model file name */
00221     if (!strlen(model->fileName))
00222         return 0;
00223 
00224     /* helper */
00225 #define _obj_mtl_error_return \
00226     { \
00227         _pico_free_parser( p ); \
00228         _pico_free_file( mtlBuffer ); \
00229         _pico_free( fileName ); \
00230         return 0; \
00231     }
00232     /* alloc copy of model file name */
00233     fileName = _pico_clone_alloc(model->fileName);
00234     if (fileName == NULL)
00235         return 0;
00236 
00237     /* change extension of model file to .mtl */
00238     _pico_setfext(fileName, "mtl");
00239 
00240     /* load .mtl file contents */
00241     _pico_load_file(fileName, &mtlBuffer, &mtlBufSize);
00242 
00243     /* check result */
00244     if (mtlBufSize == 0)
00245         return 1; /* file is empty: no error */
00246     if (mtlBufSize < 0)
00247         return 0; /* load failed: error */
00248 
00249     /* create a new pico parser */
00250     p = _pico_new_parser(mtlBuffer, mtlBufSize);
00251     if (p == NULL)
00252         _obj_mtl_error_return;
00253 
00254     /* doo teh .mtl parse */
00255     while (1) {
00256         /* get next token in material file */
00257         if (_pico_parse(p, 1) == NULL)
00258             break;
00259 #if 0
00260 
00261         /* skip empty lines */
00262         if (p->token == NULL || !strlen(p->token))
00263             continue;
00264 
00265         /* skip comment lines */
00266         if (p->token[0] == '#') {
00267             _pico_parse_skip_rest(p);
00268             continue;
00269         }
00270         /* new material */
00271         if (!_pico_stricmp(p->token, "newmtl")) {
00272             picoShader_t *shader;
00273             char *name;
00274 
00275             /* get material name */
00276             name = _pico_parse(p, 0);
00277 
00278             /* validate material name */
00279             if (name == NULL || !strlen(name)) {
00280                 _pico_printf(PICO_ERROR, "Missing material name in MTL, line %d.", p->curLine);
00281                 _obj_mtl_error_return;
00282             }
00283             /* create a new pico shader */
00284             shader = PicoNewShader(model);
00285             if (shader == NULL)
00286                 _obj_mtl_error_return;
00287 
00288             /* set shader name */
00289             PicoSetShaderName(shader, name);
00290 
00291             /* assign pointer to current shader */
00292             curShader = shader;
00293         }
00294         /* diffuse map name */
00295         else if (!_pico_stricmp(p->token, "map_kd")) {
00296             char *mapName;
00297 
00298             /* pointer to current shader must be valid */
00299             if (curShader == NULL)
00300                 _obj_mtl_error_return;
00301 
00302             /* get material's diffuse map name */
00303             mapName = _pico_parse(p, 0);
00304 
00305             /* validate map name */
00306             if (mapName == NULL || !strlen(mapName)) {
00307                 _pico_printf(PICO_ERROR, "Missing material map name in MTL, line %d.", p->curLine);
00308                 _obj_mtl_error_return;
00309             }
00310             /* set shader map name */
00311             PicoSetShaderMapName(shader, mapName);
00312         }
00313         /* dissolve factor (pseudo transparency 0..1) */
00314         /* where 0 means 100% transparent and 1 means opaque */
00315         else if (!_pico_stricmp(p->token, "d")) {
00316             picoByte_t *diffuse;
00317             float value;
00318 
00319             /* get dissolve factor */
00320             if (!_pico_parse_float(p, &value))
00321                 _obj_mtl_error_return;
00322 
00323             /* set shader transparency */
00324             PicoSetShaderTransparency(curShader, value);
00325 
00326             /* get shader's diffuse color */
00327             diffuse = PicoGetShaderDiffuseColor(curShader);
00328 
00329             /* set diffuse alpha to transparency */
00330             diffuse[3] = (picoByte_t) (value * 255.0);
00331 
00332             /* set shader's new diffuse color */
00333             PicoSetShaderDiffuseColor(curShader, diffuse);
00334         }
00335         /* shininess (phong specular component) */
00336         else if (!_pico_stricmp(p->token, "ns")) {
00337             /* remark:
00338              * - well, this is some major obj spec fuckup once again. some
00339              *   apps store this in 0..1 range, others use 0..100 range,
00340              *   even others use 0..2048 range, and again others use the
00341              *   range 0..128, some even use 0..1000, 0..200, 400..700,
00342              *   honestly, what's up with the 3d app coders? happens when
00343              *   you smoke too much weed i guess. -sea
00344              */
00345             float value;
00346 
00347             /* pointer to current shader must be valid */
00348             if (curShader == NULL)
00349                 _obj_mtl_error_return;
00350 
00351             /* get totally screwed up shininess (a random value in fact ;) */
00352             if (!_pico_parse_float(p, &value))
00353                 _obj_mtl_error_return;
00354 
00355             /* okay, there is no way to set this correctly, so we simply */
00356             /* try to guess a few ranges (most common ones i have seen) */
00357 
00358             /* assume 0..2048 range */
00359             if (value > 1000)
00360                 value = 128.0 * (value / 2048.0);
00361             /* assume 0..1000 range */
00362             else if (value > 200)
00363                 value = 128.0 * (value / 1000.0);
00364             /* assume 0..200 range */
00365             else if (value > 100)
00366                 value = 128.0 * (value / 200.0);
00367             /* assume 0..100 range */
00368             else if (value > 1)
00369                 value = 128.0 * (value / 100.0);
00370             /* assume 0..1 range */
00371             else {
00372                 value *= 128.0;
00373             }
00374             /* negative shininess is bad (yes, i have seen it...) */
00375             if (value < 0.0)
00376                 value = 0.0;
00377 
00378             /* set the pico shininess value in range 0..127 */
00379             /* geez, .obj is such a mess... */
00380             PicoSetShaderShininess(curShader, value);
00381         }
00382         /* kol0r ambient (wut teh fuk does "ka" stand for?) */
00383         else if (!_pico_stricmp(p->token, "ka")) {
00384             picoColor_t color;
00385             picoVec3_t v;
00386 
00387             /* pointer to current shader must be valid */
00388             if (curShader == NULL)
00389                 _obj_mtl_error_return;
00390 
00391             /* get color vector */
00392             if (!_pico_parse_vec(p, v))
00393                 _obj_mtl_error_return;
00394 
00395             /* scale to byte range */
00396             color[0] = (picoByte_t) (v[0] * 255);
00397             color[1] = (picoByte_t) (v[1] * 255);
00398             color[2] = (picoByte_t) (v[2] * 255);
00399             color[3] = (picoByte_t) (255);
00400 
00401             /* set ambient color */
00402             PicoSetShaderAmbientColor(curShader, color);
00403         }
00404         /* kol0r diffuse */
00405         else if (!_pico_stricmp(p->token, "kd")) {
00406             picoColor_t color;
00407             picoVec3_t v;
00408 
00409             /* pointer to current shader must be valid */
00410             if (curShader == NULL)
00411                 _obj_mtl_error_return;
00412 
00413             /* get color vector */
00414             if (!_pico_parse_vec(p, v))
00415                 _obj_mtl_error_return;
00416 
00417             /* scale to byte range */
00418             color[0] = (picoByte_t) (v[0] * 255);
00419             color[1] = (picoByte_t) (v[1] * 255);
00420             color[2] = (picoByte_t) (v[2] * 255);
00421             color[3] = (picoByte_t) (255);
00422 
00423             /* set diffuse color */
00424             PicoSetShaderDiffuseColor(curShader, color);
00425         }
00426         /* kol0r specular */
00427         else if (!_pico_stricmp(p->token, "ks")) {
00428             picoColor_t color;
00429             picoVec3_t v;
00430 
00431             /* pointer to current shader must be valid */
00432             if (curShader == NULL)
00433                 _obj_mtl_error_return;
00434 
00435             /* get color vector */
00436             if (!_pico_parse_vec(p, v))
00437                 _obj_mtl_error_return;
00438 
00439             /* scale to byte range */
00440             color[0] = (picoByte_t) (v[0] * 255);
00441             color[1] = (picoByte_t) (v[1] * 255);
00442             color[2] = (picoByte_t) (v[2] * 255);
00443             color[3] = (picoByte_t) (255);
00444 
00445             /* set specular color */
00446             PicoSetShaderSpecularColor(curShader, color);
00447         }
00448 #endif
00449         /* skip rest of line */
00450         _pico_parse_skip_rest(p);
00451     }
00452 
00453     /* free parser, file buffer, and file name */
00454     _pico_free_parser(p);
00455     _pico_free_file(mtlBuffer);
00456     _pico_free(fileName);
00457 
00458     /* return with success */
00459     return 1;
00460 }
00461 #endif
00462 
00466 static picoModel_t *_obj_load (PM_PARAMS_LOAD)
00467 {
00468     TObjVertexData *vertexData = NULL;
00469     picoModel_t *model;
00470     picoSurface_t *curSurface = NULL;
00471     picoParser_t *p;
00472     int allocated;
00473     int entries;
00474     int numVerts = 0;
00475     int numNormals = 0;
00476     int numUVs = 0;
00477     int curVertex = 0;
00478     int curFace = 0;
00479     picoShader_t *shader;
00480 
00481     /* helper */
00482 #define _obj_error_return(m) \
00483     { \
00484         _pico_printf( PICO_ERROR,"%s in OBJ, line %d.",m,p->curLine); \
00485         _pico_free_parser( p ); \
00486         FreeObjVertexData( vertexData ); \
00487         PicoFreeModel( model ); \
00488         return NULL; \
00489     }
00490     /* alllocate a new pico parser */
00491     p = _pico_new_parser((picoByte_t *) buffer, bufSize);
00492     if (p == NULL)
00493         return NULL;
00494 
00495     /* create a new pico model */
00496     model = PicoNewModel();
00497     if (model == NULL) {
00498         _pico_free_parser(p);
00499         return NULL;
00500     }
00501     /* do model setup */
00502     PicoSetModelFrameNum(model, frameNum);
00503     PicoSetModelName(model, fileName);
00504     PicoSetModelFileName(model, fileName);
00505 
00506     /* try loading the materials; we don't handle the result */
00507     shader = _obj_default_shader(model);
00508 #if 0
00509     shader = _obj_mtl_load(model);
00510 #endif
00511 
00512 
00513     /* parse obj line by line */
00514     while (1) {
00515         /* get first token on line */
00516         if (_pico_parse_first(p) == NULL)
00517             break;
00518 
00519         /* skip empty lines */
00520         if (p->token == NULL || !strlen(p->token))
00521             continue;
00522 
00523         /* skip comment lines */
00524         if (p->token[0] == '#') {
00525             _pico_parse_skip_rest(p);
00526             continue;
00527         }
00528         /* vertex */
00529         if (!_pico_stricmp(p->token, "v")) {
00530             TObjVertexData *data;
00531             picoVec3_t v;
00532 
00533             vertexData = SizeObjVertexData(vertexData, numVerts + 1, &entries, &allocated);
00534             if (vertexData == NULL)
00535                 _obj_error_return("Realloc of vertex data failed (1)");
00536 
00537             data = &vertexData[numVerts++];
00538 
00539             /* get and copy vertex */
00540             if (!_pico_parse_vec(p, v))
00541                 _obj_error_return("Vertex parse error");
00542 
00543             _pico_copy_vec(v, data->v);
00544 
00545 #ifdef DEBUG_PM_OBJ_EX
00546             printf("Vertex: x: %f y: %f z: %f\n",v[0],v[1],v[2]);
00547 #endif
00548         }
00549         /* uv coord */
00550         else if (!_pico_stricmp(p->token, "vt")) {
00551             TObjVertexData *data;
00552             picoVec2_t coord;
00553 
00554             vertexData = SizeObjVertexData(vertexData, numUVs + 1, &entries, &allocated);
00555             if (vertexData == NULL)
00556                 _obj_error_return("Realloc of vertex data failed (2)");
00557 
00558             data = &vertexData[numUVs++];
00559 
00560             /* get and copy tex coord */
00561             if (!_pico_parse_vec2(p, coord))
00562                 _obj_error_return("UV coord parse error");
00563 
00564             _pico_copy_vec2(coord, data->vt);
00565 
00566 #ifdef DEBUG_PM_OBJ_EX
00567             printf("TexCoord: u: %f v: %f\n",coord[0],coord[1]);
00568 #endif
00569         }
00570         /* vertex normal */
00571         else if (!_pico_stricmp(p->token, "vn")) {
00572             TObjVertexData *data;
00573             picoVec3_t n;
00574 
00575             vertexData = SizeObjVertexData(vertexData, numNormals + 1, &entries, &allocated);
00576             if (vertexData == NULL)
00577                 _obj_error_return("Realloc of vertex data failed (3)");
00578 
00579             data = &vertexData[numNormals++];
00580 
00581             /* get and copy vertex normal */
00582             if (!_pico_parse_vec(p, n))
00583                 _obj_error_return("Vertex normal parse error");
00584 
00585             _pico_copy_vec(n, data->vn);
00586 
00587 #ifdef DEBUG_PM_OBJ_EX
00588             printf("Normal: x: %f y: %f z: %f\n",n[0],n[1],n[2]);
00589 #endif
00590         }
00591         /* new group (for us this means a new surface) */
00592         else if (!_pico_stricmp(p->token, "g")) {
00593             picoSurface_t *newSurface;
00594             char *groupName;
00595 
00596             /* get first group name (ignore 2nd,3rd,etc.) */
00597             groupName = _pico_parse(p, 0);
00598             if (groupName == NULL || !strlen(groupName)) {
00599                 /* some obj exporters feel like they don't need to */
00600                 /* supply a group name. so we gotta handle it here */
00601 #if 1
00602                 strcpy(p->token, "default");
00603                 groupName = p->token;
00604 #else
00605                 _obj_error_return("Invalid or missing group name");
00606 #endif
00607             }
00608             /* allocate a pico surface */
00609             newSurface = PicoNewSurface(model);
00610             if (newSurface == NULL)
00611                 _obj_error_return("Error allocating surface");
00612 
00613             /* reset face index for surface */
00614             curFace = 0;
00615 
00616             /* set ptr to current surface */
00617             curSurface = newSurface;
00618 
00619             /* we use triangle meshes */
00620             PicoSetSurfaceType(newSurface, PICO_TRIANGLES);
00621 
00622             /* set surface name */
00623             PicoSetSurfaceName(newSurface, groupName);
00624 
00625             /* associate current surface with newly created shader */
00626             PicoSetSurfaceShader(newSurface, shader);
00627 
00628 #ifdef DEBUG_PM_OBJ_EX
00629             printf("Group: '%s'\n",groupName);
00630 #endif
00631         }
00632         /* face (oh jesus, hopefully this will do the job right ;) */
00633         else if (!_pico_stricmp(p->token, "f")) {
00634             /* okay, this is a mess. some 3d apps seem to try being unique, */
00635             /* hello cinema4d & 3d exploration, feel good today?, and save */
00636             /* this crap in tons of different formats. gah, those screwed */
00637             /* coders. tho the wavefront obj standard defines exactly two */
00638             /* ways of storing face information. so, i really won't support */
00639             /* such stupid extravaganza here! */
00640 
00641             picoVec3_t verts[4];
00642             picoVec3_t normals[4];
00643             picoVec2_t coords[4];
00644 
00645             int iv[4], has_v;
00646             int ivt[4], has_vt = 0;
00647             int ivn[4], has_vn = 0;
00648             int have_quad = 0;
00649             int slashcount;
00650             int doubleslash;
00651             int i;
00652 
00653             /* group defs *must* come before faces */
00654             if (curSurface == NULL)
00655                 _obj_error_return("No group defined for faces");
00656 
00657 #ifdef DEBUG_PM_OBJ_EX
00658             printf("Face: ");
00659 #endif
00660             /* read vertex/uv/normal indices for the first three face */
00661             /* vertices (cause we only support triangles) into 'i*[]' */
00662             /* store the actual vertex/uv/normal data in three arrays */
00663             /* called 'verts','coords' and 'normals'. */
00664             for (i = 0; i < 4; i++) {
00665                 char *str;
00666 
00667                 /* get next vertex index string (different */
00668                 /* formats are handled below) */
00669                 str = _pico_parse(p, 0);
00670                 if (str == NULL) {
00671                     /* just break for quads */
00672                     if (i == 3)
00673                         break;
00674 
00675                     /* error otherwise */
00676                     _obj_error_return("Face parse error");
00677                 }
00678                 /* if this is the fourth index string we're */
00679                 /* parsing we assume that we have a quad */
00680                 if (i == 3)
00681                     have_quad = 1;
00682 
00683                 /* get slash count once */
00684                 if (i == 0) {
00685                     slashcount = _pico_strchcount(str, '/');
00686                     doubleslash = strstr(str, "//") != NULL;
00687                 }
00688                 /* handle format 'v//vn' */
00689                 if (doubleslash && (slashcount == 2)) {
00690                     has_v = has_vn = 1;
00691                     sscanf(str, "%d//%d", &iv[i], &ivn[i]);
00692                 }
00693                 /* handle format 'v/vt/vn' */
00694                 else if (!doubleslash && (slashcount == 2)) {
00695                     has_v = has_vt = has_vn = 1;
00696                     sscanf(str, "%d/%d/%d", &iv[i], &ivt[i], &ivn[i]);
00697                 }
00698                 /* handle format 'v/vt' (non-standard fuckage) */
00699                 else if (!doubleslash && (slashcount == 1)) {
00700                     has_v = has_vt = 1;
00701                     sscanf(str, "%d/%d", &iv[i], &ivt[i]);
00702                 }
00703                 /* else assume face format 'v' */
00704                 /* (must have been invented by some bored granny) */
00705                 else {
00706                     /* get single vertex index */
00707                     has_v = 1;
00708                     iv[i] = atoi(str);
00709 
00710                     /* either invalid face format or out of range */
00711                     if (iv[i] == 0)
00712                         _obj_error_return("Invalid face format");
00713                 }
00714                 /* fix useless back references */
00715                 /* todo: check if this works as it is supposed to */
00716 
00717                 /* assign new indices */
00718                 if (iv[i] < 0)
00719                     iv[i] = (numVerts - iv[i]);
00720                 if (ivt[i] < 0)
00721                     ivt[i] = (numUVs - ivt[i]);
00722                 if (ivn[i] < 0)
00723                     ivn[i] = (numNormals - ivn[i]);
00724 
00725                 /* validate indices */
00726                 /* - commented out. index range checks will trigger
00727                  if (iv [ i ] < 1) iv [ i ] = 1;
00728                  if (ivt[ i ] < 1) ivt[ i ] = 1;
00729                  if (ivn[ i ] < 1) ivn[ i ] = 1;
00730                  */
00731                 /* set vertex origin */
00732                 if (has_v) {
00733                     /* check vertex index range */
00734                     if (iv[i] < 1 || iv[i] > numVerts)
00735                         _obj_error_return("Vertex index out of range");
00736 
00737                     /* get vertex data */
00738                     verts[i][0] = vertexData[iv[i] - 1].v[0];
00739                     verts[i][1] = vertexData[iv[i] - 1].v[1];
00740                     verts[i][2] = vertexData[iv[i] - 1].v[2];
00741                 }
00742                 /* set vertex normal */
00743                 if (has_vn) {
00744                     /* check normal index range */
00745                     if (ivn[i] < 1 || ivn[i] > numNormals)
00746                         _obj_error_return("Normal index out of range");
00747 
00748                     /* get normal data */
00749                     normals[i][0] = vertexData[ivn[i] - 1].vn[0];
00750                     normals[i][1] = vertexData[ivn[i] - 1].vn[1];
00751                     normals[i][2] = vertexData[ivn[i] - 1].vn[2];
00752                 }
00753                 /* set texture coordinate */
00754                 if (has_vt) {
00755                     /* check uv index range */
00756                     if (ivt[i] < 1 || ivt[i] > numUVs)
00757                         _obj_error_return("UV coord index out of range");
00758 
00759                     /* get uv coord data */
00760                     coords[i][0] = vertexData[ivt[i] - 1].vt[0];
00761                     coords[i][1] = vertexData[ivt[i] - 1].vt[1];
00762                     coords[i][1] = -coords[i][1];
00763                 }
00764 #ifdef DEBUG_PM_OBJ_EX
00765                 printf("(%4d",iv[ i ]);
00766                 if (has_vt) printf(" %4d",ivt[ i ]);
00767                 if (has_vn) printf(" %4d",ivn[ i ]);
00768                 printf(") ");
00769 #endif
00770             }
00771 #ifdef DEBUG_PM_OBJ_EX
00772             printf("\n");
00773 #endif
00774             /* now that we have extracted all the indices and have
00775              * read the actual data we need to assign all the crap
00776              * to our current pico surface */
00777             if (has_v) {
00778                 int max = 3;
00779                 if (have_quad)
00780                     max = 4;
00781 
00782                 /* assign all surface information */
00783                 for (i = 0; i < max; i++) {
00784                     PicoSetSurfaceXYZ(curSurface, (curVertex + i), verts[i]);
00785                     PicoSetSurfaceST(curSurface, 0, (curVertex + i), coords[i]);
00786                     PicoSetSurfaceNormal(curSurface, (curVertex + i), normals[i]);
00787                 }
00788                 /* add our triangle (A B C) */
00789                 PicoSetSurfaceIndex(curSurface, (curFace * 3 + 2), (picoIndex_t) (curVertex + 0));
00790                 PicoSetSurfaceIndex(curSurface, (curFace * 3 + 1), (picoIndex_t) (curVertex + 1));
00791                 PicoSetSurfaceIndex(curSurface, (curFace * 3 + 0), (picoIndex_t) (curVertex + 2));
00792                 curFace++;
00793 
00794                 /* if we don't have a simple triangle, but a quad... */
00795                 if (have_quad) {
00796                     /* we have to add another triangle (2nd half of quad which is A C D) */
00797                     PicoSetSurfaceIndex(curSurface, (curFace * 3 + 2), (picoIndex_t) (curVertex + 0));
00798                     PicoSetSurfaceIndex(curSurface, (curFace * 3 + 1), (picoIndex_t) (curVertex + 2));
00799                     PicoSetSurfaceIndex(curSurface, (curFace * 3 + 0), (picoIndex_t) (curVertex + 3));
00800                     curFace++;
00801                 }
00802 
00803                 /* associate current surface with newly created shader */
00804                 PicoSetSurfaceShader(curSurface, shader);
00805 
00806                 /* increase vertex count */
00807                 curVertex += max;
00808             }
00809         }
00810         /* skip unparsed rest of line and continue */
00811         _pico_parse_skip_rest(p);
00812     }
00813     /* free memory used by temporary vertexdata */
00814     FreeObjVertexData(vertexData);
00815 
00816     /* return allocated pico model */
00817     return model;
00818 }
00819 
00820 /* pico file format module definition */
00821 const picoModule_t picoModuleOBJ = { "0.6-b", /* module version string */
00822 "Wavefront ASCII", /* module display name */
00823 "seaw0lf", /* author's name */
00824 "2002 seaw0lf", /* module copyright */
00825 { "obj", NULL, NULL, NULL /* default extensions to use */
00826 }, _obj_canload, /* validation routine */
00827 _obj_load, /* load routine */
00828 NULL, /* save validation routine */
00829 NULL /* save routine */
00830 };

Generated by  doxygen 1.6.2