Skip to main content

How to load glTF models at runtime in C++ using Unreal Engine (glTFRuntime plugin)

Unreal Engine provides loading of 3D models stored in Content directory as a format of converted assets. However, In some project, you may need to load 3D models at runtime which cannot be stored at Content directory. 

What is glTF?

glTF is a standard format (ISO/IEC International Standard) for 3D asset delivery and interchange. Wide variety of application and service supports glTF.

https://www.khronos.org/gltf/


glTFRuntime plugin

You can use glTF 3D files in Unreal Engine thanks to the glTFRutime plugin.  

https://github.com/rdeioris/glTFRuntime

However, most of the documentation focuses on Blueprint usage. If you want to use this plugin in C++, you can use below example.

(This plugin already provides AglTFRuntimeAssetActor class, but this class is derived from AActor to show how to use C++ APIs).  


How to use glTF 3D model as a member component of an actor

To use this plugin in my project, I had to use it as a member component of my pre-existing actor class. I modified AglTFRuntimeAssetActor to be USceneComponent derived class so to be added attached member of actor object.

Dynamically loaded glTF models in animation

UglTFRuntimeComponent is the class to use glTFRuntime plugin at runtime, and it can load local file or remote url. It uses original AglTFRuntimeAssetActor copied and modified its super class from USceneComponent.

// glTFRuntimeComponent.h
 
#pragma once
 
#include "CoreMinimal.h"
#include "Components/SceneComponent.h"
#include "glTFRuntimeAsset.h"
#include "glTFRuntimeComponent.generated.h"
 
UCLASS()
class MYPROJECT_API UglTFRuntimeComponent : public USceneComponent
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    UglTFRuntimeComponent();
 
    void LoadFromFile(const FString& FileName);
    void LoadFromUrl(const FString& Url);
    
protected:
    UFUNCTION()
    void OnLoadCompleted(UglTFRuntimeAsset* InAsset);
    
    void BuildMesh();
 
    // Interface function from AActor
    void SetRootComponent(USceneComponent* Component) { RootComponent = Component; }
    USceneComponent* GetRootComponent() { return RootComponent; }
    void AddInstanceComponent(USceneComponent* SceneComponent) { }
 
    USceneComponent* RootComponent;

...
// From source of glTFRuntime/Plugin's glTFRuntimeAssetActor.h

Inside of AglTFRuntimeComponent.cpp, LoadFromUrl() calls asynchronous function glTFLoadAssetFromUrl(), so it has delegate  function OnLoadCompleted() to receive result of remote loading.
// glTFRuntimeComponent.cpp
 
#include "glTFRuntimeComponent.h"
#include "Kismet/KismetSystemLibrary.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Components/LightComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/StaticMeshSocket.h"
#include "glTFRuntimeFunctionLibrary.h"
#include "Animation/AnimSequence.h"
 
// Sets default values
UglTFRuntimeComponent::UglTFRuntimeComponent()
{
    // Set this actor to call Tick() every frame.  
    PrimaryComponentTick.bCanEverTick = true;
 
    AssetRoot = CreateDefaultSubobject<USceneComponent>(TEXT("AssetRoot"));
    AssetRoot->SetupAttachment(this);
    RootComponent = AssetRoot;
 
    bAllowNodeAnimations = true;
    bStaticMeshesAsSkeletal = false;
    bAllowSkeletalAnimations = true;
    bAllowPoseAnimations = true;
    bAllowCameras = true;
    bAllowLights = true;
    bForceSkinnedMeshToRoot = false;
    RootNodeIndex = INDEX_NONE;
}
 
// Called when the game starts or when spawned
void UglTFRuntimeComponent::BeginPlay()
{
    Super::BeginPlay();
}
 
void UglTFRuntimeComponent::LoadFromFile(const FString& FileName)
{
    FglTFRuntimeConfig LoaderConfig;
    Asset = UglTFRuntimeFunctionLibrary::glTFLoadAssetFromFilename(FileName, false, LoaderConfig);
 
    BuildMesh();
}
 
void UglTFRuntimeComponent::LoadFromUrl(const FString& Url)
{
    FglTFRuntimeConfig LoaderConfig;
    TMap<FString, FString> Headers;
    FglTFRuntimeHttpResponse Response;
 
    Response.BindUFunction(this, TEXT("OnLoadCompleted"));
 
    // If you want to load glTF Samples from 'https://github.com/KhronosGroup/glTF-Sample-Models',
    // Replace model's url as below.
    // https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master 
    / + /2.0/BoxAnimated/glTF-Binary/BoxAnimated.glb
 
    UglTFRuntimeFunctionLibrary::glTFLoadAssetFromUrl(Url, Headers, Response, LoaderConfig);
}
 
void UglTFRuntimeComponent::OnLoadCompleted(UglTFRuntimeAsset* InAsset)
{
    if (InAsset == nullptr)
    {
        UE_LOG(LogTemp, Log, TEXT("Failed to load url"));
        return;
    }
 
    Asset = InAsset;
 
    BuildMesh();
}

...
// From source of glTFRuntime/Plugin's glTFRuntimeAssetActor.cpp



To show how to use this class, MyActor had member variable of glTFRuntimeComponent.

// MyActor.h
#pragma once 

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "glTFRuntimeComponent.h"
#include "MyActor.generated.h"
 
UCLASS()
class MYPROJECT_API AMyActor : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    AMyActor();
 
protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
 
    UPROPERTY(EditAnywhere, Category = "MyActor")
    FString FileName;
 
    UPROPERTY(EditAnywhere, Category = "MyActor")
    FString Url;
 
public:    
    // Called every frame
    virtual void Tick(float DeltaTime) override;
 
    UglTFRuntimeComponent* GltfComponent;
};
 
cs

// MyActor.cpp
 
#include "MyActor.h"
 
// Sets default values
AMyActor::AMyActor()
{
    // Set this actor to call Tick() every frame. 
    PrimaryActorTick.bCanEverTick = true;
 
    GltfComponent = CreateDefaultSubobject<UglTFRuntimeComponent>(TEXT("GltfComponent"));
    GltfComponent->SetupAttachment(RootComponent);
}
 
// Called when the game starts or when spawned
void AMyActor::BeginPlay()
{
    Super::BeginPlay();
 
    if (!FileName.IsEmpty())
    {
        FString FullPath = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() + FileName);
        GltfComponent->LoadFromFile(FullPath);
    }
    else if (!Url.IsEmpty())
    {
        GltfComponent->LoadFromUrl(Url);
    }
}
 
// Called every frame
void AMyActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
 
cs


 Here you can add MyActor in a level and set FileName or Url property.

MyActors with glTF components


You can see below reference as an example.

Example project:

https://github.com/odyssey2010/unreal_gltf_runtime_scene

Plus, If you download and run the example, you can watch dancing robot. 



Comments

Popular posts from this blog

Which string class consumes lower memory between std::string and FString in C++ using Unreal Engine

In many programming projects, strings are a fundamental data type that are used extensively to represent text, identifiers, and other types of data. However, strings can also consume a significant amount of memory, particularly if they are used extensively or if they contain large amounts of data. In a project that needs to save memory, it is important to carefully consider the memory usage of string objects and to take steps to minimize it. When we use Unreal Engine, FString is easy to use class due to the convenience of functionalities and tight coupling of engine's overall structure. However in some specific situation, you can use std::string instead. Let's check memory consumption of string classes. // 13 characters std:: string  MyStdString  =   "Hello, world!" ; FString  MyFString  =   "Hello, world!" ;   UE_LOG(LogTemp, Log, TEXT( "allocated size[std::string] = %d" ), MyStdString.capacity()); UE_...