Panda3D
modelPool.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file modelPool.cxx
10  * @author drose
11  * @date 2002-03-12
12  */
13 
14 #include "modelPool.h"
15 #include "loader.h"
16 #include "config_pgraph.h"
17 #include "lightMutexHolder.h"
18 #include "virtualFileSystem.h"
19 
20 
21 ModelPool *ModelPool::_global_ptr = nullptr;
22 
23 /**
24  * Lists the contents of the model pool to the indicated output stream. Helps
25  * with debugging.
26  */
27 void ModelPool::
28 write(std::ostream &out) {
29  get_ptr()->ns_list_contents(out);
30 }
31 
32 /**
33  * The nonstatic implementation of has_model().
34  */
35 bool ModelPool::
36 ns_has_model(const Filename &filename) {
37  LightMutexHolder holder(_lock);
38  Models::const_iterator ti;
39  ti = _models.find(filename);
40  if (ti != _models.end() && (*ti).second != nullptr) {
41  // This model was previously loaded.
42  return true;
43  }
44 
45  return false;
46 }
47 
48 /**
49  * The nonstatic implementation of get_model().
50  */
51 ModelRoot *ModelPool::
52 ns_get_model(const Filename &filename, bool verify) {
53 
54  PT(ModelRoot) cached_model;
55  bool got_cached_model = false;
56 
57  {
58  LightMutexHolder holder(_lock);
59  Models::const_iterator ti;
60  ti = _models.find(filename);
61  if (ti != _models.end()) {
62  // This filename was previously loaded.
63  cached_model = (*ti).second;
64  got_cached_model = true;
65  }
66  }
67 
68  if (got_cached_model && verify) {
69  if (pgraph_cat.is_debug()) {
70  pgraph_cat.debug()
71  << "ModelPool found " << cached_model << " for " << filename << "\n";
72  }
73 
74  if (cached_model == nullptr) {
75  // This filename was previously attempted, but it did not exist (or the
76  // model could not be loaded for some reason).
77  if (cache_check_timestamps) {
78  // Check to see if there is a file there now.
80  if (vfs->exists(filename)) {
81  // There is, so try to load it.
82  got_cached_model = false;
83  }
84  }
85  } else {
86  // This filename was previously attempted, and successfully loaded.
87  if (cache_check_timestamps && cached_model->get_timestamp() != 0 &&
88  !cached_model->get_fullpath().empty()) {
89  // Compare the timestamp to the file on-disk.
91  PT(VirtualFile) vfile = vfs->get_file(cached_model->get_fullpath());
92  if (vfile == nullptr) {
93  // The file has disappeared! Look further along the model-path.
94  got_cached_model = false;
95 
96  } else if (vfile->get_timestamp() > cached_model->get_timestamp()) {
97  // The file still exists, but it has a newer timestamp than the one
98  // we previously loaded. Force it to re-load.
99  got_cached_model = false;
100  }
101  }
102  }
103  }
104 
105  if (got_cached_model) {
106  if (pgraph_cat.is_debug()) {
107  pgraph_cat.debug()
108  << "ModelPool returning " << cached_model << " for " << filename << "\n";
109  }
110  return cached_model;
111  } else {
112  return nullptr;
113  }
114 }
115 
116 /**
117  * The nonstatic implementation of load_model().
118  */
119 ModelRoot *ModelPool::
120 ns_load_model(const Filename &filename, const LoaderOptions &options) {
121 
122  // First check if it has already been loaded and is still current.
123  PT(ModelRoot) cached_model = ns_get_model(filename, true);
124  if (cached_model != nullptr) {
125  return cached_model;
126  }
127 
128  // Look on disk for the current file.
129  LoaderOptions new_options(options);
130  new_options.set_flags((new_options.get_flags() | LoaderOptions::LF_no_ram_cache) &
131  ~LoaderOptions::LF_search);
132 
133  Loader *model_loader = Loader::get_global_ptr();
134  PT(PandaNode) panda_node = model_loader->load_sync(filename, new_options);
135  PT(ModelRoot) node;
136 
137  if (panda_node.is_null()) {
138  // This model was not found.
139 
140  } else {
141  if (panda_node->is_of_type(ModelRoot::get_class_type())) {
142  node = DCAST(ModelRoot, panda_node);
143 
144  } else {
145  // We have to construct a ModelRoot node to put it under.
146  node = new ModelRoot(filename);
147  node->add_child(panda_node);
148  }
149  node->set_fullpath(filename);
150  }
151 
152  {
153  LightMutexHolder holder(_lock);
154 
155  // Look again, in case someone has just loaded the model in another
156  // thread.
157  Models::const_iterator ti;
158  ti = _models.find(filename);
159  if (ti != _models.end() && (*ti).second != cached_model) {
160  // This model was previously loaded.
161  return (*ti).second;
162  }
163 
164  _models[filename] = node;
165  }
166 
167  return node;
168 }
169 
170 /**
171  * The nonstatic implementation of add_model().
172  */
173 void ModelPool::
174 ns_add_model(const Filename &filename, ModelRoot *model) {
175  LightMutexHolder holder(_lock);
176  if (pgraph_cat.is_debug()) {
177  pgraph_cat.debug()
178  << "ModelPool storing " << model << " for " << filename << "\n";
179  }
180  // We blow away whatever model was there previously, if any.
181  _models[filename] = model;
182 }
183 
184 /**
185  * The nonstatic implementation of release_model().
186  */
187 void ModelPool::
188 ns_release_model(const Filename &filename) {
189  LightMutexHolder holder(_lock);
190  Models::iterator ti;
191  ti = _models.find(filename);
192  if (ti != _models.end()) {
193  _models.erase(ti);
194  }
195 }
196 
197 /**
198  * The nonstatic implementation of add_model().
199  */
200 void ModelPool::
201 ns_add_model(ModelRoot *model) {
202  LightMutexHolder holder(_lock);
203  // We blow away whatever model was there previously, if any.
204  _models[model->get_fullpath()] = model;
205 }
206 
207 /**
208  * The nonstatic implementation of release_model().
209  */
210 void ModelPool::
211 ns_release_model(ModelRoot *model) {
212  LightMutexHolder holder(_lock);
213  Models::iterator ti;
214  ti = _models.find(model->get_fullpath());
215  if (ti != _models.end()) {
216  _models.erase(ti);
217  }
218 }
219 
220 /**
221  * The nonstatic implementation of release_all_models().
222  */
223 void ModelPool::
224 ns_release_all_models() {
225  LightMutexHolder holder(_lock);
226  _models.clear();
227 }
228 
229 /**
230  * The nonstatic implementation of garbage_collect().
231  */
232 int ModelPool::
233 ns_garbage_collect() {
234  LightMutexHolder holder(_lock);
235 
236  int num_released = 0;
237  Models new_set;
238 
239  Models::iterator ti;
240  for (ti = _models.begin(); ti != _models.end(); ++ti) {
241  ModelRoot *node = (*ti).second;
242  if (node == nullptr ||
243  node->get_model_ref_count() == 1) {
244  if (loader_cat.is_debug()) {
245  loader_cat.debug()
246  << "Releasing " << (*ti).first << "\n";
247  }
248  ++num_released;
249  } else {
250  new_set.insert(new_set.end(), *ti);
251  }
252  }
253 
254  _models.swap(new_set);
255  return num_released;
256 }
257 
258 /**
259  * The nonstatic implementation of list_contents().
260  */
261 void ModelPool::
262 ns_list_contents(std::ostream &out) const {
263  LightMutexHolder holder(_lock);
264 
265  out << "model pool contents:\n";
266 
267  Models::const_iterator ti;
268  int num_models = 0;
269  for (ti = _models.begin(); ti != _models.end(); ++ti) {
270  if ((*ti).second != nullptr) {
271  ++num_models;
272  out << (*ti).first << "\n"
273  << " (count = " << (*ti).second->get_model_ref_count()
274  << ")\n";
275  }
276  }
277 
278  out << "total number of models: " << num_models << " (plus "
279  << _models.size() - num_models << " entries for nonexistent files)\n";
280 }
281 
282 /**
283  * Initializes and/or returns the global pointer to the one ModelPool object
284  * in the system.
285  */
286 ModelPool *ModelPool::
287 get_ptr() {
288  if (_global_ptr == nullptr) {
289  _global_ptr = new ModelPool;
290  }
291  return _global_ptr;
292 }
A node of this type is created automatically at the root of each model file that is loaded.
Definition: modelRoot.h:27
A basic node of the scene graph or data graph.
Definition: pandaNode.h:64
get_model_ref_count
Returns the number of copies that exist of this particular ModelRoot node.
Definition: modelRoot.h:33
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_fullpath
Returns the full pathname of the model represented by this node, as found on disk.
Definition: modelRoot.h:37
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
A hierarchy of directories and files that appears to be one continuous file system,...
This class unifies all references to the same filename, so that multiple attempts to load the same mo...
Definition: modelPool.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A convenient class for loading models from disk, in bam or egg format (or any of a number of other fo...
Definition: loader.h:42
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static Loader * get_global_ptr()
Returns a pointer to the global Loader.
Definition: loader.I:212
static void write(std::ostream &out)
Lists the contents of the model pool to the indicated output stream.
Definition: modelPool.cxx:28
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
Similar to MutexHolder, but for a light mutex.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.