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