added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / libs / lib3ds / mesh.c
1 /*
2  * The 3D Studio File Format Library
3  * Copyright (C) 1996-2001 by J.E. Hoffmann <je-h@gmx.net>
4  * All rights reserved.
5  *
6  * This program is  free  software;  you can redistribute it and/or modify it
7  * under the terms of the  GNU Lesser General Public License  as published by 
8  * the  Free Software Foundation;  either version 2.1 of the License,  or (at 
9  * your option) any later version.
10  *
11  * This  program  is  distributed in  the  hope that it will  be useful,  but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or  FITNESS FOR A  PARTICULAR PURPOSE.  See the  GNU Lesser General Public  
14  * License for more details.
15  *
16  * You should  have received  a copy of the GNU Lesser General Public License
17  * along with  this program;  if not, write to the  Free Software Foundation,
18  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * $Id: mesh.c,v 1.22 2004/11/20 08:33:56 efalk Exp $
21  */
22 #define LIB3DS_EXPORT
23 #include <lib3ds/mesh.h>
24 #include <lib3ds/io.h>
25 #include <lib3ds/chunk.h>
26 #include <lib3ds/vector.h>
27 #include <lib3ds/matrix.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #ifdef WITH_DMALLOC
32 #include <dmalloc.h>
33 #endif
34
35
36 /*!
37  * \defgroup mesh Meshes
38  *
39  * \author J.E. Hoffmann <je-h@gmx.net>
40  */
41
42
43 static Lib3dsBool
44 face_array_read(Lib3dsMesh *mesh, Lib3dsIo *io)
45 {
46   Lib3dsChunk c;
47   Lib3dsWord chunk;
48   int i;
49   int faces;
50
51   if (!lib3ds_chunk_read_start(&c, LIB3DS_FACE_ARRAY, io)) {
52     return(LIB3DS_FALSE);
53   }
54   lib3ds_mesh_free_face_list(mesh);
55   
56   faces=lib3ds_io_read_word(io);
57   if (faces) {
58     if (!lib3ds_mesh_new_face_list(mesh, faces)) {
59       LIB3DS_ERROR_LOG;
60       return(LIB3DS_FALSE);
61     }
62     for (i=0; i<faces; ++i) {
63       strcpy(mesh->faceL[i].material, "");
64       mesh->faceL[i].points[0]=lib3ds_io_read_word(io);
65       mesh->faceL[i].points[1]=lib3ds_io_read_word(io);
66       mesh->faceL[i].points[2]=lib3ds_io_read_word(io);
67       mesh->faceL[i].flags=lib3ds_io_read_word(io);
68     }
69     lib3ds_chunk_read_tell(&c, io);
70
71     while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
72       switch (chunk) {
73         case LIB3DS_SMOOTH_GROUP:
74           {
75             unsigned i;
76
77             for (i=0; i<mesh->faces; ++i) {
78               mesh->faceL[i].smoothing=lib3ds_io_read_dword(io);
79             }
80           }
81           break;
82         case LIB3DS_MSH_MAT_GROUP:
83           {
84             char name[64];
85             unsigned faces;
86             unsigned i;
87             unsigned index;
88
89             if (!lib3ds_io_read_string(io, name, 64)) {
90               return(LIB3DS_FALSE);
91             }
92             faces=lib3ds_io_read_word(io);
93             for (i=0; i<faces; ++i) {
94               index=lib3ds_io_read_word(io);
95               ASSERT(index<mesh->faces);
96               strcpy(mesh->faceL[index].material, name);
97             }
98           }
99           break;
100         case LIB3DS_MSH_BOXMAP:
101           {
102             char name[64];
103
104             if (!lib3ds_io_read_string(io, name, 64)) {
105               return(LIB3DS_FALSE);
106             }
107             strcpy(mesh->box_map.front, name);
108             if (!lib3ds_io_read_string(io, name, 64)) {
109               return(LIB3DS_FALSE);
110             }
111             strcpy(mesh->box_map.back, name);
112             if (!lib3ds_io_read_string(io, name, 64)) {
113               return(LIB3DS_FALSE);
114             }
115             strcpy(mesh->box_map.left, name);
116             if (!lib3ds_io_read_string(io, name, 64)) {
117               return(LIB3DS_FALSE);
118             }
119             strcpy(mesh->box_map.right, name);
120             if (!lib3ds_io_read_string(io, name, 64)) {
121               return(LIB3DS_FALSE);
122             }
123             strcpy(mesh->box_map.top, name);
124             if (!lib3ds_io_read_string(io, name, 64)) {
125               return(LIB3DS_FALSE);
126             }
127             strcpy(mesh->box_map.bottom, name);
128           }
129           break;
130         default:
131           lib3ds_chunk_unknown(chunk);
132       }
133     }
134     
135   }
136   lib3ds_chunk_read_end(&c, io);
137   return(LIB3DS_TRUE);
138 }
139
140
141 /*!
142  * Create and return a new empty mesh object.
143  *
144  * Mesh is initialized with the name and an identity matrix; all
145  * other fields are zero.
146  *
147  * See Lib3dsFaceFlag for definitions of per-face flags.
148  *
149  * \param name Mesh name.  Must not be NULL.  Must be < 64 characters.
150  *
151  * \return mesh object or NULL on error.
152  *
153  * \ingroup mesh
154  */
155 Lib3dsMesh*
156 lib3ds_mesh_new(const char *name)
157 {
158   Lib3dsMesh *mesh;
159
160   ASSERT(name);
161   ASSERT(strlen(name)<64);
162   
163   mesh=(Lib3dsMesh*)calloc(sizeof(Lib3dsMesh), 1);
164   if (!mesh) {
165     return(0);
166   }
167   strcpy(mesh->name, name);
168   lib3ds_matrix_identity(mesh->matrix);
169   mesh->map_data.maptype=LIB3DS_MAP_NONE;
170   return(mesh);
171 }
172
173
174 /*!
175  * Free a mesh object and all of its resources.
176  *
177  * \param mesh Mesh object to be freed.
178  *
179  * \ingroup mesh
180  */
181 void
182 lib3ds_mesh_free(Lib3dsMesh *mesh)
183 {
184   lib3ds_mesh_free_point_list(mesh);
185   lib3ds_mesh_free_flag_list(mesh);
186   lib3ds_mesh_free_texel_list(mesh);
187   lib3ds_mesh_free_face_list(mesh);
188   memset(mesh, 0, sizeof(Lib3dsMesh));
189   free(mesh);
190 }
191
192
193 /*!
194  * Allocate point list in mesh object.
195  *
196  * This function frees the current point list, if any, and allocates
197  * a new one large enough to hold the specified number of points.
198  *
199  * \param mesh Mesh object for which points are to be allocated.
200  * \param points Number of points in the new point list.
201  *
202  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
203  *
204  * \ingroup mesh
205  */
206 Lib3dsBool
207 lib3ds_mesh_new_point_list(Lib3dsMesh *mesh, Lib3dsDword points)
208 {
209   ASSERT(mesh);
210   if (mesh->pointL) {
211     ASSERT(mesh->points);
212     lib3ds_mesh_free_point_list(mesh);
213   }
214   ASSERT(!mesh->pointL && !mesh->points);
215   mesh->points=0;
216   mesh->pointL=calloc(sizeof(Lib3dsPoint)*points,1);
217   if (!mesh->pointL) {
218     LIB3DS_ERROR_LOG;
219     return(LIB3DS_FALSE);
220   }
221   mesh->points=points;
222   return(LIB3DS_TRUE);
223 }
224
225
226 /*!
227  * Free point list in mesh object.
228  *
229  * The current point list is freed and set to NULL.  mesh->points is
230  * set to zero.
231  *
232  * \param mesh Mesh object to be modified.
233  *
234  * \ingroup mesh
235  */
236 void
237 lib3ds_mesh_free_point_list(Lib3dsMesh *mesh)
238 {
239   ASSERT(mesh);
240   if (mesh->pointL) {
241     ASSERT(mesh->points);
242     free(mesh->pointL);
243     mesh->pointL=0;
244     mesh->points=0;
245   }
246   else {
247     ASSERT(!mesh->points);
248   }
249 }
250
251
252 /*!
253  * Allocate flag list in mesh object.
254  *
255  * This function frees the current flag list, if any, and allocates
256  * a new one large enough to hold the specified number of flags.
257  * All flags are initialized to 0
258  *
259  * \param mesh Mesh object for which points are to be allocated.
260  * \param flags Number of flags in the new flag list.
261  *
262  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
263  *
264  * \ingroup mesh
265  */
266 Lib3dsBool
267 lib3ds_mesh_new_flag_list(Lib3dsMesh *mesh, Lib3dsDword flags)
268 {
269   ASSERT(mesh);
270   if (mesh->flagL) {
271     ASSERT(mesh->flags);
272     lib3ds_mesh_free_flag_list(mesh);
273   }
274   ASSERT(!mesh->flagL && !mesh->flags);
275   mesh->flags=0;
276   mesh->flagL=calloc(sizeof(Lib3dsWord)*flags,1);
277   if (!mesh->flagL) {
278     LIB3DS_ERROR_LOG;
279     return(LIB3DS_FALSE);
280   }
281   mesh->flags=flags;
282   return(LIB3DS_TRUE);
283 }
284
285
286 /*!
287  * Free flag list in mesh object.
288  *
289  * The current flag list is freed and set to NULL.  mesh->flags is
290  * set to zero.
291  *
292  * \param mesh Mesh object to be modified.
293  *
294  * \ingroup mesh
295  */
296 void
297 lib3ds_mesh_free_flag_list(Lib3dsMesh *mesh)
298 {
299   ASSERT(mesh);
300   if (mesh->flagL) {
301     ASSERT(mesh->flags);
302     free(mesh->flagL);
303     mesh->flagL=0;
304     mesh->flags=0;
305   }
306   else {
307     ASSERT(!mesh->flags);
308   }
309 }
310
311
312 /*!
313  * Allocate texel list in mesh object.
314  *
315  * This function frees the current texel list, if any, and allocates
316  * a new one large enough to hold the specified number of texels.
317  *
318  * \param mesh Mesh object for which points are to be allocated.
319  * \param texels Number of texels in the new texel list.
320  *
321  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
322  *
323  * \ingroup mesh
324  */
325 Lib3dsBool
326 lib3ds_mesh_new_texel_list(Lib3dsMesh *mesh, Lib3dsDword texels)
327 {
328   ASSERT(mesh);
329   if (mesh->texelL) {
330     ASSERT(mesh->texels);
331     lib3ds_mesh_free_texel_list(mesh);
332   }
333   ASSERT(!mesh->texelL && !mesh->texels);
334   mesh->texels=0;
335   mesh->texelL=calloc(sizeof(Lib3dsTexel)*texels,1);
336   if (!mesh->texelL) {
337     LIB3DS_ERROR_LOG;
338     return(LIB3DS_FALSE);
339   }
340   mesh->texels=texels;
341   return(LIB3DS_TRUE);
342 }
343
344
345 /*!
346  * Free texel list in mesh object.
347  *
348  * The current texel list is freed and set to NULL.  mesh->texels is
349  * set to zero.
350  *
351  * \param mesh Mesh object to be modified.
352  *
353  * \ingroup mesh
354  */
355 void
356 lib3ds_mesh_free_texel_list(Lib3dsMesh *mesh)
357 {
358   ASSERT(mesh);
359   if (mesh->texelL) {
360     ASSERT(mesh->texels);
361     free(mesh->texelL);
362     mesh->texelL=0;
363     mesh->texels=0;
364   }
365   else {
366     ASSERT(!mesh->texels);
367   }
368 }
369
370
371 /*!
372  * Allocate face list in mesh object.
373  *
374  * This function frees the current face list, if any, and allocates
375  * a new one large enough to hold the specified number of faces.
376  *
377  * \param mesh Mesh object for which points are to be allocated.
378  * \param faces Number of faces in the new face list.
379  *
380  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
381  *
382  * \ingroup mesh
383  */
384 Lib3dsBool
385 lib3ds_mesh_new_face_list(Lib3dsMesh *mesh, Lib3dsDword faces)
386 {
387   ASSERT(mesh);
388   if (mesh->faceL) {
389     ASSERT(mesh->faces);
390     lib3ds_mesh_free_face_list(mesh);
391   }
392   ASSERT(!mesh->faceL && !mesh->faces);
393   mesh->faces=0;
394   mesh->faceL=calloc(sizeof(Lib3dsFace)*faces,1);
395   if (!mesh->faceL) {
396     LIB3DS_ERROR_LOG;
397     return(LIB3DS_FALSE);
398   }
399   mesh->faces=faces;
400   return(LIB3DS_TRUE);
401 }
402
403
404 /*!
405  * Free face list in mesh object.
406  *
407  * The current face list is freed and set to NULL.  mesh->faces is
408  * set to zero.
409  *
410  * \param mesh Mesh object to be modified.
411  *
412  * \ingroup mesh
413  */
414 void
415 lib3ds_mesh_free_face_list(Lib3dsMesh *mesh)
416 {
417   ASSERT(mesh);
418   if (mesh->faceL) {
419     ASSERT(mesh->faces);
420     free(mesh->faceL);
421     mesh->faceL=0;
422     mesh->faces=0;
423   }
424   else {
425     ASSERT(!mesh->faces);
426   }
427 }
428
429
430 typedef struct _Lib3dsFaces Lib3dsFaces; 
431
432 struct _Lib3dsFaces {
433   Lib3dsFaces *next;
434   Lib3dsFace *face;
435 };
436
437
438
439
440 /*!
441  * Find the bounding box of a mesh object.
442  *
443  * \param mesh The mesh object
444  * \param min Returned bounding box
445  * \param max Returned bounding box
446  *
447  * \ingroup mesh
448  */
449 void
450 lib3ds_mesh_bounding_box(Lib3dsMesh *mesh, Lib3dsVector min, Lib3dsVector max)
451 {
452   unsigned i,j;
453   Lib3dsFloat v;
454
455   if (!mesh->points) {
456     lib3ds_vector_zero(min);
457     lib3ds_vector_zero(max);
458     return;
459   }
460  
461   lib3ds_vector_copy(min, mesh->pointL[0].pos);
462   lib3ds_vector_copy(max, mesh->pointL[0].pos);
463   for (i=1; i<mesh->points; ++i) {
464     for (j=0; j<3; ++j) {
465       v=mesh->pointL[i].pos[j];
466       if (v<min[j]) {
467         min[j]=v;
468       }
469       if (v>max[j]) {
470         max[j]=v;
471       }
472     }
473   };
474 }
475
476
477 /*!
478  * Calculates the vertex normals corresponding to the smoothing group
479  * settings for each face of a mesh.
480  *
481  * \param mesh      A pointer to the mesh to calculate the normals for.
482  * \param normalL   A pointer to a buffer to store the calculated
483  *                  normals. The buffer must have the size:
484  *                  3*sizeof(Lib3dsVector)*mesh->faces. 
485  *
486  * To allocate the normal buffer do for example the following:
487  * \code
488  *  Lib3dsVector *normalL = malloc(3*sizeof(Lib3dsVector)*mesh->faces);
489  * \endcode
490  *
491  * To access the normal of the i-th vertex of the j-th face do the 
492  * following:
493  * \code
494  *   normalL[3*j+i]
495  * \endcode
496  *
497  * \ingroup mesh
498  */
499 void
500 lib3ds_mesh_calculate_normals(Lib3dsMesh *mesh, Lib3dsVector *normalL)
501 {
502   Lib3dsFaces **fl; 
503   Lib3dsFaces *fa; 
504   unsigned i,j,k;
505
506   if (!mesh->faces) {
507     return;
508   }
509
510   fl=calloc(sizeof(Lib3dsFaces*),mesh->points);
511   ASSERT(fl);
512   fa=calloc(sizeof(Lib3dsFaces),3*mesh->faces);
513   ASSERT(fa);
514   k=0;
515   for (i=0; i<mesh->faces; ++i) {
516     Lib3dsFace *f=&mesh->faceL[i];
517     for (j=0; j<3; ++j) {
518       Lib3dsFaces* l=&fa[k++];
519       ASSERT(f->points[j]<mesh->points);
520       l->face=f;
521       l->next=fl[f->points[j]];
522       fl[f->points[j]]=l;
523     }
524   }
525   
526   for (i=0; i<mesh->faces; ++i) {
527     Lib3dsFace *f=&mesh->faceL[i];
528     for (j=0; j<3; ++j) {
529       /* FIXME: static array needs at least check!! */
530       Lib3dsVector n,N[128];
531       Lib3dsFaces *p;
532       int k,l;
533       int found;
534
535       ASSERT(f->points[j]<mesh->points);
536
537       if (f->smoothing) {
538         lib3ds_vector_zero(n);
539         k=0;
540         for (p=fl[f->points[j]]; p; p=p->next) {
541           found=0;
542           for (l=0; l<k; ++l) {
543             if( l >= 128 )
544               printf("array N overflow: i=%d, j=%d, k=%d\n", i,j,k);
545             if (fabs(lib3ds_vector_dot(N[l], p->face->normal)-1.0)<1e-5) {
546               found=1;
547               break;
548             }
549           }
550           if (!found) {
551             if (f->smoothing & p->face->smoothing) {
552               lib3ds_vector_add(n,n, p->face->normal);
553               lib3ds_vector_copy(N[k], p->face->normal);
554               ++k;
555             }
556           }
557         }
558       } 
559       else {
560         lib3ds_vector_copy(n, f->normal);
561       }
562       lib3ds_vector_normalize(n);
563
564       lib3ds_vector_copy(normalL[3*i+j], n);
565     }
566   }
567
568   free(fa);
569   free(fl);
570 }
571
572
573 /*!
574  * This function prints data associated with the specified mesh such as
575  * vertex and point lists.
576  *
577  * \param mesh  Points to a mesh that you wish to view the data for.
578  *
579  * \return None
580  *
581  * \warning WIN32: Should only be used in a console window not in a GUI.
582  *
583  * \ingroup mesh
584  */
585 void
586 lib3ds_mesh_dump(Lib3dsMesh *mesh)
587 {
588   unsigned i;
589   Lib3dsVector p;
590
591   ASSERT(mesh);
592   printf("  %s vertices=%ld faces=%ld\n",
593     mesh->name,
594     mesh->points,
595     mesh->faces
596   );
597   printf("  matrix:\n");
598   lib3ds_matrix_dump(mesh->matrix);
599   printf("  point list:\n");
600   for (i=0; i<mesh->points; ++i) {
601     lib3ds_vector_copy(p, mesh->pointL[i].pos);
602     printf ("    %8f %8f %8f\n", p[0], p[1], p[2]);
603   }
604   printf("  facelist:\n");
605   for (i=0; i<mesh->faces; ++i) {
606     printf ("    %4d %4d %4d  smoothing:%X  flags:%X  material:\"%s\"\n",
607       mesh->faceL[i].points[0],
608       mesh->faceL[i].points[1],
609       mesh->faceL[i].points[2],
610       (unsigned)mesh->faceL[i].smoothing,
611       mesh->faceL[i].flags,
612       mesh->faceL[i].material
613     );
614   }
615 }
616
617
618 /*!
619  * \ingroup mesh
620  */
621 Lib3dsBool
622 lib3ds_mesh_read(Lib3dsMesh *mesh, Lib3dsIo *io)
623 {
624   Lib3dsChunk c;
625   Lib3dsWord chunk;
626
627   if (!lib3ds_chunk_read_start(&c, LIB3DS_N_TRI_OBJECT, io)) {
628     return(LIB3DS_FALSE);
629   }
630
631   while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
632     switch (chunk) {
633       case LIB3DS_MESH_MATRIX:
634         {
635           int i,j;
636           
637           lib3ds_matrix_identity(mesh->matrix);
638           for (i=0; i<4; i++) {
639             for (j=0; j<3; j++) {
640               mesh->matrix[i][j]=lib3ds_io_read_float(io);
641             }
642           }
643         }
644         break;
645       case LIB3DS_MESH_COLOR:
646         {
647           mesh->color=lib3ds_io_read_byte(io);
648         }
649         break;
650       case LIB3DS_POINT_ARRAY:
651         {
652           unsigned i,j;
653           unsigned points;
654           
655           lib3ds_mesh_free_point_list(mesh);
656           points=lib3ds_io_read_word(io);
657           if (points) {
658             if (!lib3ds_mesh_new_point_list(mesh, points)) {
659               LIB3DS_ERROR_LOG;
660               return(LIB3DS_FALSE);
661             }
662             for (i=0; i<mesh->points; ++i) {
663               for (j=0; j<3; ++j) {
664                 mesh->pointL[i].pos[j]=lib3ds_io_read_float(io);
665               }
666             }
667             ASSERT((!mesh->flags) || (mesh->points==mesh->flags));
668             ASSERT((!mesh->texels) || (mesh->points==mesh->texels));
669           }
670         }
671         break;
672       case LIB3DS_POINT_FLAG_ARRAY:
673         {
674           unsigned i;
675           unsigned flags;
676           
677           lib3ds_mesh_free_flag_list(mesh);
678           flags=lib3ds_io_read_word(io);
679           if (flags) {
680             if (!lib3ds_mesh_new_flag_list(mesh, flags)) {
681               LIB3DS_ERROR_LOG;
682               return(LIB3DS_FALSE);
683             }
684             for (i=0; i<mesh->flags; ++i) {
685               mesh->flagL[i]=lib3ds_io_read_word(io);
686             }
687             ASSERT((!mesh->points) || (mesh->flags==mesh->points));
688             ASSERT((!mesh->texels) || (mesh->flags==mesh->texels));
689           }
690         }
691         break;
692       case LIB3DS_FACE_ARRAY:
693         {
694           lib3ds_chunk_read_reset(&c, io);
695           if (!face_array_read(mesh, io)) {
696             return(LIB3DS_FALSE);
697           }
698         }
699         break;
700       case LIB3DS_MESH_TEXTURE_INFO:
701         {
702           int i,j;
703
704           for (i=0; i<2; ++i) {
705             mesh->map_data.tile[i]=lib3ds_io_read_float(io);
706           }
707           for (i=0; i<3; ++i) {
708             mesh->map_data.pos[i]=lib3ds_io_read_float(io);
709           }
710           mesh->map_data.scale=lib3ds_io_read_float(io);
711
712           lib3ds_matrix_identity(mesh->map_data.matrix);
713           for (i=0; i<4; i++) {
714             for (j=0; j<3; j++) {
715               mesh->map_data.matrix[i][j]=lib3ds_io_read_float(io);
716             }
717           }
718           for (i=0; i<2; ++i) {
719             mesh->map_data.planar_size[i]=lib3ds_io_read_float(io);
720           }
721           mesh->map_data.cylinder_height=lib3ds_io_read_float(io);
722         }
723         break;
724       case LIB3DS_TEX_VERTS:
725         {
726           unsigned i;
727           unsigned texels;
728           
729           lib3ds_mesh_free_texel_list(mesh);
730           texels=lib3ds_io_read_word(io);
731           if (texels) {
732             if (!lib3ds_mesh_new_texel_list(mesh, texels)) {
733               LIB3DS_ERROR_LOG;
734               return(LIB3DS_FALSE);
735             }
736             for (i=0; i<mesh->texels; ++i) {
737               mesh->texelL[i][0]=lib3ds_io_read_float(io);
738               mesh->texelL[i][1]=lib3ds_io_read_float(io);
739             }
740             ASSERT((!mesh->points) || (mesh->texels==mesh->points));
741             ASSERT((!mesh->flags) || (mesh->texels==mesh->flags));
742           }
743         }
744         break;
745       default:
746         lib3ds_chunk_unknown(chunk);
747     }
748   }
749   {
750     unsigned j;
751
752     for (j=0; j<mesh->faces; ++j) {
753       ASSERT(mesh->faceL[j].points[0]<mesh->points);
754       ASSERT(mesh->faceL[j].points[1]<mesh->points);
755       ASSERT(mesh->faceL[j].points[2]<mesh->points);
756       lib3ds_vector_normal(
757         mesh->faceL[j].normal,
758         mesh->pointL[mesh->faceL[j].points[0]].pos,
759         mesh->pointL[mesh->faceL[j].points[1]].pos,
760         mesh->pointL[mesh->faceL[j].points[2]].pos
761       );
762     }
763   }
764   
765   lib3ds_chunk_read_end(&c, io);
766   return(LIB3DS_TRUE);
767 }
768
769
770 static Lib3dsBool
771 point_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
772 {
773   Lib3dsChunk c;
774   unsigned i;
775
776   if (!mesh->points || !mesh->pointL) {
777     return(LIB3DS_TRUE);
778   }
779   ASSERT(mesh->points<0x10000);
780   c.chunk=LIB3DS_POINT_ARRAY;
781   c.size=8+12*mesh->points;
782   lib3ds_chunk_write(&c, io);
783   
784   lib3ds_io_write_word(io, (Lib3dsWord)mesh->points);
785   for (i=0; i<mesh->points; ++i) {
786     lib3ds_io_write_vector(io, mesh->pointL[i].pos);
787   }
788   return(LIB3DS_TRUE);
789 }
790
791
792 static Lib3dsBool
793 flag_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
794 {
795   Lib3dsChunk c;
796   unsigned i;
797   
798   if (!mesh->flags || !mesh->flagL) {
799     return(LIB3DS_TRUE);
800   }
801   ASSERT(mesh->flags<0x10000);
802   c.chunk=LIB3DS_POINT_FLAG_ARRAY;
803   c.size=8+2*mesh->flags;
804   lib3ds_chunk_write(&c, io);
805   
806   lib3ds_io_write_word(io, (Lib3dsWord)mesh->flags);
807   for (i=0; i<mesh->flags; ++i) {
808     lib3ds_io_write_word(io, mesh->flagL[i]);
809   }
810   return(LIB3DS_TRUE);
811 }
812
813
814 static Lib3dsBool
815 face_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
816 {
817   Lib3dsChunk c;
818   
819   if (!mesh->faces || !mesh->faceL) {
820     return(LIB3DS_TRUE);
821   }
822   ASSERT(mesh->faces<0x10000);
823   c.chunk=LIB3DS_FACE_ARRAY;
824   if (!lib3ds_chunk_write_start(&c, io)) {
825     return(LIB3DS_FALSE);
826   }
827   {
828     unsigned i;
829
830     lib3ds_io_write_word(io, (Lib3dsWord)mesh->faces);
831     for (i=0; i<mesh->faces; ++i) {
832       lib3ds_io_write_word(io, mesh->faceL[i].points[0]);
833       lib3ds_io_write_word(io, mesh->faceL[i].points[1]);
834       lib3ds_io_write_word(io, mesh->faceL[i].points[2]);
835       lib3ds_io_write_word(io, mesh->faceL[i].flags);
836     }
837   }
838
839   { /*---- MSH_MAT_GROUP ----*/
840     Lib3dsChunk c;
841     unsigned i,j;
842     Lib3dsWord num;
843     char *matf=calloc(sizeof(char), mesh->faces);
844     if (!matf) {
845       return(LIB3DS_FALSE);
846     }
847     
848     for (i=0; i<mesh->faces; ++i) {
849       if (!matf[i] && strlen(mesh->faceL[i].material)) {
850         matf[i]=1;
851         num=1;
852         
853         for (j=i+1; j<mesh->faces; ++j) {
854           if (strcmp(mesh->faceL[i].material, mesh->faceL[j].material)==0) ++num;
855         }
856         
857         c.chunk=LIB3DS_MSH_MAT_GROUP;
858         c.size=6+ strlen(mesh->faceL[i].material)+1 +2+2*num;
859         lib3ds_chunk_write(&c, io);
860         lib3ds_io_write_string(io, mesh->faceL[i].material);
861         lib3ds_io_write_word(io, num);
862         lib3ds_io_write_word(io, (Lib3dsWord)i);
863         
864         for (j=i+1; j<mesh->faces; ++j) {
865           if (strcmp(mesh->faceL[i].material, mesh->faceL[j].material)==0) {
866             lib3ds_io_write_word(io, (Lib3dsWord)j);
867             matf[j]=1;
868           }
869         }
870       }      
871     }
872     free(matf);
873   }
874
875   { /*---- SMOOTH_GROUP ----*/
876     Lib3dsChunk c;
877     unsigned i;
878     
879     c.chunk=LIB3DS_SMOOTH_GROUP;
880     c.size=6+4*mesh->faces;
881     lib3ds_chunk_write(&c, io);
882     
883     for (i=0; i<mesh->faces; ++i) {
884       lib3ds_io_write_dword(io, mesh->faceL[i].smoothing);
885     }
886   }
887   
888   { /*---- MSH_BOXMAP ----*/
889     Lib3dsChunk c;
890
891     if (strlen(mesh->box_map.front) ||
892       strlen(mesh->box_map.back) ||
893       strlen(mesh->box_map.left) ||
894       strlen(mesh->box_map.right) ||
895       strlen(mesh->box_map.top) ||
896       strlen(mesh->box_map.bottom)) {
897     
898       c.chunk=LIB3DS_MSH_BOXMAP;
899       if (!lib3ds_chunk_write_start(&c, io)) {
900         return(LIB3DS_FALSE);
901       }
902       
903       lib3ds_io_write_string(io, mesh->box_map.front);
904       lib3ds_io_write_string(io, mesh->box_map.back);
905       lib3ds_io_write_string(io, mesh->box_map.left);
906       lib3ds_io_write_string(io, mesh->box_map.right);
907       lib3ds_io_write_string(io, mesh->box_map.top);
908       lib3ds_io_write_string(io, mesh->box_map.bottom);
909       
910       if (!lib3ds_chunk_write_end(&c, io)) {
911         return(LIB3DS_FALSE);
912       }
913     }
914   }
915
916   if (!lib3ds_chunk_write_end(&c, io)) {
917     return(LIB3DS_FALSE);
918   }
919   return(LIB3DS_TRUE);
920 }
921
922
923 static Lib3dsBool
924 texel_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
925 {
926   Lib3dsChunk c;
927   unsigned i;
928   
929   if (!mesh->texels || !mesh->texelL) {
930     return(LIB3DS_TRUE);
931   }
932   ASSERT(mesh->texels<0x10000);
933   c.chunk=LIB3DS_TEX_VERTS;
934   c.size=8+8*mesh->texels;
935   lib3ds_chunk_write(&c, io);
936   
937   lib3ds_io_write_word(io, (Lib3dsWord)mesh->texels);
938   for (i=0; i<mesh->texels; ++i) {
939     lib3ds_io_write_float(io, mesh->texelL[i][0]);
940     lib3ds_io_write_float(io, mesh->texelL[i][1]);
941   }
942   return(LIB3DS_TRUE);
943 }
944
945
946 /*!
947  * \ingroup mesh
948  */
949 Lib3dsBool
950 lib3ds_mesh_write(Lib3dsMesh *mesh, Lib3dsIo *io)
951 {
952   Lib3dsChunk c;
953
954   c.chunk=LIB3DS_N_TRI_OBJECT;
955   if (!lib3ds_chunk_write_start(&c,io)) {
956     return(LIB3DS_FALSE);
957   }
958   if (!point_array_write(mesh, io)) {
959     return(LIB3DS_FALSE);
960   }
961   if (!texel_array_write(mesh, io)) {
962     return(LIB3DS_FALSE);
963   }
964
965   if (mesh->map_data.maptype!=LIB3DS_MAP_NONE) { /*---- LIB3DS_MESH_TEXTURE_INFO ----*/
966     Lib3dsChunk c;
967     int i,j;
968     
969     c.chunk=LIB3DS_MESH_TEXTURE_INFO;
970     c.size=92;
971     if (!lib3ds_chunk_write(&c,io)) {
972       return(LIB3DS_FALSE);
973     }
974
975     lib3ds_io_write_word(io, mesh->map_data.maptype);
976
977     for (i=0; i<2; ++i) {
978       lib3ds_io_write_float(io, mesh->map_data.tile[i]);
979     }
980     for (i=0; i<3; ++i) {
981       lib3ds_io_write_float(io, mesh->map_data.pos[i]);
982     }
983     lib3ds_io_write_float(io, mesh->map_data.scale);
984
985     for (i=0; i<4; i++) {
986       for (j=0; j<3; j++) {
987         lib3ds_io_write_float(io, mesh->map_data.matrix[i][j]);
988       }
989     }
990     for (i=0; i<2; ++i) {
991       lib3ds_io_write_float(io, mesh->map_data.planar_size[i]);
992     }
993     lib3ds_io_write_float(io, mesh->map_data.cylinder_height);
994   }
995
996   if (!flag_array_write(mesh, io)) {
997     return(LIB3DS_FALSE);
998   }
999   { /*---- LIB3DS_MESH_MATRIX ----*/
1000     Lib3dsChunk c;
1001     int i,j;
1002
1003     c.chunk=LIB3DS_MESH_MATRIX;
1004     c.size=54;
1005     if (!lib3ds_chunk_write(&c,io)) {
1006       return(LIB3DS_FALSE);
1007     }
1008     for (i=0; i<4; i++) {
1009       for (j=0; j<3; j++) {
1010         lib3ds_io_write_float(io, mesh->matrix[i][j]);
1011       }
1012     }
1013   }
1014
1015   if (mesh->color) { /*---- LIB3DS_MESH_COLOR ----*/
1016     Lib3dsChunk c;
1017     
1018     c.chunk=LIB3DS_MESH_COLOR;
1019     c.size=7;
1020     if (!lib3ds_chunk_write(&c,io)) {
1021       return(LIB3DS_FALSE);
1022     }
1023     lib3ds_io_write_byte(io, mesh->color);
1024   }
1025   if (!face_array_write(mesh, io)) {
1026     return(LIB3DS_FALSE);
1027   }
1028
1029   if (!lib3ds_chunk_write_end(&c,io)) {
1030     return(LIB3DS_FALSE);
1031   }
1032   return(LIB3DS_TRUE);
1033 }
1034
1035
1036 /*!
1037
1038 \typedef Lib3dsFace
1039   \ingroup mesh
1040   \sa _Lib3dsFace
1041
1042 */
1043 /*!
1044
1045 \typedef Lib3dsBoxMap
1046   \ingroup mesh
1047   \sa _Lib3dsBoxMap
1048
1049 */
1050 /*!
1051
1052 \typedef Lib3dsMapData
1053   \ingroup mesh
1054   \sa _Lib3dsMapData
1055
1056 */
1057 /*!
1058
1059 \typedef Lib3dsMesh
1060   \ingroup mesh
1061   \sa _Lib3dsMesh
1062
1063 */