📜 ⬆️ ⬇️

Adding Unreal Engine support for dxf format

image

Hello, my name is Dmitry. I create computer games on the Unreal Engine as a hobby. Today I will tell you how to add support for dxf files in the Unreal Engine. (Sources as always at the end of the article).

DXF is an open vector graphics format developed by Autodesk. By virtue of its openness, this format is supported by a huge number of editors of vector graphics.
')


So let's start by creating a class that will contain information about the imported file.
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; }; 

Here it should be noted that we added a UAssetImportData object to the class. This object will contain information about the source file, and is needed to re-import the asset. An instance of this class is created in the PostInitProperties () method. The array of FDXFLayer objects actually contains all the information from the dxf file.

Asset is created now you need to create a factory class for it. More information about creating a new asset can be found in this article .
 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; virtual bool CanCreateNew() const override; // End of UFactory interface // Begin FReimportHandler interface virtual 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); }; 

The main difference of the factory class for imported assets from ordinary ones is the use of the FactoryCreateBinary method instead of FactoryCreateNew. This method, among other parameters, is referenced by an array of bytes (which is an imported file) and a pointer to the end of this array.

And of course the same constructor:

 UDXFSketchFactory::UDXFSketchFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { SupportedClass = UDXFSketch::StaticClass(); Formats.Add(TEXT("dxf;DXF")); bCreateNew = false; bEditorImport = true; } 

So that the file can be re-imported, it is also necessary to add the FReimportHandler class to the base classes, which adds the Reimport method.

The purpose of the LoadFile method is clear. In order not to reinvent the wheel, I used ribbonsoft 's dxflib library to parse a file.

Asset is now imported. But in order to understand what is in the imported file, it would not be bad to create an editor for an asset. (You can read more about creating an editor for an asset here ). Fully retell the previous article, I will not just say that we need to create a derived object from FAssetEditorToolkit in which we create the tabs we need (In this case, this is the viewport tab, in which we will draw the data, and the tab of the property panel). Creating a property panel has already been reviewed. So let's talk about the viewport.

In the viewport tab, we will create an SDXFEditorViewport object.
 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; }; 

This is the interface object it creates all the interface elements (sliders menu, etc.), besides it creates an FDXFEditorViewportClient object in which the primitives loaded from the file will actually be drawn.

 class FDXFEditorViewportClient: public FViewportClient { public: /** Constructor */ FDXFEditorViewportClient(TWeakPtr<FDXFAssetEditor> InTextureEditor, TWeakPtr<SDXFEditorViewport> InTextureEditorViewport); /** FViewportClient interface */ virtual void Draw(FViewport* Viewport, FCanvas* Canvas) override; virtual bool InputKey(FViewport* Viewport, int32 ControllerId, FKey Key, EInputEvent Event, float AmountDepressed = 1.0f, 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; }; 


The editor itself is created but there is one more trifle. In Unreal Engine there is such a thing as Thumbnail is such a small picture that is displayed in the content browser in place of the asset icon. To create this Thumbnail you need to create an object derived from 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 }; 

In this object, there is a Draw method that actually draws a Thumbnail. Of course, after creating this object, it must be registered.
 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()); } 

Now, where does the Thumbnail render run? I chose the HandleReimportManagerPostReimport method of the FDXFAssetEditor object as such a place. This method is executed after the file is imported:
 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

But how can you use an imported asset? Unfortunately, dxf file cannot be superimposed as a texture. But you can, for example, load points and use their coordinates to place objects. Or create a so-called SplineMesh and pull it along some line. So far, the plugin recognizes lines, closed contours and points (which are obtained by poking a canvas with a brush in adobe illustrator, these points are splines consisting of 10 points).

Actually that's all. I made the project as a plug-in, so in order to add dxf support to your project, it’s enough to create the Plugins folder in its directory and upload the DXFPlugin folder there. To see the source code for the VS plug-in, delete the old VS-project file and generate a new one. (You can read more about plugins here )

Source project here

Source: https://habr.com/ru/post/283554/


All Articles