Browse Source

rework loader vs. ModelPool; deprecate implicit filename extension; improve ModelPool.releaseModel()

David Rose 19 years ago
parent
commit
dfe5814829

+ 30 - 4
direct/src/showbase/Loader.py

@@ -198,12 +198,38 @@ class Loader(DirectObject):
 
 
         return model
         return model
 
 
-    def unloadModel(self, modelPath):
+    def unloadModel(self, model):
         """
         """
-        modelPath is a string.
+        model is the return value of loadModel().  For backward
+        compatibility, it may also be the filename that was passed to
+        loadModel(), though this requires a disk search.
         """
         """
-        assert Loader.notify.debug("Unloading model: %s" % (modelPath))
-        ModelPool.releaseModel(modelPath)
+        if isinstance(model, NodePath):
+            # Maybe we were given a NodePath
+            modelNode = model.node()
+
+        elif isinstance(model, ModelNode):
+            # Maybe we were given a node
+            modelNode = model
+
+        elif isinstance(model, types.StringTypes) or \
+             isinstance(model, Filename):
+            # If we were given a filename, we have to ask the loader
+            # to resolve it for us.
+            options = LoaderOptions(LoaderOptions.LFSearch | LoaderOptions.LFNoDiskCache | LoaderOptions.LFCacheOnly)
+            modelNode = self.loader.loadSync(Filename(model), options)
+            if modelNode == None:
+                # Model not found.
+                assert Loader.notify.debug("Unloading model not loaded: %s" % (model))
+                return
+
+            assert Loader.notify.debug("%s resolves to %s" % (model, modelNode.getFullpath()))
+
+        else:
+            raise 'Invalid parameter to unloadModel: %s' % (model)
+
+        assert Loader.notify.debug("Unloading model: %s" % (modelNode.getFullpath()))
+        ModelPool.releaseModel(modelNode)
 
 
     # font loading funcs
     # font loading funcs
     def loadFont(self, modelPath,
     def loadFont(self, modelPath,

+ 9 - 9
panda/src/pgraph/config_pgraph.cxx

@@ -259,15 +259,6 @@ ConfigVariableBool m_dual_flash
 ("m-dual-flash", false,
 ("m-dual-flash", false,
  PRC_DESC("Set this true to flash any objects that use M_dual, for debugging."));
  PRC_DESC("Set this true to flash any objects that use M_dual, for debugging."));
 
 
-
-ConfigVariableBool asynchronous_loads
-("asynchronous-loads", false,
- PRC_DESC("Set this true to support actual asynchronous loads via the "
-          "request_load()/fetch_load() interface to Loader.  Set it false to "
-          "map these to blocking, synchronous loads instead.  Currently, the "
-          "rest of Panda isn't quite ready for asynchronous loads, so leave "
-          "this false for now."));
-
 ConfigVariableList load_file_type
 ConfigVariableList load_file_type
 ("load-file-type",
 ("load-file-type",
  PRC_DESC("List the model loader modules that Panda will automatically "
  PRC_DESC("List the model loader modules that Panda will automatically "
@@ -275,6 +266,15 @@ ConfigVariableList load_file_type
           "either the name of a module, or a space-separate list of filename "
           "either the name of a module, or a space-separate list of filename "
           "extensions, followed by the name of the module."));
           "extensions, followed by the name of the module."));
 
 
+ConfigVariableString default_model_extension
+("default-model-extension", "",
+ PRC_DESC("This specifies the filename extension (with leading dot) that "
+          "should be assumed if an attempt is made to load a filename that "
+          "has no extension.  This is primarily designed to support legacy "
+          "code that used the now-deprecated implicit-extension feature of "
+          "Panda's loader; new code should probably give the correct name "
+          "for each model file they intend to load."));
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: init_libpgraph
 //     Function: init_libpgraph
 //  Description: Initializes the library.  This must be called at
 //  Description: Initializes the library.  This must be called at

+ 1 - 2
panda/src/pgraph/config_pgraph.h

@@ -61,9 +61,8 @@ extern ConfigVariableBool m_dual_opaque;
 extern ConfigVariableBool m_dual_transparent;
 extern ConfigVariableBool m_dual_transparent;
 extern ConfigVariableBool m_dual_flash;
 extern ConfigVariableBool m_dual_flash;
 
 
-extern ConfigVariableBool asynchronous_loads;
-
 extern ConfigVariableList load_file_type;
 extern ConfigVariableList load_file_type;
+extern ConfigVariableString default_model_extension;
 
 
 extern EXPCL_PANDA void init_libpgraph();
 extern EXPCL_PANDA void init_libpgraph();
 
 

+ 124 - 175
panda/src/pgraph/loader.cxx

@@ -60,108 +60,6 @@ Loader(const string &name, int num_threads) :
   }
   }
 }
 }
 
 
-////////////////////////////////////////////////////////////////////
-//     Function: Loader::find_all_files
-//       Access: Published
-//  Description: Searches along the given search path for the given
-//               file name, and fills up the results list with all
-//               possible matches and their associated types, in
-//               order.
-////////////////////////////////////////////////////////////////////
-int Loader::
-find_all_files(const Filename &filename, const DSearchPath &search_path,
-               Loader::Results &results) const {
-  if (!_file_types_loaded) {
-    load_file_types();
-  }
-  string extension = filename.get_extension();
-  string extra_ext;
-  bool pz_file = false;
-
-#ifdef HAVE_ZLIB
-  if (extension == "pz") {
-    // The extension ".pz" is special.  This is an explicitly-named
-    // compressed file.  We'll decompress it on the fly, if possible.
-    // (This relies on the auto_unwrap parameter to vfs->read_file(),
-    // which is different from the implicitly-named compressed file
-    // action that you might get if vfs-implicit-pz is enabled.)
-    extra_ext = extension;
-    extension = Filename(filename.get_basename_wo_extension()).get_extension();
-    pz_file = true;
-  }
-#endif  // HAVE_ZLIB
-
-  int num_added = 0;
-
-  if (!extension.empty()) {
-    // If the extension is not empty, it specifies a single file type.
-    LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
-    LoaderFileType *requested_type =
-      reg->get_type_from_extension(extension);
-
-    if (requested_type != (LoaderFileType *)NULL &&
-        (!pz_file || requested_type->supports_compressed())) {
-      if (!filename.is_local()) {
-        // Global filename, take it as it is.
-        results.add_file(filename, requested_type);
-        num_added++;
-
-      } else {
-        // Local filename, search along the path.
-        DSearchPath::Results files;
-        VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-        num_added = vfs->find_all_files(filename, search_path, files);
-        
-        for (int i = 0; i < num_added; ++i) {
-          results.add_file(files.get_file(i), requested_type);
-        }
-      }
-    }
-  } else {
-    // If the extension *is* empty, we have to search for all possible
-    // file types.
-    LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
-    int num_types = reg->get_num_types();
-
-    if (!filename.is_local()) {
-      // Global filename, take it as it is.
-      for (int t = 0; t < num_types; ++t) {
-        LoaderFileType *type = reg->get_type(t);
-        Filename file(filename);
-        file.set_extension(type->get_extension());
-        file = file.get_fullpath() + extra_ext;
-          
-        VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-        if (vfs->exists(file)) {
-          results.add_file(file, type);
-          ++num_added;
-        }
-      }
-    } else {
-      // Local filename, look it up on the model path.
-      int num_dirs = search_path.get_num_directories();
-      for (int i = 0; i < num_dirs; ++i) {
-        const Filename &directory = search_path.get_directory(i);
-        
-        for (int t = 0; t < num_types; ++t) {
-          LoaderFileType *type = reg->get_type(t);
-          Filename file(directory, filename);
-          file.set_extension(type->get_extension());
-          file = file.get_fullpath() + extra_ext;
-          
-          VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
-          if (vfs->exists(file)) {
-            results.add_file(file, type);
-            ++num_added;
-          }
-        }
-      }
-    }
-  }
-
-  return num_added;
-}
-
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: Loader::output
 //     Function: Loader::output
 //       Access: Published, Virtual
 //       Access: Published, Virtual
@@ -189,113 +87,164 @@ output(ostream &out) const {
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 PT(PandaNode) Loader::
 PT(PandaNode) Loader::
 load_file(const Filename &filename, const LoaderOptions &options) const {
 load_file(const Filename &filename, const LoaderOptions &options) const {
-  if (options.get_allow_ram_cache()) {
-    // If we're allowing a RAM cache (and we don't have any other
-    // funny options), use the ModelPool to load the file.
-    PT(PandaNode) node = ModelPool::load_model(filename, options);
-    if (node != (PandaNode *)NULL) {
-      // But return a deep copy of the shared model.
-      node = node->copy_subgraph();
+  Filename this_filename(filename);
+  LoaderOptions this_options(options);
+
+  bool report_errors = (this_options.get_flags() & LoaderOptions::LF_report_errors) != 0;
+
+  string extension = this_filename.get_extension();
+  if (extension.empty()) {
+    // If the filename has no filename extension, append the default
+    // extension specified in the Config file.
+    this_filename = this_filename.get_fullpath() + default_model_extension.get_value();
+    extension = this_filename.get_extension();
+  }
+
+  bool pz_file = false;
+#ifdef HAVE_ZLIB
+  if (extension == "pz") {
+    pz_file = true;
+    extension = Filename(this_filename.get_basename_wo_extension()).get_extension();
+  }
+#endif  // HAVE_ZLIB
+
+  if (extension.empty()) {
+    if (report_errors) {
+      loader_cat.error()
+        << "Cannot load " << this_filename
+        << " without filename extension.  Loading of model filenames with an "
+        "implicit extension is deprecated in Panda3D.  Please "
+        "correct the filename reference.  If necessary, you may put the "
+        "line \"default-model-extension .bam\" or \"default-model-extension .egg\" "
+        "in your Config.prc to globally assume a particular model "
+        "filename extension.\n";
     }
     }
-    return node;
+    return NULL;
   }
   }
 
 
-  Results results;
-  int num_files;
+  LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
+  LoaderFileType *requested_type =
+    reg->get_type_from_extension(extension);
+  if (requested_type == (LoaderFileType *)NULL) {
+    if (report_errors) {
+      loader_cat.error()
+        << "Extension of file " << this_filename
+        << " is unrecognized; cannot load.\n";
+      loader_cat.error(false)
+        << "Currently known scene file types are:\n";
+      reg->write(loader_cat.error(false), 2);
+    }
+    return NULL;
+  } else if (pz_file && !requested_type->supports_compressed()) {
+    if (report_errors) {
+      loader_cat.error()
+        << requested_type->get_name() << " file type (."
+        << extension << ") does not support in-line compression.\n";
+    }
+    return NULL;
+  }
+
+  DSearchPath::Results results;
+  bool search = (this_options.get_flags() & LoaderOptions::LF_search) != 0;
+  if (!filename.is_local()) {
+    // If we have a global filename, we don't search the model path.
+    search = false;
+  }
 
 
-  bool search = (options.get_flags() & LoaderOptions::LF_search) != 0;
+  bool cache_only = (this_options.get_flags() & LoaderOptions::LF_cache_only) != 0;
 
 
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+    
   if (search) {
   if (search) {
     // Look for the file along the model path.
     // Look for the file along the model path.
-    num_files = find_all_files(filename, get_model_path(), results);
-  } else {
-    // Look for the file only where it is.
-    num_files = find_all_files(filename, DSearchPath(Filename(".")), results);
-  }
+    vfs->find_all_files(this_filename, get_model_path(), results);
 
 
-  if (num_files == 0) {
-    // Couldn't find the file.  Either it doesn't exist, or it's an
-    // unknown file type.  Report a useful message either way.
-    string extension = filename.get_extension();
-    bool pz_file = false;
-#ifdef HAVE_ZLIB
-    if (extension == "pz") {
-      pz_file = true;
-      extension = Filename(filename.get_basename_wo_extension()).get_extension();
-    }
-#endif  // HAVE_ZLIB
-    if (!extension.empty()) {
-      LoaderFileTypeRegistry *reg = LoaderFileTypeRegistry::get_global_ptr();
-      LoaderFileType *requested_type =
-        reg->get_type_from_extension(extension);
-      if (requested_type == (LoaderFileType *)NULL) {
+    if (results.get_num_files() == 0) {
+      if (report_errors) {
         loader_cat.error()
         loader_cat.error()
-          << "Extension of file " << filename
-          << " is unrecognized; cannot load.\n";
-        loader_cat.error(false)
-          << "Currently known scene file types are:\n";
-        reg->write(loader_cat.error(false), 2);
-        return NULL;
-      } else if (pz_file && !requested_type->supports_compressed()) {
-        loader_cat.error()
-          << requested_type->get_name() << " file type (."
-          << extension << ") does not support in-line compression.\n";
-        return NULL;
+          << "Couldn't load file " << this_filename << ": not found on model path "
+          << "(which is currently: \"" << get_model_path() << "\")\n";
       }
       }
+      return NULL;
     }
     }
 
 
-    if (search) {
-      loader_cat.error()
-        << "Couldn't load file " << filename << ": not found on model path "
-        << "(which is currently: \"" << get_model_path() << "\")\n";
+  } else {
+    // Look for the file only where it is.
+    if (vfs->exists(this_filename)) {
+      results.add_file(this_filename);
+
     } else {
     } else {
-      loader_cat.error()
-        << "Couldn't load file ./" << filename << ": does not exist.\n";
+      if (report_errors) {
+        loader_cat.error()
+          << "Couldn't load file " << this_filename << ": does not exist.\n";
+      }
+      return NULL;
     }
     }
-    return NULL;
   }
   }
 
 
+  // Now that we've searched for the file, don't try to search again.
+  this_options.set_flags(this_options.get_flags() & ~LoaderOptions::LF_search);
+
   BamCache *cache = BamCache::get_global_ptr();
   BamCache *cache = BamCache::get_global_ptr();
+  int num_files = results.get_num_files();
   for (int i = 0; i < num_files; ++i) {
   for (int i = 0; i < num_files; ++i) {
     const Filename &path = results.get_file(i);
     const Filename &path = results.get_file(i);
 
 
-    PT(BamCacheRecord) record;
+    if (requested_type->get_allow_ram_cache(this_options)) {
+      // If we're allowing a RAM cache, use the ModelPool to load the
+      // file.
+      if (!cache_only || ModelPool::has_model(path)) {
+        PT(PandaNode) node = ModelPool::load_model(path, this_options);
+        if (node != (PandaNode *)NULL &&
+            (this_options.get_flags() & LoaderOptions::LF_allow_instance) == 0) {
+          // But return a deep copy of the shared model.
+          node = node->copy_subgraph();
+        }
+        return node;
+      }
+    }
 
 
-    if (cache->get_active() && options.get_allow_disk_cache()) {
-      // See if the texture can be found in the on-disk cache, if it is
+    PT(BamCacheRecord) record;
+    if (cache->get_active() && requested_type->get_allow_disk_cache(this_options)) {
+      // See if the model can be found in the on-disk cache, if it is
       // active.
       // active.
       record = cache->lookup(path, "bam");
       record = cache->lookup(path, "bam");
       if (record != (BamCacheRecord *)NULL) {
       if (record != (BamCacheRecord *)NULL) {
         if (record->has_data()) {
         if (record->has_data()) {
-          loader_cat.info()
-            << "Model " << path << " found in disk cache.\n";
+          if (report_errors) {
+            loader_cat.info()
+              << "Model " << path << " found in disk cache.\n";
+          }
           return DCAST(PandaNode, record->extract_data());
           return DCAST(PandaNode, record->extract_data());
         }
         }
       }
       }
     }
     }
 
 
-    LoaderFileType *type = results.get_file_type(i);
-    PT(PandaNode) result = type->load_file(path, options, record);
-    if (result != (PandaNode *)NULL){ 
-      if (record != (BamCacheRecord *)NULL) {
-        record->set_data(result, false);
-        cache->store(record);
+    if (!cache_only) {
+      PT(PandaNode) result = requested_type->load_file(path, this_options, record);
+      if (result != (PandaNode *)NULL){ 
+        if (record != (BamCacheRecord *)NULL) {
+          record->set_data(result, false);
+          cache->store(record);
+        }
+        
+        return result;
       }
       }
-
-      return result;
     }
     }
   }
   }
 
 
-  // None of the matching files could be loaded.  Oh well.
-  if (search) {
-    loader_cat.error()
-      << "Couldn't load file " << filename
-      << ": all matching files on model path invalid "
-      << "(the model path is currently: \"" << get_model_path() << "\")\n";
-  } else {
-    loader_cat.error()
-      << "Couldn't load file " << filename
-      << ": invalid.\n";
+  if (report_errors) {
+    // None of the matching files could be loaded.  Oh well.
+    if (num_files > 1) {
+      loader_cat.error()
+        << "Couldn't load file " << this_filename
+        << ": all matching files on model path invalid "
+        << "(the model path is currently: \"" << get_model_path() << "\")\n";
+    } else {
+      loader_cat.error()
+        << "Couldn't load file " << this_filename
+        << ": invalid.\n";
+    }
   }
   }
   return NULL;
   return NULL;
 }
 }

+ 0 - 3
panda/src/pgraph/loader.h

@@ -79,9 +79,6 @@ PUBLISHED:
 
 
   Loader(const string &name = "loader", int num_threads = -1);
   Loader(const string &name = "loader", int num_threads = -1);
 
 
-  int find_all_files(const Filename &filename, const DSearchPath &search_path,
-                     Results &results) const;
-
   INLINE PT(PandaNode) load_sync(const Filename &filename, 
   INLINE PT(PandaNode) load_sync(const Filename &filename, 
                                  const LoaderOptions &options = LoaderOptions()) const;
                                  const LoaderOptions &options = LoaderOptions()) const;
 
 

+ 30 - 0
panda/src/pgraph/loaderFileType.cxx

@@ -28,6 +28,12 @@ TypeHandle LoaderFileType::_type_handle;
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 LoaderFileType::
 LoaderFileType::
 LoaderFileType() {
 LoaderFileType() {
+  // Derived LoaderFileType classes that return a different result
+  // based on the setting of certain LoaderOptions flags (like
+  // LF_convert_anim) should set those bits in the following bitmask,
+  // so that we will not inadvertently cache a model without
+  // respecting these flags.
+  _no_cache_flags = 0;
 }
 }
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
@@ -63,6 +69,30 @@ supports_compressed() const {
   return false;
   return false;
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileType::get_allow_disk_cache
+//       Access: Published, Virtual
+//  Description: Returns true if the loader flags allow retrieving the
+//               model from the on-disk bam cache (if it is enabled),
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool LoaderFileType::
+get_allow_disk_cache(const LoaderOptions &options) const {
+  return (options.get_flags() & (LoaderOptions::LF_no_disk_cache | _no_cache_flags)) == 0;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: LoaderFileType::get_allow_ram_cache
+//       Access: Published
+//  Description: Returns true if the loader flags allow retrieving the
+//               model from the in-memory ModelPool cache, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool LoaderFileType::
+get_allow_ram_cache(const LoaderOptions &options) const {
+  return (options.get_flags() & (LoaderOptions::LF_no_ram_cache | _no_cache_flags)) == 0;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: LoaderFileType::load_file
 //     Function: LoaderFileType::load_file
 //       Access: Public, Virtual
 //       Access: Public, Virtual

+ 6 - 0
panda/src/pgraph/loaderFileType.h

@@ -50,10 +50,16 @@ PUBLISHED:
   virtual string get_additional_extensions() const;
   virtual string get_additional_extensions() const;
   virtual bool supports_compressed() const;
   virtual bool supports_compressed() const;
 
 
+  virtual bool get_allow_disk_cache(const LoaderOptions &options) const;
+  virtual bool get_allow_ram_cache(const LoaderOptions &options) const;
+
 public:
 public:
   virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options,
   virtual PT(PandaNode) load_file(const Filename &path, const LoaderOptions &options,
                                   BamCacheRecord *record) const;
                                   BamCacheRecord *record) const;
 
 
+protected:
+  int _no_cache_flags;
+
 public:
 public:
   static TypeHandle get_class_type() {
   static TypeHandle get_class_type() {
     return _type_handle;
     return _type_handle;

+ 0 - 25
panda/src/pgraph/loaderOptions.I

@@ -68,28 +68,3 @@ INLINE int LoaderOptions::
 get_flags() const {
 get_flags() const {
   return _flags;
   return _flags;
 }
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: LoaderOptions::get_allow_disk_cache
-//       Access: Published
-//  Description: Returns true if the loader flags allow retrieving the
-//               model from the on-disk bam cache (if it is enabled),
-//               false otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE bool LoaderOptions::
-get_allow_disk_cache() const {
-  return (_flags & LF_no_disk_cache) == 0;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: LoaderOptions::get_allow_ram_cache
-//       Access: Published
-//  Description: Returns true if the loader flags allow retrieving the
-//               model from the in-memory ModelPool cache, false
-//               otherwise.
-////////////////////////////////////////////////////////////////////
-INLINE bool LoaderOptions::
-get_allow_ram_cache() const {
-  return ((_flags & (LF_no_ram_cache | LF_convert_anim)) == 0 &&
-          (_flags & LF_search) != 0);
-}

+ 2 - 3
panda/src/pgraph/loaderOptions.h

@@ -39,6 +39,8 @@ PUBLISHED:
     LF_no_disk_cache     = 0x0010,  // disallow BamCache
     LF_no_disk_cache     = 0x0010,  // disallow BamCache
     LF_no_ram_cache      = 0x0020,  // disallow ModelPool
     LF_no_ram_cache      = 0x0020,  // disallow ModelPool
     LF_no_cache          = 0x0030,  // no_disk + no_ram
     LF_no_cache          = 0x0030,  // no_disk + no_ram
+    LF_cache_only        = 0x0040,  // fail if not in cache
+    LF_allow_instance    = 0x0080,  // returned pointer might be shared
   };
   };
 
 
   INLINE LoaderOptions(int flags = LF_search | LF_report_errors);
   INLINE LoaderOptions(int flags = LF_search | LF_report_errors);
@@ -48,9 +50,6 @@ PUBLISHED:
   INLINE void set_flags(int flags);
   INLINE void set_flags(int flags);
   INLINE int get_flags() const;
   INLINE int get_flags() const;
 
 
-  INLINE bool get_allow_disk_cache() const;
-  INLINE bool get_allow_ram_cache() const;
-
   void output(ostream &out) const;
   void output(ostream &out) const;
 
 
 private:
 private:

+ 38 - 0
panda/src/pgraph/modelPool.I

@@ -64,6 +64,10 @@ load_model(const string &filename, const LoaderOptions &options) {
 //               pool.  The model will always replace any
 //               pool.  The model will always replace any
 //               previously-loaded model in the pool that had the
 //               previously-loaded model in the pool that had the
 //               same filename.
 //               same filename.
+//
+//               This two-parameter version of this method is
+//               deprecated; use the one-parameter add_model(model)
+//               instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ModelPool::
 INLINE void ModelPool::
 add_model(const string &filename, ModelRoot *model) {
 add_model(const string &filename, ModelRoot *model) {
@@ -78,12 +82,46 @@ add_model(const string &filename, ModelRoot *model) {
 //               may then be freed.  If this function is never called,
 //               may then be freed.  If this function is never called,
 //               a reference count will be maintained on every model
 //               a reference count will be maintained on every model
 //               every loaded, and models will never be freed.
 //               every loaded, and models will never be freed.
+//
+//               This version of this method is deprecated; use
+//               release_model(model) instead.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 INLINE void ModelPool::
 INLINE void ModelPool::
 release_model(const string &filename) {
 release_model(const string &filename) {
   get_ptr()->ns_release_model(filename);
   get_ptr()->ns_release_model(filename);
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModelPool::add_model
+//       Access: Public, Static
+//  Description: Adds the indicated already-loaded model to the
+//               pool.  The model will always replace any
+//               previously-loaded model in the pool that had the
+//               same filename.
+////////////////////////////////////////////////////////////////////
+INLINE void ModelPool::
+add_model(ModelRoot *model) {
+  get_ptr()->ns_add_model(model);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelPool::release_model
+//       Access: Public, Static
+//  Description: Removes the indicated model from the pool,
+//               indicating it will never be loaded again; the model
+//               may then be freed.  If this function is never called,
+//               a reference count will be maintained on every model
+//               every loaded, and models will never be freed.
+//
+//               The model's get_fullpath() value should not have been
+//               changed during its lifetime, or this function may
+//               fail to locate it in the pool.
+////////////////////////////////////////////////////////////////////
+INLINE void ModelPool::
+release_model(ModelRoot *model) {
+  get_ptr()->ns_release_model(model);
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ModelPool::release_all_models
 //     Function: ModelPool::release_all_models
 //       Access: Public, Static
 //       Access: Public, Static

+ 30 - 1
panda/src/pgraph/modelPool.cxx

@@ -76,7 +76,8 @@ ns_load_model(const string &filename, const LoaderOptions &options) {
   loader_cat.info()
   loader_cat.info()
     << "Loading model " << filename << "\n";
     << "Loading model " << filename << "\n";
   LoaderOptions new_options(options);
   LoaderOptions new_options(options);
-  new_options.set_flags(new_options.get_flags() | LoaderOptions::LF_no_ram_cache);
+  new_options.set_flags((new_options.get_flags() | LoaderOptions::LF_no_ram_cache) &
+                        ~LoaderOptions::LF_search);
 
 
   PT(PandaNode) panda_node = model_loader.load_sync(filename, new_options);
   PT(PandaNode) panda_node = model_loader.load_sync(filename, new_options);
   if (panda_node.is_null()) {
   if (panda_node.is_null()) {
@@ -93,6 +94,7 @@ ns_load_model(const string &filename, const LoaderOptions &options) {
     node = new ModelRoot(filename);
     node = new ModelRoot(filename);
     node->add_child(panda_node);
     node->add_child(panda_node);
   }
   }
+  node->set_fullpath(filename);
 
 
   {
   {
     MutexHolder holder(_lock);
     MutexHolder holder(_lock);
@@ -139,6 +141,33 @@ ns_release_model(const string &filename) {
   }
   }
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModelPool::ns_add_model
+//       Access: Private
+//  Description: The nonstatic implementation of add_model().
+////////////////////////////////////////////////////////////////////
+void ModelPool::
+ns_add_model(ModelRoot *model) {
+  MutexHolder holder(_lock);
+  // We blow away whatever model was there previously, if any.
+  _models[model->get_fullpath()] = model;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelPool::ns_release_model
+//       Access: Private
+//  Description: The nonstatic implementation of release_model().
+////////////////////////////////////////////////////////////////////
+void ModelPool::
+ns_release_model(ModelRoot *model) {
+  MutexHolder holder(_lock);
+  Models::iterator ti;
+  ti = _models.find(model->get_fullpath());
+  if (ti != _models.end()) {
+    _models.erase(ti);
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ModelPool::ns_release_all_models
 //     Function: ModelPool::ns_release_all_models
 //       Access: Private
 //       Access: Private

+ 20 - 10
panda/src/pgraph/modelPool.h

@@ -30,22 +30,24 @@
 
 
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //       Class : ModelPool
 //       Class : ModelPool
-// Description : This is the preferred interface for loading models.
-//               It unifies all references to the same filename, so
-//               that multiple attempts to load the same model will
-//               return the same pointer.  Note that the default
-//               behavior is thus to make instances: use with caution.
-//               Use the copy_subgraph() method on Node (or use
-//               NodePath::copy_to) to make modifiable copies of the
-//               node.
+// Description : This class unifies all references to the same
+//               filename, so that multiple attempts to load the same
+//               model will return the same pointer.  Note that the
+//               default behavior is thus to make instances: use with
+//               caution.  Use the copy_subgraph() method on Node (or
+//               use NodePath::copy_to) to make modifiable copies of
+//               the node.
 //
 //
 //               Unlike TexturePool, this class does not automatically
 //               Unlike TexturePool, this class does not automatically
 //               resolve the model filenames before loading, so a
 //               resolve the model filenames before loading, so a
 //               relative path and an absolute path to the same model
 //               relative path and an absolute path to the same model
 //               will appear to be different filenames.
 //               will appear to be different filenames.
 //
 //
-//               This does not presently support asynchronous loading,
-//               although it wouldn't be *too* difficult to add.
+//               However, see the Loader class, which is now the
+//               preferred interface for loading models.  The Loader
+//               class can resolve filenames, supports threaded
+//               loading, and can automatically consult the ModelPool,
+//               according to the supplied LoaderOptions.
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 class EXPCL_PANDA ModelPool {
 class EXPCL_PANDA ModelPool {
 PUBLISHED:
 PUBLISHED:
@@ -56,6 +58,10 @@ PUBLISHED:
 
 
   INLINE static void add_model(const string &filename, ModelRoot *model);
   INLINE static void add_model(const string &filename, ModelRoot *model);
   INLINE static void release_model(const string &filename);
   INLINE static void release_model(const string &filename);
+
+  INLINE static void add_model(ModelRoot *model);
+  INLINE static void release_model(ModelRoot *model);
+
   INLINE static void release_all_models();
   INLINE static void release_all_models();
 
 
   INLINE static int garbage_collect();
   INLINE static int garbage_collect();
@@ -71,6 +77,10 @@ private:
                            const LoaderOptions &options);
                            const LoaderOptions &options);
   void ns_add_model(const string &filename, ModelRoot *model);
   void ns_add_model(const string &filename, ModelRoot *model);
   void ns_release_model(const string &filename);
   void ns_release_model(const string &filename);
+
+  void ns_add_model(ModelRoot *model);
+  void ns_release_model(ModelRoot *model);
+
   void ns_release_all_models();
   void ns_release_all_models();
   int ns_garbage_collect();
   int ns_garbage_collect();
   void ns_list_contents(ostream &out) const;
   void ns_list_contents(ostream &out) const;

+ 33 - 0
panda/src/pgraph/modelRoot.I

@@ -25,6 +25,7 @@
 INLINE ModelRoot::
 INLINE ModelRoot::
 ModelRoot(const string &name) :
 ModelRoot(const string &name) :
   ModelNode(name),
   ModelNode(name),
+  _fullpath(name),
   _reference(new ModelRoot::ModelReference)
   _reference(new ModelRoot::ModelReference)
 {
 {
 }
 }
@@ -45,6 +46,37 @@ get_model_ref_count() const {
   return _reference->get_ref_count();
   return _reference->get_ref_count();
 }
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: ModelRoot::get_fullpath
+//       Access: Published
+//  Description: Returns the full pathname of the model represented by
+//               this node, as found on disk.  This is mainly useful
+//               for reference purposes, but is also used to index the
+//               ModelRoot into the ModelPool.
+////////////////////////////////////////////////////////////////////
+INLINE const Filename &ModelRoot::
+get_fullpath() const {
+  return _fullpath;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: ModelRoot::set_fullpath
+//       Access: Published
+//  Description: Sets the full pathname of the model represented by
+//               this node, as found on disk.  This is mainly useful
+//               for reference purposes, but is also used to index the
+//               ModelRoot into the ModelPool.
+//
+//               This is normally set automatically when a model is
+//               loaded, and should not be set directly by the user.
+//               If you change this on a loaded model, then
+//               ModelPool::release_model() may fail.
+////////////////////////////////////////////////////////////////////
+INLINE void ModelRoot::
+set_fullpath(const Filename &fullpath) {
+  _fullpath = fullpath;
+}
+
 ////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////
 //     Function: ModelRoot::get_reference
 //     Function: ModelRoot::get_reference
 //       Access: Published
 //       Access: Published
@@ -81,6 +113,7 @@ set_reference(ModelRoot::ModelReference *ref) {
 INLINE ModelRoot::
 INLINE ModelRoot::
 ModelRoot(const ModelRoot &copy) :
 ModelRoot(const ModelRoot &copy) :
   ModelNode(copy),
   ModelNode(copy),
+  _fullpath(copy._fullpath),
   _reference(copy._reference)
   _reference(copy._reference)
 {
 {
 }
 }

+ 5 - 1
panda/src/pgraph/modelRoot.h

@@ -38,6 +38,9 @@ PUBLISHED:
 
 
   INLINE int get_model_ref_count() const;
   INLINE int get_model_ref_count() const;
 
 
+  INLINE const Filename &get_fullpath() const;
+  INLINE void set_fullpath(const Filename &fullpath);
+
   // This class is used to unify references to the same model.
   // This class is used to unify references to the same model.
   class ModelReference : public ReferenceCount {
   class ModelReference : public ReferenceCount {
   PUBLISHED:
   PUBLISHED:
@@ -45,7 +48,7 @@ PUBLISHED:
   };
   };
 
 
   INLINE ModelReference *get_reference() const;
   INLINE ModelReference *get_reference() const;
-  INLINE void set_reference(ModelReference *ref);
+  void set_reference(ModelReference *ref);
 
 
 protected:
 protected:
   INLINE ModelRoot(const ModelRoot &copy);
   INLINE ModelRoot(const ModelRoot &copy);
@@ -54,6 +57,7 @@ public:
   virtual PandaNode *make_copy() const;
   virtual PandaNode *make_copy() const;
 
 
 private:
 private:
+  Filename _fullpath;
   PT(ModelReference) _reference;
   PT(ModelReference) _reference;
 
 
 public:
 public: