Files
Onejsky4U/Plugins/Runtime/HoudiniEngine/Source/HoudiniEngineRuntime/Private/HoudiniEngineBakeUtils.cpp
T

1941 lines
75 KiB
C++

/*
* Copyright (c) <2017> Side Effects Software Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
*/
#include "HoudiniEngineBakeUtils.h"
#include "HoudiniApi.h"
#include "HoudiniEngineRuntimePrivatePCH.h"
#include "HoudiniEngineUtils.h"
#include "HoudiniRuntimeSettings.h"
#include "HoudiniAssetActor.h"
#include "HoudiniAssetComponent.h"
#include "HoudiniEngine.h"
#include "HoudiniAsset.h"
#include "HoudiniEngineString.h"
#include "HoudiniInstancedActorComponent.h"
#include "HoudiniMeshSplitInstancerComponent.h"
#include "HoudiniAssetInstanceInputField.h"
#include "CoreMinimal.h"
#include "Engine/StaticMesh.h"
#include "Engine/StaticMeshActor.h"
#include "Materials/Material.h"
#if WITH_EDITOR
#include "ActorFactories/ActorFactory.h"
#include "Editor.h"
#include "Factories/MaterialFactoryNew.h"
#include "ActorFactories/ActorFactoryStaticMesh.h"
#include "Interfaces/ITargetPlatform.h"
#include "Interfaces/ITargetPlatformManagerModule.h"
#include "FileHelpers.h"
#include "Materials/Material.h"
#include "Materials/MaterialInstance.h"
#include "Materials/MaterialExpressionTextureSample.h"
#include "Materials/MaterialExpressionTextureCoordinate.h"
#include "StaticMeshResources.h"
#include "InstancedFoliage.h"
#include "InstancedFoliageActor.h"
#include "Layers/LayersSubsystem.h"
#endif
#include "EngineUtils.h"
#include "UObject/MetaData.h"
#include "PhysicsEngine/BodySetup.h"
#include "Components/InstancedStaticMeshComponent.h"
#if PLATFORM_WINDOWS
#include "Windows/WindowsHWrapper.h"
// Of course, Windows defines its own GetGeoInfo,
// So we need to undefine that before including HoudiniApi.h to avoid collision...
#ifdef GetGeoInfo
#undef GetGeoInfo
#endif
#endif
#define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE
UPackage *
FHoudiniEngineBakeUtils::BakeCreateBlueprintPackageForComponent(
UHoudiniAssetComponent * HoudiniAssetComponent,
FString & BlueprintName )
{
UPackage * Package = nullptr;
#if WITH_EDITOR
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return nullptr;
FString HoudiniAssetName;
if ( HoudiniAssetComponent->HoudiniAsset )
HoudiniAssetName = HoudiniAssetComponent->HoudiniAsset->GetName();
else if ( HoudiniAssetComponent->GetOuter() )
HoudiniAssetName = HoudiniAssetComponent->GetOuter()->GetName();
else
HoudiniAssetName = HoudiniAssetComponent->GetName();
FGuid BakeGUID = FGuid::NewGuid();
if( !BakeGUID.IsValid() )
BakeGUID = FGuid::NewGuid();
// We only want half of generated guid string.
FString BakeGUIDString = BakeGUID.ToString().Left( FHoudiniEngineUtils::PackageGUIDItemNameLength );
// Generate Blueprint name.
BlueprintName = HoudiniAssetName + TEXT( "_" ) + BakeGUIDString;
// Generate unique package name.
FString PackageName = HoudiniAssetComponent->GetBakeFolder().ToString() + TEXT( "/" ) + BlueprintName;
PackageName = UPackageTools::SanitizePackageName( PackageName );
// See if package exists, if it does, we need to regenerate the name.
Package = FindPackage( nullptr, *PackageName );
if( Package && !Package->IsPendingKill() )
{
// Package does exist, there's a collision, we need to generate a new name.
BakeGUID.Invalidate();
}
else
{
// Create actual package.
Package = CreatePackage( nullptr, *PackageName );
}
#endif
return Package;
}
UStaticMesh *
FHoudiniEngineBakeUtils::BakeStaticMesh(
UHoudiniAssetComponent * HoudiniAssetComponent,
const FHoudiniGeoPartObject & HoudiniGeoPartObject,
UStaticMesh * InStaticMesh )
{
UStaticMesh * StaticMesh = nullptr;
#if WITH_EDITOR
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return nullptr;
UHoudiniAsset * HoudiniAsset = HoudiniAssetComponent->HoudiniAsset;
if (!HoudiniAsset || HoudiniAsset->IsPendingKill())
return nullptr;
// We cannot bake curves.
if( HoudiniGeoPartObject.IsCurve() )
return nullptr;
if( HoudiniGeoPartObject.IsInstancer() )
{
HOUDINI_LOG_MESSAGE( TEXT( "Baking of instanced static meshes is not supported at the moment." ) );
return nullptr;
}
// Get platform manager LOD specific information.
ITargetPlatform * CurrentPlatform = GetTargetPlatformManagerRef().GetRunningTargetPlatform();
check( CurrentPlatform );
const FStaticMeshLODGroup & LODGroup = CurrentPlatform->GetStaticMeshLODSettings().GetLODGroup( NAME_None );
int32 NumLODs = LODGroup.GetDefaultNumLODs();
// Get runtime settings.
const UHoudiniRuntimeSettings * HoudiniRuntimeSettings = GetDefault< UHoudiniRuntimeSettings >();
check( HoudiniRuntimeSettings );
FHoudiniCookParams HoudiniCookParams( HoudiniAssetComponent );
HoudiniCookParams.StaticMeshBakeMode = EBakeMode::CreateNewAssets;
FString MeshName;
FGuid BakeGUID;
UPackage * Package = FHoudiniEngineBakeUtils::BakeCreateStaticMeshPackageForComponent(
HoudiniCookParams, HoudiniGeoPartObject, MeshName, BakeGUID );
if( !Package || Package->IsPendingKill() )
return nullptr;
// Create static mesh.
StaticMesh = NewObject< UStaticMesh >( Package, FName( *MeshName ), RF_Public | RF_Transactional );
if ( !StaticMesh || StaticMesh->IsPendingKill() )
return nullptr;
// Add meta information to this package.
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
Package, StaticMesh, HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
Package, StaticMesh, HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *MeshName );
// Notify registry that we created a new asset.
FAssetRegistryModule::AssetCreated( StaticMesh );
// Copy materials.
StaticMesh->StaticMaterials = InStaticMesh->StaticMaterials;
// Create new source model for current static mesh.
if (!StaticMesh->GetNumSourceModels())
StaticMesh->AddSourceModel();
FStaticMeshSourceModel * SrcModel = &StaticMesh->GetSourceModel(0);
// Load raw data bytes.
FRawMesh RawMesh;
FStaticMeshSourceModel * InSrcModel = &InStaticMesh->GetSourceModel(0);
InSrcModel->LoadRawMesh( RawMesh );
// Some mesh generation settings.
HoudiniRuntimeSettings->SetMeshBuildSettings( SrcModel->BuildSettings, RawMesh );
// Setting the DistanceField resolution
SrcModel->BuildSettings.DistanceFieldResolutionScale = HoudiniAssetComponent->GeneratedDistanceFieldResolutionScale;
// We need to check light map uv set for correctness. Unreal seems to have occasional issues with
// zero UV sets when building lightmaps.
if( SrcModel->BuildSettings.bGenerateLightmapUVs )
{
// See if we need to disable lightmap generation because of bad UVs.
if( FHoudiniEngineUtils::ContainsInvalidLightmapFaces( RawMesh, StaticMesh->LightMapCoordinateIndex ) )
{
SrcModel->BuildSettings.bGenerateLightmapUVs = false;
HOUDINI_LOG_MESSAGE(
TEXT( "Skipping Lightmap Generation: Object %s " )
TEXT( "- skipping." ),
*MeshName );
}
}
// Store the new raw mesh.
SrcModel->StaticMeshOwner = StaticMesh;
SrcModel->SaveRawMesh( RawMesh );
while (StaticMesh->GetNumSourceModels() < NumLODs)
StaticMesh->AddSourceModel();
for( int32 ModelLODIndex = 0; ModelLODIndex < NumLODs; ++ModelLODIndex )
{
StaticMesh->GetSourceModel(ModelLODIndex).ReductionSettings = LODGroup.GetDefaultSettings(ModelLODIndex);
for( int32 MaterialIndex = 0; MaterialIndex < StaticMesh->StaticMaterials.Num(); ++MaterialIndex )
{
FMeshSectionInfo Info = StaticMesh->GetSectionInfoMap().Get( ModelLODIndex, MaterialIndex );
Info.MaterialIndex = MaterialIndex;
Info.bEnableCollision = true;
Info.bCastShadow = true;
StaticMesh->GetSectionInfoMap().Set( ModelLODIndex, MaterialIndex, Info );
}
}
// Assign generation parameters for this static mesh.
HoudiniAssetComponent->SetStaticMeshGenerationParameters( StaticMesh );
// Copy custom lightmap resolution if it is set.
if( InStaticMesh->LightMapResolution != StaticMesh->LightMapResolution )
StaticMesh->LightMapResolution = InStaticMesh->LightMapResolution;
if( HoudiniGeoPartObject.IsCollidable() || HoudiniGeoPartObject.IsRenderCollidable() )
{
UBodySetup * BodySetup = StaticMesh->BodySetup;
if (BodySetup && !BodySetup->IsPendingKill())
{
// Enable collisions for this static mesh.
BodySetup->CollisionTraceFlag = ECollisionTraceFlag::CTF_UseComplexAsSimple;
}
}
FHoudiniScopedGlobalSilence ScopedGlobalSilence;
StaticMesh->Build( true );
StaticMesh->MarkPackageDirty();
#endif
return StaticMesh;
}
UBlueprint *
FHoudiniEngineBakeUtils::BakeBlueprint( UHoudiniAssetComponent * HoudiniAssetComponent )
{
UBlueprint * Blueprint = nullptr;
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return nullptr;
#if WITH_EDITOR
// Create package for our Blueprint.
FString BlueprintName = TEXT( "" );
UPackage * Package = FHoudiniEngineBakeUtils::BakeCreateBlueprintPackageForComponent(
HoudiniAssetComponent, BlueprintName );
//Bake the asset's landscape
BakeLandscape(HoudiniAssetComponent);
if( Package && !Package->IsPendingKill() )
{
AActor * Actor = HoudiniAssetComponent->CloneComponentsAndCreateActor();
if( Actor && !Actor->IsPendingKill() )
{
Blueprint = FKismetEditorUtilities::CreateBlueprintFromActor( *BlueprintName, Package, Actor, false );
// If actor is rooted, unroot it. We can also delete intermediate actor.
Actor->RemoveFromRoot();
Actor->ConditionalBeginDestroy();
if( Blueprint && !Blueprint->IsPendingKill() )
FAssetRegistryModule::AssetCreated( Blueprint );
}
}
#endif
return Blueprint;
}
AActor *
FHoudiniEngineBakeUtils::ReplaceHoudiniActorWithBlueprint( UHoudiniAssetComponent * HoudiniAssetComponent )
{
AActor * Actor = nullptr;
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return nullptr;
#if WITH_EDITOR
// Create package for our Blueprint.
FString BlueprintName = TEXT( "" );
UPackage * Package = FHoudiniEngineBakeUtils::BakeCreateBlueprintPackageForComponent( HoudiniAssetComponent, BlueprintName );
if (!Package || Package->IsPendingKill())
return nullptr;
//Bake the asset's landscape
BakeLandscape( HoudiniAssetComponent );
AActor * ClonedActor = HoudiniAssetComponent->CloneComponentsAndCreateActor();
if (!ClonedActor || ClonedActor->IsPendingKill())
return nullptr;
UBlueprint * Blueprint = FKismetEditorUtilities::CreateBlueprint(
ClonedActor->GetClass(), Package, *BlueprintName,
EBlueprintType::BPTYPE_Normal, UBlueprint::StaticClass(),
UBlueprintGeneratedClass::StaticClass(), FName( "CreateFromActor" ) );
if( Blueprint && !Blueprint->IsPendingKill() )
{
Package->MarkPackageDirty();
if( ClonedActor->GetInstanceComponents().Num() > 0 )
FKismetEditorUtilities::AddComponentsToBlueprint( Blueprint, ClonedActor->GetInstanceComponents() );
if( Blueprint->GeneratedClass )
{
AActor * CDO = Cast< AActor >( Blueprint->GeneratedClass->GetDefaultObject() );
if (!CDO || CDO->IsPendingKill())
return nullptr;
const auto CopyOptions = ( EditorUtilities::ECopyOptions::Type )
( EditorUtilities::ECopyOptions::OnlyCopyEditOrInterpProperties |
EditorUtilities::ECopyOptions::PropagateChangesToArchetypeInstances );
EditorUtilities::CopyActorProperties( ClonedActor, CDO, CopyOptions );
USceneComponent * Scene = CDO->GetRootComponent();
if (Scene && !Scene->IsPendingKill())
{
Scene->SetRelativeLocation(FVector::ZeroVector);
Scene->SetRelativeRotation(FRotator::ZeroRotator);
// Clear out the attachment info after having copied the properties from the source actor
Scene->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
while ( true )
{
const int32 ChildCount = Scene->GetAttachChildren().Num();
if (ChildCount < 1)
break;
USceneComponent * Component = Scene->GetAttachChildren()[ChildCount - 1];
if ( Component && !Component->IsPendingKill() )
Component->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
}
check(Scene->GetAttachChildren().Num() == 0);
// Ensure the light mass information is cleaned up
Scene->InvalidateLightingCache();
}
}
// Compile our blueprint and notify asset system about blueprint.
FKismetEditorUtilities::CompileBlueprint( Blueprint );
FAssetRegistryModule::AssetCreated( Blueprint );
// Retrieve actor transform.
FVector Location = ClonedActor->GetActorLocation();
FRotator Rotator = ClonedActor->GetActorRotation();
// Replace cloned actor with Blueprint instance.
{
TArray< AActor * > Actors;
Actors.Add( ClonedActor );
ClonedActor->RemoveFromRoot();
Actor = FKismetEditorUtilities::CreateBlueprintInstanceFromSelection( Blueprint, Actors, Location, Rotator );
}
// We can initiate Houdini actor deletion.
DeleteBakedHoudiniAssetActor(HoudiniAssetComponent);
}
else
{
ClonedActor->RemoveFromRoot();
ClonedActor->ConditionalBeginDestroy();
}
#endif
return Actor;
}
UStaticMesh *
FHoudiniEngineBakeUtils::DuplicateStaticMeshAndCreatePackage(
const UStaticMesh * StaticMesh, UHoudiniAssetComponent * Component,
const FHoudiniGeoPartObject & HoudiniGeoPartObject, EBakeMode BakeMode )
{
UStaticMesh * DuplicatedStaticMesh = nullptr;
#if WITH_EDITOR
if( !HoudiniGeoPartObject.IsCurve() && !HoudiniGeoPartObject.IsInstancer() && !HoudiniGeoPartObject.IsPackedPrimitiveInstancer() && !HoudiniGeoPartObject.IsVolume() )
{
// Create package for this duplicated mesh.
FHoudiniCookParams HoudiniCookParams( Component );
// Transferring the CookMode to the materials
// We're either using the default one, or a custom one
HoudiniCookParams.StaticMeshBakeMode = BakeMode;
if( BakeMode == FHoudiniCookParams::GetDefaultStaticMeshesCookMode() )
HoudiniCookParams.MaterialAndTextureBakeMode = FHoudiniCookParams::GetDefaultMaterialAndTextureCookMode();
else
HoudiniCookParams.MaterialAndTextureBakeMode = BakeMode;
FString MeshName;
FGuid MeshGuid;
UPackage * MeshPackage = FHoudiniEngineBakeUtils::BakeCreateStaticMeshPackageForComponent(
HoudiniCookParams, HoudiniGeoPartObject, MeshName, MeshGuid );
if( !MeshPackage || MeshPackage->IsPendingKill() )
return nullptr;
// We need to be sure the package has been fully loaded before calling DuplicateObject
if (!MeshPackage->IsFullyLoaded())
{
FlushAsyncLoading();
if (!MeshPackage->GetOuter())
{
MeshPackage->FullyLoad();
}
else
{
MeshPackage->GetOutermost()->FullyLoad();
}
}
// Duplicate mesh for this new copied component.
DuplicatedStaticMesh = DuplicateObject< UStaticMesh >( StaticMesh, MeshPackage, *MeshName );
if ( !DuplicatedStaticMesh || DuplicatedStaticMesh->IsPendingKill() )
return nullptr;
if( BakeMode != EBakeMode::Intermediate )
DuplicatedStaticMesh->SetFlags( RF_Public | RF_Standalone );
// Add meta information.
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
MeshPackage, DuplicatedStaticMesh,
HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
MeshPackage, DuplicatedStaticMesh,
HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *MeshName );
// See if we need to duplicate materials and textures.
TArray< FStaticMaterial > DuplicatedMaterials;
TArray< FStaticMaterial > & Materials = DuplicatedStaticMesh->StaticMaterials;
for( int32 MaterialIdx = 0; MaterialIdx < Materials.Num(); ++MaterialIdx )
{
UMaterialInterface* MaterialInterface = Materials[MaterialIdx].MaterialInterface;
if( MaterialInterface )
{
UPackage * MaterialPackage = Cast< UPackage >( MaterialInterface->GetOuter() );
if( MaterialPackage && !MaterialPackage->IsPendingKill() )
{
FString MaterialName;
if( FHoudiniEngineBakeUtils::GetHoudiniGeneratedNameFromMetaInformation(
MaterialPackage, MaterialInterface, MaterialName ) )
{
// We only deal with materials.
UMaterial * Material = Cast< UMaterial >( MaterialInterface );
if( Material && !Material->IsPendingKill() )
{
// Duplicate material resource.
UMaterial * DuplicatedMaterial = FHoudiniEngineBakeUtils::DuplicateMaterialAndCreatePackage(
Material, HoudiniCookParams, MaterialName );
if( !DuplicatedMaterial || DuplicatedMaterial->IsPendingKill() )
continue;
// Store duplicated material.
FStaticMaterial DupeStaticMaterial = Materials[MaterialIdx];
DupeStaticMaterial.MaterialInterface = DuplicatedMaterial;
DuplicatedMaterials.Add( DupeStaticMaterial );
continue;
}
}
}
}
DuplicatedMaterials.Add( Materials[MaterialIdx] );
}
/*
// We need to be sure the package has been fully loaded before calling DuplicateObject
if (!MeshPackage->IsFullyLoaded())
{
FlushAsyncLoading();
if (!MeshPackage->GetOuter())
{
MeshPackage->FullyLoad();
}
else
{
MeshPackage->GetOutermost()->FullyLoad();
}
}
*/
// Assign duplicated materials.
DuplicatedStaticMesh->StaticMaterials = DuplicatedMaterials;
// Notify registry that we have created a new duplicate mesh.
FAssetRegistryModule::AssetCreated( DuplicatedStaticMesh );
// Dirty the static mesh package.
DuplicatedStaticMesh->MarkPackageDirty();
}
#endif
return DuplicatedStaticMesh;
}
bool
FHoudiniEngineBakeUtils::ReplaceHoudiniActorWithActors(UHoudiniAssetComponent * HoudiniAssetComponent, bool SelectNewActors)
{
bool bSuccess = false;
#if WITH_EDITOR
if (FHoudiniEngineBakeUtils::BakeHoudiniActorToActors(HoudiniAssetComponent, SelectNewActors))
{
bSuccess = FHoudiniEngineBakeUtils::DeleteBakedHoudiniAssetActor(HoudiniAssetComponent);
}
#endif
return bSuccess;
}
bool
FHoudiniEngineBakeUtils::BakeHoudiniActorToActors( UHoudiniAssetComponent * HoudiniAssetComponent, bool SelectNewActors )
{
bool bSuccess = false;
#if WITH_EDITOR
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return bSuccess;
const FScopedTransaction Transaction( LOCTEXT( "BakeToActors", "Bake To Actors" ) );
auto SMComponentToPart = HoudiniAssetComponent->CollectAllStaticMeshComponents();
TArray< AActor* > NewActors = BakeHoudiniActorToActors_StaticMeshes( HoudiniAssetComponent, SMComponentToPart );
auto IAComponentToPart = HoudiniAssetComponent->CollectAllInstancedActorComponents();
NewActors.Append( BakeHoudiniActorToActors_InstancedActors( HoudiniAssetComponent, IAComponentToPart ) );
auto SplitMeshInstancerComponentToPart = HoudiniAssetComponent->CollectAllMeshSplitInstancerComponents();
NewActors.Append( BakeHoudiniActorToActors_SplitMeshInstancers( HoudiniAssetComponent, SplitMeshInstancerComponentToPart ) );
bSuccess = NewActors.Num() > 0;
if( GEditor && SelectNewActors && bSuccess )
{
GEditor->SelectNone( false, true );
for( AActor* NewActor : NewActors )
{
if ( NewActor && !NewActor->IsPendingKill() )
GEditor->SelectActor( NewActor, true, false );
}
GEditor->NoteSelectionChange();
}
#endif
return bSuccess;
}
TArray< AActor* >
FHoudiniEngineBakeUtils::BakeHoudiniActorToActors_InstancedActors(
UHoudiniAssetComponent * HoudiniAssetComponent,
TMap< const UHoudiniInstancedActorComponent*, FHoudiniGeoPartObject >& ComponentToPart )
{
TArray< AActor* > NewActors;
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return NewActors;
#if WITH_EDITOR
ULevel* DesiredLevel = GWorld->GetCurrentLevel();
FName BaseName( *( HoudiniAssetComponent->GetOwner()->GetName() + TEXT( "_Baked" ) ) );
for( const auto& Iter : ComponentToPart )
{
const UHoudiniInstancedActorComponent * OtherSMC = Iter.Key;
if ( !OtherSMC || OtherSMC->IsPendingKill() )
continue;
for( AActor* InstActor : OtherSMC->Instances )
{
if ( !InstActor || InstActor->IsPendingKill() )
continue;
FName NewName = MakeUniqueObjectName( DesiredLevel, OtherSMC->InstancedAsset->StaticClass(), BaseName );
FString NewNameStr = NewName.ToString();
FTransform CurrentTransform = InstActor->GetTransform();
AActor* NewActor = OtherSMC->SpawnInstancedActor(CurrentTransform);
if( NewActor && !NewActor->IsPendingKill() )
{
NewActor->SetActorLabel( NewNameStr );
NewActor->SetFolderPath( BaseName );
NewActor->SetActorTransform( CurrentTransform );
NewActors.Add(NewActor);
}
}
}
#endif
return NewActors;
}
void
FHoudiniEngineBakeUtils::CheckedBakeStaticMesh(
UHoudiniAssetComponent* HoudiniAssetComponent,
TMap< const UStaticMesh*, UStaticMesh* >& OriginalToBakedMesh,
const FHoudiniGeoPartObject & HoudiniGeoPartObject, UStaticMesh* OriginalSM)
{
UStaticMesh* BakedSM = nullptr;
if( UStaticMesh ** FoundMeshPtr = OriginalToBakedMesh.Find(OriginalSM) )
{
// We've already baked this mesh, use it
BakedSM = *FoundMeshPtr;
}
else
{
if( FHoudiniEngineBakeUtils::StaticMeshRequiresBake(OriginalSM) )
{
// Bake the found mesh into the project
BakedSM = FHoudiniEngineBakeUtils::DuplicateStaticMeshAndCreatePackage(
OriginalSM, HoudiniAssetComponent, HoudiniGeoPartObject, EBakeMode::CreateNewAssets);
if( ensure(BakedSM) )
{
FAssetRegistryModule::AssetCreated(BakedSM);
}
}
else
{
// We didn't bake this mesh, but it's already baked so we will just use it as is
BakedSM = OriginalSM;
}
OriginalToBakedMesh.Add(OriginalSM, BakedSM);
}
}
TArray< AActor* >
FHoudiniEngineBakeUtils::BakeHoudiniActorToActors_StaticMeshes(
UHoudiniAssetComponent * HoudiniAssetComponent,
TMap< const UStaticMeshComponent*, FHoudiniGeoPartObject >& SMComponentToPart )
{
TMap< const UStaticMesh*, UStaticMesh* > OriginalToBakedMesh;
TArray< AActor* > NewActors;
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return NewActors;
#if WITH_EDITOR
// Loop over all comps, bake static mesh if not already baked, and create an actor for every one of them
for( const auto& Iter : SMComponentToPart )
{
const FHoudiniGeoPartObject & HoudiniGeoPartObject = Iter.Value;
const UStaticMeshComponent * OtherSMC = Iter.Key;
if ( !OtherSMC || OtherSMC->IsPendingKill() )
continue;
UStaticMesh * OtherSM = OtherSMC->GetStaticMesh();
if( !OtherSM || OtherSM->IsPendingKill() )
continue;
CheckedBakeStaticMesh(HoudiniAssetComponent, OriginalToBakedMesh, HoudiniGeoPartObject, OtherSM);
}
// Finished baking, now spawn the actors
for( const auto& Iter : SMComponentToPart )
{
const UStaticMeshComponent * OtherSMC = Iter.Key;
if ( !OtherSMC || OtherSMC->IsPendingKill() )
continue;
const FHoudiniGeoPartObject & HoudiniGeoPartObject = Iter.Value;
UStaticMesh* BakedSM = OriginalToBakedMesh[OtherSMC->GetStaticMesh()];
if( !BakedSM || BakedSM->IsPendingKill() )
continue;
ULevel* DesiredLevel = GWorld->GetCurrentLevel();
FName BaseName( *( HoudiniAssetComponent->GetOwner()->GetName() + TEXT( "_Baked" ) ) );
UActorFactory* Factory = GEditor ? GEditor->FindActorFactoryByClass( UActorFactoryStaticMesh::StaticClass() ) : nullptr;
if (!Factory)
continue;
auto PrepNewStaticMeshActor = [&]( AActor* NewActor )
{
if ( !NewActor || NewActor->IsPendingKill() )
return;
// The default name will be based on the static mesh package, we would prefer it to be based on the Houdini asset
FName NewName = MakeUniqueObjectName( DesiredLevel, Factory->NewActorClass, BaseName );
FString NewNameStr = NewName.ToString();
NewActor->Rename( *NewNameStr );
NewActor->SetActorLabel( NewNameStr );
NewActor->SetFolderPath( BaseName );
// Copy properties to new actor
AStaticMeshActor* SMActor = Cast< AStaticMeshActor>(NewActor);
if ( !SMActor || SMActor->IsPendingKill() )
return;
UStaticMeshComponent* SMC = SMActor->GetStaticMeshComponent();
if (!SMC || SMC->IsPendingKill())
return;
UStaticMeshComponent* OtherSMC_NonConst = const_cast<UStaticMeshComponent*>( OtherSMC );
SMC->SetCollisionProfileName( OtherSMC_NonConst->GetCollisionProfileName() );
SMC->SetCollisionEnabled( OtherSMC->GetCollisionEnabled() );
SMC->LightmassSettings = OtherSMC->LightmassSettings;
SMC->CastShadow = OtherSMC->CastShadow;
SMC->SetMobility( OtherSMC->Mobility );
if (OtherSMC_NonConst->GetBodySetup() && SMC->GetBodySetup())
{
// Copy the BodySetup
SMC->GetBodySetup()->CopyBodyPropertiesFrom(OtherSMC_NonConst->GetBodySetup());
// We need to recreate the physics mesh for the new body setup
SMC->GetBodySetup()->InvalidatePhysicsData();
SMC->GetBodySetup()->CreatePhysicsMeshes();
// Only copy the physical material if it's different from the default one,
// As this was causing crashes on BakeToActors in some cases
if (GEngine != NULL && OtherSMC_NonConst->GetBodySetup()->GetPhysMaterial() != GEngine->DefaultPhysMaterial)
SMC->SetPhysMaterialOverride(OtherSMC_NonConst->GetBodySetup()->GetPhysMaterial());
}
SMActor->SetActorHiddenInGame( OtherSMC->bHiddenInGame );
SMC->SetVisibility( OtherSMC->IsVisible() );
// Reapply the uproperties modified by attributes on the new component
FHoudiniEngineUtils::UpdateUPropertyAttributesOnObject( SMC, HoudiniGeoPartObject );
SMC->PostEditChange();
};
const UInstancedStaticMeshComponent* OtherISMC = Cast< const UInstancedStaticMeshComponent>(OtherSMC);
if( OtherISMC && !OtherISMC->IsPendingKill() )
{
#ifdef BAKE_TO_INSTANCEDSTATICMESHCOMPONENT_ACTORS
// This is an instanced static mesh component - we will create a generic AActor with a UInstancedStaticMeshComponent root
FActorSpawnParameters SpawnInfo;
SpawnInfo.OverrideLevel = DesiredLevel;
SpawnInfo.ObjectFlags = RF_Transactional;
SpawnInfo.Name = MakeUniqueObjectName( DesiredLevel, AActor::StaticClass(), BaseName );
SpawnInfo.bDeferConstruction = true;
if( AActor* NewActor = DesiredLevel->OwningWorld->SpawnActor<AActor>( SpawnInfo ) )
{
NewActor->SetActorLabel( NewActor->GetName() );
NewActor->SetActorHiddenInGame( OtherISMC->bHiddenInGame );
// Do we need to create a HISMC?
const UHierarchicalInstancedStaticMeshComponent* OtherHISMC = Cast< const UHierarchicalInstancedStaticMeshComponent>( OtherSMC );
UInstancedStaticMeshComponent* NewISMC = nullptr;
if ( OtherHISMC )
NewISMC = DuplicateObject< UHierarchicalInstancedStaticMeshComponent >(OtherHISMC, NewActor, *OtherHISMC->GetName() ) );
else
NewISMC = DuplicateObject< UInstancedStaticMeshComponent >( OtherISMC, NewActor, *OtherISMC->GetName() ) );
if( NewISMC )
{
NewISMC->SetupAttachment( nullptr );
NewISMC->SetStaticMesh( BakedSM );
NewActor->AddInstanceComponent( NewISMC );
NewActor->SetRootComponent( NewISMC );
NewISMC->SetWorldTransform( OtherISMC->GetComponentTransform() );
NewISMC->RegisterComponent();
NewActor->SetFolderPath( BaseName );
NewActor->FinishSpawning( OtherISMC->GetComponentTransform() );
NewActors.Add( NewActor );
NewActor->InvalidateLightingCache();
NewActor->PostEditMove( true );
NewActor->MarkPackageDirty();
}
}
#else
// This is an instanced static mesh component - we will split it up into StaticMeshActors
for( int32 InstanceIx = 0; InstanceIx < OtherISMC->GetInstanceCount(); ++InstanceIx )
{
FTransform InstanceTransform;
OtherISMC->GetInstanceTransform( InstanceIx, InstanceTransform, true );
AActor* NewActor = Factory->CreateActor(BakedSM, DesiredLevel, InstanceTransform, RF_Transactional);
if (!NewActor || NewActor->IsPendingKill())
continue;
PrepNewStaticMeshActor( NewActor );
// We need to set the modified uproperty on the created actor
AStaticMeshActor* SMActor = Cast< AStaticMeshActor>(NewActor);
if ( !SMActor || SMActor->IsPendingKill() )
continue;
UStaticMeshComponent* SMC = SMActor->GetStaticMeshComponent();
if ( !SMC || SMC->IsPendingKill() )
continue;
FHoudiniGeoPartObject GeoPartObject = HoudiniAssetComponent->LocateGeoPartObject( OtherSMC->GetStaticMesh() );
// Set the part id to 0 so we can access the instancer
GeoPartObject.PartId = 0;
FHoudiniEngineUtils::UpdateUPropertyAttributesOnObject( SMC, GeoPartObject );
NewActors.Add(NewActor);
}
#endif
}
else
{
AActor* NewActor = Factory->CreateActor(BakedSM, DesiredLevel, OtherSMC->GetComponentTransform(), RF_Transactional);
if( NewActor && !NewActor->IsPendingKill() )
{
PrepNewStaticMeshActor( NewActor );
NewActors.Add(NewActor);
}
}
}
#endif
return NewActors;
}
TArray<AActor*>
FHoudiniEngineBakeUtils::BakeHoudiniActorToActors_SplitMeshInstancers(UHoudiniAssetComponent * HoudiniAssetComponent,
TMap<const UHoudiniMeshSplitInstancerComponent *, FHoudiniGeoPartObject> SplitMeshInstancerComponentToPart)
{
TArray< AActor* > NewActors;
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return NewActors;
#if WITH_EDITOR
TMap< const UStaticMesh*, UStaticMesh* > OriginalToBakedMesh;
for( auto&& Iter : SplitMeshInstancerComponentToPart )
{
auto&& HoudiniGeoPartObject = Iter.Value;
auto&& OtherMSIC = Iter.Key;
if ( !OtherMSIC || OtherMSIC->IsPendingKill() )
continue;
UStaticMesh * OtherSM = OtherMSIC->GetStaticMesh();
if( !OtherSM || OtherSM->IsPendingKill() )
continue;
CheckedBakeStaticMesh(HoudiniAssetComponent, OriginalToBakedMesh, HoudiniGeoPartObject, OtherSM);
}
// Done baking, now spawn the actors
for( auto&& Iter : SplitMeshInstancerComponentToPart )
{
auto&& OtherMSIC = Iter.Key;
UStaticMesh* BakedSM = OriginalToBakedMesh[OtherMSIC->GetStaticMesh()];
if( !BakedSM || BakedSM->IsPendingKill() )
continue;
ULevel* DesiredLevel = GWorld->GetCurrentLevel();
FName BaseName(*( HoudiniAssetComponent->GetOwner()->GetName() + TEXT("_Baked") ));
// This is a split mesh instancer component - we will create a generic AActor with a bunch of SMC
FActorSpawnParameters SpawnInfo;
SpawnInfo.OverrideLevel = DesiredLevel;
SpawnInfo.ObjectFlags = RF_Transactional;
SpawnInfo.Name = MakeUniqueObjectName(DesiredLevel, AActor::StaticClass(), BaseName);
SpawnInfo.bDeferConstruction = true;
AActor* NewActor = DesiredLevel->OwningWorld->SpawnActor<AActor>(SpawnInfo);
if (!NewActor || NewActor->IsPendingKill())
continue;
NewActor->SetActorLabel(NewActor->GetName());
NewActor->SetActorHiddenInGame(OtherMSIC->bHiddenInGame);
for( UStaticMeshComponent* OtherSMC : OtherMSIC->GetInstances() )
{
if ( !OtherSMC || OtherSMC->IsPendingKill() )
continue;
UStaticMeshComponent* NewSMC = DuplicateObject< UStaticMeshComponent >(OtherSMC, NewActor, *OtherSMC->GetName());
if (!NewSMC || NewSMC->IsPendingKill())
continue;
NewSMC->SetupAttachment(nullptr);
NewSMC->SetStaticMesh(BakedSM);
NewActor->AddInstanceComponent(NewSMC);
NewSMC->SetWorldTransform(OtherSMC->GetComponentTransform());
NewSMC->RegisterComponent();
}
NewActor->SetFolderPath(BaseName);
NewActor->FinishSpawning(OtherMSIC->GetComponentTransform());
NewActors.Add(NewActor);
NewActor->InvalidateLightingCache();
NewActor->PostEditMove(true);
NewActor->MarkPackageDirty();
}
#endif
return NewActors;
}
UHoudiniAssetInput*
FHoudiniEngineBakeUtils::GetInputForBakeHoudiniActorToOutlinerInput( const UHoudiniAssetComponent * HoudiniAssetComponent )
{
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return nullptr;
UHoudiniAssetInput *FirstInput = nullptr, *Result = nullptr;
#if WITH_EDITOR
if( HoudiniAssetComponent->GetInputs().Num() && HoudiniAssetComponent->GetInputs()[0] )
{
FirstInput = HoudiniAssetComponent->GetInputs()[0];
}
else
{
TMap< FString, UHoudiniAssetParameter* > InputParams;
HoudiniAssetComponent->CollectAllParametersOfType( UHoudiniAssetInput::StaticClass(), InputParams );
TArray< UHoudiniAssetParameter* > InputParamsArray;
InputParams.GenerateValueArray( InputParamsArray );
if( InputParamsArray.Num() > 0 )
{
FirstInput = Cast<UHoudiniAssetInput>( InputParamsArray[0] );
}
}
if( FirstInput && !FirstInput->IsPendingKill() )
{
if( FirstInput->GetChoiceIndex() == EHoudiniAssetInputType::WorldInput && FirstInput->GetWorldOutlinerInputs().Num() == 1 )
{
const FHoudiniAssetInputOutlinerMesh& InputOutlinerMesh = FirstInput->GetWorldOutlinerInputs()[0];
if( InputOutlinerMesh.ActorPtr.IsValid() && InputOutlinerMesh.StaticMeshComponent )
{
Result = FirstInput;
}
}
}
#endif
return Result;
}
bool
FHoudiniEngineBakeUtils::CanComponentBakeToOutlinerInput( const UHoudiniAssetComponent * HoudiniAssetComponent )
{
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return false;
#if WITH_EDITOR
// TODO: Cache this
auto SMComponentToPart = HoudiniAssetComponent->CollectAllStaticMeshComponents();
if( SMComponentToPart.Num() == 1 )
{
return nullptr != GetInputForBakeHoudiniActorToOutlinerInput( HoudiniAssetComponent );
}
#endif
return false;
}
bool
FHoudiniEngineBakeUtils::CanComponentBakeToFoliage(const UHoudiniAssetComponent * HoudiniAssetComponent)
{
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return false;
const TArray< UHoudiniAssetInstanceInputField * > InstanceInputFields = HoudiniAssetComponent->GetAllInstanceInputFields();
return InstanceInputFields.Num() > 0;
}
void
FHoudiniEngineBakeUtils::BakeHoudiniActorToOutlinerInput( UHoudiniAssetComponent * HoudiniAssetComponent )
{
if ( !HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill() )
return;
#if WITH_EDITOR
TMap< const UStaticMesh*, UStaticMesh* > OriginalToBakedMesh;
TMap< const UStaticMeshComponent*, FHoudiniGeoPartObject > SMComponentToPart = HoudiniAssetComponent->CollectAllStaticMeshComponents();
for( const auto& Iter : SMComponentToPart )
{
const FHoudiniGeoPartObject & HoudiniGeoPartObject = Iter.Value;
const UStaticMeshComponent * OtherSMC = Iter.Key;
if ( !OtherSMC || OtherSMC->IsPendingKill() )
continue;
UStaticMesh* OtherSM = OtherSMC->GetStaticMesh();
if( !OtherSM || OtherSM->IsPendingKill() )
continue;
UStaticMesh* BakedSM = nullptr;
if( UStaticMesh ** FoundMeshPtr = OriginalToBakedMesh.Find( OtherSM ) )
{
// We've already baked this mesh, use it
BakedSM = *FoundMeshPtr;
}
else
{
// Bake the found mesh into the project
BakedSM = FHoudiniEngineBakeUtils::DuplicateStaticMeshAndCreatePackage(
OtherSM, HoudiniAssetComponent, HoudiniGeoPartObject, EBakeMode::CreateNewAssets );
if( BakedSM )
{
OriginalToBakedMesh.Add(OtherSM, BakedSM );
FAssetRegistryModule::AssetCreated( BakedSM );
}
}
}
{
const FScopedTransaction Transaction( LOCTEXT( "BakeToInput", "Bake To Input" ) );
for( auto Iter : OriginalToBakedMesh )
{
// Get the first outliner input
UHoudiniAssetInput* FirstInput = GetInputForBakeHoudiniActorToOutlinerInput(HoudiniAssetComponent);
if ( !FirstInput || FirstInput->IsPendingKill() )
continue;
const FHoudiniAssetInputOutlinerMesh& InputOutlinerMesh = FirstInput->GetWorldOutlinerInputs()[0];
if( InputOutlinerMesh.ActorPtr.IsValid() && InputOutlinerMesh.StaticMeshComponent )
{
UStaticMeshComponent* InOutSMC = InputOutlinerMesh.StaticMeshComponent;
if ( InOutSMC && !InOutSMC->IsPendingKill() )
{
InputOutlinerMesh.ActorPtr->Modify();
InOutSMC->SetStaticMesh(Iter.Value);
InOutSMC->InvalidateLightingCache();
InOutSMC->MarkPackageDirty();
}
// Disconnect the input from the asset - InputOutlinerMesh now garbage
FirstInput->RemoveWorldOutlinerInput( 0 );
}
// Only handle the first Baked Mesh
break;
}
}
#endif
}
// Bakes output instanced meshes to the level's foliage actor and removes the Houdini actor
bool
FHoudiniEngineBakeUtils::ReplaceHoudiniActorWithFoliage( UHoudiniAssetComponent * HoudiniAssetComponent )
{
bool bSuccess = false;
#if WITH_EDITOR
if ( FHoudiniEngineBakeUtils::BakeHoudiniActorToFoliage(HoudiniAssetComponent) )
{
bSuccess = FHoudiniEngineBakeUtils::DeleteBakedHoudiniAssetActor(HoudiniAssetComponent);
}
#endif
return bSuccess;
}
bool
FHoudiniEngineBakeUtils::BakeHoudiniActorToFoliage(UHoudiniAssetComponent * HoudiniAssetComponent )
{
#if WITH_EDITOR
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return false;
const FScopedTransaction Transaction(LOCTEXT("BakeToFoliage", "Bake To Foliage"));
ULevel* DesiredLevel = GWorld->GetCurrentLevel();
AInstancedFoliageActor* InstancedFoliageActor = AInstancedFoliageActor::GetInstancedFoliageActorForLevel(DesiredLevel, true);
if (!InstancedFoliageActor || InstancedFoliageActor->IsPendingKill())
return false;
// Map storing original and baked Static Meshes
TMap< const UStaticMesh*, UStaticMesh* > OriginalToBakedMesh;
int32 BakedCount = 0;
const TArray< UHoudiniAssetInstanceInputField * > InstanceInputFields = HoudiniAssetComponent->GetAllInstanceInputFields();
for ( int32 Idx = 0; Idx < InstanceInputFields.Num(); ++Idx )
{
const UHoudiniAssetInstanceInputField * HoudiniAssetInstanceInputField = InstanceInputFields[ Idx ];
if ( !HoudiniAssetInstanceInputField || HoudiniAssetInstanceInputField->IsPendingKill() )
continue;
for ( int32 VariationIdx = 0; VariationIdx < HoudiniAssetInstanceInputField->InstanceVariationCount(); VariationIdx++ )
{
UInstancedStaticMeshComponent * ISMC =
Cast<UInstancedStaticMeshComponent>( HoudiniAssetInstanceInputField->GetInstancedComponent( VariationIdx ) );
if (!ISMC || ISMC->IsPendingKill())
continue;
// If the original static mesh is used (the cooked mesh for HAPI), then we need to bake it to a file
// If not, we don't need to bake a new mesh as we're using a SM override, so can use the existing unreal asset
UStaticMesh* OutStaticMesh = Cast<UStaticMesh>( HoudiniAssetInstanceInputField->GetInstanceVariation( VariationIdx ) );
if ( HoudiniAssetInstanceInputField->IsOriginalObjectUsed( VariationIdx ) )
{
UStaticMesh* OriginalSM = Cast<UStaticMesh>(HoudiniAssetInstanceInputField->GetOriginalObject());
if (!OriginalSM || OriginalSM->IsPendingKill())
continue;
// We're instancing a mesh created by the plugin, we need to bake it first
auto&& ItemGeoPartObject = HoudiniAssetComponent->LocateGeoPartObject(OutStaticMesh);
FHoudiniEngineBakeUtils::CheckedBakeStaticMesh(HoudiniAssetComponent, OriginalToBakedMesh, ItemGeoPartObject, OriginalSM);
OutStaticMesh = OriginalToBakedMesh[ OutStaticMesh ];
}
// See if we already have a FoliageType for that mesh
UFoliageType* FoliageType = InstancedFoliageActor->GetLocalFoliageTypeForSource(OutStaticMesh);
if ( !FoliageType || FoliageType->IsPendingKill() )
{
// We need to create a new FoliageType for this Static Mesh
InstancedFoliageActor->AddMesh(OutStaticMesh, &FoliageType, HoudiniAssetComponent->GeneratedFoliageDefaultSettings);
}
// Get the FoliageMeshInfo for this Foliage type so we can add the instance to it
FFoliageInfo* FoliageInfo = InstancedFoliageActor->FindOrAddMesh(FoliageType);
if ( !FoliageInfo )
continue;
// Get the transforms for this instance
TArray< FTransform > ProcessedTransforms;
HoudiniAssetInstanceInputField->GetProcessedTransforms(ProcessedTransforms, VariationIdx);
FTransform HoudiniAssetTransform = HoudiniAssetComponent->GetComponentTransform();
FFoliageInstance FoliageInstance;
for (auto CurrentTransform : ProcessedTransforms)
{
FoliageInstance.Location = HoudiniAssetTransform.TransformPosition(CurrentTransform.GetLocation());
FoliageInstance.Rotation = HoudiniAssetTransform.TransformRotation(CurrentTransform.GetRotation()).Rotator();
FoliageInstance.DrawScale3D = CurrentTransform.GetScale3D() * HoudiniAssetTransform.GetScale3D();
FoliageInfo->AddInstance(InstancedFoliageActor, FoliageType, FoliageInstance);
}
// TODO: This was due to a bug in UE4.22-20, check if still needed!
if ( FoliageInfo->GetComponent() )
FoliageInfo->GetComponent()->BuildTreeIfOutdated(true, true);
// Notify the user that we succesfully bake the instances to foliage
FString Notification = TEXT("Successfully baked ") + FString::FromInt(ProcessedTransforms.Num()) + TEXT(" instances of ") + OutStaticMesh->GetName() + TEXT(" to Foliage");
FHoudiniEngineUtils::CreateSlateNotification(Notification);
BakedCount += ProcessedTransforms.Num();
}
}
if (BakedCount > 0)
return true;
#endif
return false;
}
bool
FHoudiniEngineBakeUtils::StaticMeshRequiresBake( const UStaticMesh * StaticMesh )
{
#if WITH_EDITOR
if( !StaticMesh || StaticMesh->IsPendingKill() )
return false;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>( "AssetRegistry" );
FAssetData BackingAssetData = AssetRegistryModule.Get().GetAssetByObjectPath( *StaticMesh->GetPathName() );
if( !BackingAssetData.IsUAsset() )
return true;
for( const auto& StaticMaterial : StaticMesh->StaticMaterials )
{
if( !StaticMaterial.MaterialInterface || StaticMaterial.MaterialInterface->IsPendingKill() )
continue;
BackingAssetData = AssetRegistryModule.Get().GetAssetByObjectPath(*StaticMaterial.MaterialInterface->GetPathName());
if (!BackingAssetData.IsUAsset())
return true;
}
#endif
return false;
}
UMaterial *
FHoudiniEngineBakeUtils::DuplicateMaterialAndCreatePackage(
UMaterial * Material, FHoudiniCookParams& HoudiniCookParams, const FString & SubMaterialName )
{
UMaterial * DuplicatedMaterial = nullptr;
#if WITH_EDITOR
// Create material package.
FString MaterialName;
UPackage * MaterialPackage = FHoudiniEngineBakeUtils::BakeCreateTextureOrMaterialPackageForComponent(
HoudiniCookParams, SubMaterialName, MaterialName );
if( !MaterialPackage || MaterialPackage->IsPendingKill() )
return nullptr;
// Clone material.
DuplicatedMaterial = DuplicateObject< UMaterial >( Material, MaterialPackage, *MaterialName );
if (!DuplicatedMaterial || DuplicatedMaterial->IsPendingKill())
return nullptr;
if( HoudiniCookParams.MaterialAndTextureBakeMode != EBakeMode::Intermediate )
DuplicatedMaterial->SetFlags( RF_Public | RF_Standalone );
// Add meta information.
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
MaterialPackage, DuplicatedMaterial,
HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
MaterialPackage, DuplicatedMaterial,
HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *MaterialName );
// Retrieve and check various sampling expressions. If they contain textures, duplicate (and bake) them.
for( auto& Expression : DuplicatedMaterial->Expressions )
{
FHoudiniEngineBakeUtils::ReplaceDuplicatedMaterialTextureSample(
Expression, HoudiniCookParams );
}
// Notify registry that we have created a new duplicate material.
FAssetRegistryModule::AssetCreated( DuplicatedMaterial );
// Dirty the material package.
DuplicatedMaterial->MarkPackageDirty();
// Reset any derived state
DuplicatedMaterial->ForceRecompileForRendering();
#endif
return DuplicatedMaterial;
}
void
FHoudiniEngineBakeUtils::ReplaceDuplicatedMaterialTextureSample(
UMaterialExpression * MaterialExpression, FHoudiniCookParams& HoudiniCookParams )
{
#if WITH_EDITOR
UMaterialExpressionTextureSample * TextureSample = Cast< UMaterialExpressionTextureSample >( MaterialExpression );
if( !TextureSample || TextureSample->IsPendingKill() )
return;
UTexture2D * Texture = Cast< UTexture2D >( TextureSample->Texture );
if ( !Texture || Texture->IsPendingKill() )
return;
UPackage * TexturePackage = Cast< UPackage >( Texture->GetOuter() );
if( !TexturePackage || TexturePackage->IsPendingKill() )
return;
FString GeneratedTextureName;
if( FHoudiniEngineBakeUtils::GetHoudiniGeneratedNameFromMetaInformation(
TexturePackage, Texture, GeneratedTextureName ) )
{
// Duplicate texture.
UTexture2D * DuplicatedTexture = FHoudiniEngineBakeUtils::DuplicateTextureAndCreatePackage(
Texture, HoudiniCookParams, GeneratedTextureName );
// Re-assign generated texture.
TextureSample->Texture = DuplicatedTexture;
}
#endif
}
UTexture2D *
FHoudiniEngineBakeUtils::DuplicateTextureAndCreatePackage(
UTexture2D * Texture, FHoudiniCookParams& HoudiniCookParams, const FString & SubTextureName )
{
UTexture2D* DuplicatedTexture = nullptr;
#if WITH_EDITOR
// Retrieve original package of this texture.
UPackage * TexturePackage = Cast< UPackage >( Texture->GetOuter() );
if( !TexturePackage || TexturePackage->IsPendingKill() )
return nullptr;
FString GeneratedTextureName;
if( FHoudiniEngineBakeUtils::GetHoudiniGeneratedNameFromMetaInformation( TexturePackage, Texture, GeneratedTextureName ) )
{
UMetaData * MetaData = TexturePackage->GetMetaData();
if ( !MetaData || MetaData->IsPendingKill() )
return nullptr;
// Retrieve texture type.
const FString & TextureType =
MetaData->GetValue( Texture, HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_TYPE );
// Create texture package.
FString TextureName;
UPackage * NewTexturePackage = FHoudiniEngineBakeUtils::BakeCreateTextureOrMaterialPackageForComponent(
HoudiniCookParams, SubTextureName, TextureName );
if( !NewTexturePackage || NewTexturePackage->IsPendingKill() )
return nullptr;
// Clone texture.
DuplicatedTexture = DuplicateObject< UTexture2D >( Texture, NewTexturePackage, *TextureName );
if ( !DuplicatedTexture || DuplicatedTexture->IsPendingKill() )
return nullptr;
if( HoudiniCookParams.MaterialAndTextureBakeMode != EBakeMode::Intermediate )
DuplicatedTexture->SetFlags( RF_Public | RF_Standalone );
// Add meta information.
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
NewTexturePackage, DuplicatedTexture,
HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT, TEXT( "true" ) );
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
NewTexturePackage, DuplicatedTexture,
HAPI_UNREAL_PACKAGE_META_GENERATED_NAME, *TextureName );
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
NewTexturePackage, DuplicatedTexture,
HAPI_UNREAL_PACKAGE_META_GENERATED_TEXTURE_TYPE, *TextureType );
// Notify registry that we have created a new duplicate texture.
FAssetRegistryModule::AssetCreated( DuplicatedTexture );
// Dirty the texture package.
DuplicatedTexture->MarkPackageDirty();
}
#endif
return DuplicatedTexture;
}
FHoudiniCookParams::FHoudiniCookParams( UHoudiniAssetComponent* HoudiniAssetComponent )
{
#if WITH_EDITOR
HoudiniAsset = HoudiniAssetComponent->GetHoudiniAsset();
HoudiniCookManager = HoudiniAssetComponent;
PackageGUID = HoudiniAssetComponent->GetComponentGuid();
BakedStaticMeshPackagesForParts = &HoudiniAssetComponent->BakedStaticMeshPackagesForParts;
BakedMaterialPackagesForIds = &HoudiniAssetComponent->BakedMaterialPackagesForIds;
CookedTemporaryStaticMeshPackages = &HoudiniAssetComponent->CookedTemporaryStaticMeshPackages;
CookedTemporaryPackages = &HoudiniAssetComponent->CookedTemporaryPackages;
CookedTemporaryLandscapeLayers = &HoudiniAssetComponent->CookedTemporaryLandscapeLayers;
TempCookFolder = HoudiniAssetComponent->GetTempCookFolder();
BakeFolder = HoudiniAssetComponent->GetBakeFolder();
BakeNameOverrides = &HoudiniAssetComponent->BakeNameOverrides;
IntermediateOuter = HoudiniAssetComponent->GetComponentLevel();
GeneratedDistanceFieldResolutionScale = HoudiniAssetComponent->GeneratedDistanceFieldResolutionScale;
#endif
}
bool
FHoudiniEngineBakeUtils::BakeLandscape( UHoudiniAssetComponent* HoudiniAssetComponent, ALandscapeProxy * OnlyBakeThisLandscape )
{
#if WITH_EDITOR
if ( !HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill() )
return false;
if ( !HoudiniAssetComponent->HasLandscape() )
return false;
TMap< FHoudiniGeoPartObject, TWeakObjectPtr<ALandscapeProxy>> * LandscapeComponentsPtr = HoudiniAssetComponent->GetLandscapeComponents();
if ( !LandscapeComponentsPtr )
return false;
TArray<UPackage *> LayerPackages;
bool bNeedToUpdateProperties = false;
for ( TMap< FHoudiniGeoPartObject, TWeakObjectPtr<ALandscapeProxy> >::TIterator Iter(* LandscapeComponentsPtr ); Iter; ++Iter)
{
ALandscapeProxy * CurrentLandscape = Iter.Value().Get();
if ( !CurrentLandscape || CurrentLandscape->IsPendingKill() || !CurrentLandscape->IsValidLowLevel() )
continue;
// If we only want to bake a single landscape
if ( OnlyBakeThisLandscape && CurrentLandscape != OnlyBakeThisLandscape )
continue;
// Simply remove the landscape from the map
FHoudiniGeoPartObject & HoudiniGeoPartObject = Iter.Key();
LandscapeComponentsPtr->Remove( HoudiniGeoPartObject );
CurrentLandscape->DetachFromActor( FDetachmentTransformRules::KeepWorldTransform );
// And save its layers to prevent them from being removed
for ( TMap< TWeakObjectPtr< UPackage >, FHoudiniGeoPartObject > ::TIterator IterPackage( HoudiniAssetComponent->CookedTemporaryLandscapeLayers ); IterPackage; ++IterPackage )
{
if ( !( HoudiniGeoPartObject == IterPackage.Value() ) )
continue;
UPackage * Package = IterPackage.Key().Get();
if ( Package && !Package->IsPendingKill() )
LayerPackages.Add( Package );
}
bNeedToUpdateProperties = true;
// If we only wanted to bake a single landscape, we're done
if ( OnlyBakeThisLandscape )
break;
}
if ( LayerPackages.Num() > 0 )
{
// Save the layer info's package
FEditorFileUtils::PromptForCheckoutAndSave( LayerPackages, true, false );
// Remove the packages from the asset component, or the asset component might
// destroy them when it is being destroyed
for ( int32 n = 0; n < LayerPackages.Num(); n++ )
HoudiniAssetComponent->CookedTemporaryLandscapeLayers.Remove( LayerPackages[n] );
}
return bNeedToUpdateProperties;
#else
return false;
#endif
}
UPackage *
FHoudiniEngineBakeUtils::BakeCreateMaterialPackageForComponent(
FHoudiniCookParams& HoudiniCookParams,
const HAPI_MaterialInfo & MaterialInfo, FString & MaterialName )
{
UHoudiniAsset * HoudiniAsset = HoudiniCookParams.HoudiniAsset;
if ( !HoudiniAsset || HoudiniAsset->IsPendingKill() )
return nullptr;
FString MaterialDescriptor;
if( HoudiniCookParams.MaterialAndTextureBakeMode != EBakeMode::Intermediate )
MaterialDescriptor = HoudiniAsset->GetName() + TEXT( "_material_" ) + FString::FromInt( MaterialInfo.nodeId ) + TEXT( "_" );
else
MaterialDescriptor = HoudiniAsset->GetName() + TEXT( "_" ) + FString::FromInt( MaterialInfo.nodeId ) + TEXT( "_" );
return FHoudiniEngineBakeUtils::BakeCreateTextureOrMaterialPackageForComponent(
HoudiniCookParams, MaterialDescriptor, MaterialName );
}
UPackage *
FHoudiniEngineBakeUtils::BakeCreateTexturePackageForComponent(
FHoudiniCookParams& HoudiniCookParams,
const HAPI_MaterialInfo & MaterialInfo, const FString & TextureType,
FString & TextureName )
{
UHoudiniAsset * HoudiniAsset = HoudiniCookParams.HoudiniAsset;
if (!HoudiniAsset || HoudiniAsset->IsPendingKill())
return nullptr;
FString TextureInfoDescriptor;
if ( HoudiniCookParams.MaterialAndTextureBakeMode != EBakeMode::Intermediate )
{
TextureInfoDescriptor = HoudiniAsset->GetName() + TEXT( "_texture_" ) + FString::FromInt( MaterialInfo.nodeId ) +
TEXT( "_" ) + TextureType + TEXT( "_" );
}
else
{
TextureInfoDescriptor = HoudiniAsset->GetName() + TEXT( "_" ) + FString::FromInt( MaterialInfo.nodeId ) + TEXT( "_" ) +
TextureType + TEXT( "_" );
}
return FHoudiniEngineBakeUtils::BakeCreateTextureOrMaterialPackageForComponent(
HoudiniCookParams, TextureInfoDescriptor, TextureName );
}
UPackage *
FHoudiniEngineBakeUtils::BakeCreateTextureOrMaterialPackageForComponent(
FHoudiniCookParams& HoudiniCookParams,
const FString & MaterialInfoDescriptor,
FString & MaterialName )
{
UPackage * PackageNew = nullptr;
#if WITH_EDITOR
EBakeMode BakeMode = HoudiniCookParams.MaterialAndTextureBakeMode;
UHoudiniAsset * HoudiniAsset = HoudiniCookParams.HoudiniAsset;
FGuid BakeGUID;
FString PackageName;
const FGuid & ComponentGUID = HoudiniCookParams.PackageGUID;
FString ComponentGUIDString = ComponentGUID.ToString().Left( FHoudiniEngineUtils::PackageGUIDComponentNameLength );
if ( ( BakeMode == EBakeMode::ReplaceExisitingAssets ) || ( BakeMode == EBakeMode::CookToTemp ) )
{
bool bRemovePackageFromCache = false;
UPackage* FoundPackage = nullptr;
if (BakeMode == EBakeMode::ReplaceExisitingAssets)
{
TWeakObjectPtr< UPackage > * FoundPointer = HoudiniCookParams.BakedMaterialPackagesForIds->Find(MaterialInfoDescriptor);
if ( FoundPointer )
{
if ( (*FoundPointer).IsValid() )
FoundPackage = (*FoundPointer).Get();
}
else
{
bRemovePackageFromCache = true;
}
}
else
{
TWeakObjectPtr< UPackage > * FoundPointer = HoudiniCookParams.CookedTemporaryPackages->Find(MaterialInfoDescriptor);
if (FoundPointer)
{
if ( (*FoundPointer).IsValid() )
FoundPackage = (*FoundPointer).Get();
}
else
{
bRemovePackageFromCache = true;
}
}
// Find a previously baked / cooked asset
if ( FoundPackage && !FoundPackage->IsPendingKill() )
{
if ( UPackage::IsEmptyPackage( FoundPackage ) )
{
// This happens when the prior baked output gets renamed, we can delete this
// orphaned package so that we can re-use the name
FoundPackage->ClearFlags( RF_Standalone );
FoundPackage->ConditionalBeginDestroy();
bRemovePackageFromCache = true;
}
else
{
if ( CheckPackageSafeForBake( FoundPackage, MaterialName ) && !MaterialName.IsEmpty() )
{
return FoundPackage;
}
else
{
// Found the package but we can't update it. We already issued an error, but should popup the standard reference error dialog
//::ErrorPopup( TEXT( "Baking Failed: Could not overwrite %s, because it is being referenced" ), *(*FoundPackage)->GetPathName() );
// If we're cooking, we'll create a new package, if baking, fail
if ( BakeMode != EBakeMode::CookToTemp )
return nullptr;
}
}
bRemovePackageFromCache = true;
}
if ( bRemovePackageFromCache )
{
// Package is either invalid / not found so we need to remove it from the cache
if ( BakeMode == EBakeMode::ReplaceExisitingAssets )
HoudiniCookParams.BakedMaterialPackagesForIds->Remove( MaterialInfoDescriptor );
else
HoudiniCookParams.CookedTemporaryPackages->Remove( MaterialInfoDescriptor );
}
}
while ( true )
{
if ( !BakeGUID.IsValid() )
BakeGUID = FGuid::NewGuid();
// We only want half of generated guid string.
FString BakeGUIDString = BakeGUID.ToString().Left( FHoudiniEngineUtils::PackageGUIDItemNameLength );
// Generate material name.
MaterialName = MaterialInfoDescriptor;
MaterialName += BakeGUIDString;
switch (BakeMode)
{
case EBakeMode::Intermediate:
{
// Generate unique package name.
PackageName = FPackageName::GetLongPackagePath( HoudiniAsset->GetOuter()->GetName() ) +
TEXT("/") +
HoudiniAsset->GetName() +
TEXT("_") +
ComponentGUIDString +
TEXT("/") +
MaterialName;
}
break;
case EBakeMode::CookToTemp:
{
PackageName = HoudiniCookParams.TempCookFolder.ToString() + TEXT("/") + MaterialName;
}
break;
default:
{
// Generate unique package name.
PackageName = HoudiniCookParams.BakeFolder.ToString() + TEXT("/") + MaterialName;
}
break;
}
// Sanitize package name.
PackageName = UPackageTools::SanitizePackageName( PackageName );
UObject * OuterPackage = nullptr;
if ( BakeMode == EBakeMode::Intermediate )
{
// If we are not baking, then use outermost package, since objects within our package need to be visible
// to external operations, such as copy paste.
OuterPackage = HoudiniCookParams.IntermediateOuter;
}
// See if package exists, if it does, we need to regenerate the name.
UPackage * Package = FindPackage( OuterPackage, *PackageName );
if ( Package && !Package->IsPendingKill() )
{
// Package does exist, there's a collision, we need to generate a new name.
BakeGUID.Invalidate();
}
else
{
// Create actual package.
PackageNew = CreatePackage( OuterPackage, *PackageName );
PackageNew->SetPackageFlags(RF_Standalone);
break;
}
}
if( PackageNew && !PackageNew->IsPendingKill()
&& ( ( BakeMode == EBakeMode::ReplaceExisitingAssets ) || ( BakeMode == EBakeMode::CookToTemp ) ) )
{
// Add the new package to the cache
if ( BakeMode == EBakeMode::ReplaceExisitingAssets )
HoudiniCookParams.BakedMaterialPackagesForIds->Add( MaterialInfoDescriptor, PackageNew );
else
HoudiniCookParams.CookedTemporaryPackages->Add( MaterialInfoDescriptor, PackageNew );
}
#endif
return PackageNew;
}
bool
FHoudiniEngineBakeUtils::CheckPackageSafeForBake( UPackage* Package, FString& FoundAssetName )
{
if (!Package || Package->IsPendingKill())
return false;
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>( "AssetRegistry" );
TArray<FAssetData> AssetsInPackage;
AssetRegistryModule.Get().GetAssetsByPackageName( Package->GetFName(), AssetsInPackage );
for( const auto& AssetInfo : AssetsInPackage )
{
UObject* AssetInPackage = AssetInfo.GetAsset();
if (!AssetInPackage || AssetInPackage->IsPendingKill())
continue;
// Check and see whether we are referenced by any objects that won't be garbage collected (*including* the undo buffer)
FReferencerInformationList ReferencesIncludingUndo;
bool bReferencedInMemoryOrUndoStack = IsReferenced( AssetInPackage, GARBAGE_COLLECTION_KEEPFLAGS, EInternalObjectFlags::GarbageCollectionKeepFlags, true, &ReferencesIncludingUndo );
if( bReferencedInMemoryOrUndoStack )
{
// warn
HOUDINI_LOG_ERROR( TEXT( "Could not bake to %s because it is being referenced" ), *Package->GetPathName() );
return false;
}
FoundAssetName = AssetInfo.AssetName.ToString();
}
return true;
}
UPackage *
FHoudiniEngineBakeUtils::BakeCreateStaticMeshPackageForComponent(
FHoudiniCookParams& HoudiniCookParams,
const FHoudiniGeoPartObject & HoudiniGeoPartObject,
FString & MeshName, FGuid & BakeGUID )
{
UPackage * PackageNew = nullptr;
#if WITH_EDITOR
EBakeMode BakeMode = HoudiniCookParams.StaticMeshBakeMode;
FString PackageName;
int32 BakeCount = 0;
const FGuid & ComponentGUID = HoudiniCookParams.PackageGUID;
FString ComponentGUIDString = ComponentGUID.ToString().Left(
FHoudiniEngineUtils::PackageGUIDComponentNameLength );
while ( true )
{
if( ( BakeMode == EBakeMode::ReplaceExisitingAssets ) || ( BakeMode == EBakeMode::CookToTemp ) )
{
bool bRemovePackageFromCache = false;
UPackage* FoundPackage = nullptr;
if (BakeMode == EBakeMode::ReplaceExisitingAssets)
{
TWeakObjectPtr< UPackage > * FoundPointer = HoudiniCookParams.BakedStaticMeshPackagesForParts->Find( HoudiniGeoPartObject );
if ( FoundPointer )
{
if ( ( *FoundPointer ).IsValid() )
FoundPackage = ( *FoundPointer ).Get();
}
else
{
bRemovePackageFromCache = true;
}
}
else
{
TWeakObjectPtr< UPackage > * FoundPointer = HoudiniCookParams.CookedTemporaryStaticMeshPackages->Find( HoudiniGeoPartObject );
if ( FoundPointer )
{
if ( ( *FoundPointer ).IsValid() )
FoundPackage = ( *FoundPointer ).Get();
}
else
{
bRemovePackageFromCache = true;
}
}
// Find a previously baked / cooked asset
if ( FoundPackage && !FoundPackage->IsPendingKill() )
{
if ( UPackage::IsEmptyPackage( FoundPackage ) )
{
// This happens when the prior baked output gets renamed, we can delete this
// orphaned package so that we can re-use the name
FoundPackage->ClearFlags( RF_Standalone );
FoundPackage->ConditionalBeginDestroy();
bRemovePackageFromCache = true;
}
else
{
if ( FHoudiniEngineBakeUtils::CheckPackageSafeForBake( FoundPackage, MeshName ) && !MeshName.IsEmpty() )
{
return FoundPackage;
}
else
{
// Found the package but we can't update it. We already issued an error, but should popup the standard reference error dialog
//::ErrorPopup( TEXT( "Baking Failed: Could not overwrite %s, because it is being referenced" ), *(*FoundPackage)->GetPathName() );
// If we're cooking, we'll create a new package, if baking, fail
if ( BakeMode != EBakeMode::CookToTemp )
return nullptr;
}
}
bRemovePackageFromCache = true;
}
if ( bRemovePackageFromCache )
{
// Package is either invalid / not found so we need to remove it from the cache
if ( BakeMode == EBakeMode::ReplaceExisitingAssets )
HoudiniCookParams.BakedStaticMeshPackagesForParts->Remove( HoudiniGeoPartObject );
else
HoudiniCookParams.CookedTemporaryStaticMeshPackages->Remove( HoudiniGeoPartObject );
}
}
if ( !BakeGUID.IsValid() )
BakeGUID = FGuid::NewGuid();
MeshName = HoudiniCookParams.HoudiniCookManager->GetBakingBaseName( HoudiniGeoPartObject );
if( BakeCount > 0 )
{
MeshName += FString::Printf( TEXT( "_%02d" ), BakeCount );
}
switch ( BakeMode )
{
case EBakeMode::Intermediate:
{
// We only want half of generated guid string.
FString BakeGUIDString = BakeGUID.ToString().Left( FHoudiniEngineUtils::PackageGUIDItemNameLength );
MeshName += TEXT("_") +
FString::FromInt(HoudiniGeoPartObject.ObjectId) + TEXT("_") +
FString::FromInt(HoudiniGeoPartObject.GeoId) + TEXT("_") +
FString::FromInt(HoudiniGeoPartObject.PartId) + TEXT("_") +
FString::FromInt(HoudiniGeoPartObject.SplitId) + TEXT("_") +
HoudiniGeoPartObject.SplitName + TEXT("_") +
BakeGUIDString;
PackageName = FPackageName::GetLongPackagePath( HoudiniCookParams.HoudiniAsset->GetOuter()->GetName() ) +
TEXT("/") +
HoudiniCookParams.HoudiniAsset->GetName() +
TEXT("_") +
ComponentGUIDString +
TEXT("/") +
MeshName;
}
break;
case EBakeMode::CookToTemp:
{
PackageName = HoudiniCookParams.TempCookFolder.ToString() + TEXT("/") + MeshName;
}
break;
default:
{
PackageName = HoudiniCookParams.BakeFolder.ToString() + TEXT("/") + MeshName;
}
break;
}
// Santize package name.
PackageName = UPackageTools::SanitizePackageName( PackageName );
UObject * OuterPackage = nullptr;
if ( BakeMode == EBakeMode::Intermediate )
{
// If we are not baking, then use outermost package, since objects within our package need to be visible
// to external operations, such as copy paste.
OuterPackage = HoudiniCookParams.IntermediateOuter;
}
// See if package exists, if it does, we need to regenerate the name.
UPackage * Package = FindPackage( OuterPackage, *PackageName );
if ( Package && !Package->IsPendingKill() )
{
if ( BakeMode != EBakeMode::Intermediate )
{
// Increment bake counter
BakeCount++;
}
else
{
// Package does exist, there's a collision, we need to generate a new name.
BakeGUID.Invalidate();
}
}
else
{
// Create actual package.
PackageNew = CreatePackage( OuterPackage, *PackageName );
break;
}
}
if ( PackageNew && !PackageNew->IsPendingKill()
&& ( ( BakeMode == EBakeMode::ReplaceExisitingAssets ) || ( BakeMode == EBakeMode::CookToTemp ) ) )
{
// Add the new package to the cache
if ( BakeMode == EBakeMode::ReplaceExisitingAssets )
HoudiniCookParams.BakedStaticMeshPackagesForParts->Add( HoudiniGeoPartObject, PackageNew );
else
HoudiniCookParams.CookedTemporaryStaticMeshPackages->Add( HoudiniGeoPartObject, PackageNew );
}
#endif
return PackageNew;
}
void
FHoudiniEngineBakeUtils::AddHoudiniMetaInformationToPackage(
UPackage * Package, UObject * Object, const TCHAR * Key,
const TCHAR * Value)
{
if ( !Package || Package->IsPendingKill() )
return;
UMetaData * MetaData = Package->GetMetaData();
if ( MetaData && !MetaData->IsPendingKill() )
MetaData->SetValue( Object, Key, Value );
}
bool
FHoudiniEngineBakeUtils::GetHoudiniGeneratedNameFromMetaInformation(
UPackage * Package, UObject * Object, FString & HoudiniName )
{
if (!Package || Package->IsPendingKill())
return false;
UMetaData * MetaData = Package->GetMetaData();
if ( !MetaData || MetaData->IsPendingKill() )
return false;
if ( MetaData->HasValue( Object, HAPI_UNREAL_PACKAGE_META_GENERATED_OBJECT ) )
{
// Retrieve name used for package generation.
const FString NameFull = MetaData->GetValue( Object, HAPI_UNREAL_PACKAGE_META_GENERATED_NAME );
HoudiniName = NameFull.Left( NameFull.Len() - FHoudiniEngineUtils::PackageGUIDItemNameLength );
return true;
}
return false;
}
bool
FHoudiniEngineBakeUtils::DeleteBakedHoudiniAssetActor(UHoudiniAssetComponent * HoudiniAssetComponent)
{
// Helper function used by the replace function to delete the Houdini Asset Actor after it's been baked
if (!HoudiniAssetComponent || HoudiniAssetComponent->IsPendingKill())
return false;
bool bSuccess = false;
#if WITH_EDITOR
// We can initiate Houdini actor deletion.
AActor * ActorOwner = HoudiniAssetComponent->GetOwner();
if (!ActorOwner || ActorOwner->IsPendingKill())
return bSuccess;
// Remove Houdini actor from active selection in editor and delete it.
if (GEditor)
{
GEditor->SelectActor(ActorOwner, false, false);
ULayersSubsystem* LayerSubSystem = GEditor->GetEditorSubsystem<ULayersSubsystem>();
if (LayerSubSystem)
LayerSubSystem->DisassociateActorFromLayers(ActorOwner);
}
UWorld * World = ActorOwner->GetWorld();
if (!World)
World = GWorld;
bSuccess = World->EditorDestroyActor(ActorOwner, false);
#endif
return bSuccess;
}