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