diff --git a/Source/VOX4U/Public/Voxel.h b/Source/VOX4U/Public/Voxel.h index 0d286b0..72d3b2f 100644 --- a/Source/VOX4U/Public/Voxel.h +++ b/Source/VOX4U/Public/Voxel.h @@ -34,6 +34,11 @@ class VOX4U_API UVoxel : public UObject UPROPERTY(EditDefaultsOnly, Category = Voxel) TMap Voxel; +#if WITH_EDITORONLY_DATA + UPROPERTY(EditAnywhere, Instanced, Category = Reimport) + class UAssetImportData* AssetImportData; +#endif + public: UVoxel(); diff --git a/Source/VOX4UEditor/Private/VOX4UEditor.cpp b/Source/VOX4UEditor/Private/VOX4UEditor.cpp index f658013..b9aaf7c 100644 --- a/Source/VOX4UEditor/Private/VOX4UEditor.cpp +++ b/Source/VOX4UEditor/Private/VOX4UEditor.cpp @@ -1,21 +1,28 @@ -// Copyright 2016 mik14a / Admix Network. All Rights Reserved. +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. #include "VOX4UEditor.h" #include "ThumbnailRendering/ThumbnailManager.h" +#include "AssetToolsModule.h" #include "Voxel.h" #include "VoxelThumbnailRenderer.h" +#include "VoxelAssetTypeActions.h" #define LOCTEXT_NAMESPACE "FVOX4UEditorModule" void FVOX4UEditorModule::StartupModule() { + IAssetTools& AssetTools = FModuleManager::LoadModuleChecked("AssetTools").Get(); + VoxelAssetTypeActions = MakeShareable(new FVoxelAssetTypeActions()); + AssetTools.RegisterAssetTypeActions(VoxelAssetTypeActions.ToSharedRef()); UThumbnailManager::Get().RegisterCustomRenderer(UVoxel::StaticClass(), UVoxelThumbnailRenderer::StaticClass()); } void FVOX4UEditorModule::ShutdownModule() { - // This function may be called during shutdown to clean up your module. For modules that support dynamic reloading, - // we call this function before unloading the module. + if (FModuleManager::Get().IsModuleLoaded("AssetTools")) { + IAssetTools& AssetTools = FModuleManager::GetModuleChecked("AssetTools").Get(); + AssetTools.UnregisterAssetTypeActions(VoxelAssetTypeActions.ToSharedRef()); + } } #undef LOCTEXT_NAMESPACE diff --git a/Source/VOX4UEditor/Private/Vox.cpp b/Source/VOX4UEditor/Private/Vox.cpp index bfbffdb..264f9e4 100644 --- a/Source/VOX4UEditor/Private/Vox.cpp +++ b/Source/VOX4UEditor/Private/Vox.cpp @@ -40,8 +40,9 @@ FVox::FVox() * Create vox data from archive * @param FArchive& Ar Read vox data from the archive */ -FVox::FVox(FArchive& Ar, const UVoxImportOption* ImportOption) +FVox::FVox(const FString& Filename, FArchive& Ar, const UVoxImportOption* ImportOption) { + this->Filename = Filename; Import(Ar, ImportOption); } diff --git a/Source/VOX4UEditor/Private/Vox.h b/Source/VOX4UEditor/Private/Vox.h index d3fbd13..81313ed 100644 --- a/Source/VOX4UEditor/Private/Vox.h +++ b/Source/VOX4UEditor/Private/Vox.h @@ -18,6 +18,9 @@ struct FVox { GENERATED_BODY() + /** Filename */ + FString Filename; + /** Magic number ( 'V' 'O' 'X' 'space' ) and terminate */ ANSICHAR MagicNumber[5]; /** version number ( current version is 150 ) */ @@ -36,7 +39,7 @@ struct FVox FVox(); /** Create vox data from archive */ - FVox(FArchive& Ar, const UVoxImportOption* ImportOption); + FVox(const FString& Filename, FArchive& Ar, const UVoxImportOption* ImportOption); /** Import vox data from archive */ bool Import(FArchive& Ar, const UVoxImportOption* ImportOption); diff --git a/Source/VOX4UEditor/Private/VoxAssetImportData.cpp b/Source/VOX4UEditor/Private/VoxAssetImportData.cpp new file mode 100644 index 0000000..b34142f --- /dev/null +++ b/Source/VOX4UEditor/Private/VoxAssetImportData.cpp @@ -0,0 +1,28 @@ +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. + +#include "VoxAssetImportData.h" + +UVoxAssetImportData::UVoxAssetImportData() + : VoxImportType(EVoxImportType::StaticMesh) + , bImportXForward(true) + , bImportXYCenter(true) + , Scale(10.f) +{ +} + +void UVoxAssetImportData::ToVoxImportOption(UVoxImportOption& OutVoxImportOption) +{ + OutVoxImportOption.VoxImportType = VoxImportType; + OutVoxImportOption.bImportXForward = bImportXForward; + OutVoxImportOption.bImportXYCenter = bImportXYCenter; + OutVoxImportOption.Scale = Scale; + OutVoxImportOption.BuildSettings.BuildScale3D = FVector(Scale); +} + +void UVoxAssetImportData::FromVoxImportOption(const UVoxImportOption& VoxImportOption) +{ + VoxImportType = VoxImportOption.VoxImportType; + bImportXForward = VoxImportOption.bImportXForward; + bImportXYCenter = VoxImportOption.bImportXYCenter; + Scale = VoxImportOption.Scale; +} diff --git a/Source/VOX4UEditor/Private/VoxAssetImportData.h b/Source/VOX4UEditor/Private/VoxAssetImportData.h new file mode 100644 index 0000000..5b83641 --- /dev/null +++ b/Source/VOX4UEditor/Private/VoxAssetImportData.h @@ -0,0 +1,39 @@ +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "EditorFramework/AssetImportData.h" +#include "VoxImportOption.h" +#include "VoxAssetImportData.generated.h" + +/** + * + */ +UCLASS() +class UVoxAssetImportData : public UAssetImportData +{ + GENERATED_BODY() + +public: + + EVoxImportType VoxImportType; + + UPROPERTY(EditAnywhere, Category = Generic) + uint32 bImportXForward : 1; + + UPROPERTY(EditAnywhere, Category = Generic) + uint32 bImportXYCenter : 1; + + UPROPERTY(EditAnywhere, Category = Generic) + float Scale; + +public: + + UVoxAssetImportData(); + + void ToVoxImportOption(UVoxImportOption& OutVoxImportOption); + + void FromVoxImportOption(const UVoxImportOption& VoxImportOption); + +}; diff --git a/Source/VOX4UEditor/Private/VoxImportOption.h b/Source/VOX4UEditor/Private/VoxImportOption.h index 93a5cff..9a54c74 100644 --- a/Source/VOX4UEditor/Private/VoxImportOption.h +++ b/Source/VOX4UEditor/Private/VoxImportOption.h @@ -51,4 +51,6 @@ class UVoxImportOption : public UObject FMeshBuildSettings BuildSettings; + friend class UVoxAssetImportData; + }; diff --git a/Source/VOX4UEditor/Private/VoxelAssetTypeActions.cpp b/Source/VOX4UEditor/Private/VoxelAssetTypeActions.cpp new file mode 100644 index 0000000..2c63834 --- /dev/null +++ b/Source/VOX4UEditor/Private/VoxelAssetTypeActions.cpp @@ -0,0 +1,32 @@ +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. + +#include "VoxelAssetTypeActions.h" + +FVoxelAssetTypeActions::FVoxelAssetTypeActions() +{ +} + +FText FVoxelAssetTypeActions::GetName() const +{ + return NSLOCTEXT("VOX4U", "VoxelAssetTypeActionsName", "Voxel Asset Type Actions."); +} + +UClass* FVoxelAssetTypeActions::GetSupportedClass() const +{ + return UVoxel::StaticClass(); +} + +FColor FVoxelAssetTypeActions::GetTypeColor() const +{ + return FColor::Cyan; +} + +uint32 FVoxelAssetTypeActions::GetCategories() +{ + return EAssetTypeCategories::Misc; +} + +bool FVoxelAssetTypeActions::IsImportedAsset() const +{ + return true; +} diff --git a/Source/VOX4UEditor/Private/VoxelAssetTypeActions.h b/Source/VOX4UEditor/Private/VoxelAssetTypeActions.h new file mode 100644 index 0000000..023698c --- /dev/null +++ b/Source/VOX4UEditor/Private/VoxelAssetTypeActions.h @@ -0,0 +1,23 @@ +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" +#include "AssetTypeActions_Base.h" + +class FVoxelAssetTypeActions : public FAssetTypeActions_Base +{ +public: + FVoxelAssetTypeActions(); + + virtual FText GetName() const override; + + virtual UClass* GetSupportedClass() const override; + + virtual FColor GetTypeColor() const override; + + virtual uint32 GetCategories() override; + + virtual bool IsImportedAsset() const override; + +}; diff --git a/Source/VOX4UEditor/Private/VoxelFactory.cpp b/Source/VOX4UEditor/Private/VoxelFactory.cpp index 8dacaad..e8514f5 100644 --- a/Source/VOX4UEditor/Private/VoxelFactory.cpp +++ b/Source/VOX4UEditor/Private/VoxelFactory.cpp @@ -15,8 +15,11 @@ #include "RawMesh.h" #include "Vox.h" #include "VoxImportOption.h" +#include "VoxAssetImportData.h" #include "Voxel.h" +DEFINE_LOG_CATEGORY_STATIC(LogVoxelFactory, Log, All) + UVoxelFactory::UVoxelFactory(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) , ImportOption(nullptr) @@ -67,7 +70,7 @@ UObject* UVoxelFactory::FactoryCreateBinary(UClass* InClass, UObject* InParent, if (!bShowOption || ImportOption->GetImportOption(bImportAll)) { bShowOption = !bImportAll; FBufferReader Reader((void*)Buffer, BufferEnd - Buffer, false); - FVox Vox(Reader, ImportOption); + FVox Vox(GetCurrentFilename(), Reader, ImportOption); switch (ImportOption->VoxImportType) { case EVoxImportType::StaticMesh: Result = CreateStaticMesh(InParent, InName, Flags, &Vox); @@ -89,20 +92,120 @@ UObject* UVoxelFactory::FactoryCreateBinary(UClass* InClass, UObject* InParent, return Result; } +bool UVoxelFactory::CanReimport(UObject* Obj, TArray& OutFilenames) +{ + UStaticMesh* StaticMesh = Cast(Obj); + USkeletalMesh* SkeletalMesh = Cast(Obj); + UDestructibleMesh* DestructibleMesh = Cast(Obj); + UVoxel* Voxel = Cast(Obj); + + const auto& AssetImportData = StaticMesh != nullptr ? StaticMesh->AssetImportData + : SkeletalMesh != nullptr ? SkeletalMesh->AssetImportData + : DestructibleMesh != nullptr ? DestructibleMesh->AssetImportData + : Voxel != nullptr ? Voxel->AssetImportData + : nullptr; + if (AssetImportData != nullptr) { + const auto& SourcePath = AssetImportData->GetFirstFilename(); + FString Path, Filename, Extension; + FPaths::Split(SourcePath, Path, Filename, Extension); + if (Extension.Compare("vox", ESearchCase::IgnoreCase) == 0) { + AssetImportData->ExtractFilenames(OutFilenames); + return true; + } + } + return false; +} + +void UVoxelFactory::SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) +{ + UStaticMesh* StaticMesh = Cast(Obj); + USkeletalMesh* SkeletalMesh = Cast(Obj); + UDestructibleMesh* DestructibleMesh = Cast(Obj); + UVoxel* Voxel = Cast(Obj); + + const auto& AssetImportData = StaticMesh ? StaticMesh->AssetImportData + : SkeletalMesh ? SkeletalMesh->AssetImportData + : DestructibleMesh ? DestructibleMesh->AssetImportData + : Voxel ? Voxel->AssetImportData + : nullptr; + if (AssetImportData && ensure(NewReimportPaths.Num() == 1)) { + AssetImportData->UpdateFilenameOnly(NewReimportPaths[0]); + } +} + +EReimportResult::Type UVoxelFactory::Reimport(UObject* Obj) +{ + UStaticMesh* StaticMesh = Cast(Obj); + USkeletalMesh* SkeletalMesh = Cast(Obj); + UDestructibleMesh* DestructibleMesh = Cast(Obj); + UVoxel* Voxel = Cast(Obj); + + const auto& AssetImportData = StaticMesh ? Cast(StaticMesh->AssetImportData) + : SkeletalMesh ? Cast(SkeletalMesh->AssetImportData) + : DestructibleMesh ? Cast(DestructibleMesh->AssetImportData) + : Voxel ? Cast(Voxel->AssetImportData) + : nullptr; + if (!AssetImportData) { + return EReimportResult::Failed; + } + + const auto& Filename = AssetImportData->GetFirstFilename(); + if (!Filename.Len() || IFileManager::Get().FileSize(*Filename) == INDEX_NONE) { + return EReimportResult::Failed; + } + + auto Result = EReimportResult::Failed; + auto OutCanceled = false; + AssetImportData->ToVoxImportOption(*ImportOption); + bShowOption = false; + if (ImportObject(Obj->GetClass(), Obj->GetOuter(), *Obj->GetName(), RF_Public | RF_Standalone, Filename, nullptr, OutCanceled) != nullptr) { + UE_LOG(LogVoxelFactory, Verbose, TEXT("Reimport successfully.")); + AssetImportData->Update(Filename); + if (Obj->GetOuter()) { + Obj->GetOuter()->MarkPackageDirty(); + } else { + Obj->MarkPackageDirty(); + } + Result = EReimportResult::Succeeded; + } else { + if (OutCanceled) { + UE_LOG(LogVoxelFactory, Warning, TEXT("Reimport canceled.")); + } else { + UE_LOG(LogVoxelFactory, Warning, TEXT("Reimport failed.")); + } + Result = EReimportResult::Failed; + } + return Result; +} + UStaticMesh* UVoxelFactory::CreateStaticMesh(UObject* InParent, FName InName, EObjectFlags Flags, const FVox* Vox) const { UStaticMesh* StaticMesh = NewObject(InParent, InName, Flags | RF_Public); + if (!StaticMesh->AssetImportData || !StaticMesh->AssetImportData->IsA()) { + auto AssetImportData = NewObject(StaticMesh); + AssetImportData->FromVoxImportOption(*ImportOption); + StaticMesh->AssetImportData = AssetImportData; + } + FRawMesh RawMesh; Vox->CreateOptimizedRawMesh(RawMesh, ImportOption); UMaterialInterface* Material = CreateMaterial(InParent, InName, Flags, Vox); StaticMesh->StaticMaterials.Add(FStaticMaterial(Material)); BuildStaticMesh(StaticMesh, RawMesh); + StaticMesh->AssetImportData->Update(Vox->Filename); return StaticMesh; } USkeletalMesh* UVoxelFactory::CreateSkeletalMesh(UObject* InParent, FName InName, EObjectFlags Flags, const FVox* Vox) const { USkeletalMesh* SkeletalMesh = NewObject(InParent, InName, Flags | RF_Public); + if (!SkeletalMesh->AssetImportData || !SkeletalMesh->AssetImportData->IsA()) { + auto AssetImportData = NewObject(SkeletalMesh); + AssetImportData->FromVoxImportOption(*ImportOption); + SkeletalMesh->AssetImportData = AssetImportData; + } + + SkeletalMesh->AssetImportData->Update(Vox->Filename); return SkeletalMesh; } @@ -116,6 +219,11 @@ USkeletalMesh* UVoxelFactory::CreateSkeletalMesh(UObject* InParent, FName InName UDestructibleMesh* UVoxelFactory::CreateDestructibleMesh(UObject* InParent, FName InName, EObjectFlags Flags, const FVox* Vox) const { UDestructibleMesh* DestructibleMesh = NewObject(InParent, InName, Flags | RF_Public); + if (!DestructibleMesh->AssetImportData || !DestructibleMesh->AssetImportData->IsA()) { + auto AssetImportData = NewObject(DestructibleMesh); + AssetImportData->FromVoxImportOption(*ImportOption); + DestructibleMesh->AssetImportData = AssetImportData; + } FRawMesh RawMesh; Vox->CreateOptimizedRawMesh(RawMesh, ImportOption); @@ -137,6 +245,7 @@ UDestructibleMesh* UVoxelFactory::CreateDestructibleMesh(UObject* InParent, FNam DestructibleMesh->SetupChunksFromStaticMeshes(FractureMeshes); BuildDestructibleMeshFromFractureSettings(*DestructibleMesh, nullptr); DestructibleMesh->SourceStaticMesh = nullptr; + DestructibleMesh->AssetImportData->Update(Vox->Filename); return DestructibleMesh; } @@ -144,6 +253,11 @@ UDestructibleMesh* UVoxelFactory::CreateDestructibleMesh(UObject* InParent, FNam UVoxel* UVoxelFactory::CreateVoxel(UObject* InParent, FName InName, EObjectFlags Flags, const FVox* Vox) const { UVoxel* Voxel = NewObject(InParent, InName, Flags | RF_Public); + if (!Voxel->AssetImportData || !Voxel->AssetImportData->IsA()) { + auto AssetImportData = NewObject(Voxel); + AssetImportData->FromVoxImportOption(*ImportOption); + Voxel->AssetImportData = AssetImportData; + } Voxel->Size = Vox->Size; TArray Palette; for (const auto& cell : Vox->Voxel) { @@ -185,6 +299,7 @@ UVoxel* UVoxelFactory::CreateVoxel(UObject* InParent, FName InName, EObjectFlags } Voxel->bXYCenter = ImportOption->bImportXYCenter; Voxel->CalcCellBounds(); + Voxel->AssetImportData->Update(Vox->Filename); return Voxel; } diff --git a/Source/VOX4UEditor/Private/VoxelFactory.h b/Source/VOX4UEditor/Private/VoxelFactory.h index 6b078f5..94b196d 100644 --- a/Source/VOX4UEditor/Private/VoxelFactory.h +++ b/Source/VOX4UEditor/Private/VoxelFactory.h @@ -3,6 +3,7 @@ #pragma once #include "Factories/Factory.h" +#include "EditorReimportHandler.h" #include "Engine.h" #include "RawMesh.h" #include "DestructibleMesh.h" @@ -20,7 +21,7 @@ struct FVox; * */ UCLASS() -class UVoxelFactory : public UFactory +class UVoxelFactory : public UFactory, public FReimportHandler { GENERATED_BODY() @@ -36,6 +37,12 @@ class UVoxelFactory : public UFactory virtual UObject* FactoryCreateBinary(UClass* InClass, UObject* InParent, FName InName, EObjectFlags Flags, UObject* Context, const TCHAR* Type, const uint8*& Buffer, const uint8* BufferEnd, FFeedbackContext* Warn) override; + virtual bool CanReimport(UObject* Obj, TArray& OutFilenames) override; + + virtual void SetReimportPaths(UObject* Obj, const TArray& NewReimportPaths) override; + + virtual EReimportResult::Type Reimport(UObject* Obj) override; + private: UStaticMesh* CreateStaticMesh(UObject* InParent, FName InName, EObjectFlags Flags, const FVox* Vox) const; diff --git a/Source/VOX4UEditor/Public/VOX4UEditor.h b/Source/VOX4UEditor/Public/VOX4UEditor.h index edaaff7..9c404a3 100644 --- a/Source/VOX4UEditor/Public/VOX4UEditor.h +++ b/Source/VOX4UEditor/Public/VOX4UEditor.h @@ -1,8 +1,9 @@ -// Copyright 2016 mik14a / Admix Network. All Rights Reserved. +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. #pragma once #include "ModuleManager.h" +#include "IAssetTypeActions.h" class FVOX4UEditorModule : public IModuleInterface { @@ -11,4 +12,7 @@ class FVOX4UEditorModule : public IModuleInterface /** IModuleInterface implementation */ virtual void StartupModule() override; virtual void ShutdownModule() override; + +private: + TSharedPtr VoxelAssetTypeActions; }; diff --git a/Source/VOX4UEditor/VOX4UEditor.Build.cs b/Source/VOX4UEditor/VOX4UEditor.Build.cs index a41a24e..9ded8c6 100644 --- a/Source/VOX4UEditor/VOX4UEditor.Build.cs +++ b/Source/VOX4UEditor/VOX4UEditor.Build.cs @@ -1,4 +1,4 @@ -// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved. +// Copyright 2016-2018 mik14a / Admix Network. All Rights Reserved. using UnrealBuildTool; @@ -35,6 +35,7 @@ public VOX4UEditor(ReadOnlyTargetRules Target) : base(Target) "RawMesh", "MainFrame", "PropertyEditor", + "AssetTools", } );