1 /*
2     Copyright (C) 1996-2008 by Jan Eric Kyprianidis <www.kyprianidis.com>
3     All rights reserved.
4 
5     This program is free  software: you can redistribute it and/or modify
6     it under the terms of the GNU Lesser General Public License as published
7     by the Free Software Foundation, either version 2.1 of the License, or
8     (at your option) any later version.
9 
10     Thisprogram  is  distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13     GNU Lesser General Public License for more details.
14 
15     You should  have received a copy of the GNU Lesser General Public License
16     along with  this program; If not, see <http://www.gnu.org/licenses/>.
17 */
18 #include "lib3ds_impl.h"
19 
20 
21 /*!
22  * Create and return a new empty mesh object.
23  *
24  * Mesh is initialized with the name and an identity matrix; all
25  * other fields are zero.
26  *
27  * See Lib3dsFaceFlag for definitions of per-face flags.
28  *
29  * \param name Mesh name.  Must not be NULL.  Must be < 64 characters.
30  *
31  * \return mesh object or NULL on error.
32  */
33 Lib3dsMesh*
lib3ds_mesh_new(const char * name)34 lib3ds_mesh_new(const char *name) {
35     Lib3dsMesh *mesh;
36 
37     assert(name);
38     assert(strlen(name) < 64);
39 
40     mesh = (Lib3dsMesh*) calloc(sizeof(Lib3dsMesh), 1);
41     if (!mesh) {
42         return (0);
43     }
44     strcpy(mesh->name, name);
45     lib3ds_matrix_identity(mesh->matrix);
46     mesh->map_type = LIB3DS_MAP_NONE;
47     return (mesh);
48 }
49 
50 
51 /*!
52  * Free a mesh object and all of its resources.
53  *
54  * \param mesh Mesh object to be freed.
55  */
56 void
lib3ds_mesh_free(Lib3dsMesh * mesh)57 lib3ds_mesh_free(Lib3dsMesh *mesh) {
58     lib3ds_mesh_resize_vertices(mesh, 0, 0, 0);
59     lib3ds_mesh_resize_faces(mesh, 0);
60     memset(mesh, 0, sizeof(Lib3dsMesh));
61     free(mesh);
62 }
63 
64 
65 void
lib3ds_mesh_resize_vertices(Lib3dsMesh * mesh,int nvertices,int use_texcos,int use_flags)66 lib3ds_mesh_resize_vertices(Lib3dsMesh *mesh, int nvertices, int use_texcos, int use_flags) {
67     assert(mesh);
68     mesh->vertices = (float(*)[3])lib3ds_util_realloc_array(mesh->vertices, mesh->nvertices, nvertices, 3 * sizeof(float));
69     mesh->texcos = (float(*)[2])lib3ds_util_realloc_array(
70         mesh->texcos,
71         mesh->texcos? mesh->nvertices : 0,
72         use_texcos? nvertices : 0,
73         2 * sizeof(float)
74     );
75     mesh->vflags = (unsigned short*)lib3ds_util_realloc_array(
76         mesh->vflags,
77         mesh->vflags? mesh->nvertices : 0,
78         use_flags? nvertices : 0,
79         2 * sizeof(float)
80     );
81     mesh->nvertices = (unsigned short)nvertices;
82 }
83 
84 
85 void
lib3ds_mesh_resize_faces(Lib3dsMesh * mesh,int nfaces)86 lib3ds_mesh_resize_faces(Lib3dsMesh *mesh, int nfaces) {
87     int i;
88     assert(mesh);
89     mesh->faces = (Lib3dsFace*)lib3ds_util_realloc_array(mesh->faces, mesh->nfaces, nfaces, sizeof(Lib3dsFace));
90     for (i = mesh->nfaces; i < nfaces; ++i) {
91         mesh->faces[i].material = -1;
92     }
93     mesh->nfaces = (unsigned short)nfaces;
94 }
95 
96 
97 /*!
98  * Find the bounding box of a mesh object.
99  *
100  * \param mesh The mesh object
101  * \param bmin Returned bounding box
102  * \param bmax Returned bounding box
103  */
104 void
lib3ds_mesh_bounding_box(Lib3dsMesh * mesh,float bmin[3],float bmax[3])105 lib3ds_mesh_bounding_box(Lib3dsMesh *mesh, float bmin[3], float bmax[3]) {
106     int i;
107     bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
108     bmax[0] = bmax[1] = bmax[2] = -FLT_MAX;
109 
110     for (i = 0; i < mesh->nvertices; ++i) {
111         lib3ds_vector_min(bmin, mesh->vertices[i]);
112         lib3ds_vector_max(bmax, mesh->vertices[i]);
113     }
114 }
115 
116 
117 void
lib3ds_mesh_calculate_face_normals(Lib3dsMesh * mesh,float (* face_normals)[3])118 lib3ds_mesh_calculate_face_normals(Lib3dsMesh *mesh, float (*face_normals)[3]) {
119     int i;
120 
121     if (!mesh->nfaces) {
122         return;
123     }
124     for (i = 0; i < mesh->nfaces; ++i) {
125         lib3ds_vector_normal(
126             face_normals[i],
127             mesh->vertices[mesh->faces[i].index[0]],
128             mesh->vertices[mesh->faces[i].index[1]],
129             mesh->vertices[mesh->faces[i].index[2]]
130         );
131     }
132 }
133 
134 
135 typedef struct Lib3dsFaces {
136     struct Lib3dsFaces *next;
137     int index;
138     float normal[3];
139 } Lib3dsFaces;
140 
141 
142 /*!
143  * Calculates the vertex normals corresponding to the smoothing group
144  * settings for each face of a mesh.
145  *
146  * \param mesh      A pointer to the mesh to calculate the normals for.
147  * \param normals   A pointer to a buffer to store the calculated
148  *                  normals. The buffer must have the size:
149  *                  3*3*sizeof(float)*mesh->nfaces.
150  *
151  * To allocate the normal buffer do for example the following:
152  * \code
153  *  Lib3dsVector *normals = malloc(3*3*sizeof(float)*mesh->nfaces);
154  * \endcode
155  *
156  * To access the normal of the i-th vertex of the j-th face do the
157  * following:
158  * \code
159  *   normals[3*j+i]
160  * \endcode
161  */
162 void
lib3ds_mesh_calculate_vertex_normals(Lib3dsMesh * mesh,float (* normals)[3])163 lib3ds_mesh_calculate_vertex_normals(Lib3dsMesh *mesh, float (*normals)[3]) {
164     Lib3dsFaces **fl;
165     Lib3dsFaces *fa;
166     int i, j;
167 
168     if (!mesh->nfaces) {
169         return;
170     }
171 
172     fl = (Lib3dsFaces**)calloc(sizeof(Lib3dsFaces*), mesh->nvertices);
173     fa = (Lib3dsFaces*)malloc(sizeof(Lib3dsFaces) * 3 * mesh->nfaces);
174 
175     for (i = 0; i < mesh->nfaces; ++i) {
176         for (j = 0; j < 3; ++j) {
177             Lib3dsFaces* l = &fa[3*i+j];
178             float p[3], q[3], n[3];
179             float len, weight;
180 
181             l->index = i;
182             l->next = fl[mesh->faces[i].index[j]];
183             fl[mesh->faces[i].index[j]] = l;
184 
185             lib3ds_vector_sub(p, mesh->vertices[mesh->faces[i].index[j<2? j + 1 : 0]], mesh->vertices[mesh->faces[i].index[j]]);
186             lib3ds_vector_sub(q, mesh->vertices[mesh->faces[i].index[j>0? j - 1 : 2]], mesh->vertices[mesh->faces[i].index[j]]);
187             lib3ds_vector_cross(n, p, q);
188             len = lib3ds_vector_length(n);
189             if (len > 0) {
190                 weight = (float)atan2(len, lib3ds_vector_dot(p, q));
191                 lib3ds_vector_scalar_mul(l->normal, n, weight / len);
192             } else {
193                 lib3ds_vector_zero(l->normal);
194             }
195         }
196     }
197 
198     for (i = 0; i < mesh->nfaces; ++i) {
199         Lib3dsFace *f = &mesh->faces[i];
200         for (j = 0; j < 3; ++j) {
201             float n[3];
202             Lib3dsFaces *p;
203             Lib3dsFace *pf;
204 
205             assert(mesh->faces[i].index[j] < mesh->nvertices);
206 
207             if (f->smoothing_group) {
208                 unsigned smoothing_group = f->smoothing_group;
209 
210                 lib3ds_vector_zero(n);
211                 for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) {
212                     pf = &mesh->faces[p->index];
213                     if (pf->smoothing_group & f->smoothing_group)
214                         smoothing_group |= pf->smoothing_group;
215                 }
216 
217                 for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) {
218                     pf = &mesh->faces[p->index];
219                     if (smoothing_group & pf->smoothing_group) {
220                         lib3ds_vector_add(n, n, p->normal);
221                     }
222                 }
223             } else {
224                 lib3ds_vector_copy(n, fa[3*i+j].normal);
225             }
226 
227             lib3ds_vector_normalize(n);
228             lib3ds_vector_copy(normals[3*i+j], n);
229         }
230     }
231 
232     free(fa);
233     free(fl);
234 }
235 
236 
237 static void
face_array_read(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)238 face_array_read(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
239     Lib3dsChunk c;
240     uint16_t chunk;
241     int i;
242     uint16_t nfaces;
243 
244     lib3ds_chunk_read_start(&c, CHK_FACE_ARRAY, io);
245 
246     lib3ds_mesh_resize_faces(mesh, 0);
247     nfaces = lib3ds_io_read_word(io);
248     if (nfaces) {
249         lib3ds_mesh_resize_faces(mesh, nfaces);
250         for (i = 0; i < nfaces; ++i) {
251             mesh->faces[i].index[0] = lib3ds_io_read_word(io);
252             mesh->faces[i].index[1] = lib3ds_io_read_word(io);
253             mesh->faces[i].index[2] = lib3ds_io_read_word(io);
254             mesh->faces[i].flags = lib3ds_io_read_word(io);
255         }
256         lib3ds_chunk_read_tell(&c, io);
257 
258         while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
259             switch (chunk) {
260                 case CHK_MSH_MAT_GROUP: {
261                     char name[64];
262                     unsigned n;
263                     unsigned i;
264                     int index;
265                     int material;
266 
267                     lib3ds_io_read_string(io, name, 64);
268                     material = lib3ds_file_material_by_name(file, name);
269 
270                     n = lib3ds_io_read_word(io);
271                     for (i = 0; i < n; ++i) {
272                         index = lib3ds_io_read_word(io);
273                         if (index < mesh->nfaces) {
274                             mesh->faces[index].material = material;
275                         } else {
276                             // TODO warning
277                         }
278                     }
279                     break;
280                 }
281 
282                 case CHK_SMOOTH_GROUP: {
283                     int i;
284                     for (i = 0; i < mesh->nfaces; ++i) {
285                         mesh->faces[i].smoothing_group = lib3ds_io_read_dword(io);
286                     }
287                     break;
288                 }
289 
290                 case CHK_MSH_BOXMAP: {
291                     lib3ds_io_read_string(io, mesh->box_front, 64);
292                     lib3ds_io_read_string(io, mesh->box_back, 64);
293                     lib3ds_io_read_string(io, mesh->box_left, 64);
294                     lib3ds_io_read_string(io, mesh->box_right, 64);
295                     lib3ds_io_read_string(io, mesh->box_top, 64);
296                     lib3ds_io_read_string(io, mesh->box_bottom, 64);
297                     break;
298                 }
299 
300                 default:
301                     lib3ds_chunk_unknown(chunk,io);
302             }
303         }
304 
305     }
306     lib3ds_chunk_read_end(&c, io);
307 }
308 
309 
310 void
lib3ds_mesh_read(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)311 lib3ds_mesh_read(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
312     Lib3dsChunk c;
313     uint16_t chunk;
314 
315     lib3ds_chunk_read_start(&c, CHK_N_TRI_OBJECT, io);
316 
317     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
318         switch (chunk) {
319             case CHK_MESH_MATRIX: {
320                 int i, j;
321 
322                 lib3ds_matrix_identity(mesh->matrix);
323                 for (i = 0; i < 4; i++) {
324                     for (j = 0; j < 3; j++) {
325                         mesh->matrix[i][j] = lib3ds_io_read_float(io);
326                     }
327                 }
328                 break;
329             }
330 
331             case CHK_MESH_COLOR: {
332                 mesh->color = lib3ds_io_read_byte(io);
333                 break;
334             }
335 
336             case CHK_POINT_ARRAY: {
337                 int i;
338                 uint16_t nvertices = lib3ds_io_read_word(io);
339                 lib3ds_mesh_resize_vertices(mesh, nvertices, mesh->texcos != NULL, mesh->vflags != NULL);
340                 for (i = 0; i < mesh->nvertices; ++i) {
341                     lib3ds_io_read_vector(io, mesh->vertices[i]);
342                 }
343                 break;
344             }
345 
346             case CHK_POINT_FLAG_ARRAY: {
347                 int i;
348                 uint16_t nflags = lib3ds_io_read_word(io);
349                 uint16_t nvertices = (mesh->nvertices >= nflags)? mesh->nvertices : nflags;
350                 lib3ds_mesh_resize_vertices(mesh, nvertices, mesh->texcos != NULL, 1);
351                 for (i = 0; i < nflags; ++i) {
352                     mesh->vflags[i] = lib3ds_io_read_word(io);
353                 }
354                 break;
355             }
356 
357             case CHK_FACE_ARRAY: {
358                 lib3ds_chunk_read_reset(&c, io);
359                 face_array_read(file, mesh, io);
360                 break;
361             }
362 
363             case CHK_MESH_TEXTURE_INFO: {
364                 int i, j;
365 
366                 //FIXME: mesh->map_type = lib3ds_io_read_word(io);
367 
368                 for (i = 0; i < 2; ++i) {
369                     mesh->map_tile[i] = lib3ds_io_read_float(io);
370                 }
371                 for (i = 0; i < 3; ++i) {
372                     mesh->map_pos[i] = lib3ds_io_read_float(io);
373                 }
374                 mesh->map_scale = lib3ds_io_read_float(io);
375 
376                 lib3ds_matrix_identity(mesh->map_matrix);
377                 for (i = 0; i < 4; i++) {
378                     for (j = 0; j < 3; j++) {
379                         mesh->map_matrix[i][j] = lib3ds_io_read_float(io);
380                     }
381                 }
382                 for (i = 0; i < 2; ++i) {
383                     mesh->map_planar_size[i] = lib3ds_io_read_float(io);
384                 }
385                 mesh->map_cylinder_height = lib3ds_io_read_float(io);
386                 break;
387             }
388 
389             case CHK_TEX_VERTS: {
390                 int i;
391                 uint16_t ntexcos = lib3ds_io_read_word(io);
392                 uint16_t nvertices = (mesh->nvertices >= ntexcos)? mesh->nvertices : ntexcos;;
393                 if (!mesh->texcos) {
394                     lib3ds_mesh_resize_vertices(mesh, nvertices, 1, mesh->vflags != NULL);
395                 }
396                 for (i = 0; i < ntexcos; ++i) {
397                     mesh->texcos[i][0] = lib3ds_io_read_float(io);
398                     mesh->texcos[i][1] = lib3ds_io_read_float(io);
399                 }
400                 break;
401             }
402 
403             default:
404                 lib3ds_chunk_unknown(chunk, io);
405         }
406     }
407 
408     if (lib3ds_matrix_det(mesh->matrix) < 0.0) {
409         /* Flip X coordinate of vertices if mesh matrix
410            has negative determinant */
411         float inv_matrix[4][4], M[4][4];
412         float tmp[3];
413         int i;
414 
415         lib3ds_matrix_copy(inv_matrix, mesh->matrix);
416         lib3ds_matrix_inv(inv_matrix);
417 
418         lib3ds_matrix_copy(M, mesh->matrix);
419         lib3ds_matrix_scale(M, -1.0f, 1.0f, 1.0f);
420         lib3ds_matrix_mult(M, M, inv_matrix);
421 
422         for (i = 0; i < mesh->nvertices; ++i) {
423             lib3ds_vector_transform(tmp, M, mesh->vertices[i]);
424             lib3ds_vector_copy(mesh->vertices[i], tmp);
425         }
426     }
427 
428     lib3ds_chunk_read_end(&c, io);
429 }
430 
431 
432 static void
point_array_write(Lib3dsMesh * mesh,Lib3dsIo * io)433 point_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
434     Lib3dsChunk c;
435     int i;
436 
437     c.chunk = CHK_POINT_ARRAY;
438     c.size = 8 + 12 * mesh->nvertices;
439     lib3ds_chunk_write(&c, io);
440 
441     lib3ds_io_write_word(io, (uint16_t) mesh->nvertices);
442 
443     if (lib3ds_matrix_det(mesh->matrix) >= 0.0f) {
444         for (i = 0; i < mesh->nvertices; ++i) {
445             lib3ds_io_write_vector(io, mesh->vertices[i]);
446         }
447     } else {
448         /* Flip X coordinate of vertices if mesh matrix
449            has negative determinant */
450         float inv_matrix[4][4], M[4][4];
451         float tmp[3];
452 
453         lib3ds_matrix_copy(inv_matrix, mesh->matrix);
454         lib3ds_matrix_inv(inv_matrix);
455         lib3ds_matrix_copy(M, mesh->matrix);
456         lib3ds_matrix_scale(M, -1.0f, 1.0f, 1.0f);
457         lib3ds_matrix_mult(M, M, inv_matrix);
458 
459         for (i = 0; i < mesh->nvertices; ++i) {
460             lib3ds_vector_transform(tmp, M, mesh->vertices[i]);
461             lib3ds_io_write_vector(io, tmp);
462         }
463     }
464 }
465 
466 
467 static void
flag_array_write(Lib3dsMesh * mesh,Lib3dsIo * io)468 flag_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
469     Lib3dsChunk c;
470     int i;
471 
472     if (!mesh->vflags) {
473         return;
474     }
475 
476     c.chunk = CHK_POINT_FLAG_ARRAY;
477     c.size = 8 + 2 * mesh->nvertices;
478     lib3ds_chunk_write(&c, io);
479 
480     lib3ds_io_write_word(io, (uint16_t) mesh->nvertices);
481     for (i = 0; i < mesh->nvertices; ++i) {
482         lib3ds_io_write_word(io, mesh->vflags[i]);
483     }
484 }
485 
486 
487 static void
face_array_write(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)488 face_array_write(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
489     Lib3dsChunk c;
490 
491     if (mesh->nfaces == 0) {
492         return;
493     }
494     c.chunk = CHK_FACE_ARRAY;
495     lib3ds_chunk_write_start(&c, io);
496 
497     {
498         int i;
499 
500         lib3ds_io_write_word(io, (uint16_t) mesh->nfaces);
501         for (i = 0; i < mesh->nfaces; ++i) {
502             lib3ds_io_write_word(io, mesh->faces[i].index[0]);
503             lib3ds_io_write_word(io, mesh->faces[i].index[1]);
504             lib3ds_io_write_word(io, mesh->faces[i].index[2]);
505             lib3ds_io_write_word(io, mesh->faces[i].flags);
506         }
507     }
508 
509     {
510         /*---- MSH_CHK_MAT_GROUP ----*/
511         Lib3dsChunk c;
512         int i, j;
513         uint16_t num;
514         Lib3dsIoImpl *impl = (Lib3dsIoImpl*)io->impl;
515 
516         char *matf = (char*)calloc(sizeof(char), mesh->nfaces);
517         impl->tmp_mem = matf;
518         assert(matf);
519 
520         for (i = 0; i < mesh->nfaces; ++i) {
521             if (!matf[i] && (mesh->faces[i].material >= 0) && (mesh->faces[i].material < file->nmaterials)) {
522                 matf[i] = 1;
523                 num = 1;
524 
525                 for (j = i + 1; j < mesh->nfaces; ++j) {
526                     if (mesh->faces[i].material == mesh->faces[j].material) ++num;
527                 }
528 
529                 c.chunk = CHK_MSH_MAT_GROUP;
530                 c.size = 6 + (uint32_t)strlen(file->materials[mesh->faces[i].material]->name) + 1 + 2 + 2 * num;
531                 lib3ds_chunk_write(&c, io);
532                 lib3ds_io_write_string(io, file->materials[mesh->faces[i].material]->name);
533                 lib3ds_io_write_word(io, num);
534                 lib3ds_io_write_word(io, (uint16_t) i);
535 
536                 for (j = i + 1; j < mesh->nfaces; ++j) {
537                     if (mesh->faces[i].material == mesh->faces[j].material) {
538                         lib3ds_io_write_word(io, (uint16_t) j);
539                         matf[j] = 1;
540                     }
541                 }
542             }
543         }
544         impl->tmp_mem = NULL;
545         free(matf);
546     }
547 
548     {
549         /*---- SMOOTH_GROUP ----*/
550         Lib3dsChunk c;
551         int i;
552 
553         c.chunk = CHK_SMOOTH_GROUP;
554         c.size = 6 + 4 * mesh->nfaces;
555         lib3ds_chunk_write(&c, io);
556 
557         for (i = 0; i < mesh->nfaces; ++i) {
558             lib3ds_io_write_dword(io, mesh->faces[i].smoothing_group);
559         }
560     }
561 
562     {
563         /*---- MSH_BOXMAP ----*/
564         Lib3dsChunk c;
565 
566         if (strlen(mesh->box_front) ||
567             strlen(mesh->box_back) ||
568             strlen(mesh->box_left) ||
569             strlen(mesh->box_right) ||
570             strlen(mesh->box_top) ||
571             strlen(mesh->box_bottom)) {
572 
573             c.chunk = CHK_MSH_BOXMAP;
574             lib3ds_chunk_write_start(&c, io);
575 
576             lib3ds_io_write_string(io, mesh->box_front);
577             lib3ds_io_write_string(io, mesh->box_back);
578             lib3ds_io_write_string(io, mesh->box_left);
579             lib3ds_io_write_string(io, mesh->box_right);
580             lib3ds_io_write_string(io, mesh->box_top);
581             lib3ds_io_write_string(io, mesh->box_bottom);
582 
583             lib3ds_chunk_write_end(&c, io);
584         }
585     }
586 
587     lib3ds_chunk_write_end(&c, io);
588 }
589 
590 
591 static void
texco_array_write(Lib3dsMesh * mesh,Lib3dsIo * io)592 texco_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
593     Lib3dsChunk c;
594     int i;
595 
596     if (!mesh->texcos) {
597         return;
598     }
599 
600     c.chunk = CHK_TEX_VERTS;
601     c.size = 8 + 8 * mesh->nvertices;
602     lib3ds_chunk_write(&c, io);
603 
604     lib3ds_io_write_word(io, mesh->nvertices);
605     for (i = 0; i < mesh->nvertices; ++i) {
606         lib3ds_io_write_float(io, mesh->texcos[i][0]);
607         lib3ds_io_write_float(io, mesh->texcos[i][1]);
608     }
609 }
610 
611 
612 void
lib3ds_mesh_write(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)613 lib3ds_mesh_write(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
614     Lib3dsChunk c;
615 
616     c.chunk = CHK_N_TRI_OBJECT;
617     lib3ds_chunk_write_start(&c, io);
618 
619     point_array_write(mesh, io);
620     texco_array_write(mesh, io);
621 
622     if (mesh->map_type != LIB3DS_MAP_NONE) {   /*---- LIB3DS_MESH_TEXTURE_INFO ----*/
623         Lib3dsChunk c;
624         int i, j;
625 
626         c.chunk = CHK_MESH_TEXTURE_INFO;
627         c.size = 92;
628         lib3ds_chunk_write(&c, io);
629 
630         lib3ds_io_write_word(io, (uint16_t)mesh->map_type);
631 
632         for (i = 0; i < 2; ++i) {
633             lib3ds_io_write_float(io, mesh->map_tile[i]);
634         }
635         lib3ds_io_write_vector(io, mesh->map_pos);
636         lib3ds_io_write_float(io, mesh->map_scale);
637 
638         for (i = 0; i < 4; i++) {
639             for (j = 0; j < 3; j++) {
640                 lib3ds_io_write_float(io, mesh->map_matrix[i][j]);
641             }
642         }
643         for (i = 0; i < 2; ++i) {
644             lib3ds_io_write_float(io, mesh->map_planar_size[i]);
645         }
646         lib3ds_io_write_float(io, mesh->map_cylinder_height);
647     }
648 
649     flag_array_write(mesh, io);
650 
651     {
652         /*---- LIB3DS_MESH_MATRIX ----*/
653         Lib3dsChunk c;
654         int i, j;
655 
656         c.chunk = CHK_MESH_MATRIX;
657         c.size = 54;
658         lib3ds_chunk_write(&c, io);
659         for (i = 0; i < 4; i++) {
660             for (j = 0; j < 3; j++) {
661                 lib3ds_io_write_float(io, mesh->matrix[i][j]);
662             }
663         }
664     }
665 
666     if (mesh->color) {   /*---- LIB3DS_MESH_COLOR ----*/
667         Lib3dsChunk c;
668 
669         c.chunk = CHK_MESH_COLOR;
670         c.size = 7;
671         lib3ds_chunk_write(&c, io);
672         lib3ds_io_write_byte(io, (uint8_t)mesh->color);
673     }
674 
675     face_array_write(file, mesh, io);
676 
677     lib3ds_chunk_write_end(&c, io);
678 }
679 
680