Panda3D
 All Classes Functions Variables Enumerations
loader.cxx
00001 // Filename: loader.cxx
00002 // Created by:  mike (09Jan97)
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 "loader.h"
00016 #include "loaderFileType.h"
00017 #include "loaderFileTypeRegistry.h"
00018 #include "config_pgraph.h"
00019 #include "modelPool.h"
00020 #include "modelLoadRequest.h"
00021 #include "config_express.h"
00022 #include "config_util.h"
00023 #include "virtualFileSystem.h"
00024 #include "filename.h"
00025 #include "load_dso.h"
00026 #include "string_utils.h"
00027 #include "bamCache.h"
00028 #include "bamCacheRecord.h"
00029 #include "sceneGraphReducer.h"
00030 #include "renderState.h"
00031 #include "bamFile.h"
00032 #include "configVariableInt.h"
00033 #include "configVariableEnum.h"
00034 
00035 bool Loader::_file_types_loaded = false;
00036 PT(Loader) Loader::_global_ptr;
00037 TypeHandle Loader::_type_handle;
00038 
00039 ////////////////////////////////////////////////////////////////////
00040 //     Function: Loader::Constructor
00041 //       Access: Published
00042 //  Description:
00043 ////////////////////////////////////////////////////////////////////
00044 Loader::
00045 Loader(const string &name) :
00046   Namable(name)
00047 {
00048   _task_manager = AsyncTaskManager::get_global_ptr();
00049   _task_chain = name;
00050 
00051   if (_task_manager->find_task_chain(_task_chain) == NULL) {
00052     PT(AsyncTaskChain) chain = _task_manager->make_task_chain(_task_chain);
00053 
00054     ConfigVariableInt loader_num_threads
00055       ("loader-num-threads", 1,
00056        PRC_DESC("The number of threads that will be started by the Loader class "
00057                 "to load models asynchronously.  These threads will only be "
00058                 "started if the asynchronous interface is used, and if threading "
00059                 "support is compiled into Panda.  The default is one thread, "
00060                 "which allows models to be loaded one at a time in a single "
00061                 "asychronous thread.  You can set this higher, particularly if "
00062                 "you have many CPU's available, to allow loading multiple models "
00063                 "simultaneously."));
00064     chain->set_num_threads(loader_num_threads);
00065 
00066     ConfigVariableEnum<ThreadPriority> loader_thread_priority
00067       ("loader-thread-priority", TP_low,
00068        PRC_DESC("The default thread priority to assign to the threads created "
00069                 "for asynchronous loading.  The default is 'low'; you may "
00070                 "also specify 'normal', 'high', or 'urgent'."));
00071     chain->set_thread_priority(loader_thread_priority);
00072   }
00073 }
00074 
00075 ////////////////////////////////////////////////////////////////////
00076 //     Function: Loader::make_async_request
00077 //       Access: Published
00078 //  Description: Returns a new AsyncTask object suitable for adding to
00079 //               load_async() to start an asynchronous model load.
00080 ////////////////////////////////////////////////////////////////////
00081 PT(AsyncTask) Loader::
00082 make_async_request(const Filename &filename, const LoaderOptions &options) {
00083   return new ModelLoadRequest(string("model:")+filename.get_basename(),
00084                               filename, options, this);
00085 }
00086 
00087 ////////////////////////////////////////////////////////////////////
00088 //     Function: Loader::load_bam_stream
00089 //       Access: Published
00090 //  Description: Attempts to read a bam file from the indicated stream
00091 //               and return the scene graph defined there.
00092 ////////////////////////////////////////////////////////////////////
00093 PT(PandaNode) Loader::
00094 load_bam_stream(istream &in) {
00095   BamFile bam_file;
00096   if (!bam_file.open_read(in)) {
00097     return NULL;
00098   }
00099 
00100   return bam_file.read_node();
00101 }
00102 
00103 ////////////////////////////////////////////////////////////////////
00104 //     Function: Loader::output
00105 //       Access: Published, Virtual
00106 //  Description: 
00107 ////////////////////////////////////////////////////////////////////
00108 void Loader::
00109 output(ostream &out) const {
00110   out << get_type() << " " << get_name();
00111 
00112   int num_tasks = _task_manager->make_task_chain(_task_chain)->get_num_tasks();
00113   if (num_tasks != 0) {
00114     out << " (" << num_tasks << " models pending)";
00115   }
00116 }
00117 
00118 ////////////////////////////////////////////////////////////////////
00119 //     Function: Loader::load_file
00120 //       Access: Private
00121 //  Description: Loads a single scene graph file, if possible.
00122 //               Returns the Node that is the root of the file, or
00123 //               NULL if the file cannot be loaded.
00124 //
00125 //               If search is true, the file is searched for along the
00126 //               model path; otherwise, only the exact filename is
00127 //               loaded.
00128 ////////////////////////////////////////////////////////////////////
00129 PT(PandaNode) Loader::
00130 load_file(const Filename &filename, const LoaderOptions &options) const {
00131   Filename this_filename(filename);
00132   LoaderOptions this_options(options);
00133 
00134   bool report_errors = (this_options.get_flags() & LoaderOptions::LF_report_errors) != 0;
00135 
00136   string extension = this_filename.get_extension();
00137   if (extension.empty()) {
00138     // If the filename has no filename extension, append the default
00139     // extension specified in the Config file.
00140     this_filename = this_filename.get_fullpath() + default_model_extension.get_value();
00141     extension = this_filename.get_extension();
00142   }
00143 
00144   bool pz_file = false;
00145 #ifdef HAVE_ZLIB
00146   if (extension == "pz") {
00147     pz_file = true;
00148     extension = Filename(this_filename.get_basename_wo_extension()).get_extension();
00149   }
00150 #endif  // HAVE_ZLIB
00151 
00152   if (extension.empty()) {
00153     if (report_errors) {
00154       loader_cat.error()
00155         << "Cannot load " << this_filename
00156         << " without filename extension.  Loading of model filenames with an "
00157         "implicit extension is deprecated in Panda3D.  Please "
00158         "correct the filename reference.  If necessary, you may put the "
00159         "line \"default-model-extension .bam\" or \"default-model-extension .egg\" "
00160         "in your Config.prc to globally assume a particular model "
00161         "filename extension.\n";
00162     }
00163     return NULL;
00164   }
00165 
00166   LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
00167   LoaderFileType *requested_type =
00168     reg->get_type_from_extension(extension);
00169   if (requested_type == (LoaderFileType *)NULL) {
00170     if (report_errors) {
00171       loader_cat.error()
00172         << "Extension of file " << this_filename
00173         << " is unrecognized; cannot load.\n";
00174       loader_cat.error(false)
00175         << "Currently known scene file types are:\n";
00176       reg->write(loader_cat.error(false), 2);
00177     }
00178     return NULL;
00179   } else if (pz_file && !requested_type->supports_compressed()) {
00180     if (report_errors) {
00181       loader_cat.error()
00182         << requested_type->get_name() << " file type (."
00183         << extension << ") does not support in-line compression.\n";
00184     }
00185     return NULL;
00186   }
00187 
00188   bool search = (this_options.get_flags() & LoaderOptions::LF_search) != 0;
00189   if (!filename.is_local()) {
00190     // If we have a global filename, we don't search the model path.
00191     search = false;
00192   }
00193 
00194   // Now that we've decided whether to search for the file, don't try
00195   // to search again.
00196   this_options.set_flags(this_options.get_flags() & ~LoaderOptions::LF_search);
00197 
00198   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00199 
00200   if (search) {
00201     // Look for the file along the model path.
00202     const ConfigVariableSearchPath &model_path = get_model_path();
00203     int num_dirs = model_path.get_num_directories();
00204     for (int i = 0; i < num_dirs; ++i) {
00205       Filename pathname(model_path.get_directory(i), this_filename);
00206       PT(PandaNode) result = try_load_file(pathname, this_options, 
00207                                            requested_type);
00208       if (result != (PandaNode *)NULL) {
00209         return result;
00210       }
00211     }
00212 
00213     if (report_errors) {
00214       bool any_exist = false;
00215       for (int i = 0; i < num_dirs; ++i) {
00216         Filename pathname(model_path.get_directory(i), this_filename);
00217         if (vfs->exists(pathname)) {
00218           any_exist = true;
00219           break;
00220         }
00221       }
00222 
00223       if (any_exist) {
00224         loader_cat.error()
00225           << "Couldn't load file " << this_filename
00226           << ": all matching files on model path invalid "
00227           << "(the model path is currently: \"" << get_model_path() << "\")\n";
00228       } else {
00229         loader_cat.error()
00230           << "Couldn't load file " << this_filename
00231           << ": not found on model path "
00232           << "(currently: \"" << get_model_path() << "\")\n";
00233       }
00234     }
00235 
00236   } else {
00237     // Look for the file only where it is.
00238     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00239     PT(PandaNode) result = try_load_file(this_filename, this_options, requested_type);
00240     if (result != (PandaNode *)NULL) {
00241       return result;
00242     }
00243     if (report_errors) {
00244       if (vfs->exists(this_filename)) {
00245         loader_cat.error()
00246           << "Couldn't load file " << this_filename << ": invalid.\n";
00247       } else {
00248         loader_cat.error()
00249           << "Couldn't load file " << this_filename << ": does not exist.\n";
00250       }
00251     }
00252   }
00253   return NULL;
00254 }
00255 
00256 ////////////////////////////////////////////////////////////////////
00257 //     Function: Loader::try_load_file
00258 //       Access: Private
00259 //  Description: The implementatin of load_file(), this tries a single
00260 //               possible file without searching further along the
00261 //               path.
00262 ////////////////////////////////////////////////////////////////////
00263 PT(PandaNode) Loader::
00264 try_load_file(const Filename &pathname, const LoaderOptions &options,
00265               LoaderFileType *requested_type) const {
00266   BamCache *cache = BamCache::get_global_ptr();
00267 
00268   bool cache_only = (options.get_flags() & LoaderOptions::LF_cache_only) != 0;
00269 
00270   if (requested_type->get_allow_ram_cache(options)) {
00271     // If we're allowing a RAM cache, use the ModelPool to load the
00272     // file.
00273     if (!cache_only || ModelPool::has_model(pathname)) {
00274       PT(PandaNode) node = ModelPool::load_model(pathname, options);
00275       if (node != (PandaNode *)NULL &&
00276           (options.get_flags() & LoaderOptions::LF_allow_instance) == 0) {
00277         if (loader_cat.is_debug()) {
00278           loader_cat.debug()
00279             << "Model " << pathname << " found in ModelPool.\n";
00280         }
00281         // But return a deep copy of the shared model.
00282         node = node->copy_subgraph();
00283       }
00284       return node;
00285     }
00286   }
00287 
00288   bool report_errors = ((options.get_flags() & LoaderOptions::LF_report_errors) != 0 || loader_cat.is_debug());
00289 
00290   PT(BamCacheRecord) record;
00291   if (cache->get_cache_models() && requested_type->get_allow_disk_cache(options)) {
00292     // See if the model can be found in the on-disk cache, if it is
00293     // active.
00294     record = cache->lookup(pathname, "bam");
00295     if (record != (BamCacheRecord *)NULL) {
00296       if (record->has_data()) {
00297         if (report_errors) {
00298           loader_cat.info()
00299             << "Model " << pathname << " found in disk cache.\n";
00300         }
00301         PT(PandaNode) result = DCAST(PandaNode, record->get_data());
00302         if (premunge_data) {
00303           SceneGraphReducer sgr;
00304           sgr.premunge(result, RenderState::make_empty());
00305         }
00306         return result;
00307       }
00308     }
00309   }
00310 
00311   if (loader_cat.is_debug()) {
00312     loader_cat.debug()
00313       << "Model " << pathname << " not found in cache.\n";
00314   }
00315   
00316   if (!cache_only) {
00317     PT(PandaNode) result = requested_type->load_file(pathname, options, record);
00318     if (result != (PandaNode *)NULL){ 
00319       if (record != (BamCacheRecord *)NULL) {
00320         record->set_data(result, result);
00321         cache->store(record);
00322       }
00323       
00324       if (premunge_data) {
00325         SceneGraphReducer sgr;
00326         sgr.premunge(result, RenderState::make_empty());
00327       }
00328       return result;
00329     }
00330   }
00331 
00332   return NULL;
00333 }
00334 
00335 
00336 ////////////////////////////////////////////////////////////////////
00337 //     Function: Loader::load_file_types
00338 //       Access: Private, Static
00339 //  Description: Loads up all of the dynamic libraries named in a
00340 //               load-file-type Configure variable.  Presumably this
00341 //               will make the various file types available for
00342 //               runtime loading.
00343 ////////////////////////////////////////////////////////////////////
00344 void Loader::
00345 load_file_types() {
00346   if (!_file_types_loaded) {
00347     int num_unique_values = load_file_type.get_num_unique_values();
00348 
00349     for (int i = 0; i < num_unique_values; i++) {
00350       string param = load_file_type.get_unique_value(i);
00351 
00352       vector_string words;
00353       extract_words(param, words);
00354 
00355       if (words.size() == 1) {
00356         // Exactly one word: load the named library immediately.
00357         string name = words[0];
00358         Filename dlname = Filename::dso_filename("lib" + name + ".so");
00359         loader_cat.info()
00360           << "loading file type module: " << name << endl;
00361         void *tmp = load_dso(get_plugin_path().get_value(), dlname);
00362         if (tmp == (void *)NULL) {
00363           loader_cat.warning()
00364             << "Unable to load " << dlname.to_os_specific()
00365             << ": " << load_dso_error() << endl;
00366         }
00367         
00368       } else if (words.size() > 1) {
00369         // Multiple words: the first n words are filename extensions,
00370         // and the last word is the name of the library to load should
00371         // any of those filename extensions be encountered.
00372         LoaderFileTypeRegistry *registry = LoaderFileTypeRegistry::get_global_ptr();
00373         size_t num_extensions = words.size() - 1;
00374         string library_name = words[num_extensions];
00375         
00376         for (size_t i = 0; i < num_extensions; i++) {
00377           string extension = words[i];
00378           if (extension[0] == '.') {
00379             extension = extension.substr(1);
00380           }
00381           
00382           registry->register_deferred_type(extension, library_name);
00383         }
00384       }
00385     }
00386 
00387     _file_types_loaded = true;
00388   }
00389 }
00390 
00391 ////////////////////////////////////////////////////////////////////
00392 //     Function: Loader::make_global_ptr
00393 //       Access: Private, Static
00394 //  Description: Called once per application to create the global
00395 //               loader object.
00396 ////////////////////////////////////////////////////////////////////
00397 void Loader::
00398 make_global_ptr() {
00399   nassertv(_global_ptr == (Loader *)NULL);
00400 
00401   _global_ptr = new Loader("loader");
00402 }
00403 
 All Classes Functions Variables Enumerations