@@ -63,14 +63,28 @@ class Loader::Impl
6363 void pre_load_model (LoaderOptions& this_options, bool & this_ok_missing,
6464 boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing);
6565
66+ /* *
67+ * A model or sound file or some such thing has just been
68+ * loaded asynchronously by the sub-thread. Add it to the list
69+ * of loaded objects, and call the appropriate callback when it's
70+ * time.
71+ */
72+ void got_async_object (const Event* ev);
73+
6674public:
75+ static size_t loader_index_;
76+
6777 ShowBase& base_;
6878 ::Loader* loader_;
79+
80+ std::string hook_;
81+ std::unordered_map<AsyncTask*, std::pair<std::shared_ptr<Callback>, size_t >> requests_;
6982};
7083
84+ size_t Loader::Impl::loader_index_ = 0 ;
85+
7186Loader::Impl::Impl (ShowBase& base): base_(base)
7287{
73- loader_ = ::Loader::get_global_ptr ();
7488}
7589
7690void Loader::Impl::pre_load_model (LoaderOptions& this_options, bool & this_ok_missing,
@@ -101,19 +115,114 @@ void Loader::Impl::pre_load_model(LoaderOptions& this_options, bool& this_ok_mis
101115 this_options.set_flags (this_options.get_flags () | LoaderOptions::LF_allow_instance);
102116}
103117
118+ void Loader::Impl::got_async_object (const Event* ev)
119+ {
120+ if (ev->get_num_parameters () != 1 )
121+ {
122+ rppanda_showbase_cat.error () << " Invalid number of paramter: " << ev->get_num_parameters () << std::endl;
123+ return ;
124+ }
125+
126+ const auto & param = ev->get_parameter (0 );
127+ auto request = DCAST (AsyncTask, param.get_typed_ref_count_value ());
128+
129+ auto found = requests_.find (request);
130+ if (found == requests_.end ())
131+ return ;
132+
133+ auto cb = found->second .first ; // should hold callback for Callback::get_object
134+ auto i = found->second .second ;
135+
136+ if (cb->cancelled () || request->cancelled ())
137+ {
138+ // Shouldn't be here.
139+ requests_.erase (request);
140+ return ;
141+ }
142+
143+ cb->requests_ .erase (request);
144+ if (!cb->requests_ .empty ())
145+ requests_.erase (request);
146+
147+ PandaNode* result = DCAST (PandaNode, request->get_result ());
148+
149+ cb->got_object (i, NodePath (result));
150+ }
151+
152+ // ************************************************************************************************
153+
154+ Loader::Callback::Callback (Loader* loader, int num_objects,
155+ const std::function<void (const std::vector<NodePath>&)>& callback) : loader_(loader), callback_(callback)
156+ {
157+ objects_.resize (num_objects);
158+ }
159+
160+ void Loader::Callback::got_object (size_t index, NodePath object)
161+ {
162+ objects_[index] = object;
163+
164+ if (!done ())
165+ return ;
166+
167+ loader_ = nullptr ;
168+ if (callback_)
169+ callback_ (objects_);
170+ }
171+
172+ void Loader::Callback::cancel ()
173+ {
174+ if (!loader_)
175+ return ;
176+
177+ for (const auto & request : requests_)
178+ {
179+ loader_->impl_ ->loader_ ->remove (request);
180+ loader_->impl_ ->requests_ .erase (request);
181+ }
182+
183+ loader_ = nullptr ;
184+ requests_.clear ();
185+ request_list_.clear ();
186+ }
187+
188+ bool Loader::Callback::cancelled () const
189+ {
190+ return request_list_.empty ();
191+ }
192+
193+ bool Loader::Callback::done () const
194+ {
195+ return requests_.empty ();
196+ }
197+
198+ void Loader::Callback::wait () const
199+ {
200+ for (const auto & r : requests_)
201+ r->wait ();
202+ }
203+
104204// ************************************************************************************************
105205
106206TypeHandle Loader::type_handle_;
107207
108208Loader::Loader (ShowBase& base): impl_(std::make_unique<Impl>(base))
109209{
210+ impl_->loader_ = ::Loader::get_global_ptr ();
211+
212+ impl_->hook_ = " async_loader_" + std::to_string (Loader::Impl::loader_index_);
213+ Loader::Impl::loader_index_ += 1 ;
214+ accept (impl_->hook_ , std::bind (&Impl::got_async_object, impl_.get (), std::placeholders::_1));
110215}
111216
112217#if !defined(_MSC_VER) || _MSC_VER >= 1900
113218Loader::Loader (Loader&&) = default;
114219#endif
115220
116- Loader::~Loader () = default ;
221+ Loader::~Loader ()
222+ {
223+ ignore (impl_->hook_ );
224+ impl_->loader_ ->stop_threads ();
225+ }
117226
118227#if !defined(_MSC_VER) || _MSC_VER >= 1900
119228Loader& Loader::operator =(Loader&&) = default ;
@@ -122,7 +231,8 @@ Loader& Loader::operator=(Loader&&) = default;
122231NodePath Loader::load_model (const Filename& model_path, const LoaderOptions& loader_options,
123232 boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing)
124233{
125- return load_model (std::vector<Filename>{model_path}, loader_options, no_cache, allow_instance, ok_missing).front ();
234+ return load_model (std::vector<Filename>{model_path}, loader_options, no_cache,
235+ allow_instance, ok_missing).front ();
126236}
127237
128238std::vector<NodePath> Loader::load_model (const std::vector<Filename>& model_list, const LoaderOptions& loader_options,
@@ -134,6 +244,7 @@ std::vector<NodePath> Loader::load_model(const std::vector<Filename>& model_list
134244 bool this_ok_missing;
135245 impl_->pre_load_model (this_options, this_ok_missing, no_cache, allow_instance, ok_missing);
136246
247+ // We got no callback, so it's a synchronous load.
137248 std::vector<NodePath> result;
138249 for (const auto & model_path: model_list)
139250 {
@@ -151,6 +262,50 @@ std::vector<NodePath> Loader::load_model(const std::vector<Filename>& model_list
151262 return result;
152263}
153264
265+ std::shared_ptr<Loader::Callback> Loader::load_model_async (const Filename& model_path, const LoaderOptions& loader_options,
266+ boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing,
267+ const std::function<void (const std::vector<NodePath>&)>& callback,
268+ boost::optional<int> priority)
269+ {
270+ return load_model_async (std::vector<Filename>{model_path}, loader_options, no_cache,
271+ allow_instance, ok_missing);
272+ }
273+
274+ std::shared_ptr<Loader::Callback> Loader::load_model_async (const std::vector<Filename>& model_list, const LoaderOptions& loader_options,
275+ boost::optional<bool > no_cache, bool allow_instance, boost::optional<bool > ok_missing,
276+ const std::function<void (const std::vector<NodePath>&)>& callback,
277+ boost::optional<int> priority)
278+ {
279+ rppanda_showbase_cat.debug () << " Loading model: " << join_to_string (model_list) << std::endl;
280+
281+ LoaderOptions this_options (loader_options);
282+ bool this_ok_missing;
283+ impl_->pre_load_model (this_options, this_ok_missing, no_cache, allow_instance, ok_missing);
284+
285+ // We got a callback, so we want an asynchronous(threaded)
286+ // load.We'll return immediately, but when all of the
287+ // requested models have been loaded, we'll invoke the
288+ // callback(passing it the models on the parameter list).
289+
290+ auto cb = std::make_shared<Callback>(this , model_list.size (), callback);
291+
292+ size_t i = 0 ;
293+ for (const auto & model_path: model_list)
294+ {
295+ PT (AsyncTask) request = impl_->loader_ ->make_async_request (model_path, this_options);
296+ if (priority)
297+ request->set_priority (priority.value ());
298+ request->set_done_event (impl_->hook_ );
299+ impl_->loader_ ->load_async (request);
300+ cb->requests_ .insert (request);
301+ cb->request_list_ .push_back (request);
302+ impl_->requests_ .insert ({request.p (), {cb, i}});
303+ i += 1 ;
304+ }
305+
306+ return cb;
307+ }
308+
154309PT (TextFont) Loader::load_font(const std::string& model_path,
155310 boost::optional<float > space_advance, boost::optional<float > line_height,
156311 boost::optional<float > point_size,
0 commit comments