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