Home | API | File List | Examples | Download

lib3ds_mesh.c

00001 /*
00002     Copyright (C) 1996-2008 by Jan Eric Kyprianidis <www.kyprianidis.com>
00003     All rights reserved.
00004     
00005     This program is free  software: you can redistribute it and/or modify 
00006     it under the terms of the GNU Lesser General Public License as published 
00007     by the Free Software Foundation, either version 2.1 of the License, or 
00008     (at your option) any later version.
00009 
00010     Thisprogram  is  distributed in the hope that it will be useful, 
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of 
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
00013     GNU Lesser General Public License for more details.
00014     
00015     You should  have received a copy of the GNU Lesser General Public License
00016     along with  this program; If not, see <http://www.gnu.org/licenses/>. 
00017 */
00018 #include "lib3ds_impl.h"
00019 
00020 
00033 Lib3dsMesh*
00034 lib3ds_mesh_new(const char *name) {
00035     Lib3dsMesh *mesh;
00036 
00037     assert(name);
00038     assert(strlen(name) < 64);
00039 
00040     mesh = (Lib3dsMesh*) calloc(sizeof(Lib3dsMesh), 1);
00041     if (!mesh) {
00042         return (0);
00043     }
00044     strcpy(mesh->name, name);
00045     lib3ds_matrix_identity(mesh->matrix);
00046     mesh->map_type = LIB3DS_MAP_NONE;
00047     return (mesh);
00048 }
00049 
00050 
00056 void
00057 lib3ds_mesh_free(Lib3dsMesh *mesh) {
00058     lib3ds_mesh_resize_vertices(mesh, 0, 0, 0);
00059     lib3ds_mesh_resize_faces(mesh, 0);
00060     memset(mesh, 0, sizeof(Lib3dsMesh));
00061     free(mesh);
00062 }
00063 
00064 
00065 void
00066 lib3ds_mesh_resize_vertices(Lib3dsMesh *mesh, int nvertices, int use_texcos, int use_flags) {
00067     assert(mesh);
00068     mesh->vertices = lib3ds_util_realloc_array(mesh->vertices, mesh->nvertices, nvertices, 3 * sizeof(float));
00069     mesh->texcos = lib3ds_util_realloc_array(
00070         mesh->texcos, 
00071         mesh->texcos? mesh->nvertices : 0, 
00072         use_texcos? nvertices : 0, 
00073         2 * sizeof(float)
00074     );
00075     mesh->vflags = lib3ds_util_realloc_array(
00076         mesh->vflags, 
00077         mesh->vflags? mesh->nvertices : 0, 
00078         use_flags? nvertices : 0, 
00079         2 * sizeof(float)
00080     );
00081     mesh->nvertices = nvertices;
00082 }
00083 
00084 
00085 void 
00086 lib3ds_mesh_resize_faces(Lib3dsMesh *mesh, int nfaces) {
00087     int i;
00088     assert(mesh);
00089     mesh->faces = lib3ds_util_realloc_array(mesh->faces, mesh->nfaces, nfaces, sizeof(Lib3dsFace));
00090     for (i = mesh->nfaces; i < nfaces; ++i) {
00091         mesh->faces[i].material = -1;
00092     }
00093     mesh->nfaces = nfaces;
00094 }
00095 
00096 
00104 void
00105 lib3ds_mesh_bounding_box(Lib3dsMesh *mesh, float bmin[3], float bmax[3]) {
00106     int i;
00107     bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
00108     bmax[0] = bmax[1] = bmax[2] = -FLT_MAX;
00109 
00110     for (i = 0; i < mesh->nvertices; ++i) {
00111         lib3ds_vector_min(bmin, mesh->vertices[i]);
00112         lib3ds_vector_max(bmax, mesh->vertices[i]);
00113     }
00114 }
00115 
00116 
00117 void
00118 lib3ds_mesh_calculate_face_normals(Lib3dsMesh *mesh, float (*face_normals)[3]) {
00119     int i;
00120 
00121     if (!mesh->nfaces) {
00122         return;
00123     }
00124     for (i = 0; i < mesh->nfaces; ++i) {
00125         lib3ds_vector_normal(
00126             face_normals[i],
00127             mesh->vertices[mesh->faces[i].index[0]],
00128             mesh->vertices[mesh->faces[i].index[1]],
00129             mesh->vertices[mesh->faces[i].index[2]]
00130         );
00131     }
00132 }
00133 
00134 
00135 typedef struct Lib3dsFaces {
00136     struct Lib3dsFaces *next;
00137     int index;
00138     float normal[3];
00139 } Lib3dsFaces;
00140 
00141 
00162 void
00163 lib3ds_mesh_calculate_vertex_normals(Lib3dsMesh *mesh, float (*normals)[3]) {
00164     Lib3dsFaces **fl;
00165     Lib3dsFaces *fa;
00166     int i, j;
00167 
00168     if (!mesh->nfaces) {
00169         return;
00170     }
00171 
00172     fl = calloc(sizeof(Lib3dsFaces*), mesh->nvertices);
00173     fa = malloc(sizeof(Lib3dsFaces) * 3 * mesh->nfaces);
00174 
00175     for (i = 0; i < mesh->nfaces; ++i) {
00176         for (j = 0; j < 3; ++j) {
00177             Lib3dsFaces* l = &fa[3*i+j];
00178             float p[3], q[3], n[3];
00179             float len, weight;
00180 
00181             l->index = i;
00182             l->next = fl[mesh->faces[i].index[j]];
00183             fl[mesh->faces[i].index[j]] = l;
00184 
00185             lib3ds_vector_sub(p, mesh->vertices[mesh->faces[i].index[j<2? j + 1 : 0]], mesh->vertices[mesh->faces[i].index[j]]);
00186             lib3ds_vector_sub(q, mesh->vertices[mesh->faces[i].index[j>0? j - 1 : 2]], mesh->vertices[mesh->faces[i].index[j]]);
00187             lib3ds_vector_cross(n, p, q);
00188             len = lib3ds_vector_length(n);
00189             if (len > 0) {       
00190                 weight = (float)atan2(len, lib3ds_vector_dot(p, q));
00191                 lib3ds_vector_scalar_mul(l->normal, n, weight / len);
00192             } else {
00193                 lib3ds_vector_zero(l->normal);
00194             }
00195         }
00196     }
00197 
00198     for (i = 0; i < mesh->nfaces; ++i) {
00199         Lib3dsFace *f = &mesh->faces[i];
00200         for (j = 0; j < 3; ++j) {
00201             float n[3];
00202             Lib3dsFaces *p;
00203             Lib3dsFace *pf;
00204 
00205             assert(mesh->faces[i].index[j] < mesh->nvertices);
00206 
00207             if (f->smoothing_group) {
00208                 unsigned smoothing_group = f->smoothing_group;
00209 
00210                 lib3ds_vector_zero(n);
00211                 for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) {
00212                     pf = &mesh->faces[p->index];
00213                     if (pf->smoothing_group & f->smoothing_group)
00214                         smoothing_group |= pf->smoothing_group;
00215                 }
00216 
00217                 for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) {
00218                     pf = &mesh->faces[p->index];                
00219                     if (smoothing_group & pf->smoothing_group) {
00220                         lib3ds_vector_add(n, n, p->normal);
00221                     }
00222                 }
00223             } else {
00224                 lib3ds_vector_copy(n, fa[3*i+j].normal);
00225             }
00226 
00227             lib3ds_vector_normalize(n);
00228             lib3ds_vector_copy(normals[3*i+j], n);
00229         }
00230     }
00231 
00232     free(fa);
00233     free(fl);
00234 }
00235 
00236 
00237 static void
00238 face_array_read(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
00239     Lib3dsChunk c;
00240     uint16_t chunk;
00241     int i;
00242     uint16_t nfaces;
00243 
00244     lib3ds_chunk_read_start(&c, CHK_FACE_ARRAY, io);
00245 
00246     lib3ds_mesh_resize_faces(mesh, 0);
00247     nfaces = lib3ds_io_read_word(io);
00248     if (nfaces) {
00249         lib3ds_mesh_resize_faces(mesh, nfaces);
00250         for (i = 0; i < nfaces; ++i) {
00251             mesh->faces[i].index[0] = lib3ds_io_read_word(io);
00252             mesh->faces[i].index[1] = lib3ds_io_read_word(io);
00253             mesh->faces[i].index[2] = lib3ds_io_read_word(io);
00254             mesh->faces[i].flags = lib3ds_io_read_word(io);
00255         }
00256         lib3ds_chunk_read_tell(&c, io);
00257 
00258         while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00259             switch (chunk) {
00260                 case CHK_MSH_MAT_GROUP: {
00261                     char name[64];
00262                     unsigned n;
00263                     unsigned i;
00264                     int index;
00265                     int material;
00266 
00267                     lib3ds_io_read_string(io, name, 64);
00268                     material = lib3ds_file_material_by_name(file, name);
00269 
00270                     n = lib3ds_io_read_word(io);
00271                     for (i = 0; i < n; ++i) {
00272                         index = lib3ds_io_read_word(io);
00273                         if (index < mesh->nfaces) {
00274                             mesh->faces[index].material = material;
00275                         } else {
00276                             // TODO warning
00277                         }
00278                     }
00279                     break;
00280                 }
00281 
00282                 case CHK_SMOOTH_GROUP: {
00283                     int i;
00284                     for (i = 0; i < mesh->nfaces; ++i) {
00285                         mesh->faces[i].smoothing_group = lib3ds_io_read_dword(io);
00286                     }
00287                     break;
00288                 }
00289 
00290                 case CHK_MSH_BOXMAP: {
00291                     lib3ds_io_read_string(io, mesh->box_front, 64);
00292                     lib3ds_io_read_string(io, mesh->box_back, 64);
00293                     lib3ds_io_read_string(io, mesh->box_left, 64);
00294                     lib3ds_io_read_string(io, mesh->box_right, 64);
00295                     lib3ds_io_read_string(io, mesh->box_top, 64);
00296                     lib3ds_io_read_string(io, mesh->box_bottom, 64);
00297                     break;
00298                 }
00299 
00300                 default:
00301                     lib3ds_chunk_unknown(chunk,io);
00302             }
00303         }
00304 
00305     }
00306     lib3ds_chunk_read_end(&c, io);
00307 }
00308 
00309 
00310 void
00311 lib3ds_mesh_read(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
00312     Lib3dsChunk c;
00313     uint16_t chunk;
00314 
00315     lib3ds_chunk_read_start(&c, CHK_N_TRI_OBJECT, io);
00316 
00317     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00318         switch (chunk) {
00319             case CHK_MESH_MATRIX: {
00320                 int i, j;
00321 
00322                 lib3ds_matrix_identity(mesh->matrix);
00323                 for (i = 0; i < 4; i++) {
00324                     for (j = 0; j < 3; j++) {
00325                         mesh->matrix[i][j] = lib3ds_io_read_float(io);
00326                     }
00327                 }
00328                 break;
00329             }
00330 
00331             case CHK_MESH_COLOR: {
00332                 mesh->color = lib3ds_io_read_byte(io);
00333                 break;
00334             }
00335 
00336             case CHK_POINT_ARRAY: {
00337                 int i;
00338                 uint16_t nvertices = lib3ds_io_read_word(io);
00339                 lib3ds_mesh_resize_vertices(mesh, nvertices, mesh->texcos != NULL, mesh->vflags != NULL);
00340                 for (i = 0; i < mesh->nvertices; ++i) {
00341                     lib3ds_io_read_vector(io, mesh->vertices[i]);
00342                 }
00343                 break;
00344             }
00345 
00346             case CHK_POINT_FLAG_ARRAY: {
00347                 int i;
00348                 uint16_t nflags = lib3ds_io_read_word(io);
00349                 uint16_t nvertices = (mesh->nvertices >= nflags)? mesh->nvertices : nflags;
00350                 lib3ds_mesh_resize_vertices(mesh, nvertices, mesh->texcos != NULL, 1);
00351                 for (i = 0; i < nflags; ++i) {
00352                     mesh->vflags[i] = lib3ds_io_read_word(io);
00353                 }
00354                 break;
00355             }
00356 
00357             case CHK_FACE_ARRAY: {
00358                 lib3ds_chunk_read_reset(&c, io);
00359                 face_array_read(file, mesh, io);
00360                 break;
00361             }
00362 
00363             case CHK_MESH_TEXTURE_INFO: {
00364                 int i, j;
00365 
00366                 //FIXME: mesh->map_type = lib3ds_io_read_word(io);
00367 
00368                 for (i = 0; i < 2; ++i) {
00369                     mesh->map_tile[i] = lib3ds_io_read_float(io);
00370                 }
00371                 for (i = 0; i < 3; ++i) {
00372                     mesh->map_pos[i] = lib3ds_io_read_float(io);
00373                 }
00374                 mesh->map_scale = lib3ds_io_read_float(io);
00375 
00376                 lib3ds_matrix_identity(mesh->map_matrix);
00377                 for (i = 0; i < 4; i++) {
00378                     for (j = 0; j < 3; j++) {
00379                         mesh->map_matrix[i][j] = lib3ds_io_read_float(io);
00380                     }
00381                 }
00382                 for (i = 0; i < 2; ++i) {
00383                     mesh->map_planar_size[i] = lib3ds_io_read_float(io);
00384                 }
00385                 mesh->map_cylinder_height = lib3ds_io_read_float(io);
00386                 break;
00387             }
00388 
00389             case CHK_TEX_VERTS: {
00390                 int i;
00391                 uint16_t ntexcos = lib3ds_io_read_word(io);
00392                 uint16_t nvertices = (mesh->nvertices >= ntexcos)? mesh->nvertices : ntexcos;;
00393                 if (!mesh->texcos) {
00394                     lib3ds_mesh_resize_vertices(mesh, nvertices, 1, mesh->vflags != NULL);
00395                 }
00396                 for (i = 0; i < ntexcos; ++i) {
00397                     mesh->texcos[i][0] = lib3ds_io_read_float(io);
00398                     mesh->texcos[i][1] = lib3ds_io_read_float(io);
00399                 }
00400                 break;
00401             }
00402 
00403             default:
00404                 lib3ds_chunk_unknown(chunk, io);
00405         }
00406     }
00407 
00408     if (lib3ds_matrix_det(mesh->matrix) < 0.0) {
00409         /* Flip X coordinate of vertices if mesh matrix
00410            has negative determinant */
00411         float inv_matrix[4][4], M[4][4];
00412         float tmp[3];
00413         int i;
00414 
00415         lib3ds_matrix_copy(inv_matrix, mesh->matrix);
00416         lib3ds_matrix_inv(inv_matrix);
00417 
00418         lib3ds_matrix_copy(M, mesh->matrix);
00419         lib3ds_matrix_scale(M, -1.0f, 1.0f, 1.0f);
00420         lib3ds_matrix_mult(M, M, inv_matrix);
00421 
00422         for (i = 0; i < mesh->nvertices; ++i) {
00423             lib3ds_vector_transform(tmp, M, mesh->vertices[i]);
00424             lib3ds_vector_copy(mesh->vertices[i], tmp);
00425         }
00426     }
00427 
00428     lib3ds_chunk_read_end(&c, io);
00429 }
00430 
00431 
00432 static void
00433 point_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
00434     Lib3dsChunk c;
00435     int i;
00436 
00437     c.chunk = CHK_POINT_ARRAY;
00438     c.size = 8 + 12 * mesh->nvertices;
00439     lib3ds_chunk_write(&c, io);
00440 
00441     lib3ds_io_write_word(io, (uint16_t) mesh->nvertices);
00442 
00443     if (lib3ds_matrix_det(mesh->matrix) >= 0.0f) {
00444         for (i = 0; i < mesh->nvertices; ++i) {
00445             lib3ds_io_write_vector(io, mesh->vertices[i]);
00446         }
00447     } else {
00448         /* Flip X coordinate of vertices if mesh matrix
00449            has negative determinant */
00450         float inv_matrix[4][4], M[4][4];
00451         float tmp[3];
00452 
00453         lib3ds_matrix_copy(inv_matrix, mesh->matrix);
00454         lib3ds_matrix_inv(inv_matrix);
00455         lib3ds_matrix_copy(M, mesh->matrix);
00456         lib3ds_matrix_scale(M, -1.0f, 1.0f, 1.0f);
00457         lib3ds_matrix_mult(M, M, inv_matrix);
00458 
00459         for (i = 0; i < mesh->nvertices; ++i) {
00460             lib3ds_vector_transform(tmp, M, mesh->vertices[i]);
00461             lib3ds_io_write_vector(io, tmp);
00462         }
00463     }
00464 }
00465 
00466 
00467 static void
00468 flag_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
00469     Lib3dsChunk c;
00470     int i;
00471 
00472     if (!mesh->vflags) {
00473         return;
00474     }
00475 
00476     c.chunk = CHK_POINT_FLAG_ARRAY;
00477     c.size = 8 + 2 * mesh->nvertices;
00478     lib3ds_chunk_write(&c, io);
00479 
00480     lib3ds_io_write_word(io, (uint16_t) mesh->nvertices);
00481     for (i = 0; i < mesh->nvertices; ++i) {
00482         lib3ds_io_write_word(io, mesh->vflags[i]);
00483     }
00484 }
00485 
00486 
00487 static void
00488 face_array_write(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
00489     Lib3dsChunk c;
00490 
00491     if (mesh->nfaces == 0) {
00492         return;
00493     }
00494     c.chunk = CHK_FACE_ARRAY;
00495     lib3ds_chunk_write_start(&c, io);
00496 
00497     {
00498         int i;
00499 
00500         lib3ds_io_write_word(io, (uint16_t) mesh->nfaces);
00501         for (i = 0; i < mesh->nfaces; ++i) {
00502             lib3ds_io_write_word(io, mesh->faces[i].index[0]);
00503             lib3ds_io_write_word(io, mesh->faces[i].index[1]);
00504             lib3ds_io_write_word(io, mesh->faces[i].index[2]);
00505             lib3ds_io_write_word(io, mesh->faces[i].flags);
00506         }
00507     }
00508 
00509     {
00510         /*---- MSH_CHK_MAT_GROUP ----*/
00511         Lib3dsChunk c;
00512         int i, j;
00513         uint16_t num;
00514         char *matf = ((Lib3dsIoImpl*)io->impl)->tmp_mem = calloc(sizeof(char), mesh->nfaces);
00515         assert(matf);
00516 
00517         for (i = 0; i < mesh->nfaces; ++i) {
00518             if (!matf[i] && (mesh->faces[i].material >= 0) && (mesh->faces[i].material < file->nmaterials)) {
00519                 matf[i] = 1;
00520                 num = 1;
00521 
00522                 for (j = i + 1; j < mesh->nfaces; ++j) {
00523                     if (mesh->faces[i].material == mesh->faces[j].material) ++num;
00524                 }
00525 
00526                 c.chunk = CHK_MSH_MAT_GROUP;
00527                 c.size = 6 + (uint32_t)strlen(file->materials[mesh->faces[i].material]->name) + 1 + 2 + 2 * num;
00528                 lib3ds_chunk_write(&c, io);
00529                 lib3ds_io_write_string(io, file->materials[mesh->faces[i].material]->name);
00530                 lib3ds_io_write_word(io, num);
00531                 lib3ds_io_write_word(io, (uint16_t) i);
00532 
00533                 for (j = i + 1; j < mesh->nfaces; ++j) {
00534                     if (mesh->faces[i].material == mesh->faces[j].material) {
00535                         lib3ds_io_write_word(io, (uint16_t) j);
00536                         matf[j] = 1;
00537                     }
00538                 }
00539             }
00540         }
00541         ((Lib3dsIoImpl*)io->impl)->tmp_mem = NULL;
00542         free(matf);
00543     }
00544 
00545     {
00546         /*---- SMOOTH_GROUP ----*/
00547         Lib3dsChunk c;
00548         int i;
00549 
00550         c.chunk = CHK_SMOOTH_GROUP;
00551         c.size = 6 + 4 * mesh->nfaces;
00552         lib3ds_chunk_write(&c, io);
00553 
00554         for (i = 0; i < mesh->nfaces; ++i) {
00555             lib3ds_io_write_dword(io, mesh->faces[i].smoothing_group);
00556         }
00557     }
00558 
00559     {
00560         /*---- MSH_BOXMAP ----*/
00561         Lib3dsChunk c;
00562 
00563         if (strlen(mesh->box_front) ||
00564             strlen(mesh->box_back) ||
00565             strlen(mesh->box_left) ||
00566             strlen(mesh->box_right) ||
00567             strlen(mesh->box_top) ||
00568             strlen(mesh->box_bottom)) {
00569 
00570             c.chunk = CHK_MSH_BOXMAP;
00571             lib3ds_chunk_write_start(&c, io);
00572 
00573             lib3ds_io_write_string(io, mesh->box_front);
00574             lib3ds_io_write_string(io, mesh->box_back);
00575             lib3ds_io_write_string(io, mesh->box_left);
00576             lib3ds_io_write_string(io, mesh->box_right);
00577             lib3ds_io_write_string(io, mesh->box_top);
00578             lib3ds_io_write_string(io, mesh->box_bottom);
00579 
00580             lib3ds_chunk_write_end(&c, io);
00581         }
00582     }
00583 
00584     lib3ds_chunk_write_end(&c, io);
00585 }
00586 
00587 
00588 static void
00589 texco_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
00590     Lib3dsChunk c;
00591     int i;
00592 
00593     if (!mesh->texcos) {
00594         return;
00595     }
00596      
00597     c.chunk = CHK_TEX_VERTS;
00598     c.size = 8 + 8 * mesh->nvertices;
00599     lib3ds_chunk_write(&c, io);
00600 
00601     lib3ds_io_write_word(io, mesh->nvertices);
00602     for (i = 0; i < mesh->nvertices; ++i) {
00603         lib3ds_io_write_float(io, mesh->texcos[i][0]);
00604         lib3ds_io_write_float(io, mesh->texcos[i][1]);
00605     }
00606 }
00607 
00608 
00609 void
00610 lib3ds_mesh_write(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
00611     Lib3dsChunk c;
00612 
00613     c.chunk = CHK_N_TRI_OBJECT;
00614     lib3ds_chunk_write_start(&c, io);
00615 
00616     point_array_write(mesh, io);
00617     texco_array_write(mesh, io);
00618 
00619     if (mesh->map_type != LIB3DS_MAP_NONE) {   /*---- LIB3DS_MESH_TEXTURE_INFO ----*/
00620         Lib3dsChunk c;
00621         int i, j;
00622 
00623         c.chunk = CHK_MESH_TEXTURE_INFO;
00624         c.size = 92;
00625         lib3ds_chunk_write(&c, io);
00626 
00627         lib3ds_io_write_word(io, mesh->map_type);
00628 
00629         for (i = 0; i < 2; ++i) {
00630             lib3ds_io_write_float(io, mesh->map_tile[i]);
00631         }
00632         lib3ds_io_write_vector(io, mesh->map_pos);
00633         lib3ds_io_write_float(io, mesh->map_scale);
00634 
00635         for (i = 0; i < 4; i++) {
00636             for (j = 0; j < 3; j++) {
00637                 lib3ds_io_write_float(io, mesh->map_matrix[i][j]);
00638             }
00639         }
00640         for (i = 0; i < 2; ++i) {
00641             lib3ds_io_write_float(io, mesh->map_planar_size[i]);
00642         }
00643         lib3ds_io_write_float(io, mesh->map_cylinder_height);
00644     }
00645 
00646     flag_array_write(mesh, io);
00647 
00648     {
00649         /*---- LIB3DS_MESH_MATRIX ----*/
00650         Lib3dsChunk c;
00651         int i, j;
00652 
00653         c.chunk = CHK_MESH_MATRIX;
00654         c.size = 54;
00655         lib3ds_chunk_write(&c, io);
00656         for (i = 0; i < 4; i++) {
00657             for (j = 0; j < 3; j++) {
00658                 lib3ds_io_write_float(io, mesh->matrix[i][j]);
00659             }
00660         }
00661     }
00662 
00663     if (mesh->color) {   /*---- LIB3DS_MESH_COLOR ----*/
00664         Lib3dsChunk c;
00665 
00666         c.chunk = CHK_MESH_COLOR;
00667         c.size = 7;
00668         lib3ds_chunk_write(&c, io);
00669         lib3ds_io_write_byte(io, mesh->color);
00670     }
00671     
00672     face_array_write(file, mesh, io);
00673 
00674     lib3ds_chunk_write_end(&c, io);
00675 }
00676