Skip to content

Latest commit

 

History

History
330 lines (220 loc) · 14.9 KB

glTFast.md

File metadata and controls

330 lines (220 loc) · 14.9 KB

glTFast Documentation

glTFast enables loading glTF™ (GL Transmission Format) asset files in Unity.

It focuses on speed, memory efficiency and a small build footprint.

Two workflows are supported

  • Load glTF assets at runtime
  • Import glTF assets as prefabs into the asset database at design-time in the Unity Editor

Try the WebGL Demo and check out the demo project.

Features

glTFast supports the full glTF 2.0 specification and many extensions. It works with Universal, High Definition and the Built-In Render Pipelines on all platforms.

See all details at the list of features/extensions.

Usage

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() {
    byte[] data = File.ReadAllBytes("/path/to/file.glb");
    var gltf = new GltfImport();
    bool success = await gltf.LoadGltfBinary(data, new Uri(m_Path));
    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)
  • Load reports 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() );

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 for details.

Tune loading performance

When loading glTFs, glTFast let's you optimize for two diametrical extremes

  • 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

Usage example

async Task CustomDeferAgent() {
    // 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.

Editor Import

To convert your glTF asset into a native Unity prefab, just move/copy it and all its companioning buffer and texture files into the Assets folder of your Unity project. It'll get imported into the Asset Database automatically. Select it in the Project view to see detailed settings and import reports in the Inspector. Expand it in the Project View to see the components (Scenes, Meshes, Materials, AnimationClips and Textures) that were imported.

Project Setup

Materials and Shader Variants

❗ IMPORTANT ❗

glTF materials might require many shader/features combinations. You have to make sure all shader variants your project will ever use are included, or the materials will not work in builds (even if they work in the Editor).

glTFast uses custom shaders that are derived from the Unity Standard shaders (and have a similar big number of variants). Including all those variants can make your build big. There's an easy way to find the right subset, if you already know what files you'll expect:

  • Run your scene that loads all glTFs you expect in the editor.
  • Go to Edit->Project Settings->Graphics
  • At the bottom end you'll see the "Shader Preloading" section
  • Save the currently tracked shaders/variants to an asset
  • Take this ShaderVariantCollection asset and add it to the "Preloaded Shaders" list

An alternative way is to create placeholder materials for all feature combinations you expect and put them in a "Resource" folder in your project.

Read the documentation about Shader.Find for details how to include shaders in builds.

Readable Mesh Data

By default glTFast discards mesh data after it was uploaded to the GPU to free up main memory (see markNoLongerReadable). You can disable this globally by using the scripting define GLTFAST_KEEP_MESH_DATA.

Motivations for this might be using meshes as physics colliders amongst other cases.

Safe Mode

Arbitrary (and potentially broken) input data is a challenge to software's robustness and safety. Some measurments to make glTFast more robust have a negative impact on its performance though.

For this reason some pedantic safety checks in glTFast are not performed by default. You can enable safe-mode by adding the scripting define GLTFAST_SAFE to your project.

Enable safe-mode if you are not in control over what content your application may end up loading and you cannot test up front.

Upgrade Guides

Upgrade to 4.x

Coordinate system conversion change

When upgrading from an older version to 4.x or newer the most notable difference is the imported models' orentation. They will appear 180° rotated around the up-axis (Y).

GltfAsset component

To counter-act this in applications that used older versions of glTFast before, make sure you rotate the parent Transform by 180° around the Y-axis, which brings the model back to where it should be.

This change was implemented to conform more closely to the glTF specification, which says:

The front of a glTF asset faces +Z.

In Unity, the positive Z axis is also defined as forward, so it makes sense to align those and so the coordinate space conversion from glTF's right-handed to Unity's left-handed system is performed by inverting the X-axis (before the Z-axis was inverted).

New Logging

During loading and instantiation, glTFast used to log messages (infos, warnings and errors) directly to Unity's console. The new logging solution allows you to:

  • Omit glTFast logging completely to avoid clogging the message log
  • Retrieve the logs to process them (e.g. reporting analytics or inform the user properly)

See Logging above.

Scene based instantiation

glTFast 4.0 introduces scene-based instantiation. While most glTF assets contain only one scene they could consist of multiple scenes and optionally have one of declared the default scene.

The old behaviour was, that all of the glTF's content was loaded. The new interface allows you to load the default scene or any scene of choice. If none of the scenes was declared the default scene (by setting the scene property), no objects are instantiated (as defined in the glTF specification).

GltfImport (formerly named GLTFast) provides the following properties and methods for scene instantiation:

// To get the number of scenes
public int sceneCount;
// Returns the default scene's index
public int? defaultSceneIndex;
// Methods for instantiation
public bool InstantiateMainScene( Transform parent );
public bool InstantiateMainScene(IInstantiator instantiator);
public bool InstantiateScene( Transform parent, int sceneIndex = 0);
public bool InstantiateScene( IInstantiator instantiator, int sceneIndex = 0 );

Please look at GltfAsset for a reference implementation and look at the properties'/methods' XML documentation comments in the source code for details.

Custom material generation

Creating a custom IMaterialGenerator was mainly about implementing the following method:

Material GenerateMaterial(Schema.Material gltfMaterial, ref Schema.Texture[] textures, ref Schema.Image[] schemaImages, ref Dictionary<int, Texture2D>[] imageVariants);

You'd receive all textures/images/image variants to pick from. This was changed to:

Material GenerateMaterial(Schema.Material gltfMaterial, IGltfReadable gltf);

IGltfReadable is an interface that allows you to query all loaded textures and much more, allowing more flexible implementations. Please look at the source code.

In the future materials can be created before textures are available/downloaded to speed up the loading.

Implementation details

glTFast uses Unity's JsonUtility for parsing, which has little overhead, is fast and memory-efficient (See https://docs.unity3d.com/Manual/JSONSerialization.html).

It also uses fast low-level memory copy methods, Unity's Job system and the Advanced Mesh API.