From 9d1ee20a490722a5e15b682f3bb83c59d7a79937 Mon Sep 17 00:00:00 2001 From: SilverWinter0511 Date: Fri, 3 Feb 2023 16:59:35 +0900 Subject: [PATCH 1/3] Fixed an issue with baked foliages not moving along the landscape when sculpting it. --- .../Private/HoudiniFoliageTools.cpp | 70 +++++++++++++++++++ .../Private/HoudiniFoliageTools.h | 3 + 2 files changed, 73 insertions(+) diff --git a/Source/HoudiniEngine/Private/HoudiniFoliageTools.cpp b/Source/HoudiniEngine/Private/HoudiniFoliageTools.cpp index 599266689f..47e63dc6bc 100644 --- a/Source/HoudiniEngine/Private/HoudiniFoliageTools.cpp +++ b/Source/HoudiniEngine/Private/HoudiniFoliageTools.cpp @@ -38,6 +38,8 @@ #include "FoliageType_InstancedStaticMesh.h" #include "InstancedFoliageActor.h" #include "AssetRegistry/AssetRegistryModule.h" +#include "Components/ModelComponent.h" +#include "Components/BrushComponent.h" #if WITH_EDITOR #include "EditorModeManager.h" @@ -209,6 +211,8 @@ void FHoudiniFoliageTools::SpawnFoliageInstance(UWorld* InWorld, UFoliageType* S Info->AddInstances(FoliageSettings, PlacedLevelInstances.Value); Info->Refresh(false, true); + + UpdateBaseInfoForFoliageInstance(IFA); } TArray FoliageInfos = FHoudiniFoliageTools::GetAllFoliageInfo(InWorld, Settings); @@ -222,3 +226,69 @@ void FHoudiniFoliageTools::SpawnFoliageInstance(UWorld* InWorld, UFoliageType* S } +void FHoudiniFoliageTools::UpdateBaseInfoForFoliageInstance(AInstancedFoliageActor* InstancedFoliageActor) +{ + InstancedFoliageActor->ForEachFoliageInfo([InstancedFoliageActor](UFoliageType* FoliageType, FFoliageInfo& FoliageInfo) + { + if (FoliageInfo.Instances.Num() > 0) + { + FoliageInfo.SelectInstances(true); + TArray SelectedIndices = FoliageInfo.SelectedIndices.Array(); + FoliageInfo.PreMoveInstances(SelectedIndices); + + UWorld* World = InstancedFoliageActor->GetWorld(); + for (int32 InstanceIndex = 0; InstanceIndex < FoliageInfo.Instances.Num(); ++InstanceIndex) + { + FFoliageInstance& FoliageInstance = FoliageInfo.Instances[InstanceIndex]; + FVector Start = FoliageInstance.Location + FVector(0.f, 0.f, 1000.f); + FVector End = FoliageInstance.Location - FVector(0.f, 0.f, 10000.f); + + FHitResult Hit; + static FName NAME_UpdateBaseInfo = FName("UpdateFoliageBaseInfo"); + if (AInstancedFoliageActor::FoliageTrace(World, Hit, FDesiredFoliageInstance(Start, End, FoliageType), NAME_UpdateBaseInfo, false, FFoliageTraceFilterFunc(), (FoliageInstance.Flags & FOLIAGE_AlignToNormal))) + { + UPrimitiveComponent* HitComponent = Hit.Component.Get(); + if (HitComponent->GetComponentLevel() != InstancedFoliageActor->GetLevel()) + { + continue; + } + + if (HitComponent->IsCreatedByConstructionScript()) + { + continue; + } + + if (UModelComponent* ModelComponent = Cast(HitComponent)) + { + if (ABrush* BrushActor = ModelComponent->GetModel()->FindBrush((FVector3f)Hit.Location)) + { + HitComponent = BrushActor->GetBrushComponent(); + } + } + + auto NewBaseID = InstancedFoliageActor->InstanceBaseCache.AddInstanceBaseId(FoliageInfo.ShouldAttachToBaseComponent() ? HitComponent : nullptr); + FoliageInfo.RemoveFromBaseHash(InstanceIndex); + FoliageInstance.BaseId = NewBaseID; + if (FoliageInstance.BaseId == FFoliageInstanceBaseCache::InvalidBaseId) + { + FoliageInstance.BaseComponent = nullptr; + } + FoliageInfo.AddToBaseHash(InstanceIndex); + + FoliageInstance.Location = Hit.Location; + FoliageInstance.ZOffset = 0.f; + + if (FoliageInstance.Flags & FOLIAGE_AlignToNormal) + { + FoliageInstance.Rotation = FoliageInstance.PreAlignRotation; + FoliageInstance.AlignToNormal(Hit.Normal, FoliageType->AlignMaxAngle); + } + } + } + + FoliageInfo.PostMoveInstances(SelectedIndices); + FoliageInfo.SelectInstances(false); + } + return true; + }); +} diff --git a/Source/HoudiniEngine/Private/HoudiniFoliageTools.h b/Source/HoudiniEngine/Private/HoudiniFoliageTools.h index 15dfc127bd..57a1617fab 100644 --- a/Source/HoudiniEngine/Private/HoudiniFoliageTools.h +++ b/Source/HoudiniEngine/Private/HoudiniFoliageTools.h @@ -63,5 +63,8 @@ class HOUDINIENGINE_API FHoudiniFoliageTools // Return all FFoliageInfo which reference the FoliageType in the given world. static TArray GetAllFoliageInfo(UWorld * World, UFoliageType * FoliageType); +private: + // Update the instance base ID of AInstancedFoliageActor. + static void UpdateBaseInfoForFoliageInstance(AInstancedFoliageActor* InstancedFoliageActor); }; From 6a0d9dd67265356fcc8aa991f8553d6a81231894 Mon Sep 17 00:00:00 2001 From: Damien Pernuit Date: Fri, 3 Feb 2023 11:14:58 -0500 Subject: [PATCH 2/3] Bumping to Houdini Version: 19.5.516 Houdini Engine Version: 5.0 (API: 5) --- HoudiniEngine.uplugin | 4 ++-- Source/HoudiniEngine/HoudiniEngine.Build.cs | 4 ++-- Source/HoudiniEngine/Public/HAPI/HAPI_Version.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/HoudiniEngine.uplugin b/HoudiniEngine.uplugin index cc636046d7..e5fecee7f9 100644 --- a/HoudiniEngine.uplugin +++ b/HoudiniEngine.uplugin @@ -1,8 +1,8 @@ { "FileVersion" : 3, "FriendlyName" : "Houdini Engine", - "Version" : 19050506, - "VersionName" : "2.0 - H19.5.506", + "Version" : 19050516, + "VersionName" : "2.0 - H19.5.516", "CreatedBy" : "Side Effects Software Inc.", "CreatedByURL" : "http://www.sidefx.com", "DocsURL" : "http://www.sidefx.com/docs/unreal/", diff --git a/Source/HoudiniEngine/HoudiniEngine.Build.cs b/Source/HoudiniEngine/HoudiniEngine.Build.cs index 9f08a30d60..667acc2d03 100644 --- a/Source/HoudiniEngine/HoudiniEngine.Build.cs +++ b/Source/HoudiniEngine/HoudiniEngine.Build.cs @@ -32,7 +32,7 @@ /* - Houdini Version: 19.5.506 + Houdini Version: 19.5.516 Houdini Engine Version: 5.0.5 Unreal Version: 5.0.0 @@ -47,7 +47,7 @@ public class HoudiniEngine : ModuleRules { private string GetHFSPath() { - string HoudiniVersion = "19.5.506"; + string HoudiniVersion = "19.5.516"; bool bIsRelease = true; string HFSPath = ""; string RegistryPath = "HKEY_LOCAL_MACHINE\\SOFTWARE\\Side Effects Software"; diff --git a/Source/HoudiniEngine/Public/HAPI/HAPI_Version.h b/Source/HoudiniEngine/Public/HAPI/HAPI_Version.h index 340189a378..9a31e77ef4 100755 --- a/Source/HoudiniEngine/Public/HAPI/HAPI_Version.h +++ b/Source/HoudiniEngine/Public/HAPI/HAPI_Version.h @@ -27,7 +27,7 @@ // expecting to compile against. #define HAPI_VERSION_HOUDINI_MAJOR 19 #define HAPI_VERSION_HOUDINI_MINOR 5 -#define HAPI_VERSION_HOUDINI_BUILD 506 +#define HAPI_VERSION_HOUDINI_BUILD 516 #define HAPI_VERSION_HOUDINI_PATCH 0 // The two components of the Houdini Engine (marketed) version. From a8853f8ca1a50d8edf3ff5e252e8015406fe701b Mon Sep 17 00:00:00 2001 From: Damien Pernuit Date: Fri, 3 Feb 2023 11:14:47 -0500 Subject: [PATCH 3/3] - Added support for all types of image packing (RGB, BGR, RGBA, ABGR etc..) This fixes a crash when trying to create an texture that wasn't packed as RGBA. - CreateUnrealTexture: Added a HOUDINI_CHECK_RETURN call when texture packing is invalid. - Fixed a regression that caused the plugin to not automatically refine meshes when saving the level on World Partition World. This was caused by UE not calling the PreSaveWorld callback, considering that only the actor (not the world) was modified. - Fixed a regression that broke the "Proxy refinement by timer" option. --- .../Private/HoudiniMaterialTranslator.cpp | 75 ++++++- .../Private/HoudiniOutputTranslator.cpp | 2 +- .../Private/HoudiniEngineEditor.cpp | 183 ++++++++++++------ .../Private/HoudiniEngineEditor.h | 19 ++ 4 files changed, 218 insertions(+), 61 deletions(-) diff --git a/Source/HoudiniEngine/Private/HoudiniMaterialTranslator.cpp b/Source/HoudiniEngine/Private/HoudiniMaterialTranslator.cpp index 7c3a628499..61fd488adb 100755 --- a/Source/HoudiniEngine/Private/HoudiniMaterialTranslator.cpp +++ b/Source/HoudiniEngine/Private/HoudiniMaterialTranslator.cpp @@ -672,20 +672,83 @@ FHoudiniMaterialTranslator::CreateUnrealTexture( uint32 SrcHeight = ImageInfo.yRes; const char * SrcData = &ImageBuffer[0]; + // Handle the different packing for the source Houdini texture + uint32 PackOffset = 4; + uint32 OffsetR = 0; + uint32 OffsetG = 1; + uint32 OffsetB = 2; + uint32 OffsetA = 3; + switch (ImageInfo.packing) + { + case HAPI_IMAGE_PACKING_SINGLE: + PackOffset = 1; + OffsetR = 0; + OffsetG = 0; + OffsetB = 0; + OffsetA = 0; + break; + + case HAPI_IMAGE_PACKING_DUAL: + PackOffset = 2; + OffsetR = 0; + OffsetG = 1; + OffsetB = 1; + OffsetA = 0; + break; + + case HAPI_IMAGE_PACKING_RGB: + PackOffset = 3; + OffsetR = 0; + OffsetG = 1; + OffsetB = 2; + OffsetA = 0; + break; + + case HAPI_IMAGE_PACKING_BGR: + PackOffset = 3; + OffsetR = 2; + OffsetG = 1; + OffsetB = 0; + OffsetA = 0; + break; + + case HAPI_IMAGE_PACKING_RGBA: + PackOffset = 4; + OffsetR = 0; + OffsetG = 1; + OffsetB = 2; + OffsetA = 3; + break; + + case HAPI_IMAGE_PACKING_ABGR: + PackOffset = 4; + OffsetR = 3; + OffsetG = 2; + OffsetB = 1; + OffsetA = 0; + break; + + case HAPI_IMAGE_PACKING_UNKNOWN: + case HAPI_IMAGE_PACKING_MAX: + // invalid packing + HOUDINI_CHECK_RETURN(false, nullptr); + break; + } + for (uint32 y = 0; y < SrcHeight; y++) { DestPtr = &MipData[(SrcHeight - 1 - y) * SrcWidth * sizeof(FColor)]; for (uint32 x = 0; x < SrcWidth; x++) { - uint32 DataOffset = y * SrcWidth * 4 + x * 4; + uint32 DataOffset = y * SrcWidth * PackOffset + x * PackOffset; - *DestPtr++ = *(uint8*)(SrcData + DataOffset + 2); // B - *DestPtr++ = *(uint8*)(SrcData + DataOffset + 1); // G - *DestPtr++ = *(uint8*)(SrcData + DataOffset + 0); // R + *DestPtr++ = *(uint8*)(SrcData + DataOffset + OffsetB); // B + *DestPtr++ = *(uint8*)(SrcData + DataOffset + OffsetG); // G + *DestPtr++ = *(uint8*)(SrcData + DataOffset + OffsetR); // R - if (TextureParameters.bUseAlpha) - *DestPtr++ = *(uint8*)(SrcData + DataOffset + 3); // A + if (TextureParameters.bUseAlpha && PackOffset == 4) + *DestPtr++ = *(uint8*)(SrcData + DataOffset + OffsetA); // A else *DestPtr++ = 0xFF; } diff --git a/Source/HoudiniEngine/Private/HoudiniOutputTranslator.cpp b/Source/HoudiniEngine/Private/HoudiniOutputTranslator.cpp index 593e16a6fe..708a04a1ba 100755 --- a/Source/HoudiniEngine/Private/HoudiniOutputTranslator.cpp +++ b/Source/HoudiniEngine/Private/HoudiniOutputTranslator.cpp @@ -332,7 +332,7 @@ FHoudiniOutputTranslator::UpdateOutputs( // Look for UHoudiniStaticMesh in the output, and set bOutHasHoudiniStaticMeshOutput accordingly if (bIsProxyStaticMeshEnabled && !bOutHasHoudiniStaticMeshOutput) { - bOutHasHoudiniStaticMeshOutput &= CurOutput->HasAnyCurrentProxy(); + bOutHasHoudiniStaticMeshOutput = bOutHasHoudiniStaticMeshOutput || CurOutput->HasAnyCurrentProxy(); } break; diff --git a/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.cpp b/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.cpp index b8df6d1ebb..7319cb632a 100755 --- a/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.cpp +++ b/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.cpp @@ -1502,23 +1502,33 @@ FHoudiniEngineEditor::UnregisterConsoleCommands() void FHoudiniEngineEditor::RegisterEditorDelegates() { - PreSaveWorldEditorDelegateHandle = FEditorDelegates::PreSaveWorldWithContext.AddLambda([](UWorld* World, FObjectPreSaveContext InContext) + // This runs when the world has been modified and saved + // (in non-WP world, this is called when saving the level) + PreSaveWorldEditorDelegateHandle = FEditorDelegates::PreSaveWorldWithContext.AddLambda([this](UWorld* World, FObjectPreSaveContext InContext) { // Skip if this is a game world or an autosave, only refine meshes when the user manually saves - if (!World->IsGameWorld() && (InContext.GetSaveFlags() & ESaveFlags::SAVE_FromAutosave) == 0) + if (World->IsGameWorld() || InContext.GetSaveFlags() & ESaveFlags::SAVE_FromAutosave || InContext.IsProceduralSave()) + return; + + // Do the refinement + HandleOnPreSave(World); + + // Set a PostSaveWorld delegate for saving all dirty temp packages + FDelegateHandle& OnPostSaveWorldHandle = FHoudiniEngineEditor::Get().GetOnPostSaveWorldOnceHandle(); + if (OnPostSaveWorldHandle.IsValid()) { - const bool bSelectedOnly = false; - const bool bSilent = false; - const bool bRefineAll = false; - const bool bOnPreSaveWorld = true; - UWorld * const OnPreSaveWorld = World; - const bool bOnPreBeginPIE = false; - FHoudiniEngineCommands::RefineHoudiniProxyMeshesToStaticMeshes(bSelectedOnly, bSilent, bRefineAll, bOnPreSaveWorld, OnPreSaveWorld, bOnPreBeginPIE); + if (FEditorDelegates::PostSaveWorldWithContext.Remove(OnPostSaveWorldHandle)) + OnPostSaveWorldHandle.Reset(); } - if (!World->IsGameWorld()) + // Save all dirty temporary cook package OnPostSaveWorld + OnPostSaveWorldHandle = FEditorDelegates::PostSaveWorldWithContext.AddLambda( + [World](UWorld* PreSaveWorld, FObjectPostSaveContext InContext) { - UWorld * const OnPreSaveWorld = World; + if (World && World != PreSaveWorld) + return; + + FHoudiniEngineEditorUtils::SaveAllHoudiniTemporaryCookData(PreSaveWorld); FDelegateHandle& OnPostSaveWorldHandle = FHoudiniEngineEditor::Get().GetOnPostSaveWorldOnceHandle(); if (OnPostSaveWorldHandle.IsValid()) @@ -1526,57 +1536,64 @@ FHoudiniEngineEditor::RegisterEditorDelegates() if (FEditorDelegates::PostSaveWorldWithContext.Remove(OnPostSaveWorldHandle)) OnPostSaveWorldHandle.Reset(); } - - // Save all dirty temporary cook package OnPostSaveWorld - OnPostSaveWorldHandle = FEditorDelegates::PostSaveWorldWithContext.AddLambda( - [OnPreSaveWorld](UWorld* InWorld, FObjectPostSaveContext InContext) - { - if (OnPreSaveWorld && OnPreSaveWorld != InWorld) - return; - - FHoudiniEngineEditorUtils::SaveAllHoudiniTemporaryCookData(InWorld); - - FDelegateHandle& OnPostSaveWorldHandle = FHoudiniEngineEditor::Get().GetOnPostSaveWorldOnceHandle(); - if (OnPostSaveWorldHandle.IsValid()) - { - if (FEditorDelegates::PostSaveWorldWithContext.Remove(OnPostSaveWorldHandle)) - OnPostSaveWorldHandle.Reset(); - } - }); - } + }); }); - - PreBeginPIEEditorDelegateHandle = FEditorDelegates::PreBeginPIE.AddLambda([&](const bool bIsSimulating) + + // WP worlds do not call the PreSaveWorld callback when saving the current level + // This prevented the refinement when saving from being executed properly + // We can instead rely on PreSavePackage, when called on HoudiniAssetActors. + // This means that the refinement is called multiple times when multiple HDAs are in the level, + // but the actual refinement process happens only once. + PreSavePackageEditorDelegateHandle = UPackage::PreSavePackageWithContextEvent.AddLambda([this](UPackage* Package, FObjectPreSaveContext InContext) { - const bool bSelectedOnly = false; - const bool bSilent = false; - const bool bRefineAll = false; - const bool bOnPreSaveWorld = false; - UWorld * const OnPreSaveWorld = nullptr; - const bool bOnPreBeginPIE = true; + // Detect if we should actually do anything (check for autosaves, cooking, etc.) + if (InContext.GetSaveFlags() & ESaveFlags::SAVE_FromAutosave || InContext.IsProceduralSave()) + return; + + // Only runs the refinement when Houdini Asset Actors are being saved + UObject* Asset = Package->FindAssetInPackage(); + if (!IsValid(Asset) || !Asset->IsA()) + return; + + AHoudiniAssetActor* HAA = Cast(Asset); + if (!IsValid(HAA)) + return; + + UWorld* World = HAA->GetWorld(); + if (World->IsGameWorld()) + return; + bool bPostSaveNeeded = HandleOnPreSave(World); + + // Only add a PostSave delegate call if refinement happened + if(!bPostSaveNeeded) + return; - // We'll need to reconnect the Houdini session after PIE. - if (FHoudiniEngine::Get().IsTicking()) + // Set a PostSavePackage delegate for saving all dirty temp packages + FDelegateHandle& OnPostSavePackageOnceHandle = FHoudiniEngineEditor::Get().GetOnPostSavePackageOnceHandle(); + if (OnPostSavePackageOnceHandle.IsValid()) { - const bool bWasConnected = FHoudiniEngine::Get().GetSessionStatus() == EHoudiniSessionStatus::Connected; - EndPIEEditorDelegateHandle = FEditorDelegates::EndPIE.AddLambda([&, bWasConnected](const bool bEndPIEIsSimulating) - { - if (bWasConnected) - { - // If the Houdini session was previously connected, we need to reestablish the connection after PIE. - // We need to restart the current Houdini Engine Session - // This will reuse the previous session if it didnt shutdown, or start a new one if needed. - // (HARS shuts down when stopping the session, so we cant just reconnect when not using Session Sync) - FHoudiniEngineCommands::RestartSession(); - } - FEditorDelegates::EndPIE.Remove(EndPIEEditorDelegateHandle); - }); + if (UPackage::PackageSavedWithContextEvent.Remove(OnPostSavePackageOnceHandle)) + OnPostSavePackageOnceHandle.Reset(); } - - FHoudiniEngineCommands::RefineHoudiniProxyMeshesToStaticMeshes(bSelectedOnly, bSilent, bRefineAll, bOnPreSaveWorld, OnPreSaveWorld, bOnPreBeginPIE); + + // Save all dirty temporary cook package on PostSavePackage + OnPostSavePackageOnceHandle = UPackage::PackageSavedWithContextEvent.AddLambda( + [World](const FString & PackageFilename, UPackage * Package, FObjectPostSaveContext ObjectSaveContext) + { + FHoudiniEngineEditorUtils::SaveAllHoudiniTemporaryCookData(World); + + FDelegateHandle& OnPostSavePackageOnceHandle = FHoudiniEngineEditor::Get().GetOnPostSavePackageOnceHandle(); + if (OnPostSavePackageOnceHandle.IsValid()) + { + if (UPackage::PackageSavedWithContextEvent.Remove(OnPostSavePackageOnceHandle)) + OnPostSavePackageOnceHandle.Reset(); + } + }); }); + PreBeginPIEEditorDelegateHandle = FEditorDelegates::PreBeginPIE.AddLambda([this](const bool bIsSimulating){ this->HandleOnBeginPIE(); }); + OnDeleteActorsBegin = FEditorDelegates::OnDeleteActorsBegin.AddLambda([this](){ this->HandleOnDeleteActorsBegin(); }); OnDeleteActorsEnd = FEditorDelegates::OnDeleteActorsEnd.AddLambda([this](){ this-> HandleOnDeleteActorsEnd(); }); } @@ -1587,6 +1604,9 @@ FHoudiniEngineEditor::UnregisterEditorDelegates() if (PreSaveWorldEditorDelegateHandle.IsValid()) FEditorDelegates::PreSaveWorldWithContext.Remove(PreSaveWorldEditorDelegateHandle); + if (PreSavePackageEditorDelegateHandle.IsValid()) + UPackage::PreSavePackageWithContextEvent.Remove(PreSavePackageEditorDelegateHandle); + if (PreBeginPIEEditorDelegateHandle.IsValid()) FEditorDelegates::PreBeginPIE.Remove(PreBeginPIEEditorDelegateHandle); @@ -1734,6 +1754,61 @@ FHoudiniEngineEditor::PDGBakePackageReplaceModeToPackageReplaceMode(const EPDGBa return Mode; } +bool +FHoudiniEngineEditor::HandleOnPreSave(UWorld* InWorld) +{ + // Refine current ProxyMeshes to Static Meshes + const bool bSelectedOnly = false; + const bool bSilent = false; + const bool bRefineAll = false; + const bool bOnPreSaveWorld = true; + //UWorld* const OnPreSaveWorld = InWorld; + const bool bOnPreBeginPIE = false; + + // Do the refinement + EHoudiniProxyRefineRequestResult RefineResult = FHoudiniEngineCommands::RefineHoudiniProxyMeshesToStaticMeshes(bSelectedOnly, bSilent, bRefineAll, bOnPreSaveWorld, InWorld, bOnPreBeginPIE); + + //Return true only if a refinement happened + if (RefineResult == EHoudiniProxyRefineRequestResult::Invalid || RefineResult == EHoudiniProxyRefineRequestResult::None) + return false; + + return true; +} + +void +FHoudiniEngineEditor::HandleOnBeginPIE() +{ + // If the Houdini Engine Session was connected and valid before PIE, + // we'll need to reconnect the Houdini session after PIE. + // Setup a delegate for that + if (FHoudiniEngine::Get().IsTicking()) + { + const bool bWasConnected = FHoudiniEngine::Get().GetSessionStatus() == EHoudiniSessionStatus::Connected; + if (bWasConnected) + { + EndPIEEditorDelegateHandle = FEditorDelegates::EndPIE.AddLambda([&, bWasConnected](const bool bEndPIEIsSimulating) + { + // If the Houdini session was previously connected, we need to reestablish the connection after PIE. + // We need to restart the current Houdini Engine Session + // This will reuse the previous session if it didnt shutdown, or start a new one if needed. + // (HARS shuts down when stopping the session, so we cant just reconnect when not using Session Sync) + FHoudiniEngineCommands::RestartSession(); + + FEditorDelegates::EndPIE.Remove(EndPIEEditorDelegateHandle); + }); + } + } + + // Refine ProxyMeshes to StaticMeshes for PIE + const bool bSelectedOnly = false; + const bool bSilent = false; + const bool bRefineAll = false; + const bool bOnPreSaveWorld = false; + UWorld* const OnPreSaveWorld = nullptr; + const bool bOnPreBeginPIE = true; + FHoudiniEngineCommands::RefineHoudiniProxyMeshesToStaticMeshes(bSelectedOnly, bSilent, bRefineAll, bOnPreSaveWorld, OnPreSaveWorld, bOnPreBeginPIE); +} + void FHoudiniEngineEditor::HandleOnDeleteActorsBegin() { diff --git a/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.h b/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.h index 8bfd0bc915..6f4da68fcf 100755 --- a/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.h +++ b/Source/HoudiniEngineEditor/Private/HoudiniEngineEditor.h @@ -193,6 +193,9 @@ class HOUDINIENGINEEDITOR_API FHoudiniEngineEditor : public IHoudiniEngineEditor // Gets the PostSaveWorldOnceHandle FDelegateHandle& GetOnPostSaveWorldOnceHandle() { return PostSaveWorldOnceHandle; } + // Gets the PostSavePackageOnceHandle + FDelegateHandle& GetOnPostSavePackageOnceHandle() { return PostSavePackageOnceHandle; } + //void ModulesChangedCallback(FName ModuleName, EModuleChangeReason ReasonForChange); //FDelegateHandle ModulesChangedHandle; @@ -256,6 +259,15 @@ class HOUDINIENGINEEDITOR_API FHoudiniEngineEditor : public IHoudiniEngineEditor // Re-select AHoudiniAssetActors that were deselected (to avoid deletion) by HandleOnDeleteActorsBegin void HandleOnDeleteActorsEnd(); + // Handle pre-save events + // Either PreSaveWorld/PreSavePackage + // This allows proper refinement of Proxies to Static Mesh when saving + bool HandleOnPreSave(UWorld* InWorld); + + // Handle Begin PlayInEditor event + // This allows proper refinement of Proxies to Static Mesh + void HandleOnBeginPIE(); + private: // Singleton instance of Houdini Engine Editor. @@ -348,12 +360,19 @@ class HOUDINIENGINEEDITOR_API FHoudiniEngineEditor : public IHoudiniEngineEditor // Delegate handle for the PreSaveWorld editor delegate FDelegateHandle PreSaveWorldEditorDelegateHandle; + + // Delegate handle for the PreSavePackage editor delegate + FDelegateHandle PreSavePackageEditorDelegateHandle; // Delegate handle for the PostSaveWorld editor delegate: this // is bound on PreSaveWorld with specific captures and then unbound // by itself FDelegateHandle PostSaveWorldOnceHandle; + // Delegate handle for the PostSavePackage delegate: + // this is bound on PreSavePackage with specific captures and then unbound by itself + FDelegateHandle PostSavePackageOnceHandle; + // Delegate handle for the PreBeginPIE editor delegate FDelegateHandle PreBeginPIEEditorDelegateHandle;