Migrating from TriLib 1 to TriLib 2

The usage of TriLib 2 is very similar to TriLib 1, but some fundamental changes have been made.

Loading a Model from a File

When loading a model from a file in TriLib 1, the following snippets are used:

// TriLib 1 Code
// --------------------
using (var assetLoader = new AssetLoader()) {
    var assetLoaderOptions = AssetLoaderOptions.CreateInstance();   //Creates the AssetLoaderOptions instance.
                                                                    //AssetLoaderOptions let you specify options to load your model.
                                                                    //(Optional) You can skip this object creation and it's parameter or pass null.
    
    //You can modify assetLoaderOptions before passing it to LoadFromFile method. You can check the AssetLoaderOptions API reference at:
    //https://ricardoreis.net/trilib/manual/html/class_tri_lib_1_1_asset_loader_options.html
    
    var wrapperGameObject = gameObject;                             //Sets the game object where your model will be loaded into.
                                                                    //(Optional) You can skip this object creation and it's parameter or pass null.

    var myGameObject = assetLoader.LoadFromFile("PATH TO MY FILE.FBX", assetLoaderOptions, wrapperGameObject); //Loads the model synchronously and stores the reference in myGameObject.
}
// TriLib 1 Code
// --------------------
using (var assetLoaderAsync = new AssetLoaderAsync()) {
    var assetLoaderOptions = AssetLoaderOptions.CreateInstance();   //Creates the AssetLoaderOptions instance.
                                                                    //AssetLoaderOptions let you specify options to load your model.
                                                                    //(Optional) You can skip this object creation and it's parameter or pass null.
    
    //You can modify assetLoaderOptions before passing it to LoadFromFile method. You can check the AssetLoaderOptions API reference at:
    //https://ricardoreis.net/trilib/manual/html/class_tri_lib_1_1_asset_loader_options.html
    
    var wrapperGameObject = gameObject;                             //Sets the game object where your model will be loaded into.
                                                                    //(Optional) You can skip this object creation and it's parameter or pass null.

    var thread = assetLoaderAsync.LoadFromFile("PATH TO MY FILE.FBX", assetLoaderOptions, wrapperGameObject, delegate(GameObject myGameObject) {
        //Here you can get the reference to the loaded model using myGameObject.
    }); //Loads the model asynchronously and returns the reference to the created Task/Thread.
}>

The first difference on TriLib 2 is that we don’t have to specify if we want to sync or async load our models since TriLib 2 will use async on platforms that support it and fallback to sync loading when there is no support.

On TriLib 2, we don’t have to instantiate an AssetLoader or AssetLoaderAsync class. All loading is done using the AssetLoader static class now.

TriLib 2 no longer returns a GameObject or a Thread when loading a model. Instead, it will produce an AssetLoaderContext object containing the loaded GameObject as the RootGameObject field and much more information regarding the model loading.

TriLib 2 also uses AssetLoaderOptions, but these have changed to adopt a cleaner and file-format agnostic approach. The way we create the AssetLoaderOptions remains the same.

Given these changes, a simple TriLib 2 model loading that matches the TriLib 1 samples above could be reproduced as:

// TriLib 2 Code
// --------------------
var assetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions();
AssetLoader.LoadModelFromFile("PATH TO MY FILE.FBX", null, delegate(AssetLoaderContext assetLoaderContext) {
    var myGameObject = assetLoaderContext.RootGameObject;
}, null, null, null, null, assetLoaderOptions);
Loading a Model from a Custom Source

TriLib 1 allows loading models from byte arrays using a callback to retrieve the custom data, a callback to check if such data exists, and a callback to return custom external textures data from any external resource the model uses:

// TriLib 1 Code
// --------------------
var modelData = File.ReadAllBytes("PATH_TO_MY_FILE.OBJ");
using (var assetLoader = new AssetLoader()) {
	var assetLoaderOptions = AssetLoaderOptions.CreateInstance();
	var myGameObject = assetLoader.LoadFromMemory(modelData, "MY_FILE.OBJ", assetLoaderOptions, null, "PATH_TO", 
	delegate(string path, int fileId, ref int fileSize) { //External data reading callback
		var resourceData = File.ReadAllBytes(path); //Reads all external file data
		fileSize = resourceData.Length; //Retrieves the external file size
		return resourceData; //Returns the external file data
	},
	delegate(string path, int fileId) { //External data checking callback
		return File.Exists(path); //Returns true when the external file exists
	},
	delegate (string path, string basePath) { //External texture data reading callback
		var finalPath = Path.Combine(basePath, path); //Combines the model base path with the texture path
		if (File.Exists(finalPath)) { //Checks if there is a file on the combined path
			return File.ReadAllBytes(finalPath); //Returns the file data
		}
		return null; //Returns null when the given file could not be found
	},
	delegate (float progress) { //Progress handling callback
		Debug.Log("Loaded:" + progress); //Writes the model loading progress to the console
	});
}

TriLib 2 uses different approaches to load models from custom data sources.

First, it doesn’t work with byte arrays, specifically anymore. TriLib 2 works with Stream data reading.
Second, any external resource the model uses has to be loaded using a class inheriting the ExternalDataMapper class.
Third, any external texture the model uses has to be loaded using a class inheriting the TextureMapper class.

The example below loads a model from a custom Stream and handles its external resources with custom ExternalDataMapper and TextureMapper classes.

// TriLib 2 Code
// --------------------
var assetLoaderOptions = AssetLoader.CreateDefaultLoaderOptions();
assetLoaderOptions.ExternalDataMapper = ScriptableObject.CreateInstance<ExternalDataMapperSample>();
assetLoaderOptions.TextureMapper = ScriptableObject.CreateInstance<TextureMapperSample>();
var modelPath = "PATH_TO_MY_MODEL.FBX";
AssetLoader.LoadModelFromStream(File.OpenRead(modelPath), modelPath, null, OnLoad, OnMaterialsLoad, OnProgress, OnError, null, assetLoaderOptions);

Here are the Mappers used on the code above. One creates Steams from external resource files and the other creates TextureLoadingContexts from files:

// TriLib 2 Code
// --------------------
public class ExternalDataMapperSample : ExternalDataMapper
{
	public override Stream Map(AssetLoaderContext assetLoaderContext, string originalFilename, out string finalPath)
	{
		finalPath = $"{assetLoaderContext.BasePath}/{FileUtils.GetFilename(originalFilename)}";
		if (File.Exists(finalPath))
		{
			Debug.Log($"Found external file at: {finalPath}");
			return File.OpenRead(finalPath);
		}
		throw new Exception($"File {originalFilename} not found.");
	}
}
// TriLib 2 Code
// --------------------
public class TextureMapperSample : TextureMapper
{
	public override TextureLoadingContext Map(AssetLoaderContext assetLoaderContext, ITexture texture)
	{
		var finalPath = $"{assetLoaderContext.BasePath}/{FileUtils.GetFilename(texture.Filename)}";
		if (File.Exists(finalPath))
		{
			var textureLoadingContext = new TextureLoadingContext
			{
				Context = assetLoaderContext,
				Stream = File.OpenRead(finalPath),
				Texture = texture
			};
			Debug.Log($"Found texture at: {finalPath}");
			return textureLoadingContext;
		}
		throw new Exception($"Texture {texture.Filename} not found.");
	}
}