Panda3D
|
00001 // Filename: eggReader.cxx 00002 // Created by: drose (14Feb00) 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 "eggReader.h" 00016 00017 #include "pnmImage.h" 00018 #include "config_util.h" 00019 #include "eggTextureCollection.h" 00020 #include "eggGroup.h" 00021 #include "eggGroupNode.h" 00022 #include "eggSwitchCondition.h" 00023 #include "string_utils.h" 00024 #include "dcast.h" 00025 00026 //////////////////////////////////////////////////////////////////// 00027 // Function: EggReader::Constructor 00028 // Access: Public 00029 // Description: 00030 //////////////////////////////////////////////////////////////////// 00031 EggReader:: 00032 EggReader() { 00033 clear_runlines(); 00034 add_runline("[opts] input.egg"); 00035 00036 redescribe_option 00037 ("cs", 00038 "Specify the coordinate system to operate in. This may be " 00039 " one of 'y-up', 'z-up', 'y-up-left', or 'z-up-left'. The default " 00040 "is the coordinate system of the input egg file."); 00041 00042 add_option 00043 ("f", "", 80, 00044 "Force complete loading: load up the egg file along with all of its " 00045 "external references.", 00046 &EggReader::dispatch_none, &_force_complete); 00047 00048 add_option 00049 ("noabs", "", 0, 00050 "Don't allow the input egg file to have absolute pathnames. " 00051 "If it does, abort with an error. This option is designed to help " 00052 "detect errors when populating or building a standalone model tree, " 00053 "which should be self-contained and include only relative pathnames.", 00054 &EggReader::dispatch_none, &_noabs); 00055 00056 _tex_type = (PNMFileType *)NULL; 00057 _delod = -1.0; 00058 00059 _got_tex_dirname = false; 00060 _got_tex_extension = false; 00061 } 00062 00063 //////////////////////////////////////////////////////////////////// 00064 // Function: EggRead::add_texture_options 00065 // Access: Public 00066 // Description: Adds -td, -te, etc. as valid options for this 00067 // program. If the user specifies one of the options on 00068 // the command line, the textures will be copied and 00069 // converted as each egg file is read. 00070 // 00071 // Note that if you call this function to add these 00072 // options, you must call do_reader_options() at the 00073 // appropriate point before or during processing to 00074 // execute the options if the user specified them. 00075 //////////////////////////////////////////////////////////////////// 00076 void EggReader:: 00077 add_texture_options() { 00078 add_option 00079 ("td", "dirname", 40, 00080 "Copy textures to the indicated directory. The copy is performed " 00081 "only if the destination file does not exist or is older than the " 00082 "source file.", 00083 &EggReader::dispatch_filename, &_got_tex_dirname, &_tex_dirname); 00084 00085 add_option 00086 ("te", "ext", 40, 00087 "Rename textures to have the indicated extension. This also " 00088 "automatically copies them to the new filename (possibly in a " 00089 "different directory if -td is also specified), and may implicitly " 00090 "convert to a different image format according to the extension.", 00091 &EggReader::dispatch_string, &_got_tex_extension, &_tex_extension); 00092 00093 add_option 00094 ("tt", "type", 40, 00095 "Explicitly specifies the image format to convert textures to " 00096 "when copying them via -td or -te. Normally, this is unnecessary as " 00097 "the image format can be determined by the extension, but sometimes " 00098 "the extension is insufficient to unambiguously specify an image " 00099 "type.", 00100 &EggReader::dispatch_image_type, NULL, &_tex_type); 00101 } 00102 00103 //////////////////////////////////////////////////////////////////// 00104 // Function: EggRead::add_delod_options 00105 // Access: Public 00106 // Description: Adds -delod as a valid option for this program. 00107 // 00108 // Note that if you call this function to add these 00109 // options, you must call do_reader_options() at the 00110 // appropriate point before or during processing to 00111 // execute the options if the user specified them. 00112 //////////////////////////////////////////////////////////////////// 00113 void EggReader:: 00114 add_delod_options(double default_delod) { 00115 _delod = default_delod; 00116 00117 if (default_delod < 0) { 00118 add_option 00119 ("delod", "dist", 40, 00120 "Eliminate LOD's by choosing the level that would be appropriate for " 00121 "a camera at the indicated fixed distance from each LOD. " 00122 "Use -delod -1 to keep all the LOD's as they are, which is " 00123 "the default.\n", 00124 &EggReader::dispatch_double, NULL, &_delod); 00125 00126 } else { 00127 add_option 00128 ("delod", "dist", 40, 00129 "Eliminate LOD's by choosing the level that would be appropriate for " 00130 "a camera at the indicated fixed distance from each LOD. " 00131 "Use -delod -1 to keep all the LOD's as they are. The default value " 00132 "is " + format_string(default_delod) + ".", 00133 &EggReader::dispatch_double, NULL, &_delod); 00134 } 00135 } 00136 00137 //////////////////////////////////////////////////////////////////// 00138 // Function: EggReader::as_reader 00139 // Access: Public, Virtual 00140 // Description: Returns this object as an EggReader pointer, if it is 00141 // in fact an EggReader, or NULL if it is not. 00142 // 00143 // This is intended to work around the C++ limitation 00144 // that prevents downcasts past virtual inheritance. 00145 // Since both EggReader and EggWriter inherit virtually 00146 // from EggSingleBase, we need functions like this to downcast 00147 // to the appropriate pointer. 00148 //////////////////////////////////////////////////////////////////// 00149 EggReader *EggReader:: 00150 as_reader() { 00151 return this; 00152 } 00153 00154 //////////////////////////////////////////////////////////////////// 00155 // Function: EggReader::pre_process_egg_file 00156 // Access: Public, Virtual 00157 // Description: Performs any processing of the egg file that is 00158 // appropriate after reading it in. 00159 // 00160 // Normally, you should not need to call this function 00161 // directly; it is called automatically at startup. 00162 //////////////////////////////////////////////////////////////////// 00163 void EggReader:: 00164 pre_process_egg_file() { 00165 } 00166 00167 //////////////////////////////////////////////////////////////////// 00168 // Function: EggReader::handle_args 00169 // Access: Protected, Virtual 00170 // Description: 00171 //////////////////////////////////////////////////////////////////// 00172 bool EggReader:: 00173 handle_args(ProgramBase::Args &args) { 00174 if (args.empty()) { 00175 nout << "You must specify the egg file(s) to read on the command line.\n"; 00176 return false; 00177 } 00178 00179 // Any separate egg files that are listed on the command line will 00180 // get implicitly loaded up into one big egg file. 00181 00182 if (!args.empty()) { 00183 _data->set_egg_filename(Filename::from_os_specific(args[0])); 00184 } 00185 Args::const_iterator ai; 00186 for (ai = args.begin(); ai != args.end(); ++ai) { 00187 Filename filename = Filename::from_os_specific(*ai); 00188 00189 EggData file_data; 00190 if (!file_data.read(filename)) { 00191 // Rather than returning false, we simply exit here, so the 00192 // ProgramBase won't try to tell the user how to run the program 00193 // just because we got a bad egg file. 00194 exit(1); 00195 } 00196 00197 if (_noabs && file_data.original_had_absolute_pathnames()) { 00198 nout << filename.get_basename() 00199 << " includes absolute pathnames!\n"; 00200 exit(1); 00201 } 00202 00203 DSearchPath file_path; 00204 file_path.append_directory(filename.get_dirname()); 00205 00206 if (_force_complete) { 00207 if (!file_data.load_externals()) { 00208 exit(1); 00209 } 00210 } 00211 00212 // Now resolve the filenames again according to the user's 00213 // specified _path_replace. 00214 convert_paths(&file_data, _path_replace, file_path); 00215 00216 _data->merge(file_data); 00217 } 00218 00219 pre_process_egg_file(); 00220 00221 return true; 00222 } 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: EggReader::post_command_line 00226 // Access: Protected, Virtual 00227 // Description: This is called after the command line has been 00228 // completely processed, and it gives the program a 00229 // chance to do some last-minute processing and 00230 // validation of the options and arguments. It should 00231 // return true if everything is fine, false if there is 00232 // an error. 00233 //////////////////////////////////////////////////////////////////// 00234 bool EggReader:: 00235 post_command_line() { 00236 return EggSingleBase::post_command_line(); 00237 } 00238 00239 //////////////////////////////////////////////////////////////////// 00240 // Function: EggReader::do_reader_options 00241 // Access: Protected 00242 // Description: Postprocesses the egg file as the user requested 00243 // according to whatever command-line options are in 00244 // effect. Returns true if everything is done 00245 // correctly, false if there was some problem. 00246 //////////////////////////////////////////////////////////////////// 00247 bool EggReader:: 00248 do_reader_options() { 00249 bool okflag = true; 00250 00251 if (_got_tex_dirname || _got_tex_extension) { 00252 if (!copy_textures()) { 00253 okflag = false; 00254 } 00255 } 00256 00257 if (_delod >= 0.0) { 00258 do_delod(_data); 00259 } 00260 00261 return okflag; 00262 } 00263 00264 //////////////////////////////////////////////////////////////////// 00265 // Function: EggReader::copy_textures 00266 // Access: Private 00267 // Description: Renames and copies the textures referenced in the egg 00268 // file, if so specified by the -td and -te options. 00269 // Returns true if all textures are copied successfully, 00270 // false if any one of them failed. 00271 //////////////////////////////////////////////////////////////////// 00272 bool EggReader:: 00273 copy_textures() { 00274 bool success = true; 00275 EggTextureCollection textures; 00276 textures.find_used_textures(_data); 00277 00278 EggTextureCollection::const_iterator ti; 00279 for (ti = textures.begin(); ti != textures.end(); ++ti) { 00280 EggTexture *tex = (*ti); 00281 Filename orig_filename = tex->get_filename(); 00282 if (!orig_filename.exists()) { 00283 bool found = orig_filename.resolve_filename(get_model_path()); 00284 if (!found) { 00285 nout << "Cannot find " << orig_filename << "\n"; 00286 success = false; 00287 continue; 00288 } 00289 } 00290 00291 Filename new_filename = orig_filename; 00292 if (_got_tex_dirname) { 00293 new_filename.set_dirname(_tex_dirname); 00294 } 00295 if (_got_tex_extension) { 00296 new_filename.set_extension(_tex_extension); 00297 } 00298 00299 if (orig_filename != new_filename) { 00300 tex->set_filename(new_filename); 00301 00302 // The new filename is different; does it need copying? 00303 int compare = 00304 orig_filename.compare_timestamps(new_filename, true, true); 00305 if (compare > 0) { 00306 // Yes, it does. Copy it! 00307 nout << "Reading " << orig_filename << "\n"; 00308 PNMImage image; 00309 if (!image.read(orig_filename)) { 00310 nout << " unable to read!\n"; 00311 success = false; 00312 } else { 00313 nout << "Writing " << new_filename << "\n"; 00314 if (!image.write(new_filename, _tex_type)) { 00315 nout << " unable to write!\n"; 00316 success = false; 00317 } 00318 } 00319 } 00320 } 00321 } 00322 00323 return success; 00324 } 00325 00326 //////////////////////////////////////////////////////////////////// 00327 // Function: EggReader::do_delod 00328 // Access: Private 00329 // Description: Removes all the LOD's in the egg file by treating the 00330 // camera as being _delod distance from each LOD. 00331 // Returns true if this particular group should be 00332 // preserved, false if it should be removed. 00333 //////////////////////////////////////////////////////////////////// 00334 bool EggReader:: 00335 do_delod(EggNode *node) { 00336 if (node->is_of_type(EggGroup::get_class_type())) { 00337 EggGroup *group = DCAST(EggGroup, node); 00338 if (group->has_lod()) { 00339 const EggSwitchCondition &cond = group->get_lod(); 00340 if (cond.is_of_type(EggSwitchConditionDistance::get_class_type())) { 00341 const EggSwitchConditionDistance *dist = 00342 DCAST(EggSwitchConditionDistance, &cond); 00343 if (_delod >= dist->_switch_out && _delod < dist->_switch_in) { 00344 // Preserve this group node, but not the LOD information 00345 // itself. 00346 nout << "Preserving LOD " << node->get_name() 00347 << " (" << dist->_switch_out << " to " << dist->_switch_in 00348 << ")\n"; 00349 group->clear_lod(); 00350 } else { 00351 // Remove this group node. 00352 nout << "Eliminating LOD " << node->get_name() 00353 << " (" << dist->_switch_out << " to " << dist->_switch_in 00354 << ")\n"; 00355 return false; 00356 } 00357 } 00358 } 00359 } 00360 00361 // Now process all the children. 00362 if (node->is_of_type(EggGroupNode::get_class_type())) { 00363 EggGroupNode *group = DCAST(EggGroupNode, node); 00364 EggGroupNode::iterator ci; 00365 ci = group->begin(); 00366 while (ci != group->end()) { 00367 EggNode *child = *ci; 00368 ++ci; 00369 00370 if (!do_delod(child)) { 00371 group->remove_child(child); 00372 } 00373 } 00374 } 00375 00376 return true; 00377 }