Home | API | File List | Examples | Download

lib3ds_file.c

00001 /*
00002     Copyright (C) 1996-2008 by Jan Eric Kyprianidis <www.kyprianidis.com>
00003     All rights reserved.
00004     
00005     This program is free  software: you can redistribute it and/or modify 
00006     it under the terms of the GNU Lesser General Public License as published 
00007     by the Free Software Foundation, either version 2.1 of the License, or 
00008     (at your option) any later version.
00009 
00010     Thisprogram  is  distributed in the hope that it will be useful, 
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of 
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
00013     GNU Lesser General Public License for more details.
00014     
00015     You should  have received a copy of the GNU Lesser General Public License
00016     along with  this program; If not, see <http://www.gnu.org/licenses/>. 
00017 */
00018 #include "lib3ds_impl.h"
00019 
00020 
00021 static long
00022 fileio_seek_func(void *self, long offset, Lib3dsIoSeek origin) {
00023     FILE *f = (FILE*)self;
00024     int o;
00025     switch (origin) {
00026         case LIB3DS_SEEK_SET:
00027             o = SEEK_SET;
00028             break;
00029 
00030         case LIB3DS_SEEK_CUR:
00031             o = SEEK_CUR;
00032             break;
00033 
00034         case LIB3DS_SEEK_END:
00035             o = SEEK_END;
00036             break;
00037 
00038         default:
00039             assert(0);
00040             return(0);
00041     }
00042     return (fseek(f, offset, o));
00043 }
00044 
00045 
00046 static long
00047 fileio_tell_func(void *self) {
00048     FILE *f = (FILE*)self;
00049     return(ftell(f));
00050 }
00051 
00052 
00053 static size_t
00054 fileio_read_func(void *self, void *buffer, size_t size) {
00055     FILE *f = (FILE*)self;
00056     return(fread(buffer, 1, size, f));
00057 }
00058 
00059 
00060 static size_t
00061 fileio_write_func(void *self, const void *buffer, size_t size) {
00062     FILE *f = (FILE*)self;
00063     return(fwrite(buffer, 1, size, f));
00064 }
00065 
00066 
00082 Lib3dsFile*
00083 lib3ds_file_open(const char *filename) {
00084     FILE *f;
00085     Lib3dsFile *file;
00086     Lib3dsIo io;
00087 
00088     f = fopen(filename, "rb");
00089     if (!f) {
00090         return NULL;
00091     }
00092     file = lib3ds_file_new();
00093     if (!file) {
00094         fclose(f);
00095         return NULL;
00096     }
00097 
00098     memset(&io, 0, sizeof(io));
00099     io.self = f;
00100     io.seek_func = fileio_seek_func;
00101     io.tell_func = fileio_tell_func;
00102     io.read_func = fileio_read_func;
00103     io.write_func = fileio_write_func;
00104     io.log_func = NULL;
00105 
00106     if (!lib3ds_file_read(file, &io)) {
00107         fclose(f);
00108         free(file);
00109         return NULL;
00110     }
00111 
00112     fclose(f);
00113     return file;
00114 }
00115 
00116 
00128 int
00129 lib3ds_file_save(Lib3dsFile *file, const char *filename) {
00130     FILE *f;
00131     Lib3dsIo io;
00132     int result;
00133 
00134     f = fopen(filename, "wb");
00135     if (!f) {
00136         return FALSE;
00137     }
00138 
00139     memset(&io, 0, sizeof(io));
00140     io.self = f;
00141     io.seek_func = fileio_seek_func;
00142     io.tell_func = fileio_tell_func;
00143     io.read_func = fileio_read_func;
00144     io.write_func = fileio_write_func;
00145     io.log_func = NULL;
00146 
00147     result = lib3ds_file_write(file, &io);
00148     fclose(f);
00149     return result;
00150 }
00151 
00152 
00159 Lib3dsFile*
00160 lib3ds_file_new() {
00161     Lib3dsFile *file;
00162 
00163     file = (Lib3dsFile*)calloc(sizeof(Lib3dsFile), 1);
00164     if (!file) {
00165         return(0);
00166     }
00167     file->mesh_version = 3;
00168     file->master_scale = 1.0f;
00169     file->keyf_revision = 5;
00170     strcpy(file->name, "LIB3DS");
00171 
00172     file->frames = 100;
00173     file->segment_from = 0;
00174     file->segment_to = 100;
00175     file->current_frame = 0;
00176 
00177     return(file);
00178 }
00179 
00180 
00186 void
00187 lib3ds_file_free(Lib3dsFile* file) {
00188     assert(file);
00189     lib3ds_file_reserve_materials(file, 0, TRUE);
00190     lib3ds_file_reserve_cameras(file, 0, TRUE);
00191     lib3ds_file_reserve_lights(file, 0, TRUE);
00192     lib3ds_file_reserve_meshes(file, 0, TRUE);
00193     {
00194         Lib3dsNode *p, *q;
00195 
00196         for (p = file->nodes; p; p = q) {
00197             q = p->next;
00198             lib3ds_node_free(p);
00199         }
00200     }
00201     free(file);
00202 }
00203 
00204 
00213 void
00214 lib3ds_file_eval(Lib3dsFile *file, float t) {
00215     Lib3dsNode *p;
00216 
00217     for (p = file->nodes; p != 0; p = p->next) {
00218         lib3ds_node_eval(p, t);
00219     }
00220 }
00221 
00222 
00223 static void
00224 named_object_read(Lib3dsFile *file, Lib3dsIo *io) {
00225     Lib3dsChunk c;
00226     char name[64];
00227     uint16_t chunk;
00228     Lib3dsMesh *mesh = NULL;
00229     Lib3dsCamera *camera = NULL;
00230     Lib3dsLight *light = NULL;
00231     uint32_t object_flags;
00232 
00233     lib3ds_chunk_read_start(&c, CHK_NAMED_OBJECT, io);
00234     
00235     lib3ds_io_read_string(io, name, 64);
00236     lib3ds_io_log(io, LIB3DS_LOG_INFO, "  NAME=%s", name);
00237     lib3ds_chunk_read_tell(&c, io);
00238 
00239     object_flags = 0;
00240     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00241         switch (chunk) {
00242             case CHK_N_TRI_OBJECT: {
00243                 mesh = lib3ds_mesh_new(name);
00244                 lib3ds_file_insert_mesh(file, mesh, -1);
00245                 lib3ds_chunk_read_reset(&c, io);
00246                 lib3ds_mesh_read(file, mesh, io);
00247                 break;
00248             }
00249 
00250             case CHK_N_CAMERA: {
00251                 camera = lib3ds_camera_new(name);
00252                 lib3ds_file_insert_camera(file, camera, -1);
00253                 lib3ds_chunk_read_reset(&c, io);
00254                 lib3ds_camera_read(camera, io);
00255                 break;
00256             }
00257 
00258             case CHK_N_DIRECT_LIGHT: {
00259                 light = lib3ds_light_new(name);
00260                 lib3ds_file_insert_light(file, light, -1);
00261                 lib3ds_chunk_read_reset(&c, io);
00262                 lib3ds_light_read(light, io);
00263                 break;
00264             }
00265 
00266             case CHK_OBJ_HIDDEN:
00267                 object_flags |= LIB3DS_OBJECT_HIDDEN;
00268                 break;
00269 
00270             case CHK_OBJ_DOESNT_CAST:
00271                 object_flags |= LIB3DS_OBJECT_DOESNT_CAST;
00272                 break;
00273 
00274             case CHK_OBJ_VIS_LOFTER:
00275                 object_flags |= LIB3DS_OBJECT_VIS_LOFTER;
00276                 break;
00277 
00278             case CHK_OBJ_MATTE:
00279                 object_flags |= LIB3DS_OBJECT_MATTE;
00280                 break;
00281 
00282             case CHK_OBJ_DONT_RCVSHADOW:
00283                 object_flags |= LIB3DS_OBJECT_DONT_RCVSHADOW;
00284                 break;
00285 
00286             case CHK_OBJ_FAST:
00287                 object_flags |= LIB3DS_OBJECT_FAST;
00288                 break;
00289 
00290             case CHK_OBJ_FROZEN:
00291                 object_flags |= LIB3DS_OBJECT_FROZEN;
00292                 break;
00293 
00294             default:
00295                 lib3ds_chunk_unknown(chunk, io);
00296         }
00297     }
00298 
00299     if (mesh)
00300         mesh->object_flags = object_flags;
00301     if (camera)
00302         camera->object_flags = object_flags;
00303     if (light)
00304         light->object_flags = object_flags;
00305 
00306     lib3ds_chunk_read_end(&c, io);
00307 }
00308 
00309 
00310 static void
00311 ambient_read(Lib3dsFile *file, Lib3dsIo *io) {
00312     Lib3dsChunk c;
00313     uint16_t chunk;
00314     int have_lin = FALSE;
00315 
00316     lib3ds_chunk_read_start(&c, CHK_AMBIENT_LIGHT, io);
00317 
00318     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00319         switch (chunk) {
00320             case CHK_LIN_COLOR_F: {
00321                 int i;
00322                 for (i = 0; i < 3; ++i) {
00323                     file->ambient[i] = lib3ds_io_read_float(io);
00324                 }
00325                 have_lin = TRUE;
00326                 break;
00327             }
00328 
00329             case CHK_COLOR_F: {
00330                 /* gamma corrected color chunk
00331                    replaced in 3ds R3 by LIN_COLOR_24 */
00332                 if (!have_lin) {
00333                     int i;
00334                     for (i = 0; i < 3; ++i) {
00335                         file->ambient[i] = lib3ds_io_read_float(io);
00336                     }
00337                 }
00338                 break;
00339             }
00340 
00341             default:
00342                 lib3ds_chunk_unknown(chunk, io);
00343         }
00344     }
00345 
00346     lib3ds_chunk_read_end(&c, io);
00347 }
00348 
00349 
00350 static void
00351 mdata_read(Lib3dsFile *file, Lib3dsIo *io) {
00352     Lib3dsChunk c;
00353     uint16_t chunk;
00354 
00355     lib3ds_chunk_read_start(&c, CHK_MDATA, io);
00356 
00357     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00358         switch (chunk) {
00359             case CHK_MESH_VERSION: {
00360                 file->mesh_version = lib3ds_io_read_intd(io);
00361                 break;
00362             }
00363 
00364             case CHK_MASTER_SCALE: {
00365                 file->master_scale = lib3ds_io_read_float(io);
00366                 break;
00367             }
00368 
00369             case CHK_SHADOW_MAP_SIZE:
00370             case CHK_LO_SHADOW_BIAS:
00371             case CHK_HI_SHADOW_BIAS:
00372             case CHK_SHADOW_SAMPLES:
00373             case CHK_SHADOW_RANGE:
00374             case CHK_SHADOW_FILTER:
00375             case CHK_RAY_BIAS: {
00376                 lib3ds_chunk_read_reset(&c, io);
00377                 lib3ds_shadow_read(&file->shadow, io);
00378                 break;
00379             }
00380 
00381             case CHK_VIEWPORT_LAYOUT:
00382             case CHK_DEFAULT_VIEW: {
00383                 lib3ds_chunk_read_reset(&c, io);
00384                 lib3ds_viewport_read(&file->viewport, io);
00385                 break;
00386             }
00387 
00388             case CHK_O_CONSTS: {
00389                 int i;
00390                 for (i = 0; i < 3; ++i) {
00391                     file->construction_plane[i] = lib3ds_io_read_float(io);
00392                 }
00393                 break;
00394             }
00395 
00396             case CHK_AMBIENT_LIGHT: {
00397                 lib3ds_chunk_read_reset(&c, io);
00398                 ambient_read(file, io);
00399                 break;
00400             }
00401 
00402             case CHK_BIT_MAP:
00403             case CHK_SOLID_BGND:
00404             case CHK_V_GRADIENT:
00405             case CHK_USE_BIT_MAP:
00406             case CHK_USE_SOLID_BGND:
00407             case CHK_USE_V_GRADIENT: {
00408                 lib3ds_chunk_read_reset(&c, io);
00409                 lib3ds_background_read(&file->background, io);
00410                 break;
00411             }
00412 
00413             case CHK_FOG:
00414             case CHK_LAYER_FOG:
00415             case CHK_DISTANCE_CUE:
00416             case CHK_USE_FOG:
00417             case CHK_USE_LAYER_FOG:
00418             case CHK_USE_DISTANCE_CUE: {
00419                 lib3ds_chunk_read_reset(&c, io);
00420                 lib3ds_atmosphere_read(&file->atmosphere, io);
00421                 break;
00422             }
00423 
00424             case CHK_MAT_ENTRY: {
00425                 Lib3dsMaterial *material = lib3ds_material_new(NULL);
00426                 lib3ds_file_insert_material(file, material, -1);
00427                 lib3ds_chunk_read_reset(&c, io);
00428                 lib3ds_material_read(material, io);
00429                 break;
00430             }
00431 
00432             case CHK_NAMED_OBJECT: {
00433                 lib3ds_chunk_read_reset(&c, io);
00434                 named_object_read(file, io);
00435                 break;
00436             }
00437 
00438             default:
00439                 lib3ds_chunk_unknown(chunk, io);
00440         }
00441     }
00442 
00443     lib3ds_chunk_read_end(&c, io);
00444 }
00445 
00446 
00447 static int 
00448 compare_node_id( const void *a, const void *b ) {
00449    return (*((Lib3dsNode**)a))->node_id - (*((Lib3dsNode**)b))->node_id;
00450 }
00451 
00452 
00453 static int 
00454 compare_node_id2( const void *a, const void *b ) {
00455    return *((unsigned short*)a) - (*((Lib3dsNode**)b))->node_id;
00456 }
00457 
00458 
00459 static void
00460 kfdata_read(Lib3dsFile *file, Lib3dsIo *io) {
00461     Lib3dsChunk c;
00462     uint16_t chunk;
00463     unsigned num_nodes = 0;
00464     Lib3dsIoImpl *impl = (Lib3dsIoImpl*)io->impl;
00465     Lib3dsNode *last = NULL;
00466 
00467     lib3ds_chunk_read_start(&c, CHK_KFDATA, io);
00468 
00469     while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00470         switch (chunk) {
00471             case CHK_KFHDR: {
00472                 file->keyf_revision = lib3ds_io_read_word(io);
00473                 lib3ds_io_read_string(io, file->name, 12 + 1);
00474                 file->frames = lib3ds_io_read_intd(io);
00475                 break;
00476             }
00477 
00478             case CHK_KFSEG: {
00479                 file->segment_from = lib3ds_io_read_intd(io);
00480                 file->segment_to = lib3ds_io_read_intd(io);
00481                 break;
00482             }
00483 
00484             case CHK_KFCURTIME: {
00485                 file->current_frame = lib3ds_io_read_intd(io);
00486                 break;
00487             }
00488 
00489             case CHK_VIEWPORT_LAYOUT:
00490             case CHK_DEFAULT_VIEW: {
00491                 lib3ds_chunk_read_reset(&c, io);
00492                 lib3ds_viewport_read(&file->viewport_keyf, io);
00493                 break;
00494             }
00495 
00496             case CHK_AMBIENT_NODE_TAG: 
00497             case CHK_OBJECT_NODE_TAG: 
00498             case CHK_CAMERA_NODE_TAG: 
00499             case CHK_TARGET_NODE_TAG: 
00500             case CHK_LIGHT_NODE_TAG: 
00501             case CHK_SPOTLIGHT_NODE_TAG: 
00502             case CHK_L_TARGET_NODE_TAG: {
00503                 Lib3dsNodeType type;
00504                 Lib3dsNode *node;
00505 
00506                 switch (chunk) {
00507                     case CHK_AMBIENT_NODE_TAG: 
00508                         type = LIB3DS_NODE_AMBIENT_COLOR;
00509                         break;
00510                     case CHK_OBJECT_NODE_TAG: 
00511                         type = LIB3DS_NODE_MESH_INSTANCE;
00512                         break;
00513                     case CHK_CAMERA_NODE_TAG: 
00514                         type = LIB3DS_NODE_CAMERA;
00515                         break;
00516                     case CHK_TARGET_NODE_TAG: 
00517                         type = LIB3DS_NODE_CAMERA_TARGET;
00518                         break;
00519                     case CHK_LIGHT_NODE_TAG: 
00520                         type = LIB3DS_NODE_OMNILIGHT;
00521                         break;
00522                     case CHK_SPOTLIGHT_NODE_TAG: 
00523                         type = LIB3DS_NODE_SPOTLIGHT;
00524                         break;
00525                     case CHK_L_TARGET_NODE_TAG:
00526                         type = LIB3DS_NODE_SPOTLIGHT_TARGET;
00527                         break;
00528                 }
00529 
00530                 node = lib3ds_node_new(type);
00531                 node->node_id = num_nodes++;
00532                 if (last) {
00533                     last->next = node;
00534                 } else {
00535                     file->nodes = node;
00536                 }
00537                 node->user_ptr = last;
00538                 last = node;
00539                 lib3ds_chunk_read_reset(&c, io);
00540                 lib3ds_node_read(node, io);
00541                 break;
00542             }
00543 
00544             default:
00545                 lib3ds_chunk_unknown(chunk, io);
00546         }
00547     }
00548 
00549     {
00550         Lib3dsNode **nodes = malloc(num_nodes * sizeof(Lib3dsNode*));
00551         unsigned i;
00552         Lib3dsNode *p, *q, *parent;
00553 
00554         p = file->nodes;
00555         for (i = 0; i < num_nodes; ++i) {
00556             nodes[i] = p;
00557             p = p->next;
00558         }
00559         qsort(nodes, num_nodes, sizeof(Lib3dsNode*), compare_node_id);
00560 
00561         p = last;
00562         while (p) {
00563             q = p->user_ptr;
00564             if (p->user_id != 65535) {
00565                 parent = *(Lib3dsNode**)bsearch(&p->user_id, nodes, num_nodes, sizeof(Lib3dsNode*), compare_node_id2);
00566                 if (parent) {
00567                     q->next = p->next;    
00568                     p->next = parent->childs;
00569                     p->parent = parent;
00570                     parent->childs = p;
00571                 } else {
00572                     /* TODO: warning */
00573                 }
00574             }
00575             p->user_id = 0;
00576             p->user_ptr = NULL;
00577             p = q;
00578         }
00579         free(nodes);
00580     }
00581 
00582     lib3ds_chunk_read_end(&c, io);
00583 }
00584 
00585 
00594 int
00595 lib3ds_file_read(Lib3dsFile *file, Lib3dsIo *io) {
00596     Lib3dsChunk c;
00597     uint16_t chunk;
00598     Lib3dsIoImpl *impl;
00599 
00600     lib3ds_io_setup(io);
00601     impl = (Lib3dsIoImpl*)io->impl;
00602 
00603     if (setjmp(impl->jmpbuf) != 0) {
00604         lib3ds_io_cleanup(io);
00605         return FALSE;
00606     }
00607 
00608     lib3ds_chunk_read_start(&c, 0, io);
00609     switch (c.chunk) {
00610         case CHK_MDATA: {
00611             lib3ds_chunk_read_reset(&c, io);
00612             mdata_read(file, io);
00613             break;
00614         }
00615 
00616         case CHK_M3DMAGIC:
00617         case CHK_MLIBMAGIC:
00618         case CHK_CMAGIC: {
00619             while ((chunk = lib3ds_chunk_read_next(&c, io)) != 0) {
00620                 switch (chunk) {
00621                     case CHK_M3D_VERSION: {
00622                         file->mesh_version = lib3ds_io_read_dword(io);
00623                         break;
00624                     }
00625 
00626                     case CHK_MDATA: {
00627                         lib3ds_chunk_read_reset(&c, io);
00628                         mdata_read(file, io);
00629                         break;
00630                     }
00631 
00632                     case CHK_KFDATA: {
00633                         lib3ds_chunk_read_reset(&c, io);
00634                         kfdata_read(file, io);
00635                         break;
00636                     }
00637 
00638                     default:
00639                         lib3ds_chunk_unknown(chunk, io);
00640                 }
00641             }
00642             break;
00643         }
00644 
00645         default:
00646             lib3ds_chunk_unknown(c.chunk, io);
00647             return FALSE;
00648     }
00649 
00650     lib3ds_chunk_read_end(&c, io);
00651 
00652     memset(impl->jmpbuf, 0, sizeof(impl->jmpbuf));
00653     lib3ds_io_cleanup(io);
00654     return TRUE;
00655 }
00656 
00657 
00658 static void
00659 colorf_write(float rgb[3], Lib3dsIo *io) {
00660     Lib3dsChunk c;
00661 
00662     c.chunk = CHK_COLOR_F;
00663     c.size = 18;
00664     lib3ds_chunk_write(&c, io);
00665     lib3ds_io_write_rgb(io, rgb);
00666 
00667     c.chunk = CHK_LIN_COLOR_F;
00668     c.size = 18;
00669     lib3ds_chunk_write(&c, io);
00670     lib3ds_io_write_rgb(io, rgb);
00671 }
00672 
00673 
00674 static void
00675 object_flags_write(uint32_t flags, Lib3dsIo *io) {
00676     if (flags) {
00677         Lib3dsChunk c;
00678         c.size = 6;
00679 
00680         if (flags & LIB3DS_OBJECT_HIDDEN) {
00681             c.chunk = CHK_OBJ_HIDDEN;
00682             lib3ds_chunk_write(&c, io);
00683         }
00684         if (flags & LIB3DS_OBJECT_VIS_LOFTER) {
00685             c.chunk = CHK_OBJ_VIS_LOFTER;
00686             lib3ds_chunk_write(&c, io);
00687         }
00688         if (flags & LIB3DS_OBJECT_DOESNT_CAST) {
00689             c.chunk = CHK_OBJ_DOESNT_CAST;
00690             lib3ds_chunk_write(&c, io);
00691         }
00692         if (flags & LIB3DS_OBJECT_MATTE) {
00693             c.chunk = CHK_OBJ_MATTE;
00694             lib3ds_chunk_write(&c, io);
00695         }
00696         if (flags & LIB3DS_OBJECT_DONT_RCVSHADOW) {
00697             c.chunk = CHK_OBJ_DOESNT_CAST;
00698             lib3ds_chunk_write(&c, io);
00699         }
00700         if (flags & LIB3DS_OBJECT_FAST) {
00701             c.chunk = CHK_OBJ_FAST;
00702             lib3ds_chunk_write(&c, io);
00703         }
00704         if (flags & LIB3DS_OBJECT_FROZEN) {
00705             c.chunk = CHK_OBJ_FROZEN;
00706             lib3ds_chunk_write(&c, io);
00707         }
00708     }
00709 }
00710 
00711 
00712 static void
00713 mdata_write(Lib3dsFile *file, Lib3dsIo *io) {
00714     Lib3dsChunk c;
00715 
00716     c.chunk = CHK_MDATA;
00717     lib3ds_chunk_write_start(&c, io);
00718 
00719     { /*---- LIB3DS_MESH_VERSION ----*/
00720         Lib3dsChunk c;
00721         c.chunk = CHK_MESH_VERSION;
00722         c.size = 10;
00723         lib3ds_chunk_write(&c, io);
00724         lib3ds_io_write_intd(io, file->mesh_version);
00725     }
00726     { /*---- LIB3DS_MASTER_SCALE ----*/
00727         Lib3dsChunk c;
00728         c.chunk = CHK_MASTER_SCALE;
00729         c.size = 10;
00730         lib3ds_chunk_write(&c, io);
00731         lib3ds_io_write_float(io, file->master_scale);
00732     }
00733     { /*---- LIB3DS_O_CONSTS ----*/
00734         int i;
00735         for (i = 0; i < 3; ++i) {
00736             if (fabs(file->construction_plane[i]) > LIB3DS_EPSILON) {
00737                 break;
00738             }
00739         }
00740         if (i < 3) {
00741             Lib3dsChunk c;
00742             c.chunk = CHK_O_CONSTS;
00743             c.size = 18;
00744             lib3ds_chunk_write(&c, io);
00745             lib3ds_io_write_vector(io, file->construction_plane);
00746         }
00747     }
00748 
00749     { /*---- LIB3DS_AMBIENT_LIGHT ----*/
00750         int i;
00751         for (i = 0; i < 3; ++i) {
00752             if (fabs(file->ambient[i]) > LIB3DS_EPSILON) {
00753                 break;
00754             }
00755         }
00756         if (i < 3) {
00757             Lib3dsChunk c;
00758             c.chunk = CHK_AMBIENT_LIGHT;
00759             c.size = 42;
00760             lib3ds_chunk_write(&c, io);
00761             colorf_write(file->ambient, io);
00762         }
00763     }
00764     lib3ds_background_write(&file->background, io);
00765     lib3ds_atmosphere_write(&file->atmosphere, io);
00766     lib3ds_shadow_write(&file->shadow, io);
00767     lib3ds_viewport_write(&file->viewport, io);
00768     {
00769         int i;
00770         for (i = 0; i < file->nmaterials; ++i) {
00771             lib3ds_material_write(file->materials[i], io);
00772         }
00773     }
00774     {
00775         Lib3dsChunk c;
00776         int i;
00777 
00778         for (i = 0; i < file->ncameras; ++i) {
00779             c.chunk = CHK_NAMED_OBJECT;
00780             lib3ds_chunk_write_start(&c, io);
00781             lib3ds_io_write_string(io, file->cameras[i]->name);
00782             lib3ds_camera_write(file->cameras[i], io);
00783             object_flags_write(file->cameras[i]->object_flags, io);
00784             lib3ds_chunk_write_end(&c, io);
00785         }
00786     }
00787     {
00788         Lib3dsChunk c;
00789         int i;
00790 
00791         for (i = 0; i < file->nlights; ++i) {
00792             c.chunk = CHK_NAMED_OBJECT;
00793             lib3ds_chunk_write_start(&c, io);
00794             lib3ds_io_write_string(io, file->lights[i]->name);
00795             lib3ds_light_write(file->lights[i], io);
00796             object_flags_write(file->lights[i]->object_flags, io);
00797             lib3ds_chunk_write_end(&c, io);
00798         }
00799     }
00800     {
00801         Lib3dsChunk c;
00802         int i;
00803 
00804         for (i = 0; i < file->nmeshes; ++i) {
00805             c.chunk = CHK_NAMED_OBJECT;
00806             lib3ds_chunk_write_start(&c, io);
00807             lib3ds_io_write_string(io, file->meshes[i]->name);
00808             lib3ds_mesh_write(file, file->meshes[i], io);
00809             object_flags_write(file->meshes[i]->object_flags, io);
00810             lib3ds_chunk_write_end(&c, io);
00811         }
00812     }
00813 
00814     lib3ds_chunk_write_end(&c, io);
00815 }
00816 
00817 
00818 
00819 static void
00820 nodes_write(Lib3dsNode *first_node, uint16_t *default_id, uint16_t parent_id, Lib3dsIo *io) {
00821     Lib3dsNode *p;
00822     for (p = first_node; p != NULL; p = p->next) {
00823         uint16_t node_id;
00824         if ((p->type == LIB3DS_NODE_AMBIENT_COLOR) || (p->node_id != 65535)) {
00825             node_id = p->node_id;
00826         } else {
00827             node_id = *default_id;
00828         }
00829         ++(*default_id);
00830         lib3ds_node_write(p, node_id, parent_id, io);
00831 
00832         nodes_write(p->childs, default_id, node_id, io);
00833     }
00834 }
00835 
00836 
00837 static void
00838 kfdata_write(Lib3dsFile *file, Lib3dsIo *io) {
00839     Lib3dsChunk c;
00840 
00841     if (!file->nodes) {
00842         return;
00843     }
00844 
00845     c.chunk = CHK_KFDATA;
00846     lib3ds_chunk_write_start(&c, io);
00847 
00848     { /*---- LIB3DS_KFHDR ----*/
00849         Lib3dsChunk c;
00850         c.chunk = CHK_KFHDR;
00851         c.size = 6 + 2 + (uint32_t)strlen(file->name) + 1 + 4;
00852         lib3ds_chunk_write(&c, io);
00853         lib3ds_io_write_intw(io, file->keyf_revision);
00854         lib3ds_io_write_string(io, file->name);
00855         lib3ds_io_write_intd(io, file->frames);
00856     }
00857     { /*---- LIB3DS_KFSEG ----*/
00858         Lib3dsChunk c;
00859         c.chunk = CHK_KFSEG;
00860         c.size = 14;
00861         lib3ds_chunk_write(&c, io);
00862         lib3ds_io_write_intd(io, file->segment_from);
00863         lib3ds_io_write_intd(io, file->segment_to);
00864     }
00865     { /*---- LIB3DS_KFCURTIME ----*/
00866         Lib3dsChunk c;
00867         c.chunk = CHK_KFCURTIME;
00868         c.size = 10;
00869         lib3ds_chunk_write(&c, io);
00870         lib3ds_io_write_intd(io, file->current_frame);
00871     }
00872     lib3ds_viewport_write(&file->viewport_keyf, io);
00873 
00874     {
00875         uint16_t default_id = 0;
00876         nodes_write(file->nodes, &default_id, 65535, io);
00877     }
00878 
00879     lib3ds_chunk_write_end(&c, io);
00880 }
00881 
00882 
00891 int
00892 lib3ds_file_write(Lib3dsFile *file, Lib3dsIo *io) {
00893     Lib3dsChunk c;
00894     Lib3dsIoImpl *impl;
00895 
00896     lib3ds_io_setup(io);
00897     impl = (Lib3dsIoImpl*)io->impl;
00898 
00899     if (setjmp(impl->jmpbuf) != 0) {
00900         lib3ds_io_cleanup(io);
00901         return FALSE;
00902     }
00903 
00904     c.chunk = CHK_M3DMAGIC;
00905     lib3ds_chunk_write_start(&c, io);
00906 
00907     { /*---- LIB3DS_M3D_VERSION ----*/
00908         Lib3dsChunk c;
00909 
00910         c.chunk = CHK_M3D_VERSION;
00911         c.size = 10;
00912         lib3ds_chunk_write(&c, io);
00913         lib3ds_io_write_dword(io, file->mesh_version);
00914     }
00915 
00916     mdata_write(file, io);
00917     kfdata_write(file, io);
00918 
00919     lib3ds_chunk_write_end(&c, io);
00920 
00921     memset(impl->jmpbuf, 0, sizeof(impl->jmpbuf));
00922     lib3ds_io_cleanup(io);
00923     return TRUE;
00924 }
00925 
00926 
00927 void lib3ds_file_reserve_materials(Lib3dsFile *file, int size, int force) {
00928     assert(file);
00929     lib3ds_util_reserve_array((void***)&file->materials, &file->nmaterials, &file->materials_size, 
00930                               size, force, (Lib3dsFreeFunc)lib3ds_material_free);
00931 }
00932 
00933 
00934 void
00935 lib3ds_file_insert_material(Lib3dsFile *file, Lib3dsMaterial *material, int index) {
00936     assert(file);
00937     lib3ds_util_insert_array((void***)&file->materials, &file->nmaterials, &file->materials_size, material, index);
00938 }
00939 
00940 
00941 void
00942 lib3ds_file_remove_material(Lib3dsFile *file, int index) {
00943     assert(file);
00944     lib3ds_util_remove_array((void***)&file->materials, &file->nmaterials, index, (Lib3dsFreeFunc)lib3ds_material_free);
00945 }
00946 
00947 
00948 int
00949 lib3ds_file_material_by_name(Lib3dsFile *file, const char *name) {
00950     int i;
00951 
00952     assert(file);
00953     for (i = 0; i < file->nmaterials; ++i) {
00954         if (strcmp(file->materials[i]->name, name) == 0) {
00955             return(i);
00956         }
00957     }
00958     return -1;
00959 }
00960 
00961 
00962 void 
00963 lib3ds_file_reserve_cameras(Lib3dsFile *file, int size, int force) {
00964     assert(file);
00965     lib3ds_util_reserve_array((void***)&file->cameras, &file->ncameras, &file->cameras_size, 
00966                               size, force, (Lib3dsFreeFunc)lib3ds_camera_free);
00967 }
00968 
00969 
00970 void
00971 lib3ds_file_insert_camera(Lib3dsFile *file, Lib3dsCamera *camera, int index) {
00972     assert(file);
00973     lib3ds_util_insert_array((void***)&file->cameras, &file->ncameras, &file->cameras_size, camera, index);
00974 }
00975 
00976 
00977 void
00978 lib3ds_file_remove_camera(Lib3dsFile *file, int index) {
00979     assert(file);
00980     lib3ds_util_remove_array((void***)&file->cameras, &file->ncameras, index, (Lib3dsFreeFunc)lib3ds_camera_free);
00981 }
00982 
00983 
00984 int
00985 lib3ds_file_camera_by_name(Lib3dsFile *file, const char *name) {
00986     int i;
00987 
00988     assert(file);
00989     for (i = 0; i < file->ncameras; ++i) {
00990         if (strcmp(file->cameras[i]->name, name) == 0) {
00991             return(i);
00992         }
00993     }
00994     return -1;
00995 }
00996 
00997 
00998 void 
00999 lib3ds_file_reserve_lights(Lib3dsFile *file, int size, int force) {
01000     assert(file);
01001     lib3ds_util_reserve_array((void***)&file->lights, &file->nlights, &file->lights_size, 
01002                               size, force, (Lib3dsFreeFunc)lib3ds_light_free);
01003 }
01004 
01005 
01006 void
01007 lib3ds_file_insert_light(Lib3dsFile *file, Lib3dsLight *light, int index) {
01008     assert(file);
01009     lib3ds_util_insert_array((void***)&file->lights, &file->nlights, &file->lights_size, light, index);
01010 }
01011 
01012 
01013 void
01014 lib3ds_file_remove_light(Lib3dsFile *file, int index) {
01015     assert(file);
01016     lib3ds_util_remove_array((void***)&file->lights, &file->nlights, index, (Lib3dsFreeFunc)lib3ds_light_free);
01017 }
01018 
01019 
01020 int
01021 lib3ds_file_light_by_name(Lib3dsFile *file, const char *name) {
01022     int i;
01023 
01024     assert(file);
01025     for (i = 0; i < file->nlights; ++i) {
01026         if (strcmp(file->lights[i]->name, name) == 0) {
01027             return(i);
01028         }
01029     }
01030     return -1;
01031 }
01032 
01033 
01034 void 
01035 lib3ds_file_reserve_meshes(Lib3dsFile *file, int size, int force) {
01036     assert(file);
01037     lib3ds_util_reserve_array((void***)&file->meshes, &file->nmeshes, &file->meshes_size, 
01038                                size, force, (Lib3dsFreeFunc)lib3ds_mesh_free);
01039 }
01040 
01041 
01042 void
01043 lib3ds_file_insert_mesh(Lib3dsFile *file, Lib3dsMesh *mesh, int index) {
01044     assert(file);
01045     lib3ds_util_insert_array((void***)&file->meshes, &file->nmeshes, &file->meshes_size, mesh, index);
01046 }
01047 
01048 
01049 void
01050 lib3ds_file_remove_mesh(Lib3dsFile *file, int index) {
01051     assert(file);
01052     lib3ds_util_remove_array((void***)&file->meshes, &file->nmeshes, index, (Lib3dsFreeFunc)lib3ds_mesh_free);
01053 }
01054 
01055 
01056 int
01057 lib3ds_file_mesh_by_name(Lib3dsFile *file, const char *name) {
01058     int i;
01059 
01060     assert(file);
01061     for (i = 0; i < file->nmeshes; ++i) {
01062         if (strcmp(file->meshes[i]->name, name) == 0) {
01063             return(i);
01064         }
01065     }
01066     return -1;
01067 }
01068 
01069 
01070 Lib3dsMesh* 
01071 lib3ds_file_mesh_for_node(Lib3dsFile *file, Lib3dsNode *node) {
01072     int index;
01073     Lib3dsMeshInstanceNode *n;
01074 
01075     if (node->type != LIB3DS_NODE_MESH_INSTANCE)
01076         return NULL;
01077     n = (Lib3dsMeshInstanceNode*)node;
01078 
01079     index = lib3ds_file_mesh_by_name(file, node->name);
01080 
01081     return (index >= 0)? file->meshes[index] : NULL;
01082 }
01083 
01084 
01099 Lib3dsNode*
01100 lib3ds_file_node_by_name(Lib3dsFile *file, const char* name, Lib3dsNodeType type) {
01101     Lib3dsNode *p, *q;
01102 
01103     assert(file);
01104     for (p = file->nodes; p != 0; p = p->next) {
01105         if ((p->type == type) && (strcmp(p->name, name) == 0)) {
01106             return(p);
01107         }
01108         q = lib3ds_node_by_name(p, name, type);
01109         if (q) {
01110             return(q);
01111         }
01112     }
01113     return(0);
01114 }
01115 
01116 
01129 Lib3dsNode*
01130 lib3ds_file_node_by_id(Lib3dsFile *file, uint16_t node_id) {
01131     Lib3dsNode *p, *q;
01132 
01133     assert(file);
01134     for (p = file->nodes; p != 0; p = p->next) {
01135         if (p->node_id == node_id) {
01136             return(p);
01137         }
01138         q = lib3ds_node_by_id(p, node_id);
01139         if (q) {
01140             return(q);
01141         }
01142     }
01143     return(0);
01144 }
01145 
01146 
01147 void
01148 lib3ds_file_append_node(Lib3dsFile *file, Lib3dsNode *node, Lib3dsNode *parent) {
01149     Lib3dsNode *p;
01150 
01151     assert(file);
01152     assert(node);
01153     p = parent? parent->childs : file->nodes;
01154     if (p) {
01155         while (p->next) {
01156             p = p->next;
01157         }
01158         p->next = node;
01159     } else {
01160         if (parent) {
01161             parent->childs = node;
01162         } else {
01163             file->nodes = node;
01164         } 
01165     }
01166     node->parent = parent;
01167     node->next = NULL;
01168 }
01169 
01170 
01171 void
01172 lib3ds_file_insert_node(Lib3dsFile *file, Lib3dsNode *node, Lib3dsNode *before) {
01173     Lib3dsNode *p, *q;
01174 
01175     assert(node);
01176     assert(file);
01177 
01178     if (before) {
01179         p = before->parent? before->parent->childs : file->nodes;
01180         assert(p);
01181         q = NULL;
01182         while (p != before) {
01183             q = p;
01184             p = p->next;
01185         }
01186         if (q) {
01187             node->next = q->next;
01188             q->next = node;
01189         } else {
01190             node->next = file->nodes;
01191             file->nodes = node;
01192         }
01193         node->parent = before->parent;
01194     } else {
01195         node->next = file->nodes;
01196         node->parent = NULL;
01197         file->nodes = node;
01198     }
01199 }
01200 
01201 
01210 void
01211 lib3ds_file_remove_node(Lib3dsFile *file, Lib3dsNode *node) {
01212     Lib3dsNode *p, *n;
01213 
01214     if (node->parent) {
01215         for (p = 0, n = node->parent->childs; n; p = n, n = n->next) {
01216             if (n == node) {
01217                 break;
01218             }
01219         }
01220         if (!n) {
01221             return;
01222         }
01223 
01224         if (!p) {
01225             node->parent->childs = n->next;
01226         } else {
01227             p->next = n->next;
01228         }
01229     } else {
01230         for (p = 0, n = file->nodes; n; p = n, n = n->next) {
01231             if (n == node) {
01232                 break;
01233             }
01234         }
01235         if (!n) {
01236             return;
01237         }
01238 
01239         if (!p) {
01240             file->nodes = n->next;
01241         } else {
01242             p->next = n->next;
01243         }
01244     }
01245 }
01246 
01247 
01248 static void
01249 file_minmax_node_id_impl(Lib3dsFile *file, Lib3dsNode *node, uint16_t *min_id, uint16_t *max_id) {
01250     Lib3dsNode *p;
01251     
01252     if (min_id && (*min_id > node->node_id))
01253         *min_id = node->node_id;
01254     if (max_id && (*max_id < node->node_id))
01255         *max_id = node->node_id;
01256     
01257     p = node->childs;
01258     while (p) {
01259         file_minmax_node_id_impl(file, p, min_id, max_id);
01260         p = p->next;
01261     }
01262 }
01263 
01264 
01265 void 
01266 lib3ds_file_minmax_node_id(Lib3dsFile *file, uint16_t *min_id, uint16_t *max_id) {
01267     Lib3dsNode *p;
01268     
01269     if (min_id)
01270         *min_id = 65535;
01271     if (max_id)
01272         *max_id = 0;
01273 
01274     p = file->nodes;
01275     while (p) {
01276         file_minmax_node_id_impl(file, p, min_id, max_id);
01277         p = p->next;
01278     }
01279 }
01280 
01281 
01282 void
01283 lib3ds_file_bounding_box_of_objects(Lib3dsFile *file, int 
01284                                     include_meshes, int include_cameras, int include_lights,
01285                                     float bmin[3], float bmax[3]) {
01286     bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
01287     bmax[0] = bmax[1] = bmax[2] = -FLT_MAX;
01288 
01289     if (include_meshes) {
01290         float lmin[3], lmax[3];
01291         int i;
01292         for (i = 0; i < file->nmeshes; ++i) {
01293             lib3ds_mesh_bounding_box(file->meshes[i], lmin, lmax);
01294             lib3ds_vector_min(bmin, lmin);
01295             lib3ds_vector_max(bmax, lmax);
01296         }
01297     }
01298     if (include_cameras) {
01299         int i;
01300         for (i = 0; i < file->ncameras; ++i) {
01301             lib3ds_vector_min(bmin, file->cameras[i]->position);
01302             lib3ds_vector_max(bmax, file->cameras[i]->position);
01303             lib3ds_vector_min(bmin, file->cameras[i]->target);
01304             lib3ds_vector_max(bmax, file->cameras[i]->target);
01305         }
01306     }
01307     if (include_lights) {
01308         int i;
01309         for (i = 0; i < file->ncameras; ++i) {
01310             lib3ds_vector_min(bmin, file->lights[i]->position);
01311             lib3ds_vector_max(bmax, file->lights[i]->position);
01312             if (file->lights[i]->spot_light) {
01313                 lib3ds_vector_min(bmin, file->lights[i]->target);
01314                 lib3ds_vector_max(bmax, file->lights[i]->target);
01315             }
01316         }
01317     }
01318 }
01319 
01320 
01321 static void
01322 file_bounding_box_of_nodes_impl(Lib3dsNode *node, Lib3dsFile *file, 
01323                                 int include_meshes, int include_cameras, int include_lights,
01324                                 float bmin[3], float bmax[3], float matrix[4][4]) {
01325     switch (node->type) {
01326         case LIB3DS_NODE_MESH_INSTANCE:
01327             if (include_meshes) {
01328                 int index;
01329                 Lib3dsMeshInstanceNode *n = (Lib3dsMeshInstanceNode*)node;
01330 
01331                 index = lib3ds_file_mesh_by_name(file, n->instance_name);
01332                 if (index < 0)
01333                     index = lib3ds_file_mesh_by_name(file, node->name);
01334                 if (index >= 0) {
01335                     Lib3dsMesh *mesh;
01336                     float inv_matrix[4][4], M[4][4];
01337                     float v[3];
01338                     int i;
01339 
01340                     mesh = file->meshes[index];
01341                     lib3ds_matrix_copy(inv_matrix, mesh->matrix);
01342                     lib3ds_matrix_inv(inv_matrix);
01343                     lib3ds_matrix_mult(M, matrix, node->matrix);
01344                     lib3ds_matrix_translate(M, -n->pivot[0], -n->pivot[1], -n->pivot[2]);
01345                     lib3ds_matrix_mult(M, M, inv_matrix);
01346 
01347                     for (i = 0; i < mesh->nvertices; ++i) {
01348                         lib3ds_vector_transform(v, M, mesh->vertices[i]);
01349                         lib3ds_vector_min(bmin, v);
01350                         lib3ds_vector_max(bmax, v);
01351                     }
01352                 }
01353             }
01354             break;
01355 
01356         case LIB3DS_NODE_CAMERA:
01357         case LIB3DS_NODE_CAMERA_TARGET:
01358             if (include_cameras) {
01359                 float z[3], v[3];
01360                 float M[4][4];
01361                 lib3ds_matrix_mult(M, matrix, node->matrix);
01362                 lib3ds_vector_zero(z);
01363                 lib3ds_vector_transform(v, M, z);
01364                 lib3ds_vector_min(bmin, v);
01365                 lib3ds_vector_max(bmax, v);
01366             }
01367             break;
01368 
01369         case LIB3DS_NODE_OMNILIGHT:
01370         case LIB3DS_NODE_SPOTLIGHT:
01371         case LIB3DS_NODE_SPOTLIGHT_TARGET:
01372             if (include_lights) {
01373                 float z[3], v[3];
01374                 float M[4][4];
01375                 lib3ds_matrix_mult(M, matrix, node->matrix);
01376                 lib3ds_vector_zero(z);
01377                 lib3ds_vector_transform(v, M, z);
01378                 lib3ds_vector_min(bmin, v);
01379                 lib3ds_vector_max(bmax, v);
01380             }
01381             break;
01382     }
01383     {
01384         Lib3dsNode *p = node->childs;
01385         while (p) {
01386             file_bounding_box_of_nodes_impl(p, file, include_meshes, include_cameras, include_lights, bmin, bmax, matrix);
01387             p = p->next;
01388         }
01389     }
01390 }
01391 
01392 
01393 void
01394 lib3ds_file_bounding_box_of_nodes(Lib3dsFile *file, 
01395                                   int include_meshes, int include_cameras,int include_lights,
01396                                   float bmin[3], float bmax[3], float matrix[4][4]) {
01397     Lib3dsNode *p;
01398     float M[4][4];
01399 
01400     if (matrix) {
01401         lib3ds_matrix_copy(M, matrix);
01402     } else {
01403         lib3ds_matrix_identity(M);
01404     }
01405 
01406     bmin[0] = bmin[1] = bmin[2] = FLT_MAX;
01407     bmax[0] = bmax[1] = bmax[2] = -FLT_MAX;
01408     p = file->nodes;
01409     while (p) {
01410         file_bounding_box_of_nodes_impl(p, file, include_meshes, include_cameras, include_lights, bmin, bmax, M);
01411         p = p->next;
01412     }
01413 }
01414 
01415 
01416 void
01417 lib3ds_file_create_nodes_for_meshes(Lib3dsFile *file) {
01418     Lib3dsNode *p;
01419     int i;
01420     for (i = 0; i < file->nmeshes; ++i) {
01421         Lib3dsMesh *mesh = file->meshes[i];
01422         p = lib3ds_node_new(LIB3DS_NODE_MESH_INSTANCE);
01423         strcpy(p->name, mesh->name);
01424         lib3ds_file_insert_node(file, p, NULL);
01425     }
01426 }