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: file.c,v 1.34 2007/06/20 17:04:08 jeh Exp $
21  */
22 #include <lib3ds/file.h>
23 #include <lib3ds/chunk.h>
24 #include <lib3ds/io.h>
25 #include <lib3ds/material.h>
26 #include <lib3ds/mesh.h>
27 #include <lib3ds/camera.h>
28 #include <lib3ds/light.h>
29 #include <lib3ds/node.h>
30 #include <lib3ds/matrix.h>
31 #include <lib3ds/vector.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <math.h>
35 #include <float.h>
36 
37 
38 /*!
39  * \defgroup file Files
40  */
41 
42 
43 static Lib3dsBool
fileio_error_func(void * self)44 fileio_error_func(void *self)
45 {
46   FILE *f = (FILE*)self;
47   return(ferror(f)!=0);
48 }
49 
50 
51 static long
fileio_seek_func(void * self,long offset,Lib3dsIoSeek origin)52 fileio_seek_func(void *self, long offset, Lib3dsIoSeek origin)
53 {
54   FILE *f = (FILE*)self;
55   int o;
56   switch (origin) {
57     case LIB3DS_SEEK_SET:
58       o = SEEK_SET;
59       break;
60     case LIB3DS_SEEK_CUR:
61       o = SEEK_CUR;
62       break;
63     case LIB3DS_SEEK_END:
64       o = SEEK_END;
65       break;
66     default:
67       ASSERT(0);
68       return(0);
69   }
70   return (fseek(f, offset, o));
71 }
72 
73 
74 static long
fileio_tell_func(void * self)75 fileio_tell_func(void *self)
76 {
77   FILE *f = (FILE*)self;
78   return(ftell(f));
79 }
80 
81 
82 static size_t
fileio_read_func(void * self,void * buffer,size_t size)83 fileio_read_func(void *self, void *buffer, size_t size)
84 {
85   FILE *f = (FILE*)self;
86   return(fread(buffer, 1, size, f));
87 }
88 
89 
90 static size_t
fileio_write_func(void * self,const void * buffer,size_t size)91 fileio_write_func(void *self, const void *buffer, size_t size)
92 {
93   FILE *f = (FILE*)self;
94   return(fwrite(buffer, 1, size, f));
95 }
96 
97 
98 /*!
99  * Loads a .3DS file from disk into memory.
100  *
101  * \param filename  The filename of the .3DS file
102  *
103  * \return   A pointer to the Lib3dsFile structure containing the
104  *           data of the .3DS file.
105  *           If the .3DS file can not be loaded NULL is returned.
106  *
107  * \note     To free the returned structure use lib3ds_free.
108  *
109  * \see lib3ds_file_save
110  * \see lib3ds_file_new
111  * \see lib3ds_file_free
112  *
113  * \ingroup file
114  */
115 Lib3dsFile*
lib3ds_file_load(const char * filename)116 lib3ds_file_load(const char *filename)
117 {
118   FILE *f;
119   Lib3dsFile *file;
120   Lib3dsIo *io;
121 
122   f = fopen(filename, "rb");
123   if (!f) {
124     return(0);
125   }
126   file = lib3ds_file_new();
127   if (!file) {
128     fclose(f);
129     return(0);
130   }
131 
132   io = lib3ds_io_new(
133     f,
134     fileio_error_func,
135     fileio_seek_func,
136     fileio_tell_func,
137     fileio_read_func,
138     fileio_write_func
139   );
140   if (!io) {
141     lib3ds_file_free(file);
142     fclose(f);
143     return(0);
144   }
145 
146   if (!lib3ds_file_read(file, io)) {
147     free(file);
148     lib3ds_io_free(io);
149     fclose(f);
150     return(0);
151   }
152 
153   lib3ds_io_free(io);
154   fclose(f);
155   return(file);
156 }
157 
158 
159 /*!
160  * Saves a .3DS file from memory to disk.
161  *
162  * \param file      A pointer to a Lib3dsFile structure containing the
163  *                  the data that should be stored.
164  * \param filename  The filename of the .3DS file to store the data in.
165  *
166  * \return          TRUE on success, FALSE otherwise.
167  *
168  * \see lib3ds_file_load
169  *
170  * \ingroup file
171  */
172 Lib3dsBool
lib3ds_file_save(Lib3dsFile * file,const char * filename)173 lib3ds_file_save(Lib3dsFile *file, const char *filename)
174 {
175   FILE *f;
176   Lib3dsIo *io;
177   Lib3dsBool result;
178 
179   f = fopen(filename, "wb");
180   if (!f) {
181     return(LIB3DS_FALSE);
182   }
183   io = lib3ds_io_new(
184     f,
185     fileio_error_func,
186     fileio_seek_func,
187     fileio_tell_func,
188     fileio_read_func,
189     fileio_write_func
190   );
191   if (!io) {
192     fclose(f);
193     return LIB3DS_FALSE;
194   }
195 
196   result = lib3ds_file_write(file, io);
197 
198   fclose(f);
199 
200   lib3ds_io_free(io);
201   return(result);
202 }
203 
204 
205 /*!
206  * Creates and returns a new, empty Lib3dsFile object.
207  *
208  * \return	A pointer to the Lib3dsFile structure.
209  *		If the structure cannot be allocated, NULL is returned.
210  *
211  * \ingroup file
212  */
213 Lib3dsFile*
lib3ds_file_new()214 lib3ds_file_new()
215 {
216   Lib3dsFile *file;
217 
218   file=(Lib3dsFile*)calloc(sizeof(Lib3dsFile),1);
219   if (!file) {
220     return(0);
221   }
222   file->mesh_version=3;
223   file->master_scale=1.0f;
224   file->keyf_revision=5;
225   strcpy(file->name, "LIB3DS");
226 
227   file->frames=100;
228   file->segment_from=0;
229   file->segment_to=100;
230   file->current_frame=0;
231 
232   return(file);
233 }
234 
235 
236 /*!
237  * Free a Lib3dsFile object and all of its resources.
238  *
239  * \param file The Lib3dsFile object to be freed.
240  *
241  * \ingroup file
242  */
243 void
lib3ds_file_free(Lib3dsFile * file)244 lib3ds_file_free(Lib3dsFile* file)
245 {
246   ASSERT(file);
247   lib3ds_viewport_set_views(&file->viewport,0);
248   lib3ds_viewport_set_views(&file->viewport_keyf,0);
249   {
250     Lib3dsMaterial *p,*q;
251 
252     for (p=file->materials; p; p=q) {
253       q=p->next;
254       lib3ds_material_free(p);
255     }
256     file->materials=0;
257   }
258   {
259     Lib3dsCamera *p,*q;
260 
261     for (p=file->cameras; p; p=q) {
262       q=p->next;
263       lib3ds_camera_free(p);
264     }
265     file->cameras=0;
266   }
267   {
268     Lib3dsLight *p,*q;
269 
270     for (p=file->lights; p; p=q) {
271       q=p->next;
272       lib3ds_light_free(p);
273     }
274     file->lights=0;
275   }
276   {
277     Lib3dsMesh *p,*q;
278 
279     for (p=file->meshes; p; p=q) {
280       q=p->next;
281       lib3ds_mesh_free(p);
282     }
283     file->meshes=0;
284   }
285   {
286     Lib3dsNode *p,*q;
287 
288     for (p=file->nodes; p; p=q) {
289       q=p->next;
290       lib3ds_node_free(p);
291     }
292   }
293   free(file);
294 }
295 
296 
297 /*!
298  * Evaluate all of the nodes in this Lib3dsFile object.
299  *
300  * \param file The Lib3dsFile object to be evaluated.
301  * \param t time value, between 0. and file->frames
302  *
303  * \see lib3ds_node_eval
304  *
305  * \ingroup file
306  */
307 void
lib3ds_file_eval(Lib3dsFile * file,Lib3dsFloat t)308 lib3ds_file_eval(Lib3dsFile *file, Lib3dsFloat t)
309 {
310   Lib3dsNode *p;
311 
312   for (p=file->nodes; p!=0; p=p->next) {
313     lib3ds_node_eval(p, t);
314   }
315 }
316 
317 
318 static Lib3dsBool
named_object_read(Lib3dsFile * file,Lib3dsIo * io)319 named_object_read(Lib3dsFile *file, Lib3dsIo *io)
320 {
321   Lib3dsChunk c;
322   char name[64];
323   Lib3dsWord chunk;
324   Lib3dsMesh *mesh = NULL;
325   Lib3dsCamera *camera = NULL;
326   Lib3dsLight *light = NULL;
327   Lib3dsDword object_flags;
328 
329   if (!lib3ds_chunk_read_start(&c, LIB3DS_NAMED_OBJECT, io)) {
330     return(LIB3DS_FALSE);
331   }
332   if (!lib3ds_io_read_string(io, name, 64)) {
333     return(LIB3DS_FALSE);
334   }
335   lib3ds_chunk_dump_info("  NAME=%s", name);
336   lib3ds_chunk_read_tell(&c, io);
337 
338   object_flags = 0;
339   while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
340     switch (chunk) {
341       case LIB3DS_N_TRI_OBJECT:
342         {
343           mesh=lib3ds_mesh_new(name);
344           if (!mesh) {
345             return(LIB3DS_FALSE);
346           }
347           lib3ds_chunk_read_reset(&c, io);
348           if (!lib3ds_mesh_read(mesh, io)) {
349             return(LIB3DS_FALSE);
350           }
351           lib3ds_file_insert_mesh(file, mesh);
352         }
353         break;
354 
355       case LIB3DS_N_CAMERA:
356         {
357           camera=lib3ds_camera_new(name);
358           if (!camera) {
359             return(LIB3DS_FALSE);
360           }
361           lib3ds_chunk_read_reset(&c, io);
362           if (!lib3ds_camera_read(camera, io)) {
363             return(LIB3DS_FALSE);
364           }
365           lib3ds_file_insert_camera(file, camera);
366         }
367         break;
368 
369       case LIB3DS_N_DIRECT_LIGHT:
370         {
371           light=lib3ds_light_new(name);
372           if (!light) {
373             return(LIB3DS_FALSE);
374           }
375           lib3ds_chunk_read_reset(&c, io);
376           if (!lib3ds_light_read(light, io)) {
377             return(LIB3DS_FALSE);
378           }
379           lib3ds_file_insert_light(file, light);
380         }
381         break;
382 
383       case LIB3DS_OBJ_HIDDEN:
384         object_flags |= LIB3DS_OBJECT_HIDDEN;
385         break;
386 
387       case LIB3DS_OBJ_DOESNT_CAST:
388         object_flags |= LIB3DS_OBJECT_DOESNT_CAST;
389         break;
390 
391       case LIB3DS_OBJ_VIS_LOFTER:
392         object_flags |= LIB3DS_OBJECT_VIS_LOFTER;
393         break;
394 
395       case LIB3DS_OBJ_MATTE:
396         object_flags |= LIB3DS_OBJECT_MATTE;
397         break;
398 
399       case LIB3DS_OBJ_DONT_RCVSHADOW:
400         object_flags |= LIB3DS_OBJECT_DONT_RCVSHADOW;
401         break;
402 
403       case LIB3DS_OBJ_FAST:
404         object_flags |= LIB3DS_OBJECT_FAST;
405         break;
406 
407       case LIB3DS_OBJ_FROZEN:
408         object_flags |= LIB3DS_OBJECT_FROZEN;
409         break;
410 
411       default:
412         lib3ds_chunk_unknown(chunk);
413     }
414   }
415 
416   if (mesh)
417     mesh->object_flags = object_flags;
418   if (camera)
419     camera->object_flags = object_flags;
420   if (light)
421     light->object_flags = object_flags;
422 
423   lib3ds_chunk_read_end(&c, io);
424   return(LIB3DS_TRUE);
425 }
426 
427 
428 static Lib3dsBool
ambient_read(Lib3dsFile * file,Lib3dsIo * io)429 ambient_read(Lib3dsFile *file, Lib3dsIo *io)
430 {
431   Lib3dsChunk c;
432   Lib3dsWord chunk;
433   Lib3dsBool have_lin=LIB3DS_FALSE;
434 
435   if (!lib3ds_chunk_read_start(&c, LIB3DS_AMBIENT_LIGHT, io)) {
436     return(LIB3DS_FALSE);
437   }
438 
439   while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
440     switch (chunk) {
441       case LIB3DS_LIN_COLOR_F:
442         {
443           int i;
444           for (i=0; i<3; ++i) {
445             file->ambient[i]=lib3ds_io_read_float(io);
446           }
447         }
448         have_lin=LIB3DS_TRUE;
449         break;
450       case LIB3DS_COLOR_F:
451         {
452           /* gamma corrected color chunk
453              replaced in 3ds R3 by LIN_COLOR_24 */
454           if (!have_lin) {
455             int i;
456             for (i=0; i<3; ++i) {
457               file->ambient[i]=lib3ds_io_read_float(io);
458             }
459           }
460         }
461         break;
462       default:
463         lib3ds_chunk_unknown(chunk);
464     }
465   }
466 
467   lib3ds_chunk_read_end(&c, io);
468   return(LIB3DS_TRUE);
469 }
470 
471 
472 static Lib3dsBool
mdata_read(Lib3dsFile * file,Lib3dsIo * io)473 mdata_read(Lib3dsFile *file, Lib3dsIo *io)
474 {
475   Lib3dsChunk c;
476   Lib3dsWord chunk;
477 
478   if (!lib3ds_chunk_read_start(&c, LIB3DS_MDATA, io)) {
479     return(LIB3DS_FALSE);
480   }
481 
482   while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
483     switch (chunk) {
484       case LIB3DS_MESH_VERSION:
485         {
486           file->mesh_version=lib3ds_io_read_intd(io);
487         }
488         break;
489       case LIB3DS_MASTER_SCALE:
490         {
491           file->master_scale=lib3ds_io_read_float(io);
492         }
493         break;
494       case LIB3DS_SHADOW_MAP_SIZE:
495       case LIB3DS_LO_SHADOW_BIAS:
496       case LIB3DS_HI_SHADOW_BIAS:
497       case LIB3DS_SHADOW_SAMPLES:
498       case LIB3DS_SHADOW_RANGE:
499       case LIB3DS_SHADOW_FILTER:
500       case LIB3DS_RAY_BIAS:
501         {
502           lib3ds_chunk_read_reset(&c, io);
503           if (!lib3ds_shadow_read(&file->shadow, io)) {
504             return(LIB3DS_FALSE);
505           }
506         }
507         break;
508       case LIB3DS_VIEWPORT_LAYOUT:
509       case LIB3DS_DEFAULT_VIEW:
510         {
511           lib3ds_chunk_read_reset(&c, io);
512           if (!lib3ds_viewport_read(&file->viewport, io)) {
513             return(LIB3DS_FALSE);
514           }
515         }
516         break;
517       case LIB3DS_O_CONSTS:
518         {
519           int i;
520           for (i=0; i<3; ++i) {
521             file->construction_plane[i]=lib3ds_io_read_float(io);
522           }
523         }
524         break;
525       case LIB3DS_AMBIENT_LIGHT:
526         {
527           lib3ds_chunk_read_reset(&c, io);
528           if (!ambient_read(file, io)) {
529             return(LIB3DS_FALSE);
530           }
531         }
532         break;
533       case LIB3DS_BIT_MAP:
534       case LIB3DS_SOLID_BGND:
535       case LIB3DS_V_GRADIENT:
536       case LIB3DS_USE_BIT_MAP:
537       case LIB3DS_USE_SOLID_BGND:
538       case LIB3DS_USE_V_GRADIENT:
539         {
540           lib3ds_chunk_read_reset(&c, io);
541           if (!lib3ds_background_read(&file->background, io)) {
542             return(LIB3DS_FALSE);
543           }
544         }
545         break;
546       case LIB3DS_FOG:
547       case LIB3DS_LAYER_FOG:
548       case LIB3DS_DISTANCE_CUE:
549       case LIB3DS_USE_FOG:
550       case LIB3DS_USE_LAYER_FOG:
551       case LIB3DS_USE_DISTANCE_CUE:
552         {
553           lib3ds_chunk_read_reset(&c, io);
554           if (!lib3ds_atmosphere_read(&file->atmosphere, io)) {
555             return(LIB3DS_FALSE);
556           }
557         }
558         break;
559       case LIB3DS_MAT_ENTRY:
560         {
561           Lib3dsMaterial *material;
562 
563           material=lib3ds_material_new();
564           if (!material) {
565             return(LIB3DS_FALSE);
566           }
567           lib3ds_chunk_read_reset(&c, io);
568           if (!lib3ds_material_read(material, io)) {
569             return(LIB3DS_FALSE);
570           }
571           lib3ds_file_insert_material(file, material);
572         }
573         break;
574       case LIB3DS_NAMED_OBJECT:
575         {
576           lib3ds_chunk_read_reset(&c, io);
577           if (!named_object_read(file, io)) {
578             return(LIB3DS_FALSE);
579           }
580         }
581         break;
582       default:
583         lib3ds_chunk_unknown(chunk);
584     }
585   }
586 
587   lib3ds_chunk_read_end(&c, io);
588   return(LIB3DS_TRUE);
589 }
590 
591 
592 static Lib3dsBool
kfdata_read(Lib3dsFile * file,Lib3dsIo * io)593 kfdata_read(Lib3dsFile *file, Lib3dsIo *io)
594 {
595   Lib3dsChunk c;
596   Lib3dsWord chunk;
597   Lib3dsDword node_number = 0;
598 
599   if (!lib3ds_chunk_read_start(&c, LIB3DS_KFDATA, io)) {
600     return(LIB3DS_FALSE);
601   }
602 
603   while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
604     switch (chunk) {
605       case LIB3DS_KFHDR:
606         {
607           file->keyf_revision=lib3ds_io_read_word(io);
608           if (!lib3ds_io_read_string(io, file->name, 12+1)) {
609             return(LIB3DS_FALSE);
610           }
611           file->frames=lib3ds_io_read_intd(io);
612         }
613         break;
614       case LIB3DS_KFSEG:
615         {
616           file->segment_from=lib3ds_io_read_intd(io);
617           file->segment_to=lib3ds_io_read_intd(io);
618         }
619         break;
620       case LIB3DS_KFCURTIME:
621         {
622           file->current_frame=lib3ds_io_read_intd(io);
623         }
624         break;
625       case LIB3DS_VIEWPORT_LAYOUT:
626       case LIB3DS_DEFAULT_VIEW:
627         {
628           lib3ds_chunk_read_reset(&c, io);
629           if (!lib3ds_viewport_read(&file->viewport_keyf, io)) {
630             return(LIB3DS_FALSE);
631           }
632         }
633         break;
634       case LIB3DS_AMBIENT_NODE_TAG:
635         {
636           Lib3dsNode *node;
637 
638           node=lib3ds_node_new_ambient();
639           if (!node) {
640             return(LIB3DS_FALSE);
641           }
642           node->node_id=node_number++;
643           lib3ds_chunk_read_reset(&c, io);
644           if (!lib3ds_node_read(node, file, io)) {
645             return(LIB3DS_FALSE);
646           }
647           lib3ds_file_insert_node(file, node);
648         }
649         break;
650       case LIB3DS_OBJECT_NODE_TAG:
651         {
652           Lib3dsNode *node;
653 
654           node=lib3ds_node_new_object();
655           if (!node) {
656             return(LIB3DS_FALSE);
657           }
658           node->node_id=node_number++;
659           lib3ds_chunk_read_reset(&c, io);
660           if (!lib3ds_node_read(node, file, io)) {
661             return(LIB3DS_FALSE);
662           }
663           lib3ds_file_insert_node(file, node);
664         }
665         break;
666       case LIB3DS_CAMERA_NODE_TAG:
667         {
668           Lib3dsNode *node;
669 
670           node=lib3ds_node_new_camera();
671           if (!node) {
672             return(LIB3DS_FALSE);
673           }
674           node->node_id=node_number++;
675           lib3ds_chunk_read_reset(&c, io);
676           if (!lib3ds_node_read(node, file, io)) {
677             return(LIB3DS_FALSE);
678           }
679           lib3ds_file_insert_node(file, node);
680         }
681         break;
682       case LIB3DS_TARGET_NODE_TAG:
683         {
684           Lib3dsNode *node;
685 
686           node=lib3ds_node_new_target();
687           if (!node) {
688             return(LIB3DS_FALSE);
689           }
690           node->node_id=node_number++;
691           lib3ds_chunk_read_reset(&c, io);
692           if (!lib3ds_node_read(node, file, io)) {
693             return(LIB3DS_FALSE);
694           }
695           lib3ds_file_insert_node(file, node);
696         }
697         break;
698       case LIB3DS_LIGHT_NODE_TAG:
699       case LIB3DS_SPOTLIGHT_NODE_TAG:
700         {
701           Lib3dsNode *node;
702 
703           node=lib3ds_node_new_light();
704           if (!node) {
705             return(LIB3DS_FALSE);
706           }
707           node->node_id=node_number++;
708           lib3ds_chunk_read_reset(&c, io);
709           if (!lib3ds_node_read(node, file, io)) {
710             return(LIB3DS_FALSE);
711           }
712           lib3ds_file_insert_node(file, node);
713         }
714         break;
715       case LIB3DS_L_TARGET_NODE_TAG:
716         {
717           Lib3dsNode *node;
718 
719           node=lib3ds_node_new_spot();
720           if (!node) {
721             return(LIB3DS_FALSE);
722           }
723           node->node_id=node_number++;
724           lib3ds_chunk_read_reset(&c, io);
725           if (!lib3ds_node_read(node, file, io)) {
726             return(LIB3DS_FALSE);
727           }
728           lib3ds_file_insert_node(file, node);
729         }
730         break;
731       default:
732         lib3ds_chunk_unknown(chunk);
733     }
734   }
735 
736   lib3ds_chunk_read_end(&c, io);
737   return(LIB3DS_TRUE);
738 }
739 
740 
741 /*!
742  * Read 3ds file data into a Lib3dsFile object.
743  *
744  * \param file The Lib3dsFile object to be filled.
745  * \param io A Lib3dsIo object previously set up by the caller.
746  *
747  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
748  *
749  * \ingroup file
750  */
751 Lib3dsBool
lib3ds_file_read(Lib3dsFile * file,Lib3dsIo * io)752 lib3ds_file_read(Lib3dsFile *file, Lib3dsIo *io)
753 {
754   Lib3dsChunk c;
755   Lib3dsWord chunk;
756 
757   if (!lib3ds_chunk_read_start(&c, 0, io)) {
758     return(LIB3DS_FALSE);
759   }
760   switch (c.chunk) {
761     case LIB3DS_MDATA:
762       {
763         lib3ds_chunk_read_reset(&c, io);
764         if (!mdata_read(file, io)) {
765           return(LIB3DS_FALSE);
766         }
767       }
768       break;
769     case LIB3DS_M3DMAGIC:
770     case LIB3DS_MLIBMAGIC:
771     case LIB3DS_CMAGIC:
772       {
773         while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
774           switch (chunk) {
775             case LIB3DS_M3D_VERSION:
776               {
777                 file->mesh_version=lib3ds_io_read_dword(io);
778               }
779               break;
780             case LIB3DS_MDATA:
781               {
782                 lib3ds_chunk_read_reset(&c, io);
783                 if (!mdata_read(file, io)) {
784                   return(LIB3DS_FALSE);
785                 }
786               }
787               break;
788             case LIB3DS_KFDATA:
789               {
790                 lib3ds_chunk_read_reset(&c, io);
791                 if (!kfdata_read(file, io)) {
792                   return(LIB3DS_FALSE);
793                 }
794               }
795               break;
796             default:
797               lib3ds_chunk_unknown(chunk);
798           }
799         }
800       }
801       break;
802     default:
803       lib3ds_chunk_unknown(c.chunk);
804       return(LIB3DS_FALSE);
805   }
806 
807   lib3ds_chunk_read_end(&c, io);
808   return(LIB3DS_TRUE);
809 }
810 
811 
812 static Lib3dsBool
colorf_write(Lib3dsRgba rgb,Lib3dsIo * io)813 colorf_write(Lib3dsRgba rgb, Lib3dsIo *io)
814 {
815   Lib3dsChunk c;
816 
817   c.chunk=LIB3DS_COLOR_F;
818   c.size=18;
819   lib3ds_chunk_write(&c,io);
820   lib3ds_io_write_rgb(io, rgb);
821 
822   c.chunk=LIB3DS_LIN_COLOR_F;
823   c.size=18;
824   lib3ds_chunk_write(&c,io);
825   lib3ds_io_write_rgb(io, rgb);
826   return(LIB3DS_TRUE);
827 }
828 
829 
830 static Lib3dsBool
object_flags_write(Lib3dsDword flags,Lib3dsIo * io)831 object_flags_write(Lib3dsDword flags, Lib3dsIo *io)
832 {
833   if (flags){
834     if (flags & LIB3DS_OBJECT_HIDDEN) {
835       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_HIDDEN, io))
836         return LIB3DS_FALSE;
837     }
838     if (flags & LIB3DS_OBJECT_VIS_LOFTER) {
839       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_VIS_LOFTER, io))
840         return LIB3DS_FALSE;
841     }
842     if (flags & LIB3DS_OBJECT_DOESNT_CAST) {
843       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_DOESNT_CAST, io))
844         return LIB3DS_FALSE;
845     }
846     if (flags & LIB3DS_OBJECT_MATTE) {
847       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_MATTE, io))
848         return LIB3DS_FALSE;
849     }
850     if (flags & LIB3DS_OBJECT_DONT_RCVSHADOW) {
851       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_DOESNT_CAST, io))
852         return LIB3DS_FALSE;
853     }
854     if (flags & LIB3DS_OBJECT_FAST) {
855       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_FAST, io))
856         return LIB3DS_FALSE;
857     }
858     if (flags & LIB3DS_OBJECT_FROZEN) {
859       if (!lib3ds_chunk_write_switch(LIB3DS_OBJ_FROZEN, io))
860         return LIB3DS_FALSE;
861     }
862   }
863   return LIB3DS_TRUE;
864 }
865 
866 
867 static Lib3dsBool
mdata_write(Lib3dsFile * file,Lib3dsIo * io)868 mdata_write(Lib3dsFile *file, Lib3dsIo *io)
869 {
870   Lib3dsChunk c;
871 
872   c.chunk=LIB3DS_MDATA;
873   if (!lib3ds_chunk_write_start(&c,io)) {
874     return(LIB3DS_FALSE);
875   }
876 
877   { /*---- LIB3DS_MESH_VERSION ----*/
878     Lib3dsChunk c;
879     c.chunk=LIB3DS_MESH_VERSION;
880     c.size=10;
881     lib3ds_chunk_write(&c,io);
882     lib3ds_io_write_intd(io, file->mesh_version);
883   }
884   { /*---- LIB3DS_MASTER_SCALE ----*/
885     Lib3dsChunk c;
886     c.chunk=LIB3DS_MASTER_SCALE;
887     c.size=10;
888     lib3ds_chunk_write(&c,io);
889     lib3ds_io_write_float(io, file->master_scale);
890   }
891   { /*---- LIB3DS_O_CONSTS ----*/
892     int i;
893     for (i=0; i<3; ++i) {
894       if (fabs(file->construction_plane[i])>LIB3DS_EPSILON) {
895         break;
896       }
897     }
898     if (i<3) {
899       Lib3dsChunk c;
900       c.chunk=LIB3DS_O_CONSTS;
901       c.size=18;
902       lib3ds_chunk_write(&c,io);
903       lib3ds_io_write_vector(io, file->construction_plane);
904     }
905   }
906 
907   { /*---- LIB3DS_AMBIENT_LIGHT ----*/
908     int i;
909     for (i=0; i<3; ++i) {
910       if (fabs(file->ambient[i])>LIB3DS_EPSILON) {
911         break;
912       }
913     }
914     if (i<3) {
915       Lib3dsChunk c;
916       c.chunk=LIB3DS_AMBIENT_LIGHT;
917       c.size=42;
918       lib3ds_chunk_write(&c,io);
919       colorf_write(file->ambient,io);
920     }
921   }
922   lib3ds_background_write(&file->background, io);
923   lib3ds_atmosphere_write(&file->atmosphere, io);
924   lib3ds_shadow_write(&file->shadow, io);
925   lib3ds_viewport_write(&file->viewport, io);
926   {
927     Lib3dsMaterial *p;
928     for (p=file->materials; p!=0; p=p->next) {
929       if (!lib3ds_material_write(p,io)) {
930         return(LIB3DS_FALSE);
931       }
932     }
933   }
934   {
935     Lib3dsCamera *p;
936     Lib3dsChunk c;
937 
938     for (p=file->cameras; p!=0; p=p->next) {
939       c.chunk=LIB3DS_NAMED_OBJECT;
940       if (!lib3ds_chunk_write_start(&c,io)) {
941         return(LIB3DS_FALSE);
942       }
943       lib3ds_io_write_string(io, p->name);
944       lib3ds_camera_write(p,io);
945       object_flags_write(p->object_flags,io);
946       if (!lib3ds_chunk_write_end(&c,io)) {
947         return(LIB3DS_FALSE);
948       }
949     }
950   }
951   {
952     Lib3dsLight *p;
953     Lib3dsChunk c;
954 
955     for (p=file->lights; p!=0; p=p->next) {
956       c.chunk=LIB3DS_NAMED_OBJECT;
957       if (!lib3ds_chunk_write_start(&c,io)) {
958         return(LIB3DS_FALSE);
959       }
960       lib3ds_io_write_string(io,p->name);
961       lib3ds_light_write(p,io);
962       object_flags_write(p->object_flags,io);
963       if (!lib3ds_chunk_write_end(&c,io)) {
964         return(LIB3DS_FALSE);
965       }
966     }
967   }
968   {
969     Lib3dsMesh *p;
970     Lib3dsChunk c;
971 
972     for (p=file->meshes; p!=0; p=p->next) {
973       c.chunk=LIB3DS_NAMED_OBJECT;
974       if (!lib3ds_chunk_write_start(&c,io)) {
975         return(LIB3DS_FALSE);
976       }
977       lib3ds_io_write_string(io, p->name);
978       lib3ds_mesh_write(p,io);
979       object_flags_write(p->object_flags,io);
980       if (!lib3ds_chunk_write_end(&c,io)) {
981         return(LIB3DS_FALSE);
982       }
983     }
984   }
985 
986   if (!lib3ds_chunk_write_end(&c,io)) {
987     return(LIB3DS_FALSE);
988   }
989   return(LIB3DS_TRUE);
990 }
991 
992 
993 
994 static Lib3dsBool
nodes_write(Lib3dsNode * node,Lib3dsFile * file,Lib3dsIo * io)995 nodes_write(Lib3dsNode *node, Lib3dsFile *file, Lib3dsIo *io)
996 {
997   {
998     Lib3dsNode *p;
999     for (p=node->childs; p!=0; p=p->next) {
1000       if (!lib3ds_node_write(p, file, io)) {
1001         return(LIB3DS_FALSE);
1002       }
1003       nodes_write(p, file, io);
1004     }
1005   }
1006   return(LIB3DS_TRUE);
1007 }
1008 
1009 
1010 static Lib3dsBool
kfdata_write(Lib3dsFile * file,Lib3dsIo * io)1011 kfdata_write(Lib3dsFile *file, Lib3dsIo *io)
1012 {
1013   Lib3dsChunk c;
1014 
1015   if (!file->nodes) {
1016     return(LIB3DS_TRUE);
1017   }
1018 
1019   c.chunk=LIB3DS_KFDATA;
1020   if (!lib3ds_chunk_write_start(&c,io)) {
1021     return(LIB3DS_FALSE);
1022   }
1023 
1024   { /*---- LIB3DS_KFHDR ----*/
1025     Lib3dsChunk c;
1026     c.chunk=LIB3DS_KFHDR;
1027     c.size=6 + 2 + (Lib3dsDword)strlen(file->name)+1 +4;
1028     lib3ds_chunk_write(&c,io);
1029     lib3ds_io_write_intw(io, file->keyf_revision);
1030     lib3ds_io_write_string(io, file->name);
1031     lib3ds_io_write_intd(io, file->frames);
1032   }
1033   { /*---- LIB3DS_KFSEG ----*/
1034     Lib3dsChunk c;
1035     c.chunk=LIB3DS_KFSEG;
1036     c.size=14;
1037     lib3ds_chunk_write(&c,io);
1038     lib3ds_io_write_intd(io, file->segment_from);
1039     lib3ds_io_write_intd(io, file->segment_to);
1040   }
1041   { /*---- LIB3DS_KFCURTIME ----*/
1042     Lib3dsChunk c;
1043     c.chunk=LIB3DS_KFCURTIME;
1044     c.size=10;
1045     lib3ds_chunk_write(&c,io);
1046     lib3ds_io_write_intd(io, file->current_frame);
1047   }
1048   lib3ds_viewport_write(&file->viewport_keyf, io);
1049 
1050   {
1051     Lib3dsNode *p;
1052     for (p=file->nodes; p!=0; p=p->next) {
1053       if (!lib3ds_node_write(p, file, io)) {
1054         return(LIB3DS_FALSE);
1055       }
1056       if (!nodes_write(p, file, io)) {
1057         return(LIB3DS_FALSE);
1058       }
1059     }
1060   }
1061 
1062   if (!lib3ds_chunk_write_end(&c,io)) {
1063     return(LIB3DS_FALSE);
1064   }
1065   return(LIB3DS_TRUE);
1066 }
1067 
1068 
1069 /*!
1070  * Write 3ds file data from a Lib3dsFile object to a file.
1071  *
1072  * \param file The Lib3dsFile object to be written.
1073  * \param io A Lib3dsIo object previously set up by the caller.
1074  *
1075  * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure.
1076  *
1077  * \ingroup file
1078  */
1079 Lib3dsBool
lib3ds_file_write(Lib3dsFile * file,Lib3dsIo * io)1080 lib3ds_file_write(Lib3dsFile *file, Lib3dsIo *io)
1081 {
1082   Lib3dsChunk c;
1083 
1084   c.chunk=LIB3DS_M3DMAGIC;
1085   if (!lib3ds_chunk_write_start(&c,io)) {
1086     LIB3DS_ERROR_LOG;
1087     return(LIB3DS_FALSE);
1088   }
1089 
1090   { /*---- LIB3DS_M3D_VERSION ----*/
1091     Lib3dsChunk c;
1092 
1093     c.chunk=LIB3DS_M3D_VERSION;
1094     c.size=10;
1095     lib3ds_chunk_write(&c,io);
1096     lib3ds_io_write_dword(io, file->mesh_version);
1097   }
1098 
1099   if (!mdata_write(file, io)) {
1100     return(LIB3DS_FALSE);
1101   }
1102   if (!kfdata_write(file, io)) {
1103     return(LIB3DS_FALSE);
1104   }
1105 
1106   if (!lib3ds_chunk_write_end(&c,io)) {
1107     return(LIB3DS_FALSE);
1108   }
1109   return(LIB3DS_TRUE);
1110 }
1111 
1112 
1113 /*!
1114  * Insert a new Lib3dsMaterial object into the materials list of
1115  * a Lib3dsFile object.
1116  *
1117  * The new Lib3dsMaterial object is inserted into the materials list
1118  * in alphabetic order by name.
1119  *
1120  * \param file The Lib3dsFile object to be modified.
1121  * \param material The Lib3dsMaterial object to be inserted into file->materials
1122  *
1123  * \ingroup file
1124  */
1125 void
lib3ds_file_insert_material(Lib3dsFile * file,Lib3dsMaterial * material)1126 lib3ds_file_insert_material(Lib3dsFile *file, Lib3dsMaterial *material)
1127 {
1128   Lib3dsMaterial *p,*q;
1129 
1130   ASSERT(file);
1131   ASSERT(material);
1132   ASSERT(!material->next);
1133 
1134   q=0;
1135   for (p=file->materials; p!=0; p=p->next) {
1136     if (strcmp(material->name, p->name)<0) {
1137       break;
1138     }
1139     q=p;
1140   }
1141   if (!q) {
1142     material->next=file->materials;
1143     file->materials=material;
1144   }
1145   else {
1146     material->next=q->next;
1147     q->next=material;
1148   }
1149 }
1150 
1151 
1152 /*!
1153  * Remove a Lib3dsMaterial object from the materials list of
1154  * a Lib3dsFile object.
1155  *
1156  * If the Lib3dsMaterial is not found in the materials list, nothing is
1157  * done (except that an error log message may be generated.)
1158  *
1159  * \param file The Lib3dsFile object to be modified.
1160  * \param material The Lib3dsMaterial object to be removed from file->materials
1161  *
1162  * \ingroup file
1163  */
1164 void
lib3ds_file_remove_material(Lib3dsFile * file,Lib3dsMaterial * material)1165 lib3ds_file_remove_material(Lib3dsFile *file, Lib3dsMaterial *material)
1166 {
1167   Lib3dsMaterial *p,*q;
1168 
1169   ASSERT(file);
1170   ASSERT(material);
1171   ASSERT(file->materials);
1172   for (p=0,q=file->materials; q; p=q,q=q->next) {
1173     if (q==material) {
1174       break;
1175     }
1176   }
1177   if (!q) {
1178     ASSERT(LIB3DS_FALSE);
1179     return;
1180   }
1181   if (!p) {
1182     file->materials=material->next;
1183   }
1184   else {
1185     p->next=q->next;
1186   }
1187   material->next=0;
1188 }
1189 
1190 
1191 /*!
1192  * Return a Lib3dsMaterial object by name.
1193  *
1194  * \param file Lib3dsFile object to be searched.
1195  * \param name Name of the Lib3dsMaterial object to be searched for.
1196  *
1197  * \return A pointer to the named Lib3dsMaterial, or NULL if not found.
1198  *
1199  * \ingroup file
1200  */
1201 Lib3dsMaterial*
lib3ds_file_material_by_name(Lib3dsFile * file,const char * name)1202 lib3ds_file_material_by_name(Lib3dsFile *file, const char *name)
1203 {
1204   Lib3dsMaterial *p;
1205 
1206   ASSERT(file);
1207   for (p=file->materials; p!=0; p=p->next) {
1208     if (strcmp(p->name,name)==0) {
1209       return(p);
1210     }
1211   }
1212   return(0);
1213 }
1214 
1215 
1216 /*!
1217  * Dump all Lib3dsMaterial objects found in a Lib3dsFile object.
1218  *
1219  * \param file Lib3dsFile object to be dumped.
1220  *
1221  * \see lib3ds_material_dump
1222  *
1223  * \ingroup file
1224  */
1225 void
lib3ds_file_dump_materials(Lib3dsFile * file)1226 lib3ds_file_dump_materials(Lib3dsFile *file)
1227 {
1228   Lib3dsMaterial *p;
1229 
1230   ASSERT(file);
1231   for (p=file->materials; p!=0; p=p->next) {
1232     lib3ds_material_dump(p);
1233   }
1234 }
1235 
1236 
1237 /*!
1238  * Insert a new Lib3dsMesh object into the meshes list of
1239  * a Lib3dsFile object.
1240  *
1241  * The new Lib3dsMesh object is inserted into the meshes list
1242  * in alphabetic order by name.
1243  *
1244  * \param file  The Lib3dsFile object to be modified.
1245  * \param mesh  The Lib3dsMesh object to be inserted into file->meshes
1246  *
1247  * \ingroup file
1248  */
1249 void
lib3ds_file_insert_mesh(Lib3dsFile * file,Lib3dsMesh * mesh)1250 lib3ds_file_insert_mesh(Lib3dsFile *file, Lib3dsMesh *mesh)
1251 {
1252   Lib3dsMesh *p,*q;
1253 
1254   ASSERT(file);
1255   ASSERT(mesh);
1256   ASSERT(!mesh->next);
1257 
1258   q=0;
1259   for (p=file->meshes; p!=0; p=p->next) {
1260     if (strcmp(mesh->name, p->name)<0) {
1261       break;
1262     }
1263     q=p;
1264   }
1265   if (!q) {
1266     mesh->next=file->meshes;
1267     file->meshes=mesh;
1268   }
1269   else {
1270     mesh->next=q->next;
1271     q->next=mesh;
1272   }
1273 }
1274 
1275 
1276 /*!
1277  * Remove a Lib3dsMesh object from the meshes list of
1278  * a Lib3dsFile object.
1279  *
1280  * If the Lib3dsMesh is not found in the meshes list, nothing is done
1281  * (except that an error log message may be generated.)
1282  *
1283  * \param file  The Lib3dsFile object to be modified.
1284  * \param mesh  The Lib3dsMesh object to be removed from file->meshes
1285  *
1286  * \ingroup file
1287  */
1288 void
lib3ds_file_remove_mesh(Lib3dsFile * file,Lib3dsMesh * mesh)1289 lib3ds_file_remove_mesh(Lib3dsFile *file, Lib3dsMesh *mesh)
1290 {
1291   Lib3dsMesh *p,*q;
1292 
1293   ASSERT(file);
1294   ASSERT(mesh);
1295   ASSERT(file->meshes);
1296   for (p=0,q=file->meshes; q; p=q,q=q->next) {
1297     if (q==mesh) {
1298       break;
1299     }
1300   }
1301   if (!q) {
1302     ASSERT(LIB3DS_FALSE);
1303     return;
1304   }
1305   if (!p) {
1306     file->meshes=mesh->next;
1307   }
1308   else {
1309     p->next=q->next;
1310   }
1311   mesh->next=0;
1312 }
1313 
1314 
1315 /*!
1316  * Return a Lib3dsMesh object from a Lib3dsFile by name.
1317  *
1318  * \param file Lib3dsFile object to be searched.
1319  * \param name Name of the Lib3dsMesh object to be searched for.
1320  *
1321  * \return A pointer to the named Lib3dsMesh, or NULL if not found.
1322  *
1323  * \ingroup file
1324  */
1325 Lib3dsMesh*
lib3ds_file_mesh_by_name(Lib3dsFile * file,const char * name)1326 lib3ds_file_mesh_by_name(Lib3dsFile *file, const char *name)
1327 {
1328   Lib3dsMesh *p;
1329 
1330   ASSERT(file);
1331   for (p=file->meshes; p!=0; p=p->next) {
1332     if (strcmp(p->name,name)==0) {
1333       return(p);
1334     }
1335   }
1336   return(0);
1337 }
1338 
1339 
1340 /*!
1341  * Dump all Lib3dsMesh objects found in a Lib3dsFile object.
1342  *
1343  * \param file Lib3dsFile object to be dumped.
1344  *
1345  * \see lib3ds_mesh_dump
1346  *
1347  * \ingroup file
1348  */
1349 void
lib3ds_file_dump_meshes(Lib3dsFile * file)1350 lib3ds_file_dump_meshes(Lib3dsFile *file)
1351 {
1352   Lib3dsMesh *p;
1353 
1354   ASSERT(file);
1355   for (p=file->meshes; p!=0; p=p->next) {
1356     lib3ds_mesh_dump(p);
1357   }
1358 }
1359 
1360 
1361 static void
dump_instances(Lib3dsNode * node,const char * parent)1362 dump_instances(Lib3dsNode *node, const char* parent)
1363 {
1364   Lib3dsNode *p;
1365   char name[255];
1366 
1367   ASSERT(node);
1368   ASSERT(parent);
1369   strcpy(name, parent);
1370   strcat(name, ".");
1371   strcat(name, node->name);
1372   if (node->type==LIB3DS_OBJECT_NODE) {
1373     printf("  %s : %s\n", name, node->data.object.instance);
1374   }
1375   for (p=node->childs; p!=0; p=p->next) {
1376     dump_instances(p, parent);
1377   }
1378 }
1379 
1380 
1381 /*!
1382  * Dump all Lib3dsNode object names found in a Lib3dsFile object.
1383  *
1384  * For each node of type OBJECT_NODE, its name and data.object.instance
1385  * fields are printed to stdout.  Consider using lib3ds_file_dump_nodes()
1386  * instead, as that function dumps more information.
1387  *
1388  * Nodes are dumped recursively.
1389  *
1390  * \param file Lib3dsFile object to be dumped.
1391  *
1392  * \see lib3ds_file_dump_nodes
1393  *
1394  * \ingroup file
1395  */
1396 void
lib3ds_file_dump_instances(Lib3dsFile * file)1397 lib3ds_file_dump_instances(Lib3dsFile *file)
1398 {
1399   Lib3dsNode *p;
1400 
1401   ASSERT(file);
1402   for (p=file->nodes; p!=0; p=p->next) {
1403     dump_instances(p,"");
1404   }
1405 }
1406 
1407 
1408 /*!
1409  * Insert a new Lib3dsCamera object into the cameras list of
1410  * a Lib3dsFile object.
1411  *
1412  * The new Lib3dsCamera object is inserted into the cameras list
1413  * in alphabetic order by name.
1414  *
1415  * \param file      The Lib3dsFile object to be modified.
1416  * \param camera    The Lib3dsCamera object to be inserted into file->cameras
1417  *
1418  * \ingroup file
1419  */
1420 void
lib3ds_file_insert_camera(Lib3dsFile * file,Lib3dsCamera * camera)1421 lib3ds_file_insert_camera(Lib3dsFile *file, Lib3dsCamera *camera)
1422 {
1423   Lib3dsCamera *p,*q;
1424 
1425   ASSERT(file);
1426   ASSERT(camera);
1427   ASSERT(!camera->next);
1428 
1429   q=0;
1430   for (p=file->cameras; p!=0; p=p->next) {
1431     if (strcmp(camera->name, p->name)<0) {
1432       break;
1433     }
1434     q=p;
1435   }
1436   if (!q) {
1437     camera->next=file->cameras;
1438     file->cameras=camera;
1439   }
1440   else {
1441     camera->next=q->next;
1442     q->next=camera;
1443   }
1444 }
1445 
1446 
1447 /*!
1448  * Remove a Lib3dsCamera object from the cameras list of
1449  * a Lib3dsFile object.
1450  *
1451  * If the Lib3dsCamera is not found in the cameras list, nothing is done
1452  * (except that an error log message may be generated.)
1453  *
1454  * \param file      The Lib3dsFile object to be modified.
1455  * \param camera    The Lib3dsCamera object to be removed from file->cameras
1456  *
1457  * \ingroup file
1458  */
1459 void
lib3ds_file_remove_camera(Lib3dsFile * file,Lib3dsCamera * camera)1460 lib3ds_file_remove_camera(Lib3dsFile *file, Lib3dsCamera *camera)
1461 {
1462   Lib3dsCamera *p,*q;
1463 
1464   ASSERT(file);
1465   ASSERT(camera);
1466   ASSERT(file->cameras);
1467   for (p=0,q=file->cameras; q; p=q,q=q->next) {
1468     if (q==camera) {
1469       break;
1470     }
1471   }
1472   if (!q) {
1473     ASSERT(LIB3DS_FALSE);
1474     return;
1475   }
1476   if (!p) {
1477     file->cameras=camera->next;
1478   }
1479   else {
1480     p->next=q->next;
1481   }
1482   camera->next=0;
1483 }
1484 
1485 
1486 /*!
1487  * Return a Lib3dsCamera object from a Lib3dsFile by name.
1488  *
1489  * \param file Lib3dsFile object to be searched.
1490  * \param name Name of the Lib3dsCamera object to be searched for.
1491  *
1492  * \return A pointer to the named Lib3dsCamera, or NULL if not found.
1493  *
1494  * \ingroup file
1495  */
1496 Lib3dsCamera*
lib3ds_file_camera_by_name(Lib3dsFile * file,const char * name)1497 lib3ds_file_camera_by_name(Lib3dsFile *file, const char *name)
1498 {
1499   Lib3dsCamera *p;
1500 
1501   ASSERT(file);
1502   for (p=file->cameras; p!=0; p=p->next) {
1503     if (strcmp(p->name,name)==0) {
1504       return(p);
1505     }
1506   }
1507   return(0);
1508 }
1509 
1510 
1511 /*!
1512  * Dump all Lib3dsCamera objects found in a Lib3dsFile object.
1513  *
1514  * \param file Lib3dsFile object to be dumped.
1515  *
1516  * \see lib3ds_camera_dump
1517  *
1518  * \ingroup file
1519  */
1520 void
lib3ds_file_dump_cameras(Lib3dsFile * file)1521 lib3ds_file_dump_cameras(Lib3dsFile *file)
1522 {
1523   Lib3dsCamera *p;
1524 
1525   ASSERT(file);
1526   for (p=file->cameras; p!=0; p=p->next) {
1527     lib3ds_camera_dump(p);
1528   }
1529 }
1530 
1531 
1532 /*!
1533  * Insert a new Lib3dsLight object into the lights list of
1534  * a Lib3dsFile object.
1535  *
1536  * The new Lib3dsLight object is inserted into the lights list
1537  * in alphabetic order by name.
1538  *
1539  * \param file  The Lib3dsFile object to be modified.
1540  * \param light The Lib3dsLight object to be inserted into file->lights
1541  *
1542  * \ingroup file
1543  */
1544 void
lib3ds_file_insert_light(Lib3dsFile * file,Lib3dsLight * light)1545 lib3ds_file_insert_light(Lib3dsFile *file, Lib3dsLight *light)
1546 {
1547   Lib3dsLight *p,*q;
1548 
1549   ASSERT(file);
1550   ASSERT(light);
1551   ASSERT(!light->next);
1552 
1553   q=0;
1554   for (p=file->lights; p!=0; p=p->next) {
1555     if (strcmp(light->name, p->name)<0) {
1556       break;
1557     }
1558     q=p;
1559   }
1560   if (!q) {
1561     light->next=file->lights;
1562     file->lights=light;
1563   }
1564   else {
1565     light->next=q->next;
1566     q->next=light;
1567   }
1568 }
1569 
1570 
1571 /*!
1572  * Remove a Lib3dsLight object from the lights list of
1573  * a Lib3dsFile object.
1574  *
1575  * If the Lib3dsLight is not found in the lights list, nothing is done
1576  * (except that an error log message may be generated.)
1577  *
1578  * \param file  The Lib3dsFile object to be modified.
1579  * \param light The Lib3dsLight object to be removed from file->lights
1580  *
1581  * \ingroup file
1582  */
1583 void
lib3ds_file_remove_light(Lib3dsFile * file,Lib3dsLight * light)1584 lib3ds_file_remove_light(Lib3dsFile *file, Lib3dsLight *light)
1585 {
1586   Lib3dsLight *p,*q;
1587 
1588   ASSERT(file);
1589   ASSERT(light);
1590   ASSERT(file->lights);
1591   for (p=0,q=file->lights; q; p=q,q=q->next) {
1592     if (q==light) {
1593       break;
1594     }
1595   }
1596   if (!q) {
1597     ASSERT(LIB3DS_FALSE);
1598     return;
1599   }
1600   if (!p) {
1601     file->lights=light->next;
1602   }
1603   else {
1604     p->next=q->next;
1605   }
1606   light->next=0;
1607 }
1608 
1609 
1610 /*!
1611  * Return a Lib3dsLight object from a Lib3dsFile by name.
1612  *
1613  * \param file Lib3dsFile object to be searched.
1614  * \param name Name of the Lib3dsLight object to be searched for.
1615  *
1616  * \return A pointer to the named Lib3dsLight, or NULL if not found.
1617  *
1618  * \ingroup file
1619  */
1620 Lib3dsLight*
lib3ds_file_light_by_name(Lib3dsFile * file,const char * name)1621 lib3ds_file_light_by_name(Lib3dsFile *file, const char *name)
1622 {
1623   Lib3dsLight *p;
1624 
1625   ASSERT(file);
1626   for (p=file->lights; p!=0; p=p->next) {
1627     if (strcmp(p->name,name)==0) {
1628       return(p);
1629     }
1630   }
1631   return(0);
1632 }
1633 
1634 
1635 /*!
1636  * Dump all Lib3dsLight objects found in a Lib3dsFile object.
1637  *
1638  * \param file Lib3dsFile object to be dumped.
1639  *
1640  * \see lib3ds_light_dump
1641  *
1642  * \ingroup file
1643  */
1644 void
lib3ds_file_dump_lights(Lib3dsFile * file)1645 lib3ds_file_dump_lights(Lib3dsFile *file)
1646 {
1647   Lib3dsLight *p;
1648 
1649   ASSERT(file);
1650   for (p=file->lights; p!=0; p=p->next) {
1651     lib3ds_light_dump(p);
1652   }
1653 }
1654 
1655 
1656 /*!
1657  * Return a node object by name and type.
1658  *
1659  * This function performs a recursive search for the specified node.
1660  * Both name and type must match.
1661  *
1662  * \param file The Lib3dsFile to be searched.
1663  * \param name The target node name.
1664  * \param type The target node type
1665  *
1666  * \return A pointer to the first matching node, or NULL if not found.
1667  *
1668  * \see lib3ds_node_by_name
1669  *
1670  * \ingroup file
1671  */
1672 Lib3dsNode*
lib3ds_file_node_by_name(Lib3dsFile * file,const char * name,Lib3dsNodeTypes type)1673 lib3ds_file_node_by_name(Lib3dsFile *file, const char* name, Lib3dsNodeTypes type)
1674 {
1675   Lib3dsNode *p,*q;
1676 
1677   ASSERT(file);
1678   for (p=file->nodes; p!=0; p=p->next) {
1679     if ((p->type==type) && (strcmp(p->name, name)==0)) {
1680       return(p);
1681     }
1682     q=lib3ds_node_by_name(p, name, type);
1683     if (q) {
1684       return(q);
1685     }
1686   }
1687   return(0);
1688 }
1689 
1690 
1691 /*!
1692  * Return a node object by id.
1693  *
1694  * This function performs a recursive search for the specified node.
1695  *
1696  * \param file The Lib3dsFile to be searched.
1697  * \param node_id The target node id.
1698  *
1699  * \return A pointer to the first matching node, or NULL if not found.
1700  *
1701  * \see lib3ds_node_by_id
1702  *
1703  * \ingroup file
1704  */
1705 Lib3dsNode*
lib3ds_file_node_by_id(Lib3dsFile * file,Lib3dsWord node_id)1706 lib3ds_file_node_by_id(Lib3dsFile *file, Lib3dsWord node_id)
1707 {
1708   Lib3dsNode *p,*q;
1709 
1710   ASSERT(file);
1711   for (p=file->nodes; p!=0; p=p->next) {
1712     if (p->node_id==node_id) {
1713       return(p);
1714     }
1715     q=lib3ds_node_by_id(p, node_id);
1716     if (q) {
1717       return(q);
1718     }
1719   }
1720   return(0);
1721 }
1722 
1723 
1724 /*!
1725  * Insert a new node into a Lib3dsFile object.
1726  *
1727  * If the node's parent_id structure is not LIB3DS_NO_PARENT and the
1728  * specified parent is found inside the Lib3dsFile object, then the
1729  * node is inserted as a child of that parent.  If the parent_id
1730  * structure is LIB3DS_NO_PARENT or the specified parent is not found,
1731  * then the node is inserted at the top level.
1732  *
1733  * Node is inserted in alphabetic order by name.
1734  *
1735  * Finally, if any other top-level nodes in file specify this node as
1736  * their parent, they are relocated as a child of this node.
1737  *
1738  * \param file The Lib3dsFile object to be modified.
1739  * \param node The node to be inserted into file
1740  *
1741  * \ingroup file
1742  */
1743 void
lib3ds_file_insert_node(Lib3dsFile * file,Lib3dsNode * node)1744 lib3ds_file_insert_node(Lib3dsFile *file, Lib3dsNode *node)
1745 {
1746   Lib3dsNode *parent,*p,*n;
1747 
1748   ASSERT(node);
1749   ASSERT(!node->next);
1750   ASSERT(!node->parent);
1751 
1752   parent=0;
1753   if (node->parent_id!=LIB3DS_NO_PARENT) {
1754     parent=lib3ds_file_node_by_id(file, node->parent_id);
1755   }
1756   node->parent=parent;
1757 
1758   if (!parent) {
1759     for (p=0,n=file->nodes; n!=0; p=n,n=n->next) {
1760       if (strcmp(n->name, node->name)>0) {
1761         break;
1762       }
1763     }
1764     if (!p) {
1765       node->next=file->nodes;
1766       file->nodes=node;
1767     }
1768     else {
1769       node->next=p->next;
1770       p->next=node;
1771     }
1772   }
1773   else {
1774     for (p=0,n=parent->childs; n!=0; p=n,n=n->next) {
1775       if (strcmp(n->name, node->name)>0) {
1776         break;
1777       }
1778     }
1779     if (!p) {
1780       node->next=parent->childs;
1781       parent->childs=node;
1782     }
1783     else {
1784       node->next=p->next;
1785       p->next=node;
1786     }
1787   }
1788 
1789   if (node->node_id!=LIB3DS_NO_PARENT) {
1790     for (n=file->nodes; n!=0; n=p) {
1791       p=n->next;
1792       if (n->parent_id==node->node_id) {
1793         lib3ds_file_remove_node(file, n);
1794         lib3ds_file_insert_node(file, n);
1795       }
1796     }
1797   }
1798 }
1799 
1800 
1801 /*!
1802  * Remove a node from the a Lib3dsFile object.
1803  *
1804  * \param file The Lib3dsFile object to be modified.
1805  * \param node The Lib3dsNode object to be removed from file
1806  *
1807  * \return LIB3DS_TRUE on success, LIB3DS_FALSE if node is not found in file
1808  *
1809  * \ingroup file
1810  */
1811 Lib3dsBool
lib3ds_file_remove_node(Lib3dsFile * file,Lib3dsNode * node)1812 lib3ds_file_remove_node(Lib3dsFile *file, Lib3dsNode *node)
1813 {
1814   Lib3dsNode *p,*n;
1815 
1816   if (node->parent) {
1817     for (p=0,n=node->parent->childs; n; p=n,n=n->next) {
1818       if (n==node) {
1819         break;
1820       }
1821     }
1822     if (!n) {
1823       return(LIB3DS_FALSE);
1824     }
1825 
1826     if (!p) {
1827       node->parent->childs=n->next;
1828     }
1829     else {
1830       p->next=n->next;
1831     }
1832   }
1833   else {
1834     for (p=0,n=file->nodes; n; p=n,n=n->next) {
1835       if (n==node) {
1836         break;
1837       }
1838     }
1839     if (!n) {
1840       return(LIB3DS_FALSE);
1841     }
1842 
1843     if (!p) {
1844       file->nodes=n->next;
1845     }
1846     else {
1847       p->next=n->next;
1848     }
1849   }
1850   return(LIB3DS_TRUE);
1851 }
1852 
1853 
1854 /*!
1855  * This function computes the bounding box of meshes, cameras and lights
1856  * defined in the 3D editor.
1857  *
1858  * \param file              The Lib3dsFile object to be examined.
1859  * \param include_meshes    Include meshes in bounding box calculation.
1860  * \param include_cameras   Include cameras in bounding box calculation.
1861  * \param include_lights    Include lights in bounding box calculation.
1862  * \param bmin              Returned minimum x,y,z values.
1863  * \param bmax              Returned maximum x,y,z values.
1864  *
1865  * \ingroup file
1866  */
1867 void
lib3ds_file_bounding_box_of_objects(Lib3dsFile * file,Lib3dsBool include_meshes,Lib3dsBool include_cameras,Lib3dsBool include_lights,Lib3dsVector bmin,Lib3dsVector bmax)1868 lib3ds_file_bounding_box_of_objects(Lib3dsFile *file, Lib3dsBool include_meshes,
1869                                     Lib3dsBool include_cameras, Lib3dsBool include_lights,
1870                                     Lib3dsVector bmin, Lib3dsVector bmax)
1871 {
1872   bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
1873   bmax[0] = bmax[1] = bmax[2] = FLT_MIN;
1874 
1875   if (include_meshes) {
1876     Lib3dsVector lmin, lmax;
1877     Lib3dsMesh *p=file->meshes;
1878     while (p) {
1879       lib3ds_mesh_bounding_box(p, lmin, lmax);
1880       lib3ds_vector_min(bmin, lmin);
1881       lib3ds_vector_max(bmax, lmax);
1882       p=p->next;
1883     }
1884   }
1885   if (include_cameras) {
1886     Lib3dsCamera *p=file->cameras;
1887     while (p) {
1888       lib3ds_vector_min(bmin, p->position);
1889       lib3ds_vector_max(bmax, p->position);
1890       lib3ds_vector_min(bmin, p->target);
1891       lib3ds_vector_max(bmax, p->target);
1892       p=p->next;
1893     }
1894   }
1895   if (include_lights) {
1896     Lib3dsLight *p=file->lights;
1897     while (p) {
1898       lib3ds_vector_min(bmin, p->position);
1899       lib3ds_vector_max(bmax, p->position);
1900       if (p->spot_light) {
1901         lib3ds_vector_min(bmin, p->spot);
1902         lib3ds_vector_max(bmax, p->spot);
1903       }
1904       p=p->next;
1905     }
1906   }
1907 }
1908 
1909 
1910 static void
file_bounding_box_of_nodes_impl(Lib3dsNode * node,Lib3dsFile * file,Lib3dsBool include_meshes,Lib3dsBool include_cameras,Lib3dsBool include_lights,Lib3dsVector bmin,Lib3dsVector bmax)1911 file_bounding_box_of_nodes_impl(Lib3dsNode *node, Lib3dsFile *file, Lib3dsBool include_meshes,
1912                                 Lib3dsBool include_cameras, Lib3dsBool include_lights,
1913                                 Lib3dsVector bmin, Lib3dsVector bmax)
1914 {
1915   switch (node->type)
1916   {
1917     case LIB3DS_OBJECT_NODE:
1918       if (include_meshes) {
1919         Lib3dsMesh *mesh;
1920 
1921         mesh = lib3ds_file_mesh_by_name(file, node->data.object.instance);
1922         if (!mesh)
1923           mesh = lib3ds_file_mesh_by_name(file, node->name);
1924         if (mesh) {
1925           Lib3dsMatrix inv_matrix, M;
1926           Lib3dsVector v;
1927           unsigned i;
1928 
1929           lib3ds_matrix_copy(inv_matrix, mesh->matrix);
1930           lib3ds_matrix_inv(inv_matrix);
1931           lib3ds_matrix_copy(M, node->matrix);
1932           lib3ds_matrix_translate_xyz(M, -node->data.object.pivot[0], -node->data.object.pivot[1], -node->data.object.pivot[2]);
1933           lib3ds_matrix_mult(M, inv_matrix);
1934 
1935           for (i=0; i<mesh->points; ++i) {
1936             lib3ds_vector_transform(v, M, mesh->pointL[i].pos);
1937             lib3ds_vector_min(bmin, v);
1938             lib3ds_vector_max(bmax, v);
1939           }
1940         }
1941       }
1942       break;
1943    /*
1944     case LIB3DS_CAMERA_NODE:
1945     case LIB3DS_TARGET_NODE:
1946       if (include_cameras) {
1947         Lib3dsVector z,v;
1948         lib3ds_vector_zero(z);
1949         lib3ds_vector_transform(v, node->matrix, z);
1950         lib3ds_vector_min(bmin, v);
1951         lib3ds_vector_max(bmax, v);
1952       }
1953       break;
1954 
1955     case LIB3DS_LIGHT_NODE:
1956     case LIB3DS_SPOT_NODE:
1957       if (include_lights) {
1958         Lib3dsVector z,v;
1959         lib3ds_vector_zero(z);
1960         lib3ds_vector_transform(v, node->matrix, z);
1961         lib3ds_vector_min(bmin, v);
1962         lib3ds_vector_max(bmax, v);
1963       }
1964       break;
1965     */
1966   }
1967   {
1968     Lib3dsNode *p=node->childs;
1969     while (p) {
1970       file_bounding_box_of_nodes_impl(p, file, include_meshes, include_cameras, include_lights, bmin, bmax);
1971       p=p->next;
1972     }
1973   }
1974 }
1975 
1976 
1977 /*!
1978  * This function computes the bounding box of mesh, camera and light instances
1979  * defined in the Keyframer.
1980  *
1981  * \param file              The Lib3dsFile object to be examined.
1982  * \param include_meshes    Include meshes in bounding box calculation.
1983  * \param include_cameras   Include cameras in bounding box calculation.
1984  * \param include_lights    Include lights in bounding box calculation.
1985  * \param bmin              Returned minimum x,y,z values.
1986  * \param bmax              Returned maximum x,y,z values.
1987  *
1988  * \ingroup file
1989  */
1990 void
lib3ds_file_bounding_box_of_nodes(Lib3dsFile * file,Lib3dsBool include_meshes,Lib3dsBool include_cameras,Lib3dsBool include_lights,Lib3dsVector bmin,Lib3dsVector bmax)1991 lib3ds_file_bounding_box_of_nodes(Lib3dsFile *file, Lib3dsBool include_meshes,
1992                                   Lib3dsBool include_cameras, Lib3dsBool include_lights,
1993                                   Lib3dsVector bmin, Lib3dsVector bmax)
1994 {
1995   Lib3dsNode *p;
1996 
1997   bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
1998   bmax[0] = bmax[1] = bmax[2] = FLT_MIN;
1999   p=file->nodes;
2000   while (p) {
2001     file_bounding_box_of_nodes_impl(p, file, include_meshes, include_cameras, include_lights, bmin, bmax);
2002     p=p->next;
2003   }
2004 }
2005 
2006 
2007 /*!
2008  * Dump all node objects found in a Lib3dsFile object.
2009  *
2010  * Nodes are dumped recursively.
2011  *
2012  * \param file Lib3dsFile object to be dumped.
2013  *
2014  * \see lib3ds_node_dump
2015  *
2016  * \ingroup file
2017  */
2018 void
lib3ds_file_dump_nodes(Lib3dsFile * file)2019 lib3ds_file_dump_nodes(Lib3dsFile *file)
2020 {
2021   Lib3dsNode *p;
2022 
2023   ASSERT(file);
2024   for (p=file->nodes; p!=0; p=p->next) {
2025     lib3ds_node_dump(p,1);
2026   }
2027 }
2028 
2029