Skip to content

Latest commit

 

History

History
281 lines (200 loc) · 11.2 KB

ImportRuntime.md

File metadata and controls

281 lines (200 loc) · 11.2 KB

Runtime Loading

You can load a glTF asset from an URL or a file path.

Note: glTFs are loaded via UnityWebRequests. File paths have to be prefixed with file:// in the Unity Editor and on certain platforms (e.g. iOS).

Runtime Loading via Component

Add a GltfAsset component to a GameObject.

GltfAsset component

Runtime Loading via Script

var gltf = gameObject.AddComponent<GLTFast.GltfAsset>();
gltf.url = "https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF/Duck.gltf";

Load from byte array

In case you want to handle download/file loading yourself, you can load glTF binary files directly from C# byte[] like so:

async void LoadGltfBinaryFromMemory() {
    var filePath = "/path/to/file.glb";
    byte[] data = File.ReadAllBytes(filePath);
    var gltf = new GltfImport();
    bool success = await gltf.LoadGltfBinary(
        data, 
        // The URI of the original data is important for resolving relative URIs within the glTF
        new Uri(filePath)
        );
    if (success) {
        success = gltf.InstantiateMainScene(transform);
    }
}

Note: Most users want to load self-contained glTF binary files this way, but LoadGltfBinary also takes the original URI of glTF file as second parameter, so it can resolve relative URIs.

Customize loading behavior

Loading via script allows you to:

  • Custom download or file loading behaviour (see IDownloadProvider)
  • Customize loading behaviour (like texture settings) via ImportSettings
  • Custom material generation (see IMaterialGenerator])
  • Customize instantiation
  • Load glTF once and instantiate its scenes many times (see example below)
  • Access data of glTF scene (for example get material; see example below)
  • Logging allow reacting and communicating incidents during loading and instantiation
  • Tweak and optimize loading performance

Import Settings

GltfImport.Load accepts an optional instance of ImportSettings as parameter. Have a look at this class to see all options available. Here's an example usage:

async void Start() {
    var gltf = new GLTFast.GltfImport();

    // Create a settings object and configure it accordingly
    var settings = new ImportSettings {
        generateMipMaps = true,
        anisotropicFilterLevel = 3,
        nodeNameMethod = ImportSettings.NameImportMethod.OriginalUnique
    };
    
    // Load the glTF and pass along the settings
    var success = await gltf.Load("file:///path/to/file.gltf", settings);

    if (success) {
        gltf.InstantiateMainScene(new GameObject("glTF").transform);
    }
    else {
        Debug.LogError("Loading glTF failed!");
    }
}

Custom Post-Loading Behaviour

The async Load method can be awaited and followed up by custom behaviour.

async void Start() {
    // First step: load glTF
    var gltf = new GLTFast.GltfImport();
    var success = await gltf.Load("file:///path/to/file.gltf");

    if (success) {
        // Here you can customize the post-loading behavior
        
        // Get the first material
        var material = gltf.GetMaterial();
        Debug.LogFormat("The first material is called {0}", material.name);

        // Instantiate the glTF's main scene
        gltf.InstantiateMainScene( new GameObject("Instance 1").transform );
        // Instantiate the glTF's main scene
        gltf.InstantiateMainScene( new GameObject("Instance 2").transform );

        // Instantiate each of the glTF's scenes
        for (int sceneId = 0; sceneId < gltf.sceneCount; sceneId++) {
            gltf.InstantiateScene(transform, sceneId);
        }
    } else {
        Debug.LogError("Loading glTF failed!");
    }
}

Instantiation

Creating actual GameObjects (or Entities) from the imported data (Meshes, Materials) is called instantiation.

You can customize it by providing an implementation of IInstantiator ( see source and the reference implementation GameObjectInstantiator for details).

Inject your custom instantiation like so

public class YourCustomInstantiator : GLTFast.IInstantiator {
  // Your code here
}// In your custom post-loading script, use it like this
  gltfAsset.InstantiateMainScene( new YourCustomInstantiator() );

GameObjectInstantiator Setup

The GameObjectInstantiator accepts settings (via the constructor's settings parameter).

skinUpdateWhenOffscreen

Meshes that are skinned or have morph targets and are animated might move way outside their initial bounding box and thus break the culling. To prevent this the SkinnedMeshRenderer's Update When Offscreen property is enabled by default. This comes at a runtime performance cost (see Determining a GameObject’s visibility from the documentation).

You can disable this by setting skinUpdateWhenOffscreen to false.

layer

Instantated GameObjects will be assigned to this layer.

mask

Allows you to filter components based on types (e.g. Meshes, Animation, Cameras or Lights).

lightIntensityFactor

Whenever glTF lights appear too bright or dim, you can use this setting to adjust their intensity, which are multiplied by this factor.

Two common use-cases are

  1. Scale-down (physically correct) intensities to compensate for the missing exposure control (or high sensitivity) of a render pipeline (e.g. Universal or Built-in Render Pipeline)
  2. Boost implausibly low light intensities

See Physical Light Units in glTF for a detailed explaination.

Instance Access

After a glTF scene was instanced, you can access selected components for further adjustments. Some of those are:

  • Animation
  • Cameras
  • Lights

GameObjectInstantiator provides a SceneInstance for that purpose. Here's some code that demonstrates how to access it

async void Start() {

    var gltfImport = new GltfImport();
    await gltfImport.Load("test.gltf");
    var instantiator = new GameObjectInstantiator(gltfImport,transform);
    var success = gltfImport.InstantiateMainScene(instantiator);
    if (success) {
        
        // Get the SceneInstance to access the instance's properties
        var sceneInstance = instantiator.sceneInstance;

        // Enable the first imported camera (which are disabled by default)
        if (sceneInstance.cameras is { Count: > 0 }) {
            sceneInstance.cameras[0].enabled = true;
        }
        
        // Decrease lights' ranges
        if (sceneInstance.lights != null) {
            foreach (var glTFLight in sceneInstance.lights) {
                glTFLight.range *= 0.1f;
            }
        }
        
        // Play the default (i.e. the first) animation clip
        var legacyAnimation = instantiator.sceneInstance.legacyAnimation;
        if (legacyAnimation != null) {
            legacyAnimation.Play();
        }
    }
}

Logging

When loading a glTF file, glTFast logs messages of varying severity (errors, warnigns or infos). Developers can choose what to make of those log messages. Examples:

  • Log to console in readable form
  • Feed the information into an analytics framework
  • Display details to the users

The provided component GltfAsset logs all of those messages to the console by default.

You can customize logging by providing an implementation of ICodeLogger to methods like GltfImport.Load or GltfImport.InstanciateMainScene.

There are two common implementations bundled. The ConsoleLogger, which logs straight to console (the default) and CollectingLogger, which stores messages in a list for users to process.

Look into ICodeLogger and [LogMessages][LogMessages] for details.

Tune loading performance

When loading glTFs, glTFast let's you optimize towards one of two diametrical goals

  • A stable frame rate
  • Fastest loading time

By default each GltfAsset instance tries not to block the main thread for longer than a certain time budget and defer the remaining loading process to the next frame / game loop iteration.

If you load many glTF files at once, by default they won't be aware of each other and collectively might block the main game loop for too long.

You can solve this by using a common "defer agent". It decides if work should continue right now or at the next game loop iteration. glTFast comes with two defer agents

  • TimeBudgetPerFrameDeferAgent for stable frame rate
  • UninterruptedDeferAgent for fastest, uninterrupted loading

The recommended way is to set a global default defer agent. The easiest way to do this is to add the prefab Runtime/Prefabs/glTF-StableFramerate.prefab to your entrance scene. You can change the FrameBudget value of its TimeBudgetPerFrameDeferAgent component to tweak performance to your needs. An alternative for fastest loading is the prefab in Runtime/Prefabs/glTF-FastestLoading.prefab.

You can accomplish the same from script by calling GltfImport.SetDefaultDeferAgent (and UnsetDefaultDeferAgent, respectively).

For most granular control, you can pass a custom defer agent to each individual GltfImport instance:

async Task CustomDeferAgentPerGltfImport() {
    // Recommended: Use a common defer agent across multiple GltfImport instances!
    // For a stable frame rate:
    IDeferAgent deferAgent = gameObject.AddComponent<TimeBudgetPerFrameDeferAgent>();
    // Or for faster loading:
    deferAgent = new UninterruptedDeferAgent();

    var tasks = new List<Task>();
    
    foreach( var url in manyUrls) {
        var gltf = new GLTFast.GltfImport(null,deferAgent);
        var task = gltf.Load(url).ContinueWith(
            t => {
                if (t.Result) {
                    gltf.InstantiateMainScene(transform);
                }
            },
            TaskScheduler.FromCurrentSynchronizationContext()
            );
        tasks.Add(task);
    }

    await Task.WhenAll(tasks);
}

Note 1: Depending on your glTF scene, using the UninterruptedDeferAgent may block the main thread for up to multiple seconds. Be sure to not do this during critical game play action.

Note2 : Using the TimeBudgetPerFrameDeferAgent does not guarantee a stutter free frame rate. This is because some sub tasks of the loading routine (like uploading a texture to the GPU) may take too long, cannot be interrupted and have to be done on the main thread.