Panda3D

eggData.cxx

00001 // Filename: eggData.cxx
00002 // Created by:  drose (20Jan99)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "eggData.h"
00016 #include "eggCoordinateSystem.h"
00017 #include "eggTextureCollection.h"
00018 #include "eggMaterialCollection.h"
00019 #include "eggComment.h"
00020 #include "eggPoolUniquifier.h"
00021 #include "config_egg.h"
00022 #include "config_util.h"
00023 #include "config_express.h"
00024 #include "string_utils.h"
00025 #include "dSearchPath.h"
00026 #include "virtualFileSystem.h"
00027 #include "lightMutexHolder.h"
00028 #include "zStream.h"
00029 
00030 extern int eggyyparse();
00031 #include "parserDefs.h"
00032 #include "lexerDefs.h"
00033 
00034 TypeHandle EggData::_type_handle;
00035 
00036 ////////////////////////////////////////////////////////////////////
00037 //     Function: EggData::resolve_egg_filename
00038 //       Access: Public, Static
00039 //  Description: Looks for the indicated filename, first along the
00040 //               indicated searchpath, and then along the model_path.
00041 //               If found, updates the filename to the full path and
00042 //               returns true; otherwise, returns false.
00043 ////////////////////////////////////////////////////////////////////
00044 bool EggData::
00045 resolve_egg_filename(Filename &egg_filename, const DSearchPath &searchpath) {
00046   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00047   
00048   if (egg_filename.is_fully_qualified() && vfs->exists(egg_filename)) {
00049     return true;
00050   }
00051   
00052   vfs->resolve_filename(egg_filename, searchpath, "egg") ||
00053     vfs->resolve_filename(egg_filename, get_model_path(), "egg");
00054   
00055   return vfs->exists(egg_filename);
00056 }
00057 
00058 ////////////////////////////////////////////////////////////////////
00059 //     Function: EggData::read
00060 //       Access: Public
00061 //  Description: Opens the indicated filename and reads the egg data
00062 //               contents from it.  Returns true if the file was
00063 //               successfully opened and read, false if there were
00064 //               some errors, in which case the data may be partially
00065 //               read.
00066 //
00067 //               error is the output stream to which to write error
00068 //               messages.
00069 ////////////////////////////////////////////////////////////////////
00070 bool EggData::
00071 read(Filename filename, string display_name) {
00072   filename.set_text();
00073   set_egg_filename(filename);
00074 
00075   if (display_name.empty()) {
00076     display_name = filename;
00077   }
00078 
00079   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00080     
00081   istream *file = vfs->open_read_file(filename, true);
00082   if (file == (istream *)NULL) {
00083     egg_cat.error() << "Unable to open " << display_name << "\n";
00084     return false;
00085   }
00086   
00087   egg_cat.info()
00088     << "Reading " << display_name << "\n";
00089   
00090   bool read_ok = read(*file);
00091   vfs->close_read_file(file);
00092   return read_ok;
00093 }
00094 
00095 
00096 ////////////////////////////////////////////////////////////////////
00097 //     Function: EggData::read
00098 //       Access: Public
00099 //  Description: Parses the egg syntax contained in the indicated
00100 //               input stream.  Returns true if the stream was a
00101 //               completely valid egg file, false if there were some
00102 //               errors, in which case the data may be partially read.
00103 //
00104 //               Before you call this routine, you should probably
00105 //               call set_egg_filename() to set the name of the egg
00106 //               file we're processing, if at all possible.  If there
00107 //               is no such filename, you may set it to the empty
00108 //               string.
00109 ////////////////////////////////////////////////////////////////////
00110 bool EggData::
00111 read(istream &in) {
00112   // First, dispense with any children we had previously.  We will
00113   // replace them with the new data.
00114   clear();
00115 
00116   // Create a temporary EggData structure to read into.  We initialize
00117   // it with a copy of ourselves, so that it will get our _coordsys
00118   // value, if the user set it.
00119   PT(EggData) data = new EggData(*this);
00120 
00121   int error_count;
00122   {
00123     LightMutexHolder holder(egg_lock);
00124     egg_init_parser(in, get_egg_filename(), data, data);
00125     eggyyparse();
00126     egg_cleanup_parser();
00127     error_count = egg_error_count();
00128   }
00129 
00130   data->post_read();
00131 
00132   steal_children(*data);
00133   (*this) = *data;
00134 
00135   return (error_count == 0);
00136 }
00137 
00138 ////////////////////////////////////////////////////////////////////
00139 //     Function: EggData::merge
00140 //       Access: Public
00141 //  Description: Appends the other egg structure to the end of this
00142 //               one.  The other egg structure is invalidated.
00143 ////////////////////////////////////////////////////////////////////
00144 void EggData::
00145 merge(EggData &other) {
00146   if (get_coordinate_system() == CS_default) {
00147     // If we haven't specified a coordinate system yet, we inherit the
00148     // other one's.
00149     set_coordinate_system(other.get_coordinate_system());
00150 
00151   } else {
00152     // Otherwise, the other one is forced into our coordinate system
00153     // before we merge.
00154     other.set_coordinate_system(get_coordinate_system());
00155   }
00156   steal_children(other);
00157 }
00158 
00159 
00160 ////////////////////////////////////////////////////////////////////
00161 //     Function: EggData::load_externals
00162 //       Access: Public
00163 //  Description: Loads up all the egg files referenced by <File>
00164 //               entries within the egg structure, and inserts their
00165 //               contents in place of the <File> entries.  Searches
00166 //               for files in the searchpath, if not found directly,
00167 //               and writes error messages to the indicated output
00168 //               stream.  Returns true if all externals were loaded
00169 //               successfully, false otherwise.
00170 ////////////////////////////////////////////////////////////////////
00171 bool EggData::
00172 load_externals(const DSearchPath &searchpath) {
00173   return
00174     r_load_externals(searchpath, get_coordinate_system(), NULL);
00175 }
00176 
00177 ////////////////////////////////////////////////////////////////////
00178 //     Function: EggData::load_externals
00179 //       Access: Public
00180 //  Description: Loads up all the egg files referenced by <File>
00181 //               entries within the egg structure, and inserts their
00182 //               contents in place of the <File> entries.  Searches
00183 //               for files in the searchpath, if not found directly,
00184 //               and writes error messages to the indicated output
00185 //               stream.  Returns true if all externals were loaded
00186 //               successfully, false otherwise.
00187 ////////////////////////////////////////////////////////////////////
00188 bool EggData::
00189 load_externals(const DSearchPath &searchpath, BamCacheRecord *record) {
00190   return
00191     r_load_externals(searchpath, get_coordinate_system(), record);
00192 }
00193 
00194 ////////////////////////////////////////////////////////////////////
00195 //     Function: EggData::collapse_equivalent_textures
00196 //       Access: Public
00197 //  Description: Removes duplicate references to the same texture
00198 //               image with the same properties.  Considers two
00199 //               texture references with identical properties, but
00200 //               different tref names, to be equivalent, and collapses
00201 //               them, choosing one tref name to keep arbitrarily.
00202 //               Returns the number of textures removed.
00203 ////////////////////////////////////////////////////////////////////
00204 int EggData::
00205 collapse_equivalent_textures() {
00206   EggTextureCollection textures;
00207   textures.find_used_textures(this);
00208   return
00209     textures.collapse_equivalent_textures(~EggTexture::E_tref_name, this);
00210 }
00211 
00212 ////////////////////////////////////////////////////////////////////
00213 //     Function: EggData::collapse_equivalent_materials
00214 //       Access: Public
00215 //  Description: Removes duplicate references to the same material
00216 //               with the same properties.  Considers two material
00217 //               references with identical properties, but different
00218 //               mref names, to be equivalent, and collapses them,
00219 //               choosing one mref name to keep arbitrarily.  Returns
00220 //               the number of materials removed.
00221 ////////////////////////////////////////////////////////////////////
00222 int EggData::
00223 collapse_equivalent_materials() {
00224   EggMaterialCollection materials;
00225   materials.find_used_materials(this);
00226   return
00227     materials.collapse_equivalent_materials(~EggMaterial::E_mref_name, this);
00228 }
00229 
00230 ////////////////////////////////////////////////////////////////////
00231 //     Function: EggData::write_egg
00232 //       Access: Public
00233 //  Description: The main interface for writing complete egg files.
00234 ////////////////////////////////////////////////////////////////////
00235 bool EggData::
00236 write_egg(Filename filename) {
00237   filename.unlink();
00238   filename.set_text();
00239 
00240 #ifdef HAVE_ZLIB
00241   bool pz_file = false;
00242   if (filename.get_extension() == "pz") {
00243     // The filename ends in .pz, which means to automatically compress
00244     // the egg file that we write.
00245     pz_file = true;
00246     filename.set_binary();
00247   }
00248 #endif  // HAVE_ZLIB
00249 
00250   pofstream file;
00251   if (!filename.open_write(file)) {
00252     egg_cat.error() << "Unable to open " << filename << " for writing.\n";
00253     return false;
00254   }
00255 
00256 #ifdef HAVE_ZLIB
00257   if (pz_file) {
00258     OCompressStream compressor(&file, false);
00259     return write_egg(compressor);
00260   }
00261 #endif  // HAVE_ZLIB
00262 
00263   return write_egg(file);
00264 }
00265 
00266 ////////////////////////////////////////////////////////////////////
00267 //     Function: EggData::write_egg
00268 //       Access: Public
00269 //  Description: The main interface for writing complete egg files.
00270 ////////////////////////////////////////////////////////////////////
00271 bool EggData::
00272 write_egg(ostream &out) {
00273   pre_write();
00274   write(out, 0);
00275   return true;
00276 }
00277 
00278 
00279 ////////////////////////////////////////////////////////////////////
00280 //     Function: EggData::set_coordinate_system
00281 //       Access: Public
00282 //  Description: Changes the coordinate system of the EggData.  If the
00283 //               coordinate system was previously different, this may
00284 //               result in a conversion of the data.
00285 ////////////////////////////////////////////////////////////////////
00286 void EggData::
00287 set_coordinate_system(CoordinateSystem new_coordsys) {
00288   if (new_coordsys == CS_default) {
00289     new_coordsys = get_default_coordinate_system();
00290   }
00291   if (new_coordsys != _coordsys &&
00292       (_coordsys != CS_default && _coordsys != CS_invalid)) {
00293     // Time to convert the data.
00294     LMatrix4d mat = LMatrix4d::convert_mat(_coordsys, new_coordsys);
00295     LMatrix4d inv = LMatrix4d::convert_mat(new_coordsys, _coordsys);
00296 
00297     r_transform(mat, inv, new_coordsys);
00298     r_transform_vertices(mat);
00299 
00300     // Now we have to update the under_flags to ensure that all the
00301     // cached relative matrices are correct.
00302     update_under(0);
00303   }
00304 
00305   _coordsys = new_coordsys;
00306 }
00307 
00308 ////////////////////////////////////////////////////////////////////
00309 //     Function: EggData::write
00310 //       Access: Protected, Virtual
00311 //  Description: Writes the egg data out to the indicated output
00312 //               stream.
00313 ////////////////////////////////////////////////////////////////////
00314 void EggData::
00315 write(ostream &out, int indent_level) const {
00316   PT(EggCoordinateSystem) ecs = new EggCoordinateSystem(_coordsys);
00317   ecs->write(out, indent_level);
00318   EggGroupNode::write(out, indent_level);
00319   out << flush;
00320 }
00321 
00322 
00323 ////////////////////////////////////////////////////////////////////
00324 //     Function: EggData::post_read
00325 //       Access: Private
00326 //  Description: Does whatever processing is appropriate after reading
00327 //               the data in from an egg file.
00328 ////////////////////////////////////////////////////////////////////
00329 void EggData::
00330 post_read() {
00331   CoordinateSystem old_coordsys = _coordsys;
00332   _coordsys = find_coordsys_entry();
00333 
00334   if (_coordsys == CS_default) {
00335     // If the egg file didn't contain a <CoordinateSystem> entry,
00336     // assume it's Y-up, by convention.
00337     _coordsys = CS_yup_right;
00338 
00339   } else if (_coordsys == CS_invalid) {
00340     egg_cat.warning()
00341       << "Contradictory <CoordinateSystem> entries encountered.\n";
00342     _coordsys = CS_yup_right;
00343   }
00344 
00345   r_mark_coordsys(_coordsys);
00346 
00347   if (old_coordsys != CS_default) {
00348     // Now if we had a previous definition, enforce it.  This might
00349     // convert the data to the given coordinate system.
00350     set_coordinate_system(old_coordsys);
00351   }
00352 
00353   // Fill this in before we automatically resolve pathnames.
00354   _had_absolute_pathnames = has_absolute_pathnames();
00355 
00356   if (get_auto_resolve_externals()) {
00357     // Resolve filenames that are relative to the egg file.
00358     DSearchPath dir;
00359     dir.append_directory(get_egg_filename().get_dirname());
00360     resolve_filenames(dir);
00361   }
00362 }
00363 
00364 ////////////////////////////////////////////////////////////////////
00365 //     Function: EggData::pre_write
00366 //       Access: Private
00367 //  Description: Does whatever processing is appropriate just before
00368 //               writing the data out to an egg file.  This includes
00369 //               verifying that vertex pool names are unique, etc.
00370 ////////////////////////////////////////////////////////////////////
00371 void EggData::
00372 pre_write() {
00373   // Pull out all of the texture definitions in the file and massage
00374   // them a bit.
00375   EggTextureCollection textures;
00376   textures.extract_textures(this);
00377 
00378   // Remove any textures that aren't being used.
00379   textures.remove_unused_textures(this);
00380 
00381   // Collapse out any textures that are completely equivalent.  For
00382   // this purpose, we consider two textures with identical properties
00383   // but different tref names to be different.
00384   textures.collapse_equivalent_textures(~0, this);
00385 
00386   // Make sure all of the textures have unique TRef names.
00387   textures.uniquify_trefs();
00388   textures.sort_by_tref();
00389 
00390   // Do the same thing with the materials.
00391   EggMaterialCollection materials;
00392   materials.extract_materials(this);
00393   materials.remove_unused_materials(this);
00394   materials.collapse_equivalent_materials(~0, this);
00395   materials.uniquify_mrefs();
00396   materials.sort_by_mref();
00397 
00398   // Now put them all back at the head of the file, after any initial
00399   // comment records.
00400   iterator ci = begin();
00401   while (ci != end() && (*ci)->is_of_type(EggComment::get_class_type())) {
00402     ++ci;
00403   }
00404   textures.insert_textures(this, ci);
00405   materials.insert_materials(this, ci);
00406 
00407   // Also make sure that the vertex pools are uniquely named.  This
00408   // also checks textures and materials, which is kind of redundant
00409   // since we just did that, but we don't mind.
00410   EggPoolUniquifier pu;
00411   pu.uniquify(this);
00412 }
 All Classes Functions Variables Enumerations