7 minutes
Understanding Assets
Before diving into our mesh loading system, it's important to understand Babylon.js's AssetContainer class. An AssetContainer is a lightweight container that holds references to assets (meshes, materials, textures, etc.) without adding them to the scene immediately.
What is an AssetContainer?
Key Benefits of AssetContainer
Memory Management: AssetContainers allow you to load assets without immediately consuming scene resources. This is perfect for:
- Preloading assets for later use
- Managing asset lifecycles independently
- Reducing initial scene complexity
Asset Reuse: Once loaded in a container, assets can be cloned or instantiated multiple times without reloading from disk:
You can add all assets from a container to the scene at once, or selectively add specific assets:
Understanding AssetManager
The AssetManager is Babylon.js's task-based asset loading system. It provides a powerful way to load multiple assets with progress tracking, error handling, and lifecycle management.
Core Concepts
Task-Based Loading: Instead of loading assets individually, you create tasks that describe what to load:
Task Types Available
The AssetManager supports several task types for different asset loading needs:
Mesh Tasks: Load individual meshes or entire model files
Container Tasks: Load assets into an AssetContainer for later use
Texture Tasks: Load individual textures
Progress Tracking and Events
AssetManager provides comprehensive progress tracking and event handling:
Loading and Lifecycle Management
The AssetManager follows a simple lifecycle pattern:
Advanced Features
Auto-Hide Loading UI: Control Babylon's built-in loading screen
Task Dependencies: While not built-in, you can chain tasks based on success:
Using AssetManager in a Entity Component System
Let's walk through building a mesh loading component system that loads 3D models for each entity in your game. We'll break down the code and explain how the load() function (which should probably be named loadAllEntities()) works to get meshes on in the game before any other system needs them.
First, we need to import everything we'll use from Babylon.js and our ECS framework:
We're pulling in Mesh for our 3D model type and the base Component class from our ECS system.
Mesh Component Options
Next, we define what data our mesh component needs when it's created:
Simple but effective - we just need the source path to the 3D model file we want to load.
Creating the Mesh Component
Now we build the actual component class that holds mesh data:
The component tracks the current source path, the last loaded source (for change detection), and holds a reference to the actual loaded mesh. This lets us know when we need to reload a mesh if the source changes.
Setting Up the System Imports
For the system, we need more Babylon.js functionality:
AssetsManager handles loading multiple assets efficiently, and DracoCompression provides compressed mesh support for better performance.
Creating the Mesh System
The system handles all the mesh loading logic. Let's start with the constructor:
We create an AssetsManager instance that will handle loading all our meshes. The autoHideLoadingUI setting prevents Babylon from showing its default loading screen.
Configuring Draco Compression
Next, we set up Draco compression for efficient mesh loading:
This configures the paths to the Draco decoder files, allowing us to load compressed meshes for better performance and smaller file sizes.
Loading Individual Meshes
Here's where the actual mesh loading happens for each entity:
We get the mesh component from the entity and create a mesh loading task. The addMeshTask method queues up the mesh for loading through the assets manager.
Handling Successful Mesh Loading
When a mesh loads successfully, we need to store it in the component:
We grab the first loaded mesh, store it in our component, and update the lastSrc to track that this source has been loaded. The console log helps with debugging.
Handling Loading Errors
We also need to handle cases where mesh loading fails:
This logs detailed error information to help debug mesh loading issues.
Triggering the Load Process
Finally, we start the loading process and reset the assets manager:
The load() method starts loading all queued assets, and reset() clears the queue for the next batch. This is the function that maybe should be named loadAllEntities() since it processes all queued mesh loading tasks.
Processing Entities Each Frame
The system checks each frame if any meshes need to be loaded:
We compare the current source with the last loaded source. If they're different, it means the mesh source has changed and we need to load the new mesh. This gives us dynamic mesh swapping capabilities.
In Sum
This mesh loading system efficiently loads 3D models for your entities using Babylon's AssetManager. It supports Draco compression for performance, handles loading errors gracefully, and can dynamically swap meshes when sources change. The load() function (which could be better named loadAllEntities()) processes all queued mesh loading tasks in batches, making it efficient for loading multiple models at once.