We’ve seen a basic way to load models in Model Files using
The major problem with this call is that it blocks the main thread while the
model is being loaded, which means that all other tasks on the main thread
(including Panda’s rendering task) are blocked until the model has finished
loading. This is noticeable by the user as a jarring lag, especially when the
application freezes for longer periods of time.
Clearly, this does not provide a good user experience. Therefore, it is recommended that models are loaded in an asynchronous manner, in a separate thread of execution, so that the application can continue rendering while the load operation occurs in the background. Panda3D provides several ways of doing so.
Loading in a coroutine
A convenient way to do this would be by using Coroutines, introduced
in C++20. These are special functions that can be suspended temporarily and
resumed at a later point (pending the completion of an asynchronous
operation). Instead, we could write our code as though it were synchronous,
but we insert the
co_await keyword where we want the task to be suspended
while waiting for the following operation.
Unfortunately, as of Panda3D 1.10, this feature of C++20 is not yet supported by Panda3D. If you are feeling adventurous, see this forum thread for a way to use C++20 coroutines with the Panda3D task system:
Loading in a thread
Alternatively, it is possible to use a separate thread to initiate the model load. Panda3D’s scene graph is thread-safe and can safely handle model operations from any thread. See the Threading page for more details.
One thing to note is that you may want to make sure that you complete all model operations (positioning, material assignments, etc.) before attaching it into the scene graph. Otherwise, if Panda3D happens to render a frame in between those calls, there is a chance that the model may briefly appear in its original state.
On-demand texture loading
In addition, you can further ask textures to be loaded to the graphics card asynchronously. This means that the first time you look at a particular model, the texture might not be available; but instead of holding up the frame while we wait for it to be loaded, Panda can render the model immediately, with a very low-resolution version of the texture or even a flat color, and start loading of the full-resolution version in the background. When the texture is eventually loaded, it will be applied. This results in fewer frame-rate chugs, but it means that the model looks a little weird at first. It has the greatest advantage when you are using lazy-load textures as well as texture compression, because it means these things will happen in the background. Use these configuration options to enable this behavior:
preload-textures 0 preload-simple-textures 1 simple-image-size 16 16 compressed-textures 1 allow-incomplete-render 1
When converting models to .bam with
preload-simple-textures active, simple
textures will be baked into the model, so that Panda (starting with version
1.10.11) doesn’t need to load the textures from disk at all until they first
come into view.
To test this process, you can set
async-load-delay with a value in seconds,
which artificially delays each individual texture load by the given amount.
This is useful for simulating the user experience on older computers with slower
hard drives. Set it to a value like
0.1 and you should see the textures pop
in as you move around the scene.
You can use
set_texture_reload_priority() if you want
ensure that textures in some scenes are loaded with higher priority than other
A similar behavior can be enabled for Actors, so that when you have an Actor
with a large number of animations (too many to preload them all at once), you
can have the Actor load them on-demand, so that when you play an animation, the
animation may not start playing immediately, but will instead be loaded in the
background. Until it is ready, the actor will hold its last pose, and then when
the animation is fully loaded, the actor will start playing where it would have
been had the animation been loaded from the beginning. To make this work, you
have to run all of the animations through
egg-optchar with the
option, and you might also want to set:
allow-async-bind 1 restore-initial-pose 0
All of the above asynchronous operations will take place on a separate
task chain, automatically created by
By default, one low-priority thread is created to serve these requests.
To increase the number of available threads, or to increase their priority,
these configuration variables can be changed:
# default is 1 loader-num-threads 2 # default is low loader-thread-priority normal