2 * The 3D Studio File Format Library
3 * Copyright (C) 1996-2001 by J.E. Hoffmann <je-h@gmx.net>
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.
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.
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.
20 * $Id: mesh.c,v 1.22 2004/11/20 08:33:56 efalk Exp $
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>
37 * \defgroup mesh Meshes
39 * \author J.E. Hoffmann <je-h@gmx.net>
44 face_array_read(Lib3dsMesh *mesh, Lib3dsIo *io)
51 if (!lib3ds_chunk_read_start(&c, LIB3DS_FACE_ARRAY, io)) {
54 lib3ds_mesh_free_face_list(mesh);
56 faces=lib3ds_io_read_word(io);
58 if (!lib3ds_mesh_new_face_list(mesh, faces)) {
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);
69 lib3ds_chunk_read_tell(&c, io);
71 while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
73 case LIB3DS_SMOOTH_GROUP:
77 for (i=0; i<mesh->faces; ++i) {
78 mesh->faceL[i].smoothing=lib3ds_io_read_dword(io);
82 case LIB3DS_MSH_MAT_GROUP:
89 if (!lib3ds_io_read_string(io, name, 64)) {
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);
100 case LIB3DS_MSH_BOXMAP:
104 if (!lib3ds_io_read_string(io, name, 64)) {
105 return(LIB3DS_FALSE);
107 strcpy(mesh->box_map.front, name);
108 if (!lib3ds_io_read_string(io, name, 64)) {
109 return(LIB3DS_FALSE);
111 strcpy(mesh->box_map.back, name);
112 if (!lib3ds_io_read_string(io, name, 64)) {
113 return(LIB3DS_FALSE);
115 strcpy(mesh->box_map.left, name);
116 if (!lib3ds_io_read_string(io, name, 64)) {
117 return(LIB3DS_FALSE);
119 strcpy(mesh->box_map.right, name);
120 if (!lib3ds_io_read_string(io, name, 64)) {
121 return(LIB3DS_FALSE);
123 strcpy(mesh->box_map.top, name);
124 if (!lib3ds_io_read_string(io, name, 64)) {
125 return(LIB3DS_FALSE);
127 strcpy(mesh->box_map.bottom, name);
131 lib3ds_chunk_unknown(chunk);
136 lib3ds_chunk_read_end(&c, io);
142 * Create and return a new empty mesh object.
144 * Mesh is initialized with the name and an identity matrix; all
145 * other fields are zero.
147 * See Lib3dsFaceFlag for definitions of per-face flags.
149 * \param name Mesh name. Must not be NULL. Must be < 64 characters.
151 * \return mesh object or NULL on error.
156 lib3ds_mesh_new(const char *name)
161 ASSERT(strlen(name)<64);
163 mesh=(Lib3dsMesh*)calloc(sizeof(Lib3dsMesh), 1);
167 strcpy(mesh->name, name);
168 lib3ds_matrix_identity(mesh->matrix);
169 mesh->map_data.maptype=LIB3DS_MAP_NONE;
175 * Free a mesh object and all of its resources.
177 * \param mesh Mesh object to be freed.
182 lib3ds_mesh_free(Lib3dsMesh *mesh)
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));
194 * Allocate point list in mesh object.
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.
199 * \param mesh Mesh object for which points are to be allocated.
200 * \param points Number of points in the new point list.
202 * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
207 lib3ds_mesh_new_point_list(Lib3dsMesh *mesh, Lib3dsDword points)
211 ASSERT(mesh->points);
212 lib3ds_mesh_free_point_list(mesh);
214 ASSERT(!mesh->pointL && !mesh->points);
216 mesh->pointL=calloc(sizeof(Lib3dsPoint)*points,1);
219 return(LIB3DS_FALSE);
227 * Free point list in mesh object.
229 * The current point list is freed and set to NULL. mesh->points is
232 * \param mesh Mesh object to be modified.
237 lib3ds_mesh_free_point_list(Lib3dsMesh *mesh)
241 ASSERT(mesh->points);
247 ASSERT(!mesh->points);
253 * Allocate flag list in mesh object.
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
259 * \param mesh Mesh object for which points are to be allocated.
260 * \param flags Number of flags in the new flag list.
262 * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
267 lib3ds_mesh_new_flag_list(Lib3dsMesh *mesh, Lib3dsDword flags)
272 lib3ds_mesh_free_flag_list(mesh);
274 ASSERT(!mesh->flagL && !mesh->flags);
276 mesh->flagL=calloc(sizeof(Lib3dsWord)*flags,1);
279 return(LIB3DS_FALSE);
287 * Free flag list in mesh object.
289 * The current flag list is freed and set to NULL. mesh->flags is
292 * \param mesh Mesh object to be modified.
297 lib3ds_mesh_free_flag_list(Lib3dsMesh *mesh)
307 ASSERT(!mesh->flags);
313 * Allocate texel list in mesh object.
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.
318 * \param mesh Mesh object for which points are to be allocated.
319 * \param texels Number of texels in the new texel list.
321 * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
326 lib3ds_mesh_new_texel_list(Lib3dsMesh *mesh, Lib3dsDword texels)
330 ASSERT(mesh->texels);
331 lib3ds_mesh_free_texel_list(mesh);
333 ASSERT(!mesh->texelL && !mesh->texels);
335 mesh->texelL=calloc(sizeof(Lib3dsTexel)*texels,1);
338 return(LIB3DS_FALSE);
346 * Free texel list in mesh object.
348 * The current texel list is freed and set to NULL. mesh->texels is
351 * \param mesh Mesh object to be modified.
356 lib3ds_mesh_free_texel_list(Lib3dsMesh *mesh)
360 ASSERT(mesh->texels);
366 ASSERT(!mesh->texels);
372 * Allocate face list in mesh object.
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.
377 * \param mesh Mesh object for which points are to be allocated.
378 * \param faces Number of faces in the new face list.
380 * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
385 lib3ds_mesh_new_face_list(Lib3dsMesh *mesh, Lib3dsDword faces)
390 lib3ds_mesh_free_face_list(mesh);
392 ASSERT(!mesh->faceL && !mesh->faces);
394 mesh->faceL=calloc(sizeof(Lib3dsFace)*faces,1);
397 return(LIB3DS_FALSE);
405 * Free face list in mesh object.
407 * The current face list is freed and set to NULL. mesh->faces is
410 * \param mesh Mesh object to be modified.
415 lib3ds_mesh_free_face_list(Lib3dsMesh *mesh)
425 ASSERT(!mesh->faces);
430 typedef struct _Lib3dsFaces Lib3dsFaces;
432 struct _Lib3dsFaces {
441 * Find the bounding box of a mesh object.
443 * \param mesh The mesh object
444 * \param min Returned bounding box
445 * \param max Returned bounding box
450 lib3ds_mesh_bounding_box(Lib3dsMesh *mesh, Lib3dsVector min, Lib3dsVector max)
456 lib3ds_vector_zero(min);
457 lib3ds_vector_zero(max);
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];
478 * Calculates the vertex normals corresponding to the smoothing group
479 * settings for each face of a mesh.
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.
486 * To allocate the normal buffer do for example the following:
488 * Lib3dsVector *normalL = malloc(3*sizeof(Lib3dsVector)*mesh->faces);
491 * To access the normal of the i-th vertex of the j-th face do the
500 lib3ds_mesh_calculate_normals(Lib3dsMesh *mesh, Lib3dsVector *normalL)
510 fl=calloc(sizeof(Lib3dsFaces*),mesh->points);
512 fa=calloc(sizeof(Lib3dsFaces),3*mesh->faces);
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);
521 l->next=fl[f->points[j]];
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];
535 ASSERT(f->points[j]<mesh->points);
538 lib3ds_vector_zero(n);
540 for (p=fl[f->points[j]]; p; p=p->next) {
542 for (l=0; l<k; ++l) {
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) {
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);
560 lib3ds_vector_copy(n, f->normal);
562 lib3ds_vector_normalize(n);
564 lib3ds_vector_copy(normalL[3*i+j], n);
574 * This function prints data associated with the specified mesh such as
575 * vertex and point lists.
577 * \param mesh Points to a mesh that you wish to view the data for.
581 * \warning WIN32: Should only be used in a console window not in a GUI.
586 lib3ds_mesh_dump(Lib3dsMesh *mesh)
592 printf(" %s vertices=%ld faces=%ld\n",
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]);
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
622 lib3ds_mesh_read(Lib3dsMesh *mesh, Lib3dsIo *io)
627 if (!lib3ds_chunk_read_start(&c, LIB3DS_N_TRI_OBJECT, io)) {
628 return(LIB3DS_FALSE);
631 while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
633 case LIB3DS_MESH_MATRIX:
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);
645 case LIB3DS_MESH_COLOR:
647 mesh->color=lib3ds_io_read_byte(io);
650 case LIB3DS_POINT_ARRAY:
655 lib3ds_mesh_free_point_list(mesh);
656 points=lib3ds_io_read_word(io);
658 if (!lib3ds_mesh_new_point_list(mesh, points)) {
660 return(LIB3DS_FALSE);
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);
667 ASSERT((!mesh->flags) || (mesh->points==mesh->flags));
668 ASSERT((!mesh->texels) || (mesh->points==mesh->texels));
672 case LIB3DS_POINT_FLAG_ARRAY:
677 lib3ds_mesh_free_flag_list(mesh);
678 flags=lib3ds_io_read_word(io);
680 if (!lib3ds_mesh_new_flag_list(mesh, flags)) {
682 return(LIB3DS_FALSE);
684 for (i=0; i<mesh->flags; ++i) {
685 mesh->flagL[i]=lib3ds_io_read_word(io);
687 ASSERT((!mesh->points) || (mesh->flags==mesh->points));
688 ASSERT((!mesh->texels) || (mesh->flags==mesh->texels));
692 case LIB3DS_FACE_ARRAY:
694 lib3ds_chunk_read_reset(&c, io);
695 if (!face_array_read(mesh, io)) {
696 return(LIB3DS_FALSE);
700 case LIB3DS_MESH_TEXTURE_INFO:
704 for (i=0; i<2; ++i) {
705 mesh->map_data.tile[i]=lib3ds_io_read_float(io);
707 for (i=0; i<3; ++i) {
708 mesh->map_data.pos[i]=lib3ds_io_read_float(io);
710 mesh->map_data.scale=lib3ds_io_read_float(io);
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);
718 for (i=0; i<2; ++i) {
719 mesh->map_data.planar_size[i]=lib3ds_io_read_float(io);
721 mesh->map_data.cylinder_height=lib3ds_io_read_float(io);
724 case LIB3DS_TEX_VERTS:
729 lib3ds_mesh_free_texel_list(mesh);
730 texels=lib3ds_io_read_word(io);
732 if (!lib3ds_mesh_new_texel_list(mesh, texels)) {
734 return(LIB3DS_FALSE);
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);
740 ASSERT((!mesh->points) || (mesh->texels==mesh->points));
741 ASSERT((!mesh->flags) || (mesh->texels==mesh->flags));
746 lib3ds_chunk_unknown(chunk);
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
765 lib3ds_chunk_read_end(&c, io);
771 point_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
776 if (!mesh->points || !mesh->pointL) {
779 ASSERT(mesh->points<0x10000);
780 c.chunk=LIB3DS_POINT_ARRAY;
781 c.size=8+12*mesh->points;
782 lib3ds_chunk_write(&c, io);
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);
793 flag_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
798 if (!mesh->flags || !mesh->flagL) {
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);
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]);
815 face_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
819 if (!mesh->faces || !mesh->faceL) {
822 ASSERT(mesh->faces<0x10000);
823 c.chunk=LIB3DS_FACE_ARRAY;
824 if (!lib3ds_chunk_write_start(&c, io)) {
825 return(LIB3DS_FALSE);
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);
839 { /*---- MSH_MAT_GROUP ----*/
843 char *matf=calloc(sizeof(char), mesh->faces);
845 return(LIB3DS_FALSE);
848 for (i=0; i<mesh->faces; ++i) {
849 if (!matf[i] && strlen(mesh->faceL[i].material)) {
853 for (j=i+1; j<mesh->faces; ++j) {
854 if (strcmp(mesh->faceL[i].material, mesh->faceL[j].material)==0) ++num;
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);
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);
875 { /*---- SMOOTH_GROUP ----*/
879 c.chunk=LIB3DS_SMOOTH_GROUP;
880 c.size=6+4*mesh->faces;
881 lib3ds_chunk_write(&c, io);
883 for (i=0; i<mesh->faces; ++i) {
884 lib3ds_io_write_dword(io, mesh->faceL[i].smoothing);
888 { /*---- MSH_BOXMAP ----*/
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)) {
898 c.chunk=LIB3DS_MSH_BOXMAP;
899 if (!lib3ds_chunk_write_start(&c, io)) {
900 return(LIB3DS_FALSE);
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);
910 if (!lib3ds_chunk_write_end(&c, io)) {
911 return(LIB3DS_FALSE);
916 if (!lib3ds_chunk_write_end(&c, io)) {
917 return(LIB3DS_FALSE);
924 texel_array_write(Lib3dsMesh *mesh, Lib3dsIo *io)
929 if (!mesh->texels || !mesh->texelL) {
932 ASSERT(mesh->texels<0x10000);
933 c.chunk=LIB3DS_TEX_VERTS;
934 c.size=8+8*mesh->texels;
935 lib3ds_chunk_write(&c, io);
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]);
950 lib3ds_mesh_write(Lib3dsMesh *mesh, Lib3dsIo *io)
954 c.chunk=LIB3DS_N_TRI_OBJECT;
955 if (!lib3ds_chunk_write_start(&c,io)) {
956 return(LIB3DS_FALSE);
958 if (!point_array_write(mesh, io)) {
959 return(LIB3DS_FALSE);
961 if (!texel_array_write(mesh, io)) {
962 return(LIB3DS_FALSE);
965 if (mesh->map_data.maptype!=LIB3DS_MAP_NONE) { /*---- LIB3DS_MESH_TEXTURE_INFO ----*/
969 c.chunk=LIB3DS_MESH_TEXTURE_INFO;
971 if (!lib3ds_chunk_write(&c,io)) {
972 return(LIB3DS_FALSE);
975 lib3ds_io_write_word(io, mesh->map_data.maptype);
977 for (i=0; i<2; ++i) {
978 lib3ds_io_write_float(io, mesh->map_data.tile[i]);
980 for (i=0; i<3; ++i) {
981 lib3ds_io_write_float(io, mesh->map_data.pos[i]);
983 lib3ds_io_write_float(io, mesh->map_data.scale);
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]);
990 for (i=0; i<2; ++i) {
991 lib3ds_io_write_float(io, mesh->map_data.planar_size[i]);
993 lib3ds_io_write_float(io, mesh->map_data.cylinder_height);
996 if (!flag_array_write(mesh, io)) {
997 return(LIB3DS_FALSE);
999 { /*---- LIB3DS_MESH_MATRIX ----*/
1003 c.chunk=LIB3DS_MESH_MATRIX;
1005 if (!lib3ds_chunk_write(&c,io)) {
1006 return(LIB3DS_FALSE);
1008 for (i=0; i<4; i++) {
1009 for (j=0; j<3; j++) {
1010 lib3ds_io_write_float(io, mesh->matrix[i][j]);
1015 if (mesh->color) { /*---- LIB3DS_MESH_COLOR ----*/
1018 c.chunk=LIB3DS_MESH_COLOR;
1020 if (!lib3ds_chunk_write(&c,io)) {
1021 return(LIB3DS_FALSE);
1023 lib3ds_io_write_byte(io, mesh->color);
1025 if (!face_array_write(mesh, io)) {
1026 return(LIB3DS_FALSE);
1029 if (!lib3ds_chunk_write_end(&c,io)) {
1030 return(LIB3DS_FALSE);
1032 return(LIB3DS_TRUE);
1045 \typedef Lib3dsBoxMap
1052 \typedef Lib3dsMapData