Panda3D

eggReader.cxx

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 }
 All Classes Functions Variables Enumerations