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     stringcopyfixedsize(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     if (!fl)
174     {
175         return;
176     }
177 
178     fa = (Lib3dsFaces*)malloc(sizeof(Lib3dsFaces) * 3 * mesh->nfaces);
179     if (!fa)
180     {
181         free(fl);
182         return;
183     }
184 
185     for (i = 0; i < mesh->nfaces; ++i) {
186         for (j = 0; j < 3; ++j) {
187             Lib3dsFaces* l = &fa[3*i+j];
188             float p[3], q[3], n[3];
189             float len, weight;
190 
191             l->index = i;
192             l->next = fl[mesh->faces[i].index[j]];
193             fl[mesh->faces[i].index[j]] = l;
194 
195             lib3ds_vector_sub(p, mesh->vertices[mesh->faces[i].index[j<2? j + 1 : 0]], mesh->vertices[mesh->faces[i].index[j]]);
196             lib3ds_vector_sub(q, mesh->vertices[mesh->faces[i].index[j>0? j - 1 : 2]], mesh->vertices[mesh->faces[i].index[j]]);
197             lib3ds_vector_cross(n, p, q);
198             len = lib3ds_vector_length(n);
199             if (len > 0) {
200                 weight = (float)atan2(len, lib3ds_vector_dot(p, q));
201                 lib3ds_vector_scalar_mul(l->normal, n, weight / len);
202             } else {
203                 lib3ds_vector_zero(l->normal);
204             }
205         }
206     }
207 
208     for (i = 0; i < mesh->nfaces; ++i) {
209         Lib3dsFace *f = &mesh->faces[i];
210         for (j = 0; j < 3; ++j) {
211             float n[3];
212             Lib3dsFaces *p;
213             Lib3dsFace *pf;
214 
215             assert(mesh->faces[i].index[j] < mesh->nvertices);
216 
217             if (f->smoothing_group) {
218                 unsigned smoothing_group = f->smoothing_group;
219 
220                 lib3ds_vector_zero(n);
221                 for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) {
222                     pf = &mesh->faces[p->index];
223                     if (pf->smoothing_group & f->smoothing_group)
224                         smoothing_group |= pf->smoothing_group;
225                 }
226 
227                 for (p = fl[mesh->faces[i].index[j]]; p; p = p->next) {
228                     pf = &mesh->faces[p->index];
229                     if (smoothing_group & pf->smoothing_group) {
230                         lib3ds_vector_add(n, n, p->normal);
231                     }
232                 }
233             } else {
234                 lib3ds_vector_copy(n, fa[3*i+j].normal);
235             }
236 
237             lib3ds_vector_normalize(n);
238             lib3ds_vector_copy(normals[3*i+j], n);
239         }
240     }
241 
242     free(fa);
243     free(fl);
244 }
245 
246 
247 static void
face_array_read(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)248 face_array_read(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
249     Lib3dsChunk c;
250     uint16_t chunk;
251     int i;
252     uint16_t nfaces;
253 
254     lib3ds_chunk_read_start(&c, CHK_FACE_ARRAY, io);
255 
256     lib3ds_mesh_resize_faces(mesh, 0);
257     nfaces = lib3ds_io_read_word(io);
258     if (nfaces) {
259         lib3ds_mesh_resize_faces(mesh, nfaces);
260         for (i = 0; i < nfaces; ++i) {
261             mesh->faces[i].index[0] = lib3ds_io_read_word(io);
262             mesh->faces[i].index[1] = lib3ds_io_read_word(io);
263             mesh->faces[i].index[2] = lib3ds_io_read_word(io);
264             mesh->faces[i].flags = lib3ds_io_read_word(io);
265         }
266         lib3ds_chunk_read_tell(&c, io);
267 
268         while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
269             switch (chunk) {
270                 case CHK_MSH_MAT_GROUP: {
271                     char name[64];
272                     unsigned n;
273                     unsigned i;
274                     int index;
275                     int material;
276 
277                     lib3ds_io_read_string(io, name, 64);
278                     material = lib3ds_file_material_by_name(file, name);
279 
280                     n = lib3ds_io_read_word(io);
281                     for (i = 0; i < n; ++i) {
282                         index = lib3ds_io_read_word(io);
283                         if (index < mesh->nfaces) {
284                             mesh->faces[index].material = material;
285                         } else {
286                             // TODO warning
287                         }
288                     }
289                     break;
290                 }
291 
292                 case CHK_SMOOTH_GROUP: {
293                     int i;
294                     for (i = 0; i < mesh->nfaces; ++i) {
295                         mesh->faces[i].smoothing_group = lib3ds_io_read_dword(io);
296                     }
297                     break;
298                 }
299 
300                 case CHK_MSH_BOXMAP: {
301                     lib3ds_io_read_string(io, mesh->box_front, 64);
302                     lib3ds_io_read_string(io, mesh->box_back, 64);
303                     lib3ds_io_read_string(io, mesh->box_left, 64);
304                     lib3ds_io_read_string(io, mesh->box_right, 64);
305                     lib3ds_io_read_string(io, mesh->box_top, 64);
306                     lib3ds_io_read_string(io, mesh->box_bottom, 64);
307                     break;
308                 }
309 
310                 default:
311                     lib3ds_chunk_unknown(chunk,io);
312             }
313         }
314 
315     }
316     lib3ds_chunk_read_end(&c, io);
317 }
318 
319 
320 void
lib3ds_mesh_read(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)321 lib3ds_mesh_read(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
322     Lib3dsChunk c;
323     uint16_t chunk;
324 
325     lib3ds_chunk_read_start(&c, CHK_N_TRI_OBJECT, io);
326 
327     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
328         switch (chunk) {
329             case CHK_MESH_MATRIX: {
330                 int i, j;
331 
332                 lib3ds_matrix_identity(mesh->matrix);
333                 for (i = 0; i < 4; i++) {
334                     for (j = 0; j < 3; j++) {
335                         mesh->matrix[i][j] = lib3ds_io_read_float(io);
336                     }
337                 }
338                 break;
339             }
340 
341             case CHK_MESH_COLOR: {
342                 mesh->color = lib3ds_io_read_byte(io);
343                 break;
344             }
345 
346             case CHK_POINT_ARRAY: {
347                 int i;
348                 uint16_t nvertices = lib3ds_io_read_word(io);
349                 lib3ds_mesh_resize_vertices(mesh, nvertices, mesh->texcos != NULL, mesh->vflags != NULL);
350                 for (i = 0; i < mesh->nvertices; ++i) {
351                     lib3ds_io_read_vector(io, mesh->vertices[i]);
352                 }
353                 break;
354             }
355 
356             case CHK_POINT_FLAG_ARRAY: {
357                 int i;
358                 uint16_t nflags = lib3ds_io_read_word(io);
359                 uint16_t nvertices = (mesh->nvertices >= nflags)? mesh->nvertices : nflags;
360                 lib3ds_mesh_resize_vertices(mesh, nvertices, mesh->texcos != NULL, 1);
361                 for (i = 0; i < nflags; ++i) {
362                     mesh->vflags[i] = lib3ds_io_read_word(io);
363                 }
364                 break;
365             }
366 
367             case CHK_FACE_ARRAY: {
368                 lib3ds_chunk_read_reset(&c, io);
369                 face_array_read(file, mesh, io);
370                 break;
371             }
372 
373             case CHK_MESH_TEXTURE_INFO: {
374                 int i, j;
375 
376                 //FIXME: mesh->map_type = lib3ds_io_read_word(io);
377 
378                 for (i = 0; i < 2; ++i) {
379                     mesh->map_tile[i] = lib3ds_io_read_float(io);
380                 }
381                 for (i = 0; i < 3; ++i) {
382                     mesh->map_pos[i] = lib3ds_io_read_float(io);
383                 }
384                 mesh->map_scale = lib3ds_io_read_float(io);
385 
386                 lib3ds_matrix_identity(mesh->map_matrix);
387                 for (i = 0; i < 4; i++) {
388                     for (j = 0; j < 3; j++) {
389                         mesh->map_matrix[i][j] = lib3ds_io_read_float(io);
390                     }
391                 }
392                 for (i = 0; i < 2; ++i) {
393                     mesh->map_planar_size[i] = lib3ds_io_read_float(io);
394                 }
395                 mesh->map_cylinder_height = lib3ds_io_read_float(io);
396                 break;
397             }
398 
399             case CHK_TEX_VERTS: {
400                 int i;
401                 uint16_t ntexcos = lib3ds_io_read_word(io);
402                 uint16_t nvertices = (mesh->nvertices >= ntexcos)? mesh->nvertices : ntexcos;;
403                 if (!mesh->texcos) {
404                     lib3ds_mesh_resize_vertices(mesh, nvertices, 1, mesh->vflags != NULL);
405                 }
406                 for (i = 0; i < ntexcos; ++i) {
407                     mesh->texcos[i][0] = lib3ds_io_read_float(io);
408                     mesh->texcos[i][1] = lib3ds_io_read_float(io);
409                 }
410                 break;
411             }
412 
413             default:
414                 lib3ds_chunk_unknown(chunk, io);
415         }
416     }
417 
418     if (lib3ds_matrix_det(mesh->matrix) < 0.0) {
419         /* Flip X coordinate of vertices if mesh matrix
420            has negative determinant */
421         float inv_matrix[4][4], M[4][4];
422         float tmp[3];
423         int i;
424 
425         lib3ds_matrix_copy(inv_matrix, mesh->matrix);
426         lib3ds_matrix_inv(inv_matrix);
427 
428         lib3ds_matrix_copy(M, mesh->matrix);
429         lib3ds_matrix_scale(M, -1.0f, 1.0f, 1.0f);
430         lib3ds_matrix_mult(M, M, inv_matrix);
431 
432         for (i = 0; i < mesh->nvertices; ++i) {
433             lib3ds_vector_transform(tmp, M, mesh->vertices[i]);
434             lib3ds_vector_copy(mesh->vertices[i], tmp);
435         }
436     }
437 
438     lib3ds_chunk_read_end(&c, io);
439 }
440 
441 
442 static void
point_array_write(Lib3dsMesh * mesh,Lib3dsIo * io)443 point_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
444     Lib3dsChunk c;
445     int i;
446 
447     c.chunk = CHK_POINT_ARRAY;
448     c.size = 8 + 12 * mesh->nvertices;
449     lib3ds_chunk_write(&c, io);
450 
451     lib3ds_io_write_word(io, (uint16_t) mesh->nvertices);
452 
453     if (lib3ds_matrix_det(mesh->matrix) >= 0.0f) {
454         for (i = 0; i < mesh->nvertices; ++i) {
455             lib3ds_io_write_vector(io, mesh->vertices[i]);
456         }
457     } else {
458         /* Flip X coordinate of vertices if mesh matrix
459            has negative determinant */
460         float inv_matrix[4][4], M[4][4];
461         float tmp[3];
462 
463         lib3ds_matrix_copy(inv_matrix, mesh->matrix);
464         lib3ds_matrix_inv(inv_matrix);
465         lib3ds_matrix_copy(M, mesh->matrix);
466         lib3ds_matrix_scale(M, -1.0f, 1.0f, 1.0f);
467         lib3ds_matrix_mult(M, M, inv_matrix);
468 
469         for (i = 0; i < mesh->nvertices; ++i) {
470             lib3ds_vector_transform(tmp, M, mesh->vertices[i]);
471             lib3ds_io_write_vector(io, tmp);
472         }
473     }
474 }
475 
476 
477 static void
flag_array_write(Lib3dsMesh * mesh,Lib3dsIo * io)478 flag_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
479     Lib3dsChunk c;
480     int i;
481 
482     if (!mesh->vflags) {
483         return;
484     }
485 
486     c.chunk = CHK_POINT_FLAG_ARRAY;
487     c.size = 8 + 2 * mesh->nvertices;
488     lib3ds_chunk_write(&c, io);
489 
490     lib3ds_io_write_word(io, (uint16_t) mesh->nvertices);
491     for (i = 0; i < mesh->nvertices; ++i) {
492         lib3ds_io_write_word(io, mesh->vflags[i]);
493     }
494 }
495 
496 
497 static void
face_array_write(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)498 face_array_write(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
499     Lib3dsChunk c;
500 
501     if (mesh->nfaces == 0) {
502         return;
503     }
504     c.chunk = CHK_FACE_ARRAY;
505     lib3ds_chunk_write_start(&c, io);
506 
507     {
508         int i;
509 
510         lib3ds_io_write_word(io, (uint16_t) mesh->nfaces);
511         for (i = 0; i < mesh->nfaces; ++i) {
512             lib3ds_io_write_word(io, mesh->faces[i].index[0]);
513             lib3ds_io_write_word(io, mesh->faces[i].index[1]);
514             lib3ds_io_write_word(io, mesh->faces[i].index[2]);
515             lib3ds_io_write_word(io, mesh->faces[i].flags);
516         }
517     }
518 
519     {
520         /*---- MSH_CHK_MAT_GROUP ----*/
521         Lib3dsChunk c;
522         int i, j;
523         uint16_t num;
524         char *matf = (char*)calloc(sizeof(char), mesh->nfaces);
525 		((Lib3dsIoImpl*)io->impl)->tmp_mem = matf;
526         assert(matf);
527 
528         for (i = 0; i < mesh->nfaces; ++i) {
529             if (!matf[i] && (mesh->faces[i].material >= 0) && (mesh->faces[i].material < file->nmaterials)) {
530                 matf[i] = 1;
531                 num = 1;
532 
533                 for (j = i + 1; j < mesh->nfaces; ++j) {
534                     if (mesh->faces[i].material == mesh->faces[j].material) ++num;
535                 }
536 
537                 c.chunk = CHK_MSH_MAT_GROUP;
538                 c.size = 6 + (uint32_t)strlen(file->materials[mesh->faces[i].material]->name) + 1 + 2 + 2 * num;
539                 lib3ds_chunk_write(&c, io);
540                 lib3ds_io_write_string(io, file->materials[mesh->faces[i].material]->name);
541                 lib3ds_io_write_word(io, num);
542                 lib3ds_io_write_word(io, (uint16_t) i);
543 
544                 for (j = i + 1; j < mesh->nfaces; ++j) {
545                     if (mesh->faces[i].material == mesh->faces[j].material) {
546                         lib3ds_io_write_word(io, (uint16_t) j);
547                         matf[j] = 1;
548                     }
549                 }
550             }
551         }
552         ((Lib3dsIoImpl*)io->impl)->tmp_mem = NULL;
553         free(matf);
554     }
555 
556     {
557         /*---- SMOOTH_GROUP ----*/
558         Lib3dsChunk c;
559         int i;
560 
561         c.chunk = CHK_SMOOTH_GROUP;
562         c.size = 6 + 4 * mesh->nfaces;
563         lib3ds_chunk_write(&c, io);
564 
565         for (i = 0; i < mesh->nfaces; ++i) {
566             lib3ds_io_write_dword(io, mesh->faces[i].smoothing_group);
567         }
568     }
569 
570     {
571         /*---- MSH_BOXMAP ----*/
572         Lib3dsChunk c;
573 
574         if (strlen(mesh->box_front) ||
575             strlen(mesh->box_back) ||
576             strlen(mesh->box_left) ||
577             strlen(mesh->box_right) ||
578             strlen(mesh->box_top) ||
579             strlen(mesh->box_bottom)) {
580 
581             c.chunk = CHK_MSH_BOXMAP;
582             lib3ds_chunk_write_start(&c, io);
583 
584             lib3ds_io_write_string(io, mesh->box_front);
585             lib3ds_io_write_string(io, mesh->box_back);
586             lib3ds_io_write_string(io, mesh->box_left);
587             lib3ds_io_write_string(io, mesh->box_right);
588             lib3ds_io_write_string(io, mesh->box_top);
589             lib3ds_io_write_string(io, mesh->box_bottom);
590 
591             lib3ds_chunk_write_end(&c, io);
592         }
593     }
594 
595     lib3ds_chunk_write_end(&c, io);
596 }
597 
598 
599 static void
texco_array_write(Lib3dsMesh * mesh,Lib3dsIo * io)600 texco_array_write(Lib3dsMesh *mesh, Lib3dsIo *io) {
601     Lib3dsChunk c;
602     int i;
603 
604     if (!mesh->texcos) {
605         return;
606     }
607 
608     c.chunk = CHK_TEX_VERTS;
609     c.size = 8 + 8 * mesh->nvertices;
610     lib3ds_chunk_write(&c, io);
611 
612     lib3ds_io_write_word(io, mesh->nvertices);
613     for (i = 0; i < mesh->nvertices; ++i) {
614         lib3ds_io_write_float(io, mesh->texcos[i][0]);
615         lib3ds_io_write_float(io, mesh->texcos[i][1]);
616     }
617 }
618 
619 
620 void
lib3ds_mesh_write(Lib3dsFile * file,Lib3dsMesh * mesh,Lib3dsIo * io)621 lib3ds_mesh_write(Lib3dsFile *file, Lib3dsMesh *mesh, Lib3dsIo *io) {
622     Lib3dsChunk c;
623 
624     c.chunk = CHK_N_TRI_OBJECT;
625     lib3ds_chunk_write_start(&c, io);
626 
627     point_array_write(mesh, io);
628     texco_array_write(mesh, io);
629 
630     if (mesh->map_type != LIB3DS_MAP_NONE) {   /*---- LIB3DS_MESH_TEXTURE_INFO ----*/
631         Lib3dsChunk c;
632         int i, j;
633 
634         c.chunk = CHK_MESH_TEXTURE_INFO;
635         c.size = 92;
636         lib3ds_chunk_write(&c, io);
637 
638         lib3ds_io_write_word(io, (uint16_t)mesh->map_type);
639 
640         for (i = 0; i < 2; ++i) {
641             lib3ds_io_write_float(io, mesh->map_tile[i]);
642         }
643         lib3ds_io_write_vector(io, mesh->map_pos);
644         lib3ds_io_write_float(io, mesh->map_scale);
645 
646         for (i = 0; i < 4; i++) {
647             for (j = 0; j < 3; j++) {
648                 lib3ds_io_write_float(io, mesh->map_matrix[i][j]);
649             }
650         }
651         for (i = 0; i < 2; ++i) {
652             lib3ds_io_write_float(io, mesh->map_planar_size[i]);
653         }
654         lib3ds_io_write_float(io, mesh->map_cylinder_height);
655     }
656 
657     flag_array_write(mesh, io);
658 
659     {
660         /*---- LIB3DS_MESH_MATRIX ----*/
661         Lib3dsChunk c;
662         int i, j;
663 
664         c.chunk = CHK_MESH_MATRIX;
665         c.size = 54;
666         lib3ds_chunk_write(&c, io);
667         for (i = 0; i < 4; i++) {
668             for (j = 0; j < 3; j++) {
669                 lib3ds_io_write_float(io, mesh->matrix[i][j]);
670             }
671         }
672     }
673 
674     if (mesh->color) {   /*---- LIB3DS_MESH_COLOR ----*/
675         Lib3dsChunk c;
676 
677         c.chunk = CHK_MESH_COLOR;
678         c.size = 7;
679         lib3ds_chunk_write(&c, io);
680         lib3ds_io_write_byte(io, (uint8_t)mesh->color);
681     }
682 
683     face_array_write(file, mesh, io);
684 
685     lib3ds_chunk_write_end(&c, io);
686 }
687 
688