언리얼 엔진에서는 변환된 에셋 형식으로 저장된 Content 디렉토리에 저장된 3D 모델을 로딩할 수 있습니다. 그러나 프로젝트에 따라 Content 디렉토리에 저장할 수 없는 런타임에 3D 모델을 로드해야 할 수도 있습니다. 예를 들어, 사용자가 만든 아바타 또는 3D 갤러리에 전시자가 업로드한 작품, 메타버스 환경의 사용자 제작 월드 등의 프로그램에서라면 동적으로 3D 모델을 로드해야 합니다.
glTF Plugin
이런 경우에 glTF 포맷과 플러그인을 사용하면 3D 모델을 런타임에 동적으로 로드해서 사용할 수 있습니다.
glTF는 3D 에셋 전달 및 교환을 위한 ISO/IEC 국제 표준 형식입니다. 다양한 애플리케이션 및 서비스에서 glTF를 지원합니다. https://www.khronos.org/gltf/
glTFRutime 플러그인을 사용하여 언리얼 엔진에서 glTF 3D 파일을 사용할 수 있습니다.
https://github.com/rdeioris/glTFRuntime
그러나 이 플러그인을 사용하기 위한 대부분의 설명 문서는 블루프린트 사용에 초점을 맞추고 있습니다.
C++에서 이 플러그인을 사용하려면 아래 예시를 사용할 수 있습니다. (이 플러그인은 이미 AglTFRuntimeAssetActor 클래스를 제공하지만, 이 클래스는 C++ API를 사용하는 방법을 보여주기 위한 목적으로 AActor에서 파생된 것입니다).
glTF 3D 모델을 Actor의 멤버 Component로 사용하기
대부분의 프로젝트에서는 주로 사용하는 Actor 객체를 클래스로 정의해서 사용하고 있기 때문에 이 플러그인을 프로젝트에서 사용하기 위해, 기존 Actor 클래스의 멤버 컴포넌트로 추가될 수 있도록 USceneComponent에서 파생된 클래스로 수정해서 사용하는 것이 좋습니다.
UglTFRuntimeComponent는 glTFRuntime 플러그인을 사용해 런타임에 로컬 파일 또는 URL을 로드할 수 있습니다. 이 클래스는 AglTFRuntimeAssetActor를 복사해서 USceneComponent에서 상속한 클래스를 만들수 있습니다.
아래는 glTFRuntimeComponent.h의 코드입니다.
// 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
AglTFRuntimeComponent.cpp 내부에서는 LoadFromUrl() 함수가 glTFLoadAssetFromUrl()을 호출하며, 이 함수는 비동기 실행되기 때문에 Remote 로딩의 결과를 전달 받기 위해 OnLoadCompleted() 함수를 가지고 있습니다.
// 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
MyActor의 멤버 컴포넌트로 사용
이 클래스의 사용법을 보여주기 위해, 아래의 코드에서 MyActor에 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;
};
// 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);
}
샘플 프로젝트
아래는 이 프로젝트에서 여러개의 MyActor에 glTF File을 로컬 파일 또는 URL로 지정해서 실행한 화면입니다.
glTF 모델에 애니메이션이 지정되어 있는경우 자동으로 Play 되기 때문에 아래 링크의 샘플 프로젝트에서 예시로 확인할 수 있습니다.
예제 프로젝트: https://github.com/odyssey2010/unreal_gltf_runtime_scene
'Unreal Engine' 카테고리의 다른 글
Unreal Engine(C++)에서 Material을 로드하는 방법 (0) | 2023.03.15 |
---|---|
정다각형(N-gon)을 Procedural Mesh로 생성하는 방법 (Unreal Engine, C++) (0) | 2023.03.14 |
Unreal Engine(C++)에서 Actor와 Component 찾는 방법 (0) | 2023.03.12 |
Unreal Engine(C++)에서 객체의 클래스 상속 관계 확인 방법 (0) | 2023.03.12 |
현재 카메라의 위치와 회전값을 얻어내는 방법 (Unreal Engine, C++) (0) | 2023.03.11 |