Додавання в Unreal Engine підтримки формату dxf

image

Привіт мене звуть Дмитро. Я займаюся створенням комп'ютерних ігор Unreal Engine в якості хобі. Сьогодні розповім як додати підтримку dxf файлів в Unreal Engine. (Исходники як завжди в кінці статті).

DXF — це відкритий формат векторної графіки, розроблений компанією Autodesk. В силу своєї відкритості цей формат підтримується величезним кількістю редакторів векторної графіки.



Отже почнемо з створення класу який буде містити інформацію про імпортованому файлі.
UCLASS(BlueprintType)

class DXFPLUGINRUNTIME_API UDXFSketch : public UObject
{
GENERATED_BODY()
public:

#if WITH_EDITORONLY_DATA
UPROPERTY(VisibleAnywhere, Instanced, Category = ImportSettings)
class UAssetImportData* AssetImportData;

virtual void PostInitProperties() override;

#endif // WITH_EDITORONLY_DATA

UPROPERTY(VisibleAnywhere, BlueprintReadOnly)
TArray<FDXFLayer> Layers;

UPROPERTY()
float DXFBackGroundSize;

}; 

Тут треба зауважити, що ми додали в клас об'єкт UAssetImportData цей об'єкт буде містити інформацію про файлі исходнике, і потрібен для реімпорту ассета. У методі PostInitProperties() створюється екземпляр даного класу. Масив об'єктів FDXFLayer власне і містить в собі всю інформацію з dxf файлу.

Ассет створений тепер потрібно для нього створити factory клас. Детальніше про створення нового ассета можна почитати статті.
UCLASS()
class UDXFSketchFactory : public UFactory, public FReimportHandler
{
GENERATED_UCLASS_BODY()

// UFactory interface
virtual UObject* FactoryCreateBinary(UClass* Class, UObject* InParent, FName Name, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn) override;
віртуальний bool CanCreateNew() const override;
// End of interface UFactory

// Begin FReimportHandler interface
віртуальний bool CanReimport(UObject* Obj, TArray<FString>& OutFilenames) override;
virtual void SetReimportPaths(UObject* Obj, const TArray<FString>& NewReimportPaths) override;
virtual EReimportResult::Type Reimport(UObject* Obj) override;
virtual int32 GetPriority() const override;
// End FReimportHandler interface

bool LoadFile(UDXFSketch* Sketch, const uint8*& Buffer, const uint8* BufferEnd);
};

Основною відмінністю factory класу для імпортованих ассетов від звичайних, полягає у використанні методу FactoryCreateBinary замість FactoryCreateNew. Цьому методу крім інших параметрів віддається посилання на масив байт (які є імпортованим файлом) і покажчик на кінець цього масиву.

Щоб файл можна було ре-імпортувати необхідно також в базові класи додати клас FReimportHandler який додасть метод Reimport.

Призначення методу LoadFile зрозуміло і так. Щоб не винаходити велосипед для парсингу файлу я використовував бібліотеку dxflib від ribbonsoft.

Ассет тепер імпортується. Але щоб зрозуміти що знаходиться в імпортованому файлі не погано б створити редактор для ассета. (Детальніше про створення редактора для ассета можна почитати тут). Повністю переказувати попередню статтю я не буду, просто скажу, що нам потрібно створити похідний об'єкт від FAssetEditorToolkit в якому ми створимо необхідні для нас вкладки (В даному випадку це вкладка вьюпорта, в якому ми будемо відображати дані, і вкладка панелі властивостей). Створення панелі власти вже було розглянуто. Тому поговоримо про вьюпорте.

У вкладці вьюпорт ми створимо об'єкт SDXFEditorViewport
class SDXFEditorViewport : public SCompoundWidget
{
public:

SLATE_BEGIN_ARGS(SDXFEditorViewport) { }
SLATE_ARGUMENT(TWeakPtr<FDXFAssetEditor>, CustomEditor)
SLATE_END_ARGS()

public:
void Construct( const FArguments& InArgs);
TSharedPtr<FSceneViewport> GetViewport( ) const;
TSharedPtr<SViewport> GetViewportWidget( ) const;
TSharedPtr<SScrollBar> GetVerticalScrollBar( ) const;
TSharedPtr<SScrollBar> GetHorizontalScrollBar( ) const;

void UpdateScreen();

protected:

TSharedRef<SWidget> GenerateViewOptionsMenu() const;

private:

// Callback for clicking the View Options menu button.
FReply HandleViewOptionsMenuButtonClicked();
// Callback for the horizontal scroll bar.
void HandleHorizontalScrollBarScrolled( float InScrollOffsetFraction );
// Callback for getting the visibility of the horizontal scroll bar.
EVisibility HandleHorizontalScrollBarVisibility( ) const;
// Callback for the vertical scroll bar.
void HandleVerticalScrollBarScrolled( float InScrollOffsetFraction );
// Callback for getting the visibility of the horizontal scroll bar.
EVisibility HandleVerticalScrollBarVisibility( ) const;
// Callback for clicking an item in the 'Zoom' menu.
void HandleZoomMenuEntryClicked( double ZoomValue );
// Callback for getting the zoom percentage text.
FText HandleZoomPercentageText( ) const;
// Callback for changes in the zoom slider.
void HandleZoomSliderChanged( float NewValue );
// Callback for getting the zoom slider's value.
float HandleZoomSliderValue( ) const;

void HandleLayerActive(int Num);
void HandleAllLayersActive();

private:

// Pointer back to the Asset editor tool that owns us.
TWeakPtr<FDXFAssetEditor> AssetEditor;
// Level viewport client.
TSharedPtr<class FDXFEditorViewportClient> ViewportClient;
// Slate viewport for rendering and IO.
TSharedPtr<FSceneViewport> Viewport;
// Viewport widget.
TSharedPtr<SViewport> ViewportWidget;
// Vertical scrollbar.
TSharedPtr<SScrollBar> TextureViewportVerticalScrollBar;
// Horizontal scrollbar.
TSharedPtr<SScrollBar> TextureViewportHorizontalScrollBar;
// Holds the anchor for the view options menu.
TSharedPtr<SMenuAnchor> ViewOptionsMenuAnchor;
};

Це об'єкт інтерфейсу він створює всі елементи інтерфейсу (меню повзунки і т. д) крім того, він створює об'єкт FDXFEditorViewportClient в якому власне і буде відбуватися відтворення примітивів завантажених з файлу.

class FDXFEditorViewportClient: public FViewportClient 
{
public:
/** Constructor */
FDXFEditorViewportClient(TWeakPtr<FDXFAssetEditor> InTextureEditor, TWeakPtr<SDXFEditorViewport> InTextureEditorViewport);

/** FViewportClient interface */
virtual void Draw(FViewport* Viewport, FCanvas* Canvas) override;
віртуальний bool InputKey(FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed = 1.0 f, bool bGamepad = false) override;
virtual UWorld* GetWorld() const override { return nullptr; }
/** Returns the ratio of the size of the Texture texture to the size of the viewport */
float GetViewportVerticalScrollBarRatio() const;
float GetViewportHorizontalScrollBarRatio() const;

void SetZoom(double ZoomValue);
void ZoomIn();
void ZoomOut();
double GetZoom() const;

DrawVar Vars; //variables for drawning viewport
private:
/** Updates the states of the scrollbars */
void UpdateScrollBars();

/** Returns the positions of the scrollbars relative to the Texture textures */
FVector2D GetViewportScrollBarPositions() const;

private:
/** Pointer back to the Texture editor tool that owns us */
TWeakPtr<FDXFAssetEditor> AssetEditor;

/** Pointer back to the Texture viewport control that owns us */
TWeakPtr<SDXFEditorViewport> AssetEditorViewport;
};


Власне редактор створений але є ще одна дрібниця. В Unreal Engine є таке поняття як Thumbnail це така маленька картинка яка відображається в контент-браузері місце значка ассета. Щоб створити цей Thumbnail потрібно створити об'єкт похідний від UThumbnailRenderer.
UCLASS()
class UDXFThumbnailRenderer : public UThumbnailRenderer
{
GENERATED_BODY()

// Begin UThumbnailRenderer Object
virtual void GetThumbnailSize(UObject* Object, float Zoom, uint32& OutWidth, uint32& OutHeight) const override;
virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height, FRenderTarget* Viewport, FCanvas* Canvas) override;
// End UThumbnailRenderer Object
};

В цьому об'єкті є метод Draw який власне і намалює Thumbnail. Конечноже після створення цього об'єкта його потрібно зареєструвати.
void FDXFPluginEditor::StartupModule()
{
// Register DXFSketch AssetActions
TSharedRef<IAssetTypeActions> Action = MakeShareable(new FDXFSketchAssetActions);
IAssetTools& AssetTools = FModuleManager::LoadModuleChecked<FAssetToolsModule>("AssetTools").Get();
AssetTools.RegisterAssetTypeActions(Action);
CreatedAssetTypeActions.Add(Action);

//Registrate ToolBarCommand for costom graph
FDXFToolBarCommandsCommands::Register();

//Registrate Thumbnail render
UThumbnailManager::Get().RegisterCustomRenderer(UDXFSketch::StaticClass(), UDXFThumbnailRenderer::StaticClass());
}

Тепер звідки ж запускати рендер Thumbnail? Я як така місця обрав метод HandleReimportManagerPostReimport обєкта FDXFAssetEditor цей метод виконується після імпорту файл:
void FDXFAssetEditor::HandleReimportManagerPostReimport(UObject* InObject, bool bSuccess)
{
TArray<UObject*> SelectedObjects;
SelectedObjects.Add(InObject);
AssetData = CastChecked<UDXFSketch>(InObject);
if (bSuccess)
{
PropertyEditor->SetObjects(SelectedObjects);
}
DXFViewport->UpdateScreen();

FThumbnailRenderingInfo* RenderInfo = GUnrealEd->GetThumbnailManager()->GetRenderingInfo(AssetData);
if (RenderInfo != NULL)
{
RenderInfo->Renderer; //Render Thumbnail
}
}

image

Але як можна використовувати імпортований ассет? На жаль накласти dxf файл в якості текстури не вийде. Але можна наприклад завантажити точки і використовувати їх координати для розміщення об'єктів. Або створити так звану SplineMesh і вытинуть її вздовж якоїсь лінії. Поки що плагін розпізнає лінії, замкнуті контури і точки (які виходять якщо в adobe illustrator пензликом ткнути в полотно, ці точки являють з себе сплайни складаються з 10 точок).

Власне на цьому все. Проект я зробив у вигляді плагіна тому щоб додати підтримку dxf в ваш проект досить створити в його директорії папку Plugins і закинути туди папку DXFPlugin, щоб побачити код плагіна в VS потрібно видалити старий файл VS — проекту і згенерувати новий. (Детальніше про плагіни можна почитати тут

Проект з исходниками тут

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.