#はじめに
皆さんはアセットのエクスポート機能は使っていますか?テクスチャアセットとかだと比較的使う機会があったりするのかもしれません。
そんなアセットのエクスポート機能ですが、実は結構お手軽に拡張できます。
今回はアセットのサムネイルを画像ファイルに出力するプラグインを見ながらご紹介したいと思います。
ぷちコンでアセットのサムネイルを使いたかったからアセットのサムネイルを画像ファイルに出力する機能作ってみた。
— Naotsun (@Naotsun_UE) March 13, 2021
エクスポートを拡張する場合はUExporterを継承するだけで作れるからお手軽だった。#UE4 pic.twitter.com/sKKVFGTRQc
#つくってみる
「はじめに」に埋め込んだtweetにある通り、必要な作業はUExporter
クラスを継承して、出力する部分の処理を作るだけです。
// Copyright 2020 Naotsun. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Exporters/Exporter.h"
#include "ThumbnailExporter.generated.h"
/*
* Class for exporting asset thumbnails.
*/
UCLASS()
class ASSETTHUMBNAILEXPORTER_API UThumbnailExporter : public UExporter
{
GENERATED_BODY()
public:
UThumbnailExporter(const FObjectInitializer& ObjectInitializer);
// UExporter interface.
virtual bool SupportsObject(UObject* Object) const override;
virtual bool ExportBinary(UObject* Object, const TCHAR* Type, FArchive& Ar, FFeedbackContext* Warn, int32 FileIndex = 0, uint32 PortFlags = 0) override;
// End of UExporter interface.
};
まずはヘッダーですが、UExporter
クラスを継承して、このプラグインでは画像ファイルへ出力するためUExporter::ExportBinary
関数をオーバライドします。
また、このプラグインでは画像のようにエディタ設定で指定した任意のクラスをサポートするようにしたいため、UExporter::SupportsObject
関数もオーバライドします。もし単一のアセットのみをサポートするのであればこの関数はオーバライドする必要はありません。
UThumbnailExporter::UThumbnailExporter(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
SupportedClass = UObject::StaticClass();
PreferredFormatIndex = 2;
BatchExportMode = true;
CancelBatch = true;
FormatExtension.Add(TEXT("EXR"));
FormatDescription.Add(TEXT("Export Thumbnail As EXR"));
FormatExtension.Add(TEXT("JPEG"));
FormatDescription.Add(TEXT("Export Thumbnail As JPEG"));
FormatExtension.Add(TEXT("PNG"));
FormatDescription.Add(TEXT("Export Thumbnail As PNG"));
}
次にソースファイルです。まずはコンストラクタで各種設定を行います。
SupportedClass
関数はサポートするアセットのクラスです。このプラグインでは複数のアセットを対象としたいためUObject
のクラスを設定します。単一のアセットをサポートする場合はここでクラスを指定すればOKです。
PreferredFormatIndex
はコメントを読む限り「追加する拡張子がプルダウンメニュー上のどのインデックスに挿入されるか」を決める変数のようですが、どんな値にしても結果は変わりませんでした。(原因がわかる方はコメントなどで教えて頂けると嬉しいです!)
BatchExportMode
は複数のアセットを一括でエクスポートできるかを決めます。CancelBatch
はBatchExportMode
がtrue
の時にtrue
にすると一括エクスポート中にキャンセルできるようになります。
FormatExtension
には追加する拡張子を、FormatDescription
にはその拡張子の説明を追加します。プルダウンメニューにはFormatDescription
の文字列が表示されます。
bool UThumbnailExporter::SupportsObject(UObject* Object) const
{
auto* Settings = GetDefault<UAssetThumbnailExporterSettings>();
if (Super::SupportsObject(Object) && IsValid(Settings) && IsValid(Object))
{
bool bIsSupportsOrChildClass = false;
UClass* ObjectClass = Object->GetClass();
for (const auto& SupportsAssetClass : Settings->SupportsAssetClasses)
{
if (SupportsAssetClass == ObjectClass || ObjectClass->IsChildOf(SupportsAssetClass))
{
bIsSupportsOrChildClass = true;
break;
}
}
return (Object->IsAsset() && bIsSupportsOrChildClass);
}
return false;
}
次にSupportsObject
関数ですがこのプラグインのような特殊なことをするのでなければ不要かつ、エディタ設定で設定されているクラスかを確かめているだけなのでサラッと流します。
ちなみにベースクラスのSupportsObject
関数ではコンストラクタで設定したSupportedClass
及びその子クラスかを判定しています。
// Returns whether this exporter supports the specific object
bool UExporter::SupportsObject(UObject* Object) const
{
return (SupportedClass && Object->IsA(SupportedClass));
}
どうでもいいことですが、UExporter
のようにヘッダーとソースファイルの名前が違うとエクスプローラやgithubで探す時に戸惑うのでちょっと困りますね...w
bool UThumbnailExporter::ExportBinary(UObject* Object, const TCHAR* Type, FArchive& Ar, FFeedbackContext* Warn, int32 FileIndex, uint32 PortFlags)
{
FVector2D ImageSize(512, 512);
if (auto* Settings = GetDefault<UAssetThumbnailExporterSettings>())
{
ImageSize = Settings->ImageSize;
}
FObjectThumbnail ObjectThumbnail;
ThumbnailTools::RenderThumbnail(
Object,
ImageSize.X, ImageSize.Y,
ThumbnailTools::EThumbnailTextureFlushMode::AlwaysFlush, NULL,
&ObjectThumbnail
);
if (ObjectThumbnail.IsEmpty())
{
Warn->Log(TEXT("The thumbnail object was empty."));
return false;
}
IImageWrapperModule& ImageWrapperModule = FModuleManager::Get().LoadModuleChecked<IImageWrapperModule>(TEXT("ImageWrapper"));
TSharedPtr<IImageWrapper> ImageWrapper = ImageWrapperModule.CreateImageWrapper(ThumbnailExporterInternal::ConvertStringToImageFormat(Type));
if (!ImageWrapper.IsValid())
{
Warn->Log(TEXT("Failed to get ImageWrapper."));
return false;
}
const TArray<uint8>& RawImageData = ObjectThumbnail.AccessImageData();
if (!ImageWrapper->SetRaw(RawImageData.GetData(), RawImageData.GetAllocatedSize(), ImageSize.X, ImageSize.Y, ERGBFormat::BGRA, 8))
{
Warn->Log(TEXT("Could not set data to ImageWrapper."));
return false;
}
const TArray64<uint8>& CompressedData = ImageWrapper->GetCompressed();
if (CompressedData.Num() == 0)
{
Warn->Log(TEXT("The compressed data was empty."));
return false;
}
Ar.Serialize((void*)CompressedData.GetData(), CompressedData.GetAllocatedSize());
return true;
}
IImageWrapperModule
を使った画像出力についてはヒストリアさんの記事や私が以前に書いた記事にあるのでそちらを読んでいただければと思います。
ImageWrapperModuleを用いて指定の拡張子でスクリーンショットを書き出す方法
UnrealC++で画像ファイルからテクスチャアセットを作成する
最後にアセットからサムネイルのデータを取得する方法ですが、ThumbnailTools::RenderThumbnail
を使います。必要なヘッダーは"Misc/ObjectThumbnail.h"です。
引数にサムネイルを取得したいオブジェクト、画像サイズ、テクスチャストリームのフラッシュ設定、描画に使用するレンダーターゲットリソース、最後に結果を出力するFObjectThumbnail
を渡すだけで動きます。
テクスチャストリームのフラッシュ設定ですが、ThumbnailTools::EThumbnailTextureFlushMode::NeverFlush
にすると出力結果がぼやける可能性があるので特に理由がなければThumbnailTools::EThumbnailTextureFlushMode::AlwaysFlush
にしておきましょう。
そしてFObjectThumbnail
からTArray<uint8>
型でデータが取得できるのでそれをIImageWrapperModule
を使って画像ファイルに出力します。
#おわりに
ここ最近誰得記事を量産している気がしますが、「ちょうど今エクスポート機能拡張したかったんだよぉ!!」って方の役に立てれば嬉しいです。あとは13連続でUnrealC++を使った記事なのでBPでできることの記事も書きたいですね。
このプラグインでは画像ファイルへの出力でしたが、テキストファイルへの出力などもできるのでもっといろんなことができるのではないでしょうか。
この記事で紹介したプラグインは以下でダウンロードできます。
https://github.com/Naotsun19B/TmbExporterPlugin