Skip to content
Li, Xizhi edited this page Jun 23, 2020 · 8 revisions

Asset Manifest & Asynchronous Asset Loading

A graphical application usually depends on tons of assets (textures, models, etc) to process and render. It is usually not possible to deploy all assets to client machine at installation time, instead, assets are downloaded from the server usually on first use.

NPL/ParaEngine has built-in support for asynchronous asset loading via asset manifest system. Basically, it resolves two problems:

  1. Use a plain text file to look up and download assets in the background in several worker threads.
  2. Most UI and 3d objects in ParaEngine provide a synchronous interface to set assets as if they are already available. In reality, they will automatically resolve assets (also their dependencies) when those assets are available locally.

Asset Manifest Manager

When an application starts, NPLRuntime will read all Assets_manifest*.txt file under the root directory. Each file has the following content

	format is [relative path],md5,fileSize 

if the name ends with .z, it is zipped. This could be 4MB uncompressed in size md5 is checksum code of the file. fileSize is the compressed file size.

	audio/music.mp3.z,3799134715,22032
	model/building/tree.dds.z,2957514200,949
	model/building/tree.x.z,2551621901,816
	games/tutorial.swf,1157008036,171105

When one of the async loader try to load an application asset(texture, model, etc), it will first search in AssetManifest using the TO-LOWER-CASED asset path, such as (model/building/tree.x). it will then search the "temp/cache/" directory for a matching file

The file matching is done by comparing the line in the asset file with the filename in the cache directory, using their md5 and size. For example:

audio/music.mp3.z,3799134715,22032 matches to file 379913471522032

Example Usage:

AssetFileEntry* pEntry = CAssetManifest::GetSingleton().GetFile("Texture/somefile.dds");
if(pEntry && pEntry->DoesFileExist())
{
	// Load from file pEntry->GetLocalFileName();
}

You can also load additional asset manifest file(s) at runtime.

local asset_manager = ParaEngine.GetAttributeObject():GetChild("AssetManager");
local asset_manifest = asset_manager:GetChild("CAssetManifest");
asset_manifest:SetField("LoadManifestFile", "path_to_your_asset_manifest.txt");

Asset Web Server & CDN toolchain

At runtime, we can dynamically set the asset web server, where the game engine will be used to download these asset files, such as below.

ParaAsset.SetAssetServerUrl("http://cdn.keepwork.com/update61/assetdownload/update/");

In a real application, like in paracraft, this is configured in GameClient.config.xml file.

<asset_server_addresses bg_loader_list ="http://update.61.com/haqi/assetupdate">
  <address host="http://update.61.com/haqi/assetupdate/"/>
</asset_server_addresses>

And we usually build toolchain to generate those asset files and upload them to a CDN web asset server. For example, to deploy a file like character/cc/02human/paperman/boy02.x we will generate a file at CDN server character/cc/02human/paperman/boy02.x.z,cb219320ace5067872f528e2f2a3427a,2518758

And if our asset server is http://cdn.keepwork.com/update61/assetdownload/update/ then, the game engine will download the following file at runtime. The game engine will also check its md5.

http://cdn.keepwork.com/update61/assetdownload/update/character/cc/02human/paperman/boy02.x.z,cb219320ace5067872f528e2f2a3427a,2518758

So you need to append character/cc/02human/paperman/boy02.x.z,cb219320ace5067872f528e2f2a3427a,2518758 to a separate line in assets_manifest.txt or assets_manifest[XXX].txt

Preload Asset

Sometimes, you may want to preload assets like textures and models and display a progress bar before presenting to the user. This can be done using the ParaAssetObject's LoadAsset, IsLoaded, UnloadAsset, Reload method.

Because asset is async loaded, calling LoadAsset() does not mean the asset is loaded, it may take a few seconds before IsLoaded can return true. So one should check it periodically in a timer.

local asset = ParaAsset.LoadTexture("", "texture/white.png", 1);
-- local asset = ParaAsset.LoadStaticMesh("", model_filename);
asset:LoadAsset();
local mytimer = commonlib.Timer:new({callbackFunc = function(timer)
  if(asset:IsLoaded()) then
     echo("asset loaded!");
     timer:Change(); 
  end  
end})
mytimer:Change(0, 1000);

There is a helper class for this task in script/ide/AssetPreloader.lua

Clone this wiki locally