Panda3D
|
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 // But return a deep copy of the shared model. 00278 node = node->copy_subgraph(); 00279 } 00280 return node; 00281 } 00282 } 00283 00284 bool report_errors = (options.get_flags() & LoaderOptions::LF_report_errors) != 0; 00285 00286 PT(BamCacheRecord) record; 00287 if (cache->get_cache_models() && requested_type->get_allow_disk_cache(options)) { 00288 // See if the model can be found in the on-disk cache, if it is 00289 // active. 00290 record = cache->lookup(pathname, "bam"); 00291 if (record != (BamCacheRecord *)NULL) { 00292 if (record->has_data()) { 00293 if (report_errors) { 00294 loader_cat.info() 00295 << "Model " << pathname << " found in disk cache.\n"; 00296 } 00297 PT(PandaNode) result = DCAST(PandaNode, record->get_data()); 00298 if (premunge_data) { 00299 SceneGraphReducer sgr; 00300 sgr.premunge(result, RenderState::make_empty()); 00301 } 00302 return result; 00303 } 00304 } 00305 } 00306 00307 if (!cache_only) { 00308 PT(PandaNode) result = requested_type->load_file(pathname, options, record); 00309 if (result != (PandaNode *)NULL){ 00310 if (record != (BamCacheRecord *)NULL) { 00311 record->set_data(result, result); 00312 cache->store(record); 00313 } 00314 00315 if (premunge_data) { 00316 SceneGraphReducer sgr; 00317 sgr.premunge(result, RenderState::make_empty()); 00318 } 00319 return result; 00320 } 00321 } 00322 00323 return NULL; 00324 } 00325 00326 00327 //////////////////////////////////////////////////////////////////// 00328 // Function: Loader::load_file_types 00329 // Access: Private, Static 00330 // Description: Loads up all of the dynamic libraries named in a 00331 // load-file-type Configure variable. Presumably this 00332 // will make the various file types available for 00333 // runtime loading. 00334 //////////////////////////////////////////////////////////////////// 00335 void Loader:: 00336 load_file_types() { 00337 if (!_file_types_loaded) { 00338 int num_unique_values = load_file_type.get_num_unique_values(); 00339 00340 for (int i = 0; i < num_unique_values; i++) { 00341 string param = load_file_type.get_unique_value(i); 00342 00343 vector_string words; 00344 extract_words(param, words); 00345 00346 if (words.size() == 1) { 00347 // Exactly one word: load the named library immediately. 00348 string name = words[0]; 00349 Filename dlname = Filename::dso_filename("lib" + name + ".so"); 00350 loader_cat.info() 00351 << "loading file type module: " << name << endl; 00352 void *tmp = load_dso(get_plugin_path().get_value(), dlname); 00353 if (tmp == (void *)NULL) { 00354 loader_cat.warning() 00355 << "Unable to load " << dlname.to_os_specific() 00356 << ": " << load_dso_error() << endl; 00357 } 00358 00359 } else if (words.size() > 1) { 00360 // Multiple words: the first n words are filename extensions, 00361 // and the last word is the name of the library to load should 00362 // any of those filename extensions be encountered. 00363 LoaderFileTypeRegistry *registry = LoaderFileTypeRegistry::get_global_ptr(); 00364 size_t num_extensions = words.size() - 1; 00365 string library_name = words[num_extensions]; 00366 00367 for (size_t i = 0; i < num_extensions; i++) { 00368 string extension = words[i]; 00369 if (extension[0] == '.') { 00370 extension = extension.substr(1); 00371 } 00372 00373 registry->register_deferred_type(extension, library_name); 00374 } 00375 } 00376 } 00377 00378 _file_types_loaded = true; 00379 } 00380 } 00381 00382 //////////////////////////////////////////////////////////////////// 00383 // Function: Loader::make_global_ptr 00384 // Access: Private, Static 00385 // Description: Called once per application to create the global 00386 // loader object. 00387 //////////////////////////////////////////////////////////////////// 00388 void Loader:: 00389 make_global_ptr() { 00390 nassertv(_global_ptr == (Loader *)NULL); 00391 00392 _global_ptr = new Loader("loader"); 00393 } 00394