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