00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036 #define PM_OBJ_C
00037
00038
00039 #include "picointernal.h"
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
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
00067 if (bufSize < 30)
00068 return PICO_PMV_ERROR_SIZE;
00069
00070
00071
00072 if (_pico_stristr(fileName, ".obj") != NULL || _pico_stristr(fileName, ".wf") != NULL) {
00073 return PICO_PMV_OK;
00074 }
00075
00076
00077
00078
00079
00080 p = _pico_new_parser((picoByte_t *) buffer, bufSize);
00081 if (p == NULL)
00082 return PICO_PMV_ERROR_MEMORY;
00083
00084
00085 while (1) {
00086
00087 if (_pico_parse_first(p) == NULL)
00088 break;
00089
00090
00091 if (p->curLine > 80)
00092 break;
00093
00094
00095 if (p->token == NULL || !strlen(p->token))
00096 continue;
00097
00098
00099 if (!_pico_stricmp(p->token, "usemtl") || !_pico_stricmp(p->token, "mtllib") || !_pico_stricmp(p->token, "g")
00100 ||
00101 !_pico_stricmp(p->token, "v"))
00102 {
00103
00104 _pico_free_parser(p);
00105
00106
00107 return PICO_PMV_OK;
00108 }
00109
00110 _pico_parse_skip_rest(p);
00111 }
00112
00113 _pico_free_parser(p);
00114
00115
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
00130 if (reqEntries < 1)
00131 return NULL;
00132 if (entries == NULL || allocated == NULL)
00133 return NULL;
00134
00135
00136 if (vertexData && (reqEntries < *allocated)) {
00137 *entries = reqEntries;
00138 return vertexData;
00139 }
00140
00141 if (vertexData == NULL) {
00142
00143 newAllocated = (reqEntries > SIZE_OBJ_STEP) ? reqEntries : SIZE_OBJ_STEP;
00144
00145
00146 #ifdef DEBUG_PM_OBJ_EX
00147 printf("SizeObjVertexData: allocate (%d entries)\n",
00148 newAllocated);
00149 #endif
00150
00151 vertexData = (TObjVertexData *) _pico_alloc(sizeof(TObjVertexData) * newAllocated);
00152
00153
00154 if (vertexData == NULL)
00155 return NULL;
00156
00157
00158 *allocated = newAllocated;
00159 *entries = reqEntries;
00160 return vertexData;
00161 }
00162
00163 if (reqEntries == *allocated) {
00164 newAllocated = (*allocated + SIZE_OBJ_STEP);
00165
00166
00167 #ifdef DEBUG_PM_OBJ_EX
00168 printf("SizeObjVertexData: reallocate (%d entries)\n",
00169 newAllocated);
00170 #endif
00171
00172 vertexData = (TObjVertexData *) _pico_realloc((void *) &vertexData, sizeof(TObjVertexData) * (*allocated),
00173 sizeof(TObjVertexData) * (newAllocated));
00174
00175
00176 if (vertexData == NULL)
00177 return NULL;
00178
00179
00180 *allocated = newAllocated;
00181 *entries = reqEntries;
00182 return vertexData;
00183 }
00184
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
00217 if (model == NULL || model->fileName == NULL)
00218 return 0;
00219
00220
00221 if (!strlen(model->fileName))
00222 return 0;
00223
00224
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
00233 fileName = _pico_clone_alloc(model->fileName);
00234 if (fileName == NULL)
00235 return 0;
00236
00237
00238 _pico_setfext(fileName, "mtl");
00239
00240
00241 _pico_load_file(fileName, &mtlBuffer, &mtlBufSize);
00242
00243
00244 if (mtlBufSize == 0)
00245 return 1;
00246 if (mtlBufSize < 0)
00247 return 0;
00248
00249
00250 p = _pico_new_parser(mtlBuffer, mtlBufSize);
00251 if (p == NULL)
00252 _obj_mtl_error_return;
00253
00254
00255 while (1) {
00256
00257 if (_pico_parse(p, 1) == NULL)
00258 break;
00259 #if 0
00260
00261
00262 if (p->token == NULL || !strlen(p->token))
00263 continue;
00264
00265
00266 if (p->token[0] == '#') {
00267 _pico_parse_skip_rest(p);
00268 continue;
00269 }
00270
00271 if (!_pico_stricmp(p->token, "newmtl")) {
00272 picoShader_t *shader;
00273 char *name;
00274
00275
00276 name = _pico_parse(p, 0);
00277
00278
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
00284 shader = PicoNewShader(model);
00285 if (shader == NULL)
00286 _obj_mtl_error_return;
00287
00288
00289 PicoSetShaderName(shader, name);
00290
00291
00292 curShader = shader;
00293 }
00294
00295 else if (!_pico_stricmp(p->token, "map_kd")) {
00296 char *mapName;
00297
00298
00299 if (curShader == NULL)
00300 _obj_mtl_error_return;
00301
00302
00303 mapName = _pico_parse(p, 0);
00304
00305
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
00311 PicoSetShaderMapName(shader, mapName);
00312 }
00313
00314
00315 else if (!_pico_stricmp(p->token, "d")) {
00316 picoByte_t *diffuse;
00317 float value;
00318
00319
00320 if (!_pico_parse_float(p, &value))
00321 _obj_mtl_error_return;
00322
00323
00324 PicoSetShaderTransparency(curShader, value);
00325
00326
00327 diffuse = PicoGetShaderDiffuseColor(curShader);
00328
00329
00330 diffuse[3] = (picoByte_t) (value * 255.0);
00331
00332
00333 PicoSetShaderDiffuseColor(curShader, diffuse);
00334 }
00335
00336 else if (!_pico_stricmp(p->token, "ns")) {
00337
00338
00339
00340
00341
00342
00343
00344
00345 float value;
00346
00347
00348 if (curShader == NULL)
00349 _obj_mtl_error_return;
00350
00351
00352 if (!_pico_parse_float(p, &value))
00353 _obj_mtl_error_return;
00354
00355
00356
00357
00358
00359 if (value > 1000)
00360 value = 128.0 * (value / 2048.0);
00361
00362 else if (value > 200)
00363 value = 128.0 * (value / 1000.0);
00364
00365 else if (value > 100)
00366 value = 128.0 * (value / 200.0);
00367
00368 else if (value > 1)
00369 value = 128.0 * (value / 100.0);
00370
00371 else {
00372 value *= 128.0;
00373 }
00374
00375 if (value < 0.0)
00376 value = 0.0;
00377
00378
00379
00380 PicoSetShaderShininess(curShader, value);
00381 }
00382
00383 else if (!_pico_stricmp(p->token, "ka")) {
00384 picoColor_t color;
00385 picoVec3_t v;
00386
00387
00388 if (curShader == NULL)
00389 _obj_mtl_error_return;
00390
00391
00392 if (!_pico_parse_vec(p, v))
00393 _obj_mtl_error_return;
00394
00395
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
00402 PicoSetShaderAmbientColor(curShader, color);
00403 }
00404
00405 else if (!_pico_stricmp(p->token, "kd")) {
00406 picoColor_t color;
00407 picoVec3_t v;
00408
00409
00410 if (curShader == NULL)
00411 _obj_mtl_error_return;
00412
00413
00414 if (!_pico_parse_vec(p, v))
00415 _obj_mtl_error_return;
00416
00417
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
00424 PicoSetShaderDiffuseColor(curShader, color);
00425 }
00426
00427 else if (!_pico_stricmp(p->token, "ks")) {
00428 picoColor_t color;
00429 picoVec3_t v;
00430
00431
00432 if (curShader == NULL)
00433 _obj_mtl_error_return;
00434
00435
00436 if (!_pico_parse_vec(p, v))
00437 _obj_mtl_error_return;
00438
00439
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
00446 PicoSetShaderSpecularColor(curShader, color);
00447 }
00448 #endif
00449
00450 _pico_parse_skip_rest(p);
00451 }
00452
00453
00454 _pico_free_parser(p);
00455 _pico_free_file(mtlBuffer);
00456 _pico_free(fileName);
00457
00458
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
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
00491 p = _pico_new_parser((picoByte_t *) buffer, bufSize);
00492 if (p == NULL)
00493 return NULL;
00494
00495
00496 model = PicoNewModel();
00497 if (model == NULL) {
00498 _pico_free_parser(p);
00499 return NULL;
00500 }
00501
00502 PicoSetModelFrameNum(model, frameNum);
00503 PicoSetModelName(model, fileName);
00504 PicoSetModelFileName(model, fileName);
00505
00506
00507 shader = _obj_default_shader(model);
00508 #if 0
00509 shader = _obj_mtl_load(model);
00510 #endif
00511
00512
00513
00514 while (1) {
00515
00516 if (_pico_parse_first(p) == NULL)
00517 break;
00518
00519
00520 if (p->token == NULL || !strlen(p->token))
00521 continue;
00522
00523
00524 if (p->token[0] == '#') {
00525 _pico_parse_skip_rest(p);
00526 continue;
00527 }
00528
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
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
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
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
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
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
00592 else if (!_pico_stricmp(p->token, "g")) {
00593 picoSurface_t *newSurface;
00594 char *groupName;
00595
00596
00597 groupName = _pico_parse(p, 0);
00598 if (groupName == NULL || !strlen(groupName)) {
00599
00600
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
00609 newSurface = PicoNewSurface(model);
00610 if (newSurface == NULL)
00611 _obj_error_return("Error allocating surface");
00612
00613
00614 curFace = 0;
00615
00616
00617 curSurface = newSurface;
00618
00619
00620 PicoSetSurfaceType(newSurface, PICO_TRIANGLES);
00621
00622
00623 PicoSetSurfaceName(newSurface, groupName);
00624
00625
00626 PicoSetSurfaceShader(newSurface, shader);
00627
00628 #ifdef DEBUG_PM_OBJ_EX
00629 printf("Group: '%s'\n",groupName);
00630 #endif
00631 }
00632
00633 else if (!_pico_stricmp(p->token, "f")) {
00634
00635
00636
00637
00638
00639
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
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
00661
00662
00663
00664 for (i = 0; i < 4; i++) {
00665 char *str;
00666
00667
00668
00669 str = _pico_parse(p, 0);
00670 if (str == NULL) {
00671
00672 if (i == 3)
00673 break;
00674
00675
00676 _obj_error_return("Face parse error");
00677 }
00678
00679
00680 if (i == 3)
00681 have_quad = 1;
00682
00683
00684 if (i == 0) {
00685 slashcount = _pico_strchcount(str, '/');
00686 doubleslash = strstr(str, "//") != NULL;
00687 }
00688
00689 if (doubleslash && (slashcount == 2)) {
00690 has_v = has_vn = 1;
00691 sscanf(str, "%d//%d", &iv[i], &ivn[i]);
00692 }
00693
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
00699 else if (!doubleslash && (slashcount == 1)) {
00700 has_v = has_vt = 1;
00701 sscanf(str, "%d/%d", &iv[i], &ivt[i]);
00702 }
00703
00704
00705 else {
00706
00707 has_v = 1;
00708 iv[i] = atoi(str);
00709
00710
00711 if (iv[i] == 0)
00712 _obj_error_return("Invalid face format");
00713 }
00714
00715
00716
00717
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
00726
00727
00728
00729
00730
00731
00732 if (has_v) {
00733
00734 if (iv[i] < 1 || iv[i] > numVerts)
00735 _obj_error_return("Vertex index out of range");
00736
00737
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
00743 if (has_vn) {
00744
00745 if (ivn[i] < 1 || ivn[i] > numNormals)
00746 _obj_error_return("Normal index out of range");
00747
00748
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
00754 if (has_vt) {
00755
00756 if (ivt[i] < 1 || ivt[i] > numUVs)
00757 _obj_error_return("UV coord index out of range");
00758
00759
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
00775
00776
00777 if (has_v) {
00778 int max = 3;
00779 if (have_quad)
00780 max = 4;
00781
00782
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
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
00795 if (have_quad) {
00796
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
00804 PicoSetSurfaceShader(curSurface, shader);
00805
00806
00807 curVertex += max;
00808 }
00809 }
00810
00811 _pico_parse_skip_rest(p);
00812 }
00813
00814 FreeObjVertexData(vertexData);
00815
00816
00817 return model;
00818 }
00819
00820
00821 const picoModule_t picoModuleOBJ = { "0.6-b",
00822 "Wavefront ASCII",
00823 "seaw0lf",
00824 "2002 seaw0lf",
00825 { "obj", NULL, NULL, NULL
00826 }, _obj_canload,
00827 _obj_load,
00828 NULL,
00829 NULL
00830 };