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