Panda3D
|
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 }