added 3dengfx into the repo, probably not the correct version for this
[summerhack] / src / 3dengfx / libs / lib3ds / node.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: node.c,v 1.13 2004/11/16 07:41:44 efalk Exp $
21  */
22 #define LIB3DS_EXPORT
23 #include <lib3ds/node.h>
24 #include <lib3ds/file.h>
25 #include <lib3ds/io.h>
26 #include <lib3ds/chunk.h>
27 #include <lib3ds/matrix.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <math.h>
31 #ifdef WITH_DMALLOC
32 #include <dmalloc.h>
33 #endif
34
35
36 /*!
37  * \defgroup node Animation Nodes
38  *
39  * \author J.E. Hoffmann <je-h@gmx.net>
40  */
41
42
43 /*!
44  * Create and return a new ambient node.
45  *
46  * The node is returned with an identity matrix.  All other fields
47  * are zero.
48  *
49  * \return Lib3dsNode
50  *
51  * \ingroup node
52  */
53 Lib3dsNode*
54 lib3ds_node_new_ambient()
55 {
56   Lib3dsNode *node=(Lib3dsNode*)calloc(sizeof(Lib3dsNode), 1);
57   node->type=LIB3DS_AMBIENT_NODE;
58   lib3ds_matrix_identity(node->matrix);
59   return(node);
60 }
61
62
63 /*!
64  * Create and return a new object node.
65  *
66  * The node is returned with an identity matrix.  All other fields
67  * are zero.
68  *
69  * \return Lib3dsNode
70  *
71  * \ingroup node
72  */
73 Lib3dsNode*
74 lib3ds_node_new_object()
75 {
76   Lib3dsNode *node=(Lib3dsNode*)calloc(sizeof(Lib3dsNode), 1);
77   node->type=LIB3DS_OBJECT_NODE;
78   lib3ds_matrix_identity(node->matrix);
79   return(node);
80 }
81
82
83 /*!
84  * Create and return a new camera node.
85  *
86  * The node is returned with an identity matrix.  All other fields
87  * are zero.
88  *
89  * \return Lib3dsNode
90  *
91  * \ingroup node
92  */
93 Lib3dsNode*
94 lib3ds_node_new_camera()
95 {
96   Lib3dsNode *node=(Lib3dsNode*)calloc(sizeof(Lib3dsNode), 1);
97   node->type=LIB3DS_CAMERA_NODE;
98   lib3ds_matrix_identity(node->matrix);
99   return(node);
100 }
101
102
103 /*!
104  * Create and return a new target node.
105  *
106  * The node is returned with an identity matrix.  All other fields
107  * are zero.
108  *
109  * \return Lib3dsNode
110  *
111  * \ingroup node
112  */
113 Lib3dsNode*
114 lib3ds_node_new_target()
115 {
116   Lib3dsNode *node=(Lib3dsNode*)calloc(sizeof(Lib3dsNode), 1);
117   node->type=LIB3DS_TARGET_NODE;
118   lib3ds_matrix_identity(node->matrix);
119   return(node);
120 }
121
122
123 /*!
124  * Create and return a new light node.
125  *
126  * The node is returned with an identity matrix.  All other fields
127  * are zero.
128  *
129  * \return Lib3dsNode
130  *
131  * \ingroup node
132  */
133 Lib3dsNode*
134 lib3ds_node_new_light()
135 {
136   Lib3dsNode *node=(Lib3dsNode*)calloc(sizeof(Lib3dsNode), 1);
137   node->type=LIB3DS_LIGHT_NODE;
138   lib3ds_matrix_identity(node->matrix);
139   return(node);
140 }
141
142
143 /*!
144  * Create and return a new spot node.
145  *
146  * The node is returned with an identity matrix.  All other fields
147  * are zero.
148  *
149  * \return Lib3dsNode
150  *
151  * \ingroup node
152  */
153 Lib3dsNode*
154 lib3ds_node_new_spot()
155 {
156   Lib3dsNode *node=(Lib3dsNode*)calloc(sizeof(Lib3dsNode), 1);
157   node->type=LIB3DS_SPOT_NODE;
158   lib3ds_matrix_identity(node->matrix);
159   return(node);
160 }
161
162
163 static void
164 free_node_and_childs(Lib3dsNode *node)
165 {
166   ASSERT(node);
167   switch (node->type) {
168     case LIB3DS_UNKNOWN_NODE:
169       break;
170     case LIB3DS_AMBIENT_NODE:
171       {
172         Lib3dsAmbientData *n=&node->data.ambient;
173         lib3ds_lin3_track_free_keys(&n->col_track);
174       }
175       break;
176     case LIB3DS_OBJECT_NODE:
177       {
178         Lib3dsObjectData *n=&node->data.object;
179
180         lib3ds_lin3_track_free_keys(&n->pos_track);
181         lib3ds_quat_track_free_keys(&n->rot_track);
182         lib3ds_lin3_track_free_keys(&n->scl_track);
183         lib3ds_bool_track_free_keys(&n->hide_track);
184         lib3ds_morph_track_free_keys(&n->morph_track);
185       }
186       break;
187     case LIB3DS_CAMERA_NODE:
188       {
189         Lib3dsCameraData *n=&node->data.camera;
190         lib3ds_lin3_track_free_keys(&n->pos_track);
191         lib3ds_lin1_track_free_keys(&n->fov_track);
192         lib3ds_lin1_track_free_keys(&n->roll_track);
193       }
194       break;
195     case LIB3DS_TARGET_NODE:
196       {
197         Lib3dsTargetData *n=&node->data.target;
198         lib3ds_lin3_track_free_keys(&n->pos_track);
199       }
200       break;
201     case LIB3DS_LIGHT_NODE:
202       {
203         Lib3dsLightData *n=&node->data.light;
204         lib3ds_lin3_track_free_keys(&n->pos_track);
205         lib3ds_lin3_track_free_keys(&n->col_track);
206         lib3ds_lin1_track_free_keys(&n->hotspot_track);
207         lib3ds_lin1_track_free_keys(&n->falloff_track);
208         lib3ds_lin1_track_free_keys(&n->roll_track);
209       }
210       break;
211     case LIB3DS_SPOT_NODE:
212       {
213         Lib3dsSpotData *n=&node->data.spot;
214         lib3ds_lin3_track_free_keys(&n->pos_track);
215       }
216       break;
217   }
218   {
219     Lib3dsNode *p,*q;
220     for (p=node->childs; p; p=q) {
221       q=p->next;
222       free_node_and_childs(p);
223     }
224   }
225   node->type=LIB3DS_UNKNOWN_NODE;
226   free(node);
227 }
228
229
230 /*!
231  * Free a node and all of its resources.
232  *
233  * \param node Lib3dsNode object to be freed.
234  *
235  * \ingroup node
236  */
237 void
238 lib3ds_node_free(Lib3dsNode *node)
239 {
240   ASSERT(node);
241   free_node_and_childs(node);
242 }
243
244
245 /*!
246  * Evaluate an animation node.
247  *
248  * Recursively sets node and its children to their appropriate values
249  * for this point in the animation.
250  *
251  * \param node Node to be evaluated.
252  * \param t time value, between 0. and file->frames
253  *
254  * \ingroup node
255  */
256 void
257 lib3ds_node_eval(Lib3dsNode *node, Lib3dsFloat t)
258 {
259   ASSERT(node);
260   switch (node->type) {
261     case LIB3DS_UNKNOWN_NODE:
262       {
263         ASSERT(LIB3DS_FALSE);
264       }
265       break;
266     case LIB3DS_AMBIENT_NODE:
267       {
268         Lib3dsAmbientData *n=&node->data.ambient;
269         if (node->parent) {
270           lib3ds_matrix_copy(node->matrix, node->parent->matrix);
271         }
272         else {
273           lib3ds_matrix_identity(node->matrix);
274         }
275         lib3ds_lin3_track_eval(&n->col_track, n->col, t);
276       }
277       break;
278     case LIB3DS_OBJECT_NODE:
279       {
280         Lib3dsMatrix M;
281         Lib3dsObjectData *n=&node->data.object;
282
283         lib3ds_lin3_track_eval(&n->pos_track, n->pos, t);
284         lib3ds_quat_track_eval(&n->rot_track, n->rot, t);
285         lib3ds_lin3_track_eval(&n->scl_track, n->scl, t);
286         lib3ds_bool_track_eval(&n->hide_track, &n->hide, t);
287         lib3ds_morph_track_eval(&n->morph_track, n->morph, t);
288
289         lib3ds_matrix_identity(M);
290         lib3ds_matrix_translate(M, n->pos);
291         lib3ds_matrix_rotate(M, n->rot);
292         lib3ds_matrix_scale(M, n->scl);
293         
294         if (node->parent) {
295           lib3ds_matrix_mul(node->matrix, node->parent->matrix, M);
296         }
297         else {
298           lib3ds_matrix_copy(node->matrix, M);
299         }
300       }
301       break;
302     case LIB3DS_CAMERA_NODE:
303       {
304         Lib3dsCameraData *n=&node->data.camera;
305         lib3ds_lin3_track_eval(&n->pos_track, n->pos, t);
306         lib3ds_lin1_track_eval(&n->fov_track, &n->fov, t);
307         lib3ds_lin1_track_eval(&n->roll_track, &n->roll, t);
308         if (node->parent) {
309           lib3ds_matrix_copy(node->matrix, node->parent->matrix);
310         }
311         else {
312           lib3ds_matrix_identity(node->matrix);
313         }
314         lib3ds_matrix_translate(node->matrix, n->pos);
315       }
316       break;
317     case LIB3DS_TARGET_NODE:
318       {
319         Lib3dsTargetData *n=&node->data.target;
320         lib3ds_lin3_track_eval(&n->pos_track, n->pos, t);
321         if (node->parent) {
322           lib3ds_matrix_copy(node->matrix, node->parent->matrix);
323         }
324         else {
325           lib3ds_matrix_identity(node->matrix);
326         }
327         lib3ds_matrix_translate(node->matrix, n->pos);
328       }
329       break;
330     case LIB3DS_LIGHT_NODE:
331       {
332         Lib3dsLightData *n=&node->data.light;
333         lib3ds_lin3_track_eval(&n->pos_track, n->pos, t);
334         lib3ds_lin3_track_eval(&n->col_track, n->col, t);
335         lib3ds_lin1_track_eval(&n->hotspot_track, &n->hotspot, t);
336         lib3ds_lin1_track_eval(&n->falloff_track, &n->falloff, t);
337         lib3ds_lin1_track_eval(&n->roll_track, &n->roll, t);
338         if (node->parent) {
339           lib3ds_matrix_copy(node->matrix, node->parent->matrix);
340         }
341         else {
342           lib3ds_matrix_identity(node->matrix);
343         }
344         lib3ds_matrix_translate(node->matrix, n->pos);
345       }
346       break;
347     case LIB3DS_SPOT_NODE:
348       {
349         Lib3dsSpotData *n=&node->data.spot;
350         lib3ds_lin3_track_eval(&n->pos_track, n->pos, t);
351         if (node->parent) {
352           lib3ds_matrix_copy(node->matrix, node->parent->matrix);
353         }
354         else {
355           lib3ds_matrix_identity(node->matrix);
356         }
357         lib3ds_matrix_translate(node->matrix, n->pos);
358       }
359       break;
360   }
361   {
362     Lib3dsNode *p;
363
364     for (p=node->childs; p!=0; p=p->next) {
365       lib3ds_node_eval(p, t);
366     }
367   }
368 }
369
370
371 /*!
372  * Return a node object by name and type.
373  *
374  * This function performs a recursive search for the specified node.
375  * Both name and type must match.
376  *
377  * \param node The parent node for the search
378  * \param name The target node name.
379  * \param type The target node type
380  *
381  * \return A pointer to the first matching node, or NULL if not found.
382  *
383  * \ingroup node
384  */
385 Lib3dsNode*
386 lib3ds_node_by_name(Lib3dsNode *node, const char* name, Lib3dsNodeTypes type)
387 {
388   Lib3dsNode *p,*q;
389
390   for (p=node->childs; p!=0; p=p->next) {
391     if ((p->type==type) && (strcmp(p->name, name)==0)) {
392       return(p);
393     }
394     q=lib3ds_node_by_name(p, name, type);
395     if (q) {
396       return(q);
397     }
398   }
399   return(0);
400 }
401
402
403 /*!
404  * Return a node object by id.
405  *
406  * This function performs a recursive search for the specified node.
407  *
408  * \param node The parent node for the search
409  * \param node_id The target node id.
410  *
411  * \return A pointer to the first matching node, or NULL if not found.
412  *
413  * \ingroup node
414  */
415 Lib3dsNode*
416 lib3ds_node_by_id(Lib3dsNode *node, Lib3dsWord node_id)
417 {
418   Lib3dsNode *p,*q;
419
420   for (p=node->childs; p!=0; p=p->next) {
421     if (p->node_id==node_id) {
422       return(p);
423     }
424     q=lib3ds_node_by_id(p, node_id);
425     if (q) {
426       return(q);
427     }
428   }
429   return(0);
430 }
431
432
433 static const char* node_names_table[]= {
434   "***Unknown***",
435   "Ambient",
436   "Object",
437   "Camera",
438   "Target",
439   "Light",
440   "Spot"
441 };
442
443
444 /*!
445  * Dump node and all descendants recursively.
446  *
447  * \param node The top-level node to be dumped.
448  * \param level current recursion depth
449  *
450  * \ingroup node
451  */
452 void
453 lib3ds_node_dump(Lib3dsNode *node, Lib3dsIntd level)
454 {
455   Lib3dsNode *p;
456   char l[128];
457
458   ASSERT(node);
459   memset(l, ' ', 2*level);
460   l[2*level]=0;
461
462   if (node->type==LIB3DS_OBJECT_NODE) {
463     printf("%s%s [%s] (%s)\n",
464       l,
465       node->name,
466       node->data.object.instance,
467       node_names_table[node->type]
468     );
469   }
470   else {
471     printf("%s%s (%s)\n",
472       l,
473       node->name,
474       node_names_table[node->type]
475     );
476   }
477   
478   for (p=node->childs; p!=0; p=p->next) {
479     lib3ds_node_dump(p, level+1);
480   }
481 }
482
483
484 /*!
485  * \ingroup node
486  */
487 Lib3dsBool
488 lib3ds_node_read(Lib3dsNode *node, Lib3dsFile *file, Lib3dsIo *io)
489 {
490   Lib3dsChunk c;
491   Lib3dsWord chunk;
492
493   ASSERT(node);
494   if (!lib3ds_chunk_read_start(&c, 0, io)) {
495     return(LIB3DS_FALSE);
496   }
497   switch (c.chunk) {
498     case LIB3DS_AMBIENT_NODE_TAG:
499     case LIB3DS_OBJECT_NODE_TAG:
500     case LIB3DS_CAMERA_NODE_TAG:
501     case LIB3DS_TARGET_NODE_TAG:
502     case LIB3DS_LIGHT_NODE_TAG:
503     case LIB3DS_SPOTLIGHT_NODE_TAG:
504     case LIB3DS_L_TARGET_NODE_TAG:
505       break;
506     default:
507       return(LIB3DS_FALSE);
508   }
509
510   while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) {
511     switch (chunk) {
512       case LIB3DS_NODE_ID:
513         {
514           node->node_id=lib3ds_io_read_word(io);
515           lib3ds_chunk_dump_info("  ID = %d", (short)node->node_id);
516         }
517         break;
518       case LIB3DS_NODE_HDR:
519         {
520           if (!lib3ds_io_read_string(io, node->name, 64)) {
521             return(LIB3DS_FALSE);
522           }
523           node->flags1=lib3ds_io_read_word(io);
524           node->flags2=lib3ds_io_read_word(io);
525           node->parent_id=lib3ds_io_read_word(io);
526           lib3ds_chunk_dump_info("  NAME =%s", node->name);
527           lib3ds_chunk_dump_info("  PARENT=%d", (short)node->parent_id);
528         }
529         break;
530       case LIB3DS_PIVOT:
531         {
532           if (node->type==LIB3DS_OBJECT_NODE) {
533             int i;
534             for (i=0; i<3; ++i) {
535               node->data.object.pivot[i]=lib3ds_io_read_float(io);
536             }
537           }
538           else {
539             lib3ds_chunk_unknown(chunk);
540           }
541         }
542         break;
543       case LIB3DS_INSTANCE_NAME:
544         {
545           if (node->type==LIB3DS_OBJECT_NODE) {
546             if (!lib3ds_io_read_string(io, node->data.object.instance, 64)) {
547               return(LIB3DS_FALSE);
548             }
549           }
550           else {
551             lib3ds_chunk_unknown(chunk);
552           }
553         }
554         break;
555       case LIB3DS_BOUNDBOX:
556         {
557           if (node->type==LIB3DS_OBJECT_NODE) {
558             int i;
559             for (i=0; i<3; ++i) {
560               node->data.object.bbox_min[i]=lib3ds_io_read_float(io);
561             }
562             for (i=0; i<3; ++i) {
563               node->data.object.bbox_max[i]=lib3ds_io_read_float(io);
564             }
565           }
566           else {
567             lib3ds_chunk_unknown(chunk);
568           }
569         }
570         break;
571       case LIB3DS_COL_TRACK_TAG:
572         {
573           Lib3dsBool result=LIB3DS_TRUE;
574           
575           switch (node->type) {
576             case LIB3DS_AMBIENT_NODE:
577               result=lib3ds_lin3_track_read(&node->data.ambient.col_track, io);
578               break;
579             case LIB3DS_LIGHT_NODE:
580               result=lib3ds_lin3_track_read(&node->data.light.col_track, io);
581               break;
582             default:
583               lib3ds_chunk_unknown(chunk);
584           }
585           if (!result) {
586             return(LIB3DS_FALSE);
587           }
588         }
589         break;
590       case LIB3DS_POS_TRACK_TAG:
591         {
592           Lib3dsBool result=LIB3DS_TRUE;
593
594           switch (node->type) {
595             case LIB3DS_OBJECT_NODE:
596               result=lib3ds_lin3_track_read(&node->data.object.pos_track, io);
597               break;
598             case LIB3DS_CAMERA_NODE:
599               result=lib3ds_lin3_track_read(&node->data.camera.pos_track, io);
600               break;
601             case LIB3DS_TARGET_NODE:
602               result=lib3ds_lin3_track_read(&node->data.target.pos_track, io);
603               break;
604             case LIB3DS_LIGHT_NODE:
605               result=lib3ds_lin3_track_read(&node->data.light.pos_track, io);
606               break;
607             case LIB3DS_SPOT_NODE:
608               result=lib3ds_lin3_track_read(&node->data.spot.pos_track, io);
609               break;
610             default:
611               lib3ds_chunk_unknown(chunk);
612           }
613           if (!result) {
614             return(LIB3DS_FALSE);
615           }
616         }
617         break;
618       case LIB3DS_ROT_TRACK_TAG:
619         {
620           if (node->type==LIB3DS_OBJECT_NODE) {
621             if (!lib3ds_quat_track_read(&node->data.object.rot_track, io)) {
622               return(LIB3DS_FALSE);
623             }
624           }
625           else {
626             lib3ds_chunk_unknown(chunk);
627           }
628         }
629         break;
630       case LIB3DS_SCL_TRACK_TAG:
631         {
632           if (node->type==LIB3DS_OBJECT_NODE) {
633             if (!lib3ds_lin3_track_read(&node->data.object.scl_track, io)) {
634               return(LIB3DS_FALSE);
635             }
636           }
637           else {
638             lib3ds_chunk_unknown(chunk);
639           }
640         }
641         break;
642       case LIB3DS_FOV_TRACK_TAG:
643         {
644           if (node->type==LIB3DS_CAMERA_NODE) {
645             if (!lib3ds_lin1_track_read(&node->data.camera.fov_track, io)) {
646               return(LIB3DS_FALSE);
647             }
648           }
649           else {
650             lib3ds_chunk_unknown(chunk);
651           }
652         }
653         break;
654       case LIB3DS_HOT_TRACK_TAG:
655         {
656           if (node->type==LIB3DS_LIGHT_NODE) {
657             if (!lib3ds_lin1_track_read(&node->data.light.hotspot_track, io)) {
658               return(LIB3DS_FALSE);
659             }
660           }
661           else {
662             lib3ds_chunk_unknown(chunk);
663           }
664         }
665         break;
666       case LIB3DS_FALL_TRACK_TAG:
667         {
668           if (node->type==LIB3DS_LIGHT_NODE) {
669             if (!lib3ds_lin1_track_read(&node->data.light.falloff_track, io)) {
670               return(LIB3DS_FALSE);
671             }
672           }
673           else {
674             lib3ds_chunk_unknown(chunk);
675           }
676         }
677         break;
678       case LIB3DS_ROLL_TRACK_TAG:
679         {
680           Lib3dsBool result=LIB3DS_TRUE;
681
682           switch (node->type) {
683             case LIB3DS_CAMERA_NODE:
684               result=lib3ds_lin1_track_read(&node->data.camera.roll_track, io);
685               break;
686             case LIB3DS_LIGHT_NODE:
687               result=lib3ds_lin1_track_read(&node->data.light.roll_track, io);
688               break;
689             default:
690               lib3ds_chunk_unknown(chunk);
691           }
692           if (!result) {
693             return(LIB3DS_FALSE);
694           }
695         }
696         break;
697       case LIB3DS_HIDE_TRACK_TAG:
698         {
699           if (node->type==LIB3DS_OBJECT_NODE) {
700             if (!lib3ds_bool_track_read(&node->data.object.hide_track, io)) {
701               return(LIB3DS_FALSE);
702             }
703           }
704           else {
705             lib3ds_chunk_unknown(chunk);
706           }
707         }
708         break;
709       case LIB3DS_MORPH_SMOOTH:
710         {
711           if (node->type==LIB3DS_OBJECT_NODE) {
712             node->data.object.morph_smooth=lib3ds_io_read_float(io);
713           }
714           else {
715             lib3ds_chunk_unknown(chunk);
716           }
717         }
718         break;
719       case LIB3DS_MORPH_TRACK_TAG:
720         {
721           if (node->type==LIB3DS_OBJECT_NODE) {
722             if (!lib3ds_morph_track_read(&node->data.object.morph_track, io)) {
723               return(LIB3DS_FALSE);
724             }
725           }
726           else {
727             lib3ds_chunk_unknown(chunk);
728           }
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  * \ingroup node
743  */
744 Lib3dsBool
745 lib3ds_node_write(Lib3dsNode *node, Lib3dsFile *file, Lib3dsIo *io)
746 {
747   Lib3dsChunk c;
748
749   switch (node->type) {
750     case LIB3DS_AMBIENT_NODE:
751       c.chunk=LIB3DS_AMBIENT_NODE_TAG;
752       break;
753     case LIB3DS_OBJECT_NODE:
754       c.chunk=LIB3DS_OBJECT_NODE_TAG;
755       break;
756     case LIB3DS_CAMERA_NODE:
757       c.chunk=LIB3DS_CAMERA_NODE_TAG;
758       break;
759     case LIB3DS_TARGET_NODE:
760       c.chunk=LIB3DS_TARGET_NODE_TAG;
761       break;
762     case LIB3DS_LIGHT_NODE:
763       if (lib3ds_file_node_by_name(file, node->name, LIB3DS_SPOT_NODE)) {
764         c.chunk=LIB3DS_SPOTLIGHT_NODE_TAG;
765       }
766       else {
767         c.chunk=LIB3DS_LIGHT_NODE_TAG;
768       }
769       break;
770     case LIB3DS_SPOT_NODE:
771       c.chunk=LIB3DS_L_TARGET_NODE_TAG;
772       break;
773     default:
774       return(LIB3DS_FALSE);
775   }
776   if (!lib3ds_chunk_write_start(&c,io)) {
777     return(LIB3DS_FALSE);
778   }
779
780   { /*---- LIB3DS_NODE_ID ----*/
781     Lib3dsChunk c;
782     c.chunk=LIB3DS_NODE_ID;
783     c.size=8;
784     lib3ds_chunk_write(&c,io);
785     lib3ds_io_write_intw(io, node->node_id);
786   }
787
788   { /*---- LIB3DS_NODE_HDR ----*/
789     Lib3dsChunk c;
790     c.chunk=LIB3DS_NODE_HDR;
791     c.size=6+ 1+strlen(node->name) +2+2+2;
792     lib3ds_chunk_write(&c,io);
793     lib3ds_io_write_string(io, node->name);
794     lib3ds_io_write_word(io, node->flags1);
795     lib3ds_io_write_word(io, node->flags2);
796     lib3ds_io_write_word(io, node->parent_id);
797   }
798
799   switch (c.chunk) {
800     case LIB3DS_AMBIENT_NODE_TAG:
801       { /*---- LIB3DS_COL_TRACK_TAG ----*/
802         Lib3dsChunk c;
803         c.chunk=LIB3DS_COL_TRACK_TAG;
804         if (!lib3ds_chunk_write_start(&c,io)) {
805           return(LIB3DS_FALSE);
806         }
807         if (!lib3ds_lin3_track_write(&node->data.ambient.col_track,io)) {
808           return(LIB3DS_FALSE);
809         }
810         if (!lib3ds_chunk_write_end(&c,io)) {
811           return(LIB3DS_FALSE);
812         }
813       }
814       break;
815     case LIB3DS_OBJECT_NODE_TAG:
816       { /*---- LIB3DS_PIVOT ----*/
817         Lib3dsChunk c;
818         c.chunk=LIB3DS_PIVOT;
819         c.size=18;
820         lib3ds_chunk_write(&c,io);
821         lib3ds_io_write_vector(io, node->data.object.pivot);
822       }
823       { /*---- LIB3DS_INSTANCE_NAME ----*/
824         Lib3dsChunk c;
825         const char *name;
826         if (strlen(node->data.object.instance)) {
827           name=node->data.object.instance;
828
829           c.chunk=LIB3DS_INSTANCE_NAME;
830           c.size=6+1+strlen(name);
831           lib3ds_chunk_write(&c,io);
832           lib3ds_io_write_string(io, name);
833         }
834       }
835       {
836         int i;
837         for (i=0; i<3; ++i) {
838           if ((fabs(node->data.object.bbox_min[i])>LIB3DS_EPSILON) ||
839             (fabs(node->data.object.bbox_max[i])>LIB3DS_EPSILON)) {
840             break;
841           }
842         }
843         
844         if (i<3) { /*---- LIB3DS_BOUNDBOX ----*/
845           Lib3dsChunk c;
846           c.chunk=LIB3DS_BOUNDBOX;
847           c.size=30;
848           lib3ds_chunk_write(&c,io);
849           lib3ds_io_write_vector(io, node->data.object.bbox_min);
850           lib3ds_io_write_vector(io, node->data.object.bbox_max);
851         }
852       }
853       { /*---- LIB3DS_POS_TRACK_TAG ----*/
854         Lib3dsChunk c;
855         c.chunk=LIB3DS_POS_TRACK_TAG;
856         if (!lib3ds_chunk_write_start(&c,io)) {
857           return(LIB3DS_FALSE);
858         }
859         if (!lib3ds_lin3_track_write(&node->data.object.pos_track,io)) {
860           return(LIB3DS_FALSE);
861         }
862         if (!lib3ds_chunk_write_end(&c,io)) {
863           return(LIB3DS_FALSE);
864         }
865       }
866       { /*---- LIB3DS_ROT_TRACK_TAG ----*/
867         Lib3dsChunk c;
868         c.chunk=LIB3DS_ROT_TRACK_TAG;
869         if (!lib3ds_chunk_write_start(&c,io)) {
870           return(LIB3DS_FALSE);
871         }
872         if (!lib3ds_quat_track_write(&node->data.object.rot_track,io)) {
873           return(LIB3DS_FALSE);
874         }
875         if (!lib3ds_chunk_write_end(&c,io)) {
876           return(LIB3DS_FALSE);
877         }
878       }
879       { /*---- LIB3DS_SCL_TRACK_TAG ----*/
880         Lib3dsChunk c;
881         c.chunk=LIB3DS_SCL_TRACK_TAG;
882         if (!lib3ds_chunk_write_start(&c,io)) {
883           return(LIB3DS_FALSE);
884         }
885         if (!lib3ds_lin3_track_write(&node->data.object.scl_track,io)) {
886           return(LIB3DS_FALSE);
887         }
888         if (!lib3ds_chunk_write_end(&c,io)) {
889           return(LIB3DS_FALSE);
890         }
891       }
892       if (node->data.object.hide_track.keyL) { /*---- LIB3DS_HIDE_TRACK_TAG ----*/
893         Lib3dsChunk c;
894         c.chunk=LIB3DS_HIDE_TRACK_TAG;
895         if (!lib3ds_chunk_write_start(&c,io)) {
896           return(LIB3DS_FALSE);
897         }
898         if (!lib3ds_bool_track_write(&node->data.object.hide_track,io)) {
899           return(LIB3DS_FALSE);
900         }
901         if (!lib3ds_chunk_write_end(&c,io)) {
902           return(LIB3DS_FALSE);
903         }
904       }
905       if (fabs(node->data.object.morph_smooth)>LIB3DS_EPSILON){ /*---- LIB3DS_MORPH_SMOOTH ----*/
906         Lib3dsChunk c;
907         c.chunk=LIB3DS_MORPH_SMOOTH;
908         c.size=10;
909         lib3ds_chunk_write(&c,io);
910         lib3ds_io_write_float(io, node->data.object.morph_smooth);
911       }
912       break;
913     case LIB3DS_CAMERA_NODE_TAG:
914       { /*---- LIB3DS_POS_TRACK_TAG ----*/
915         Lib3dsChunk c;
916         c.chunk=LIB3DS_POS_TRACK_TAG;
917         if (!lib3ds_chunk_write_start(&c,io)) {
918           return(LIB3DS_FALSE);
919         }
920         if (!lib3ds_lin3_track_write(&node->data.camera.pos_track,io)) {
921           return(LIB3DS_FALSE);
922         }
923         if (!lib3ds_chunk_write_end(&c,io)) {
924           return(LIB3DS_FALSE);
925         }
926       }
927       { /*---- LIB3DS_FOV_TRACK_TAG ----*/
928         Lib3dsChunk c;
929         c.chunk=LIB3DS_FOV_TRACK_TAG;
930         if (!lib3ds_chunk_write_start(&c,io)) {
931           return(LIB3DS_FALSE);
932         }
933         if (!lib3ds_lin1_track_write(&node->data.camera.fov_track,io)) {
934           return(LIB3DS_FALSE);
935         }
936         if (!lib3ds_chunk_write_end(&c,io)) {
937           return(LIB3DS_FALSE);
938         }
939       }
940       { /*---- LIB3DS_ROLL_TRACK_TAG ----*/
941         Lib3dsChunk c;
942         c.chunk=LIB3DS_ROLL_TRACK_TAG;
943         if (!lib3ds_chunk_write_start(&c,io)) {
944           return(LIB3DS_FALSE);
945         }
946         if (!lib3ds_lin1_track_write(&node->data.camera.roll_track,io)) {
947           return(LIB3DS_FALSE);
948         }
949         if (!lib3ds_chunk_write_end(&c,io)) {
950           return(LIB3DS_FALSE);
951         }
952       }
953       break;
954     case LIB3DS_TARGET_NODE_TAG:
955       { /*---- LIB3DS_POS_TRACK_TAG ----*/
956         Lib3dsChunk c;
957         c.chunk=LIB3DS_POS_TRACK_TAG;
958         if (!lib3ds_chunk_write_start(&c,io)) {
959           return(LIB3DS_FALSE);
960         }
961         if (!lib3ds_lin3_track_write(&node->data.target.pos_track,io)) {
962           return(LIB3DS_FALSE);
963         }
964         if (!lib3ds_chunk_write_end(&c,io)) {
965           return(LIB3DS_FALSE);
966         }
967       }
968       break;
969     case LIB3DS_LIGHT_NODE_TAG:
970       { /*---- LIB3DS_POS_TRACK_TAG ----*/
971         Lib3dsChunk c;
972         c.chunk=LIB3DS_POS_TRACK_TAG;
973         if (!lib3ds_chunk_write_start(&c,io)) {
974           return(LIB3DS_FALSE);
975         }
976         if (!lib3ds_lin3_track_write(&node->data.light.pos_track,io)) {
977           return(LIB3DS_FALSE);
978         }
979         if (!lib3ds_chunk_write_end(&c,io)) {
980           return(LIB3DS_FALSE);
981         }
982       }
983       { /*---- LIB3DS_COL_TRACK_TAG ----*/
984         Lib3dsChunk c;
985         c.chunk=LIB3DS_COL_TRACK_TAG;
986         if (!lib3ds_chunk_write_start(&c,io)) {
987           return(LIB3DS_FALSE);
988         }
989         if (!lib3ds_lin3_track_write(&node->data.light.col_track,io)) {
990           return(LIB3DS_FALSE);
991         }
992         if (!lib3ds_chunk_write_end(&c,io)) {
993           return(LIB3DS_FALSE);
994         }
995       }
996       break;
997     case LIB3DS_SPOTLIGHT_NODE_TAG:
998       { /*---- LIB3DS_POS_TRACK_TAG ----*/
999         Lib3dsChunk c;
1000         c.chunk=LIB3DS_POS_TRACK_TAG;
1001         if (!lib3ds_chunk_write_start(&c,io)) {
1002           return(LIB3DS_FALSE);
1003         }
1004         if (!lib3ds_lin3_track_write(&node->data.light.pos_track,io)) {
1005           return(LIB3DS_FALSE);
1006         }
1007         if (!lib3ds_chunk_write_end(&c,io)) {
1008           return(LIB3DS_FALSE);
1009         }
1010       }
1011       { /*---- LIB3DS_COL_TRACK_TAG ----*/
1012         Lib3dsChunk c;
1013         c.chunk=LIB3DS_COL_TRACK_TAG;
1014         if (!lib3ds_chunk_write_start(&c,io)) {
1015           return(LIB3DS_FALSE);
1016         }
1017         if (!lib3ds_lin3_track_write(&node->data.light.col_track,io)) {
1018           return(LIB3DS_FALSE);
1019         }
1020         if (!lib3ds_chunk_write_end(&c,io)) {
1021           return(LIB3DS_FALSE);
1022         }
1023       }
1024       { /*---- LIB3DS_HOT_TRACK_TAG ----*/
1025         Lib3dsChunk c;
1026         c.chunk=LIB3DS_HOT_TRACK_TAG;
1027         if (!lib3ds_chunk_write_start(&c,io)) {
1028           return(LIB3DS_FALSE);
1029         }
1030         if (!lib3ds_lin1_track_write(&node->data.light.hotspot_track,io)) {
1031           return(LIB3DS_FALSE);
1032         }
1033         if (!lib3ds_chunk_write_end(&c,io)) {
1034           return(LIB3DS_FALSE);
1035         }
1036       }
1037       { /*---- LIB3DS_FALL_TRACK_TAG ----*/
1038         Lib3dsChunk c;
1039         c.chunk=LIB3DS_FALL_TRACK_TAG;
1040         if (!lib3ds_chunk_write_start(&c,io)) {
1041           return(LIB3DS_FALSE);
1042         }
1043         if (!lib3ds_lin1_track_write(&node->data.light.falloff_track,io)) {
1044           return(LIB3DS_FALSE);
1045         }
1046         if (!lib3ds_chunk_write_end(&c,io)) {
1047           return(LIB3DS_FALSE);
1048         }
1049       }
1050       { /*---- LIB3DS_ROLL_TRACK_TAG ----*/
1051         Lib3dsChunk c;
1052         c.chunk=LIB3DS_ROLL_TRACK_TAG;
1053         if (!lib3ds_chunk_write_start(&c,io)) {
1054           return(LIB3DS_FALSE);
1055         }
1056         if (!lib3ds_lin1_track_write(&node->data.light.roll_track,io)) {
1057           return(LIB3DS_FALSE);
1058         }
1059         if (!lib3ds_chunk_write_end(&c,io)) {
1060           return(LIB3DS_FALSE);
1061         }
1062       }
1063       break;
1064     case LIB3DS_L_TARGET_NODE_TAG:
1065       { /*---- LIB3DS_POS_TRACK_TAG ----*/
1066         Lib3dsChunk c;
1067         c.chunk=LIB3DS_POS_TRACK_TAG;
1068         if (!lib3ds_chunk_write_start(&c,io)) {
1069           return(LIB3DS_FALSE);
1070         }
1071         if (!lib3ds_lin3_track_write(&node->data.spot.pos_track,io)) {
1072           return(LIB3DS_FALSE);
1073         }
1074         if (!lib3ds_chunk_write_end(&c,io)) {
1075           return(LIB3DS_FALSE);
1076         }
1077       }
1078       break;
1079     default:
1080       return(LIB3DS_FALSE);
1081   }
1082
1083   if (!lib3ds_chunk_write_end(&c,io)) {
1084     return(LIB3DS_FALSE);
1085   }
1086   return(LIB3DS_TRUE);
1087 }
1088
1089
1090 /*!
1091
1092 \typedef Lib3dsNodeTypes
1093   \ingroup node
1094
1095 */
1096 /*!
1097
1098 \enum _Lib3dsNodeTypes
1099   \ingroup node
1100
1101 */
1102 /*!
1103
1104 \typedef Lib3dsBoolKey
1105   \ingroup node
1106   \sa _Lib3dsBoolKey
1107
1108 */
1109 /*!
1110
1111 \typedef Lib3dsBoolTrack
1112   \ingroup node
1113   \sa _Lib3dsBoolTrack
1114
1115 */
1116 /*!
1117
1118 \typedef Lib3dsLin1Key
1119   \ingroup node
1120   \sa _Lib3dsLin1Key
1121
1122 */
1123 /*!
1124
1125 \typedef Lib3dsLin1Track
1126   \ingroup node
1127   \sa _Lib3dsLin1Track
1128
1129 */
1130 /*!
1131
1132 \typedef Lib3dsLin3Key
1133   \ingroup node
1134   \sa _Lib3dsLin3Key
1135
1136 */
1137 /*!
1138
1139 \typedef Lib3dsLin3Track
1140   \ingroup node
1141   \sa _Lib3dsLin3Track
1142
1143 */
1144 /*!
1145
1146 \typedef Lib3dsQuatKey
1147   \ingroup node
1148   \sa _Lib3dsQuatKey
1149
1150 */
1151 /*!
1152
1153 \typedef Lib3dsQuatTrack
1154   \ingroup node
1155   \sa _Lib3dsLin3Key
1156
1157 */
1158 /*!
1159
1160 \typedef Lib3dsMorphKey
1161   \ingroup node
1162   \sa _Lib3dsMorphKey
1163
1164 */
1165 /*!
1166
1167 \typedef Lib3dsMorphTrack
1168   \ingroup node
1169   \sa _Lib3dsMorphTrack
1170
1171 */
1172
1173