X-Git-Url: http://git.mutantstargoat.com/user/nuclear/?a=blobdiff_plain;ds=sidebyside;f=libs%2Flib3ds%2Ffile.c;fp=libs%2Flib3ds%2Ffile.c;h=33df4b9872fcd16536d01fb1c09ab0ec509498e3;hb=6c322d339ec9c5063c8f47117633b46e2a8eafd0;hp=0000000000000000000000000000000000000000;hpb=787ad7856f5074db4fe805b15bfca61553895de1;p=summerhack diff --git a/libs/lib3ds/file.c b/libs/lib3ds/file.c new file mode 100644 index 0000000..33df4b9 --- /dev/null +++ b/libs/lib3ds/file.c @@ -0,0 +1,1917 @@ +/* + * The 3D Studio File Format Library + * Copyright (C) 1996-2001 by J.E. Hoffmann + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: file.c,v 1.23 2005/01/11 10:20:36 madmac Exp $ + */ +#define LIB3DS_EXPORT +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef WITH_DMALLOC +#include +#endif + + + +/*! + * \defgroup file Files + * + * \author J.E. Hoffmann + */ + + +static Lib3dsBool +fileio_error_func(void *self) +{ + FILE *f = (FILE*)self; + return(ferror(f)!=0); +} + + +static long +fileio_seek_func(void *self, long offset, Lib3dsIoSeek origin) +{ + FILE *f = (FILE*)self; + int o; + switch (origin) { + case LIB3DS_SEEK_SET: + o = SEEK_SET; + break; + case LIB3DS_SEEK_CUR: + o = SEEK_CUR; + break; + case LIB3DS_SEEK_END: + o = SEEK_END; + break; + default: + ASSERT(0); + return(0); + } + return (fseek(f, offset, o)); +} + + +static long +fileio_tell_func(void *self) +{ + FILE *f = (FILE*)self; + return(ftell(f)); +} + + +static int +fileio_read_func(void *self, Lib3dsByte *buffer, int size) +{ + FILE *f = (FILE*)self; + return(fread(buffer, 1, size, f)); +} + + +static int +fileio_write_func(void *self, const Lib3dsByte *buffer, int size) +{ + FILE *f = (FILE*)self; + return(fwrite(buffer, 1, size, f)); +} + + +/*! + * Loads a .3DS file from disk into memory. + * + * \param filename The filename of the .3DS file + * + * \return A pointer to the Lib3dsFile structure containing the + * data of the .3DS file. + * If the .3DS file can not be loaded NULL is returned. + * + * \note To free the returned structure use lib3ds_free. + * + * \see lib3ds_file_save + * \see lib3ds_file_new + * \see lib3ds_file_free + * + * \ingroup file + */ +Lib3dsFile* +lib3ds_file_load(const char *filename) +{ + FILE *f; + Lib3dsFile *file; + Lib3dsIo *io; + + + f = fopen(filename, "rb"); + if (!f) { + return(0); + } + file = lib3ds_file_new(); + if (!file) { + fclose(f); + return(0); + } + io = lib3ds_io_new( + f, + fileio_error_func, + fileio_seek_func, + fileio_tell_func, + fileio_read_func, + fileio_write_func + ); + if (!io) { + lib3ds_file_free(file); + fclose(f); + return(0); + } + + if (!lib3ds_file_read(file, io)) { + free(file); + fclose(f); + return(0); + } + + lib3ds_io_free(io); + fclose(f); + return(file); +} + + +/*! + * Saves a .3DS file from memory to disk. + * + * \param file A pointer to a Lib3dsFile structure containing the + * the data that should be stored. + * \param filename The filename of the .3DS file to store the data in. + * + * \return TRUE on success, FALSE otherwise. + * + * \see lib3ds_file_load + * + * \ingroup file + */ +Lib3dsBool +lib3ds_file_save(Lib3dsFile *file, const char *filename) +{ + FILE *f; + Lib3dsIo *io; + Lib3dsBool result; + + f = fopen(filename, "wb"); + if (!f) { + return(LIB3DS_FALSE); + } + io = lib3ds_io_new( + f, + fileio_error_func, + fileio_seek_func, + fileio_tell_func, + fileio_read_func, + fileio_write_func + ); + if (!io) { + fclose(f); + return LIB3DS_FALSE; + } + + result = lib3ds_file_write(file, io); + + fclose(f); + + lib3ds_io_free(io); + return(result); +} + + +/*! + * Creates and returns a new, empty Lib3dsFile object. + * + * \return A pointer to the Lib3dsFile structure. + * If the structure cannot be allocated, NULL is returned. + * + * \ingroup file + */ +Lib3dsFile* +lib3ds_file_new() +{ + Lib3dsFile *file; + + file=(Lib3dsFile*)calloc(sizeof(Lib3dsFile),1); + if (!file) { + return(0); + } + file->mesh_version=3; + file->master_scale=1.0f; + file->keyf_revision=5; + strcpy(file->name, "LIB3DS"); + + file->frames=100; + file->segment_from=0; + file->segment_to=100; + file->current_frame=0; + + return(file); +} + + +/*! + * Free a Lib3dsFile object and all of its resources. + * + * \param file The Lib3dsFile object to be freed. + * + * \ingroup file + */ +void +lib3ds_file_free(Lib3dsFile* file) +{ + ASSERT(file); + lib3ds_viewport_set_views(&file->viewport,0); + { + Lib3dsMaterial *p,*q; + + for (p=file->materials; p; p=q) { + q=p->next; + lib3ds_material_free(p); + } + file->materials=0; + } + { + Lib3dsCamera *p,*q; + + for (p=file->cameras; p; p=q) { + q=p->next; + lib3ds_camera_free(p); + } + file->cameras=0; + } + { + Lib3dsLight *p,*q; + + for (p=file->lights; p; p=q) { + q=p->next; + lib3ds_light_free(p); + } + file->lights=0; + } + { + Lib3dsMesh *p,*q; + + for (p=file->meshes; p; p=q) { + q=p->next; + lib3ds_mesh_free(p); + } + file->meshes=0; + } + { + Lib3dsNode *p,*q; + + for (p=file->nodes; p; p=q) { + q=p->next; + lib3ds_node_free(p); + } + } + free(file); +} + + +/*! + * Evaluate all of the nodes in this Lib3dsFile object. + * + * \param file The Lib3dsFile object to be evaluated. + * \param t time value, between 0. and file->frames + * + * \see lib3ds_node_eval + * + * \ingroup file + */ +void +lib3ds_file_eval(Lib3dsFile *file, Lib3dsFloat t) +{ + Lib3dsNode *p; + + for (p=file->nodes; p!=0; p=p->next) { + lib3ds_node_eval(p, t); + } +} + + +static Lib3dsBool +named_object_read(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + char name[64]; + Lib3dsWord chunk; + + if (!lib3ds_chunk_read_start(&c, LIB3DS_NAMED_OBJECT, io)) { + return(LIB3DS_FALSE); + } + if (!lib3ds_io_read_string(io, name, 64)) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_dump_info(" NAME=%s", name); + lib3ds_chunk_read_tell(&c, io); + + while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) { + switch (chunk) { + case LIB3DS_N_TRI_OBJECT: + { + Lib3dsMesh *mesh; + + mesh=lib3ds_mesh_new(name); + if (!mesh) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_mesh_read(mesh, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_mesh(file, mesh); + } + break; + case LIB3DS_N_CAMERA: + { + Lib3dsCamera *camera; + + camera=lib3ds_camera_new(name); + if (!camera) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_camera_read(camera, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_camera(file, camera); + } + break; + case LIB3DS_N_DIRECT_LIGHT: + { + Lib3dsLight *light; + + light=lib3ds_light_new(name); + if (!light) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_light_read(light, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_light(file, light); + } + break; + default: + lib3ds_chunk_unknown(chunk); + } + } + + lib3ds_chunk_read_end(&c, io); + return(LIB3DS_TRUE); +} + + +static Lib3dsBool +ambient_read(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + Lib3dsWord chunk; + Lib3dsBool have_lin=LIB3DS_FALSE; + + if (!lib3ds_chunk_read_start(&c, LIB3DS_AMBIENT_LIGHT, io)) { + return(LIB3DS_FALSE); + } + + while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) { + switch (chunk) { + case LIB3DS_LIN_COLOR_F: + { + int i; + for (i=0; i<3; ++i) { + file->ambient[i]=lib3ds_io_read_float(io); + } + } + have_lin=LIB3DS_TRUE; + break; + case LIB3DS_COLOR_F: + { + /* gamma corrected color chunk + replaced in 3ds R3 by LIN_COLOR_24 */ + if (!have_lin) { + int i; + for (i=0; i<3; ++i) { + file->ambient[i]=lib3ds_io_read_float(io); + } + } + } + break; + default: + lib3ds_chunk_unknown(chunk); + } + } + + lib3ds_chunk_read_end(&c, io); + return(LIB3DS_TRUE); +} + + +static Lib3dsBool +mdata_read(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + Lib3dsWord chunk; + + if (!lib3ds_chunk_read_start(&c, LIB3DS_MDATA, io)) { + return(LIB3DS_FALSE); + } + + while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) { + switch (chunk) { + case LIB3DS_MESH_VERSION: + { + file->mesh_version=lib3ds_io_read_intd(io); + } + break; + case LIB3DS_MASTER_SCALE: + { + file->master_scale=lib3ds_io_read_float(io); + } + break; + case LIB3DS_SHADOW_MAP_SIZE: + case LIB3DS_LO_SHADOW_BIAS: + case LIB3DS_HI_SHADOW_BIAS: + case LIB3DS_SHADOW_SAMPLES: + case LIB3DS_SHADOW_RANGE: + case LIB3DS_SHADOW_FILTER: + case LIB3DS_RAY_BIAS: + { + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_shadow_read(&file->shadow, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_VIEWPORT_LAYOUT: + case LIB3DS_DEFAULT_VIEW: + { + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_viewport_read(&file->viewport, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_O_CONSTS: + { + int i; + for (i=0; i<3; ++i) { + file->construction_plane[i]=lib3ds_io_read_float(io); + } + } + break; + case LIB3DS_AMBIENT_LIGHT: + { + lib3ds_chunk_read_reset(&c, io); + if (!ambient_read(file, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_BIT_MAP: + case LIB3DS_SOLID_BGND: + case LIB3DS_V_GRADIENT: + case LIB3DS_USE_BIT_MAP: + case LIB3DS_USE_SOLID_BGND: + case LIB3DS_USE_V_GRADIENT: + { + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_background_read(&file->background, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_FOG: + case LIB3DS_LAYER_FOG: + case LIB3DS_DISTANCE_CUE: + case LIB3DS_USE_FOG: + case LIB3DS_USE_LAYER_FOG: + case LIB3DS_USE_DISTANCE_CUE: + { + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_atmosphere_read(&file->atmosphere, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_MAT_ENTRY: + { + Lib3dsMaterial *material; + + material=lib3ds_material_new(); + if (!material) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_material_read(material, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_material(file, material); + } + break; + case LIB3DS_NAMED_OBJECT: + { + lib3ds_chunk_read_reset(&c, io); + if (!named_object_read(file, io)) { + return(LIB3DS_FALSE); + } + } + break; + default: + lib3ds_chunk_unknown(chunk); + } + } + + lib3ds_chunk_read_end(&c, io); + return(LIB3DS_TRUE); +} + + +static Lib3dsBool +kfdata_read(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + Lib3dsWord chunk; + + if (!lib3ds_chunk_read_start(&c, LIB3DS_KFDATA, io)) { + return(LIB3DS_FALSE); + } + + while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) { + switch (chunk) { + case LIB3DS_KFHDR: + { + file->keyf_revision=lib3ds_io_read_word(io); + if (!lib3ds_io_read_string(io, file->name, 12+1)) { + return(LIB3DS_FALSE); + } + file->frames=lib3ds_io_read_intd(io); + } + break; + case LIB3DS_KFSEG: + { + file->segment_from=lib3ds_io_read_intd(io); + file->segment_to=lib3ds_io_read_intd(io); + } + break; + case LIB3DS_KFCURTIME: + { + file->current_frame=lib3ds_io_read_intd(io); + } + break; + case LIB3DS_VIEWPORT_LAYOUT: + case LIB3DS_DEFAULT_VIEW: + { + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_viewport_read(&file->viewport_keyf, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_AMBIENT_NODE_TAG: + { + Lib3dsNode *node; + + node=lib3ds_node_new_ambient(); + if (!node) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_node_read(node, file, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_node(file, node); + } + break; + case LIB3DS_OBJECT_NODE_TAG: + { + Lib3dsNode *node; + + node=lib3ds_node_new_object(); + if (!node) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_node_read(node, file, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_node(file, node); + } + break; + case LIB3DS_CAMERA_NODE_TAG: + { + Lib3dsNode *node; + + node=lib3ds_node_new_camera(); + if (!node) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_node_read(node, file, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_node(file, node); + } + break; + case LIB3DS_TARGET_NODE_TAG: + { + Lib3dsNode *node; + + node=lib3ds_node_new_target(); + if (!node) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_node_read(node, file, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_node(file, node); + } + break; + case LIB3DS_LIGHT_NODE_TAG: + case LIB3DS_SPOTLIGHT_NODE_TAG: + { + Lib3dsNode *node; + + node=lib3ds_node_new_light(); + if (!node) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_node_read(node, file, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_node(file, node); + } + break; + case LIB3DS_L_TARGET_NODE_TAG: + { + Lib3dsNode *node; + + node=lib3ds_node_new_spot(); + if (!node) { + return(LIB3DS_FALSE); + } + lib3ds_chunk_read_reset(&c, io); + if (!lib3ds_node_read(node, file, io)) { + return(LIB3DS_FALSE); + } + lib3ds_file_insert_node(file, node); + } + break; + default: + lib3ds_chunk_unknown(chunk); + } + } + + lib3ds_chunk_read_end(&c, io); + return(LIB3DS_TRUE); +} + + +/*! + * Read 3ds file data into a Lib3dsFile object. + * + * \param file The Lib3dsFile object to be filled. + * \param io A Lib3dsIo object previously set up by the caller. + * + * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure. + * + * \ingroup file + */ +Lib3dsBool +lib3ds_file_read(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + Lib3dsWord chunk; + + if (!lib3ds_chunk_read_start(&c, 0, io)) { + return(LIB3DS_FALSE); + } + switch (c.chunk) { + case LIB3DS_MDATA: + { + lib3ds_chunk_read_reset(&c, io); + if (!mdata_read(file, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_M3DMAGIC: + case LIB3DS_MLIBMAGIC: + case LIB3DS_CMAGIC: + { + while ((chunk=lib3ds_chunk_read_next(&c, io))!=0) { + switch (chunk) { + case LIB3DS_M3D_VERSION: + { + file->mesh_version=lib3ds_io_read_dword(io); + } + break; + case LIB3DS_MDATA: + { + lib3ds_chunk_read_reset(&c, io); + if (!mdata_read(file, io)) { + return(LIB3DS_FALSE); + } + } + break; + case LIB3DS_KFDATA: + { + lib3ds_chunk_read_reset(&c, io); + if (!kfdata_read(file, io)) { + return(LIB3DS_FALSE); + } + } + break; + default: + lib3ds_chunk_unknown(chunk); + } + } + } + break; + default: + lib3ds_chunk_unknown(c.chunk); + return(LIB3DS_FALSE); + } + + lib3ds_chunk_read_end(&c, io); + return(LIB3DS_TRUE); +} + + +static Lib3dsBool +colorf_write(Lib3dsRgba rgb, Lib3dsIo *io) +{ + Lib3dsChunk c; + + c.chunk=LIB3DS_COLOR_F; + c.size=18; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_rgb(io, rgb); + + c.chunk=LIB3DS_LIN_COLOR_F; + c.size=18; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_rgb(io, rgb); + return(LIB3DS_TRUE); +} + + +static Lib3dsBool +mdata_write(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + + c.chunk=LIB3DS_MDATA; + if (!lib3ds_chunk_write_start(&c,io)) { + return(LIB3DS_FALSE); + } + + { /*---- LIB3DS_MESH_VERSION ----*/ + Lib3dsChunk c; + c.chunk=LIB3DS_MESH_VERSION; + c.size=10; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_intd(io, file->mesh_version); + } + { /*---- LIB3DS_MASTER_SCALE ----*/ + Lib3dsChunk c; + c.chunk=LIB3DS_MASTER_SCALE; + c.size=10; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_float(io, file->master_scale); + } + { /*---- LIB3DS_O_CONSTS ----*/ + int i; + for (i=0; i<3; ++i) { + if (fabs(file->construction_plane[i])>LIB3DS_EPSILON) { + break; + } + } + if (i<3) { + Lib3dsChunk c; + c.chunk=LIB3DS_O_CONSTS; + c.size=18; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_vector(io, file->construction_plane); + } + } + + { /*---- LIB3DS_AMBIENT_LIGHT ----*/ + int i; + for (i=0; i<3; ++i) { + if (fabs(file->ambient[i])>LIB3DS_EPSILON) { + break; + } + } + if (i<3) { + Lib3dsChunk c; + c.chunk=LIB3DS_AMBIENT_LIGHT; + c.size=42; + lib3ds_chunk_write(&c,io); + colorf_write(file->ambient,io); + } + } + lib3ds_background_write(&file->background, io); + lib3ds_atmosphere_write(&file->atmosphere, io); + lib3ds_shadow_write(&file->shadow, io); + lib3ds_viewport_write(&file->viewport, io); + { + Lib3dsMaterial *p; + for (p=file->materials; p!=0; p=p->next) { + if (!lib3ds_material_write(p,io)) { + return(LIB3DS_FALSE); + } + } + } + { + Lib3dsCamera *p; + Lib3dsChunk c; + + for (p=file->cameras; p!=0; p=p->next) { + c.chunk=LIB3DS_NAMED_OBJECT; + if (!lib3ds_chunk_write_start(&c,io)) { + return(LIB3DS_FALSE); + } + lib3ds_io_write_string(io, p->name); + lib3ds_camera_write(p,io); + if (!lib3ds_chunk_write_end(&c,io)) { + return(LIB3DS_FALSE); + } + } + } + { + Lib3dsLight *p; + Lib3dsChunk c; + + for (p=file->lights; p!=0; p=p->next) { + c.chunk=LIB3DS_NAMED_OBJECT; + if (!lib3ds_chunk_write_start(&c,io)) { + return(LIB3DS_FALSE); + } + lib3ds_io_write_string(io,p->name); + lib3ds_light_write(p,io); + if (!lib3ds_chunk_write_end(&c,io)) { + return(LIB3DS_FALSE); + } + } + } + { + Lib3dsMesh *p; + Lib3dsChunk c; + + for (p=file->meshes; p!=0; p=p->next) { + c.chunk=LIB3DS_NAMED_OBJECT; + if (!lib3ds_chunk_write_start(&c,io)) { + return(LIB3DS_FALSE); + } + lib3ds_io_write_string(io, p->name); + lib3ds_mesh_write(p,io); + if (!lib3ds_chunk_write_end(&c,io)) { + return(LIB3DS_FALSE); + } + } + } + + if (!lib3ds_chunk_write_end(&c,io)) { + return(LIB3DS_FALSE); + } + return(LIB3DS_TRUE); +} + + + +static Lib3dsBool +nodes_write(Lib3dsNode *node, Lib3dsFile *file, Lib3dsIo *io) +{ + { + Lib3dsNode *p; + for (p=node->childs; p!=0; p=p->next) { + if (!lib3ds_node_write(p, file, io)) { + return(LIB3DS_FALSE); + } + nodes_write(p, file, io); + } + } + return(LIB3DS_TRUE); +} + + +static Lib3dsBool +kfdata_write(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + + if (!file->nodes) { + return(LIB3DS_TRUE); + } + + c.chunk=LIB3DS_KFDATA; + if (!lib3ds_chunk_write_start(&c,io)) { + return(LIB3DS_FALSE); + } + + { /*---- LIB3DS_KFHDR ----*/ + Lib3dsChunk c; + c.chunk=LIB3DS_KFHDR; + c.size=6 + 2 + strlen(file->name)+1 +4; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_intw(io, file->keyf_revision); + lib3ds_io_write_string(io, file->name); + lib3ds_io_write_intd(io, file->frames); + } + { /*---- LIB3DS_KFSEG ----*/ + Lib3dsChunk c; + c.chunk=LIB3DS_KFSEG; + c.size=14; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_intd(io, file->segment_from); + lib3ds_io_write_intd(io, file->segment_to); + } + { /*---- LIB3DS_KFCURTIME ----*/ + Lib3dsChunk c; + c.chunk=LIB3DS_KFCURTIME; + c.size=10; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_intd(io, file->current_frame); + } + lib3ds_viewport_write(&file->viewport_keyf, io); + + { + Lib3dsNode *p; + for (p=file->nodes; p!=0; p=p->next) { + if (!lib3ds_node_write(p, file, io)) { + return(LIB3DS_FALSE); + } + if (!nodes_write(p, file, io)) { + return(LIB3DS_FALSE); + } + } + } + + if (!lib3ds_chunk_write_end(&c,io)) { + return(LIB3DS_FALSE); + } + return(LIB3DS_TRUE); +} + + +/*! + * Write 3ds file data from a Lib3dsFile object to a file. + * + * \param file The Lib3dsFile object to be written. + * \param io A Lib3dsIo object previously set up by the caller. + * + * \return LIB3DS_TRUE on success, LIB3DS_FALSE on failure. + * + * \ingroup file + */ +Lib3dsBool +lib3ds_file_write(Lib3dsFile *file, Lib3dsIo *io) +{ + Lib3dsChunk c; + + c.chunk=LIB3DS_M3DMAGIC; + if (!lib3ds_chunk_write_start(&c,io)) { + LIB3DS_ERROR_LOG; + return(LIB3DS_FALSE); + } + + { /*---- LIB3DS_M3D_VERSION ----*/ + Lib3dsChunk c; + + c.chunk=LIB3DS_M3D_VERSION; + c.size=10; + lib3ds_chunk_write(&c,io); + lib3ds_io_write_dword(io, file->mesh_version); + } + + if (!mdata_write(file, io)) { + return(LIB3DS_FALSE); + } + if (!kfdata_write(file, io)) { + return(LIB3DS_FALSE); + } + + if (!lib3ds_chunk_write_end(&c,io)) { + return(LIB3DS_FALSE); + } + return(LIB3DS_TRUE); +} + + +/*! + * Insert a new Lib3dsMaterial object into the materials list of + * a Lib3dsFile object. + * + * The new Lib3dsMaterial object is inserted into the materials list + * in alphabetic order by name. + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsMaterial object to be inserted into file->materials + * + * \ingroup file + */ +void +lib3ds_file_insert_material(Lib3dsFile *file, Lib3dsMaterial *material) +{ + Lib3dsMaterial *p,*q; + + ASSERT(file); + ASSERT(material); + ASSERT(!material->next); + + q=0; + for (p=file->materials; p!=0; p=p->next) { + if (strcmp(material->name, p->name)<0) { + break; + } + q=p; + } + if (!q) { + material->next=file->materials; + file->materials=material; + } + else { + material->next=q->next; + q->next=material; + } +} + + +/*! + * Remove a Lib3dsMaterial object from the materials list of + * a Lib3dsFile object. + * + * If the Lib3dsMaterial is not found in the materials list, nothing is + * done (except that an error log message may be generated.) + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsMaterial object to be removed from file->materials + * + * \ingroup file + */ +void +lib3ds_file_remove_material(Lib3dsFile *file, Lib3dsMaterial *material) +{ + Lib3dsMaterial *p,*q; + + ASSERT(file); + ASSERT(material); + ASSERT(file->materials); + for (p=0,q=file->materials; q; p=q,q=q->next) { + if (q==material) { + break; + } + } + if (!q) { + ASSERT(LIB3DS_FALSE); + return; + } + if (!p) { + file->materials=material->next; + } + else { + p->next=q->next; + } + material->next=0; +} + + +/*! + * Return a Lib3dsMaterial object by name. + * + * \param file Lib3dsFile object to be searched. + * \param name Name of the Lib3dsMaterial object to be searched for. + * + * \return A pointer to the named Lib3dsMaterial, or NULL if not found. + * + * \ingroup file + */ +Lib3dsMaterial* +lib3ds_file_material_by_name(Lib3dsFile *file, const char *name) +{ + Lib3dsMaterial *p; + + ASSERT(file); + for (p=file->materials; p!=0; p=p->next) { + if (strcmp(p->name,name)==0) { + return(p); + } + } + return(0); +} + + +/*! + * Dump all Lib3dsMaterial objects found in a Lib3dsFile object. + * + * \param file Lib3dsFile object to be dumped. + * + * \see lib3ds_material_dump + * + * \ingroup file + */ +void +lib3ds_file_dump_materials(Lib3dsFile *file) +{ + Lib3dsMaterial *p; + + ASSERT(file); + for (p=file->materials; p!=0; p=p->next) { + lib3ds_material_dump(p); + } +} + + +/*! + * Insert a new Lib3dsMesh object into the meshes list of + * a Lib3dsFile object. + * + * The new Lib3dsMesh object is inserted into the meshes list + * in alphabetic order by name. + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsMesh object to be inserted into file->meshes + * + * \ingroup file + */ +void +lib3ds_file_insert_mesh(Lib3dsFile *file, Lib3dsMesh *mesh) +{ + Lib3dsMesh *p,*q; + + ASSERT(file); + ASSERT(mesh); + ASSERT(!mesh->next); + + q=0; + for (p=file->meshes; p!=0; p=p->next) { + if (strcmp(mesh->name, p->name)<0) { + break; + } + q=p; + } + if (!q) { + mesh->next=file->meshes; + file->meshes=mesh; + } + else { + mesh->next=q->next; + q->next=mesh; + } +} + + +/*! + * Remove a Lib3dsMesh object from the meshes list of + * a Lib3dsFile object. + * + * If the Lib3dsMesh is not found in the meshes list, nothing is done + * (except that an error log message may be generated.) + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsMesh object to be removed from file->meshes + * + * \ingroup file + */ +void +lib3ds_file_remove_mesh(Lib3dsFile *file, Lib3dsMesh *mesh) +{ + Lib3dsMesh *p,*q; + + ASSERT(file); + ASSERT(mesh); + ASSERT(file->meshes); + for (p=0,q=file->meshes; q; p=q,q=q->next) { + if (q==mesh) { + break; + } + } + if (!q) { + ASSERT(LIB3DS_FALSE); + return; + } + if (!p) { + file->meshes=mesh->next; + } + else { + p->next=q->next; + } + mesh->next=0; +} + + +/*! + * Return a Lib3dsMesh object from a Lib3dsFile by name. + * + * \param file Lib3dsFile object to be searched. + * \param name Name of the Lib3dsMesh object to be searched for. + * + * \return A pointer to the named Lib3dsMesh, or NULL if not found. + * + * \ingroup file + */ +Lib3dsMesh* +lib3ds_file_mesh_by_name(Lib3dsFile *file, const char *name) +{ + Lib3dsMesh *p; + + ASSERT(file); + for (p=file->meshes; p!=0; p=p->next) { + if (strcmp(p->name,name)==0) { + return(p); + } + } + return(0); +} + + +/*! + * Dump all Lib3dsMesh objects found in a Lib3dsFile object. + * + * \param file Lib3dsFile object to be dumped. + * + * \see lib3ds_mesh_dump + * + * \ingroup file + */ +void +lib3ds_file_dump_meshes(Lib3dsFile *file) +{ + Lib3dsMesh *p; + + ASSERT(file); + for (p=file->meshes; p!=0; p=p->next) { + lib3ds_mesh_dump(p); + } +} + + +static void +dump_instances(Lib3dsNode *node, const char* parent) +{ + Lib3dsNode *p; + char name[255]; + + ASSERT(node); + ASSERT(parent); + strcpy(name, parent); + strcat(name, "."); + strcat(name, node->name); + if (node->type==LIB3DS_OBJECT_NODE) { + printf(" %s : %s\n", name, node->data.object.instance); + } + for (p=node->childs; p!=0; p=p->next) { + dump_instances(p, parent); + } +} + + +/*! + * Dump all Lib3dsNode object names found in a Lib3dsFile object. + * + * For each node of type OBJECT_NODE, its name and data.object.instance + * fields are printed to stdout. Consider using lib3ds_file_dump_nodes() + * instead, as that function dumps more information. + * + * Nodes are dumped recursively. + * + * \param file Lib3dsFile object to be dumped. + * + * \see lib3ds_file_dump_nodes + * + * \ingroup file + */ +void +lib3ds_file_dump_instances(Lib3dsFile *file) +{ + Lib3dsNode *p; + + ASSERT(file); + for (p=file->nodes; p!=0; p=p->next) { + dump_instances(p,""); + } +} + + +/*! + * Insert a new Lib3dsCamera object into the cameras list of + * a Lib3dsFile object. + * + * The new Lib3dsCamera object is inserted into the cameras list + * in alphabetic order by name. + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsCamera object to be inserted into file->cameras + * + * \ingroup file + */ +void +lib3ds_file_insert_camera(Lib3dsFile *file, Lib3dsCamera *camera) +{ + Lib3dsCamera *p,*q; + + ASSERT(file); + ASSERT(camera); + ASSERT(!camera->next); + + q=0; + for (p=file->cameras; p!=0; p=p->next) { + if (strcmp(camera->name, p->name)<0) { + break; + } + q=p; + } + if (!q) { + camera->next=file->cameras; + file->cameras=camera; + } + else { + camera->next=q->next; + q->next=camera; + } +} + + +/*! + * Remove a Lib3dsCamera object from the cameras list of + * a Lib3dsFile object. + * + * If the Lib3dsCamera is not found in the cameras list, nothing is done + * (except that an error log message may be generated.) + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsCamera object to be removed from file->cameras + * + * \ingroup file + */ +void +lib3ds_file_remove_camera(Lib3dsFile *file, Lib3dsCamera *camera) +{ + Lib3dsCamera *p,*q; + + ASSERT(file); + ASSERT(camera); + ASSERT(file->cameras); + for (p=0,q=file->cameras; q; p=q,q=q->next) { + if (q==camera) { + break; + } + } + if (!q) { + ASSERT(LIB3DS_FALSE); + return; + } + if (!p) { + file->cameras=camera->next; + } + else { + p->next=q->next; + } + camera->next=0; +} + + +/*! + * Return a Lib3dsCamera object from a Lib3dsFile by name. + * + * \param file Lib3dsFile object to be searched. + * \param name Name of the Lib3dsCamera object to be searched for. + * + * \return A pointer to the named Lib3dsCamera, or NULL if not found. + * + * \ingroup file + */ +Lib3dsCamera* +lib3ds_file_camera_by_name(Lib3dsFile *file, const char *name) +{ + Lib3dsCamera *p; + + ASSERT(file); + for (p=file->cameras; p!=0; p=p->next) { + if (strcmp(p->name,name)==0) { + return(p); + } + } + return(0); +} + + +/*! + * Dump all Lib3dsCamera objects found in a Lib3dsFile object. + * + * \param file Lib3dsFile object to be dumped. + * + * \see lib3ds_camera_dump + * + * \ingroup file + */ +void +lib3ds_file_dump_cameras(Lib3dsFile *file) +{ + Lib3dsCamera *p; + + ASSERT(file); + for (p=file->cameras; p!=0; p=p->next) { + lib3ds_camera_dump(p); + } +} + + +/*! + * Insert a new Lib3dsLight object into the lights list of + * a Lib3dsFile object. + * + * The new Lib3dsLight object is inserted into the lights list + * in alphabetic order by name. + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsLight object to be inserted into file->lights + * + * \ingroup file + */ +void +lib3ds_file_insert_light(Lib3dsFile *file, Lib3dsLight *light) +{ + Lib3dsLight *p,*q; + + ASSERT(file); + ASSERT(light); + ASSERT(!light->next); + + q=0; + for (p=file->lights; p!=0; p=p->next) { + if (strcmp(light->name, p->name)<0) { + break; + } + q=p; + } + if (!q) { + light->next=file->lights; + file->lights=light; + } + else { + light->next=q->next; + q->next=light; + } +} + + +/*! + * Remove a Lib3dsLight object from the lights list of + * a Lib3dsFile object. + * + * If the Lib3dsLight is not found in the lights list, nothing is done + * (except that an error log message may be generated.) + * + * \param file The Lib3dsFile object to be modified. + * \param material The Lib3dsLight object to be removed from file->lights + * + * \ingroup file + */ +void +lib3ds_file_remove_light(Lib3dsFile *file, Lib3dsLight *light) +{ + Lib3dsLight *p,*q; + + ASSERT(file); + ASSERT(light); + ASSERT(file->lights); + for (p=0,q=file->lights; q; p=q,q=q->next) { + if (q==light) { + break; + } + } + if (!q) { + ASSERT(LIB3DS_FALSE); + return; + } + if (!p) { + file->lights=light->next; + } + else { + p->next=q->next; + } + light->next=0; +} + + +/*! + * Return a Lib3dsLight object from a Lib3dsFile by name. + * + * \param file Lib3dsFile object to be searched. + * \param name Name of the Lib3dsLight object to be searched for. + * + * \return A pointer to the named Lib3dsLight, or NULL if not found. + * + * \ingroup file + */ +Lib3dsLight* +lib3ds_file_light_by_name(Lib3dsFile *file, const char *name) +{ + Lib3dsLight *p; + + ASSERT(file); + for (p=file->lights; p!=0; p=p->next) { + if (strcmp(p->name,name)==0) { + return(p); + } + } + return(0); +} + + +/*! + * Dump all Lib3dsLight objects found in a Lib3dsFile object. + * + * \param file Lib3dsFile object to be dumped. + * + * \see lib3ds_light_dump + * + * \ingroup file + */ +void +lib3ds_file_dump_lights(Lib3dsFile *file) +{ + Lib3dsLight *p; + + ASSERT(file); + for (p=file->lights; p!=0; p=p->next) { + lib3ds_light_dump(p); + } +} + + +/*! + * Compute the bounding box for Lib3dsFile objects. + * + * This function computes the bounding box for all meshes + * in the Lib3dsFile object. Cameras and lights are not included. + * + * \param file The Lib3dsFile object to be examined. + * \param min Returned minimum x,y,z values. + * \param max Returned maximum x,y,z values. + * + * \ingroup file + */ +void +lib3ds_object_bounding_box(Lib3dsFile *file, Lib3dsVector min, Lib3dsVector max) +{ + { + Lib3dsVector lmin, lmax; + Lib3dsMesh *p=file->meshes; + + if (p) { + lib3ds_mesh_bounding_box(p, min, max); + p = p->next; + } + while (p) { + lib3ds_mesh_bounding_box(p, lmin, lmax); + lib3ds_vector_min(min, lmin); + lib3ds_vector_max(max, lmax); + p=p->next; + } + } +} + + +/*! + * Compute the bounding box for a Lib3dsFile. + * + * This function computes the bounding box for all meshes, cameras, + * and lights in the Lib3dsFile object. + * + * \param file The Lib3dsFile object to be examined. + * \param min Returned minimum x,y,z values. + * \param max Returned maximum x,y,z values. + * + * \ingroup file + */ +void +lib3ds_file_bounding_box(Lib3dsFile *file, Lib3dsVector min, Lib3dsVector max) +{ + Lib3dsBool init=LIB3DS_FALSE; + + { + Lib3dsVector lmin, lmax; + Lib3dsMesh *p=file->meshes; + + if (!init && p) { + init = LIB3DS_TRUE; + lib3ds_mesh_bounding_box(p, min, max); + p = p->next; + } + while (p) { + lib3ds_mesh_bounding_box(p, lmin, lmax); + lib3ds_vector_min(min, lmin); + lib3ds_vector_max(max, lmax); + p=p->next; + } + } + { + Lib3dsCamera *p=file->cameras; + if (!init && p) { + init = LIB3DS_TRUE; + lib3ds_vector_copy(min, p->position); + lib3ds_vector_copy(max, p->position); + } + + while (p) { + lib3ds_vector_min(min, p->position); + lib3ds_vector_max(max, p->position); + lib3ds_vector_min(min, p->target); + lib3ds_vector_max(max, p->target); + p=p->next; + } + } + { + Lib3dsLight *p=file->lights; + if (!init && p) { + init = LIB3DS_TRUE; + lib3ds_vector_copy(min, p->position); + lib3ds_vector_copy(max, p->position); + } + + while (p) { + lib3ds_vector_min(min, p->position); + lib3ds_vector_max(max, p->position); + if (p->spot_light) { + lib3ds_vector_min(min, p->spot); + lib3ds_vector_max(max, p->spot); + } + p=p->next; + } + } +} + + +/*! + * Return a node object by name and type. + * + * This function performs a recursive search for the specified node. + * Both name and type must match. + * + * \param file The Lib3dsFile to be searched. + * \param name The target node name. + * \param type The target node type + * + * \return A pointer to the first matching node, or NULL if not found. + * + * \see lib3ds_node_by_name + * + * \ingroup file + */ +Lib3dsNode* +lib3ds_file_node_by_name(Lib3dsFile *file, const char* name, Lib3dsNodeTypes type) +{ + Lib3dsNode *p,*q; + + ASSERT(file); + for (p=file->nodes; p!=0; p=p->next) { + if ((p->type==type) && (strcmp(p->name, name)==0)) { + return(p); + } + q=lib3ds_node_by_name(p, name, type); + if (q) { + return(q); + } + } + return(0); +} + + +/*! + * Return a node object by id. + * + * This function performs a recursive search for the specified node. + * + * \param file The Lib3dsFile to be searched. + * \param node_id The target node id. + * + * \return A pointer to the first matching node, or NULL if not found. + * + * \see lib3ds_node_by_id + * + * \ingroup file + */ +Lib3dsNode* +lib3ds_file_node_by_id(Lib3dsFile *file, Lib3dsWord node_id) +{ + Lib3dsNode *p,*q; + + ASSERT(file); + for (p=file->nodes; p!=0; p=p->next) { + if (p->node_id==node_id) { + return(p); + } + q=lib3ds_node_by_id(p, node_id); + if (q) { + return(q); + } + } + return(0); +} + + +/*! + * Insert a new node into a Lib3dsFile object. + * + * If the node's parent_id structure is not LIB3DS_NO_PARENT and the + * specified parent is found inside the Lib3dsFile object, then the + * node is inserted as a child of that parent. If the parent_id + * structure is LIB3DS_NO_PARENT or the specified parent is not found, + * then the node is inserted at the top level. + * + * Node is inserted in alphabetic order by name. + * + * Finally, if any other top-level nodes in file specify this node as + * their parent, they are relocated as a child of this node. + * + * \param file The Lib3dsFile object to be modified. + * \param node The node to be inserted into file + * + * \ingroup file + */ +void +lib3ds_file_insert_node(Lib3dsFile *file, Lib3dsNode *node) +{ + Lib3dsNode *parent,*p,*n; + + ASSERT(node); + ASSERT(!node->next); + ASSERT(!node->parent); + + parent=0; + if (node->parent_id!=LIB3DS_NO_PARENT) { + parent=lib3ds_file_node_by_id(file, node->parent_id); + } + node->parent=parent; + + if (!parent) { + for (p=0,n=file->nodes; n!=0; p=n,n=n->next) { + if (strcmp(n->name, node->name)>0) { + break; + } + } + if (!p) { + node->next=file->nodes; + file->nodes=node; + } + else { + node->next=p->next; + p->next=node; + } + } + else { + for (p=0,n=parent->childs; n!=0; p=n,n=n->next) { + if (strcmp(n->name, node->name)>0) { + break; + } + } + if (!p) { + node->next=parent->childs; + parent->childs=node; + } + else { + node->next=p->next; + p->next=node; + } + } + + if (node->node_id!=LIB3DS_NO_PARENT) { + for (n=file->nodes; n!=0; n=p) { + p=n->next; + if (n->parent_id==node->node_id) { + lib3ds_file_remove_node(file, n); + lib3ds_file_insert_node(file, n); + } + } + } +} + + +/*! + * Remove a node from the a Lib3dsFile object. + * + * \param file The Lib3dsFile object to be modified. + * \param node The Lib3dsNode object to be removed from file + * + * \return LIB3DS_TRUE on success, LIB3DS_FALSE if node is not found in file + * + * \ingroup file + */ +Lib3dsBool +lib3ds_file_remove_node(Lib3dsFile *file, Lib3dsNode *node) +{ + Lib3dsNode *p,*n; + + if (node->parent) { + for (p=0,n=node->parent->childs; n; p=n,n=n->next) { + if (n==node) { + break; + } + } + if (!n) { + return(LIB3DS_FALSE); + } + + if (!p) { + node->parent->childs=n->next; + } + else { + p->next=n->next; + } + } + else { + for (p=0,n=file->nodes; n; p=n,n=n->next) { + if (n==node) { + break; + } + } + if (!n) { + return(LIB3DS_FALSE); + } + + if (!p) { + file->nodes=n->next; + } + else { + p->next=n->next; + } + } + return(LIB3DS_TRUE); +} + + +/*! + * Dump all node objects found in a Lib3dsFile object. + * + * Nodes are dumped recursively. + * + * \param file Lib3dsFile object to be dumped. + * + * \see lib3ds_node_dump + * + * \ingroup file + */ +void +lib3ds_file_dump_nodes(Lib3dsFile *file) +{ + Lib3dsNode *p; + + ASSERT(file); + for (p=file->nodes; p!=0; p=p->next) { + lib3ds_node_dump(p,1); + } +} + + +/*! + +\typedef Lib3dsFile + \ingroup file + \sa _Lib3dsFile + +*/ + + + +/* Programming trick to force users to compile their source code against the + * correct headers. The symbol lib3ds_version1_3 will be defined iff the users + * compile with the current version of + */ + +/* +extern int lib3ds_version1_3; +static const int *lib3ds_version = &lib3ds_version1_3; +*/