Files
Andron666 9c38e93fa4 part7
2022-12-05 20:31:35 +05:00

940 lines
22 KiB
C++

/*
* Copyright (c) <2021> Side Effects Software Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. The name of Side Effects Software may not be used to endorse or
* promote products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "HoudiniOutput.h"
#include "HoudiniAssetComponent.h"
#include "HoudiniEngineRuntimeUtils.h"
#include "HoudiniSplineComponent.h"
#include "Components/SceneComponent.h"
#include "Components/MeshComponent.h"
#include "Components/SplineComponent.h"
#include "Misc/StringFormatArg.h"
#include "Engine/Engine.h"
#include "LandscapeLayerInfoObject.h"
UHoudiniLandscapePtr::UHoudiniLandscapePtr(class FObjectInitializer const& Initializer)
{
// bIsWorldCompositionLandscape = false;
// BakeType = EHoudiniLandscapeOutputBakeType::Detachment;
};
uint32
GetTypeHash(const FHoudiniOutputObjectIdentifier& HoudiniOutputObjectIdentifier)
{
return HoudiniOutputObjectIdentifier.GetTypeHash();
}
void
FHoudiniInstancedOutput::SetVariationObjectAt(const int32& AtIndex, UObject* InObject)
{
// Resize the array if needed
if (VariationObjects.Num() <= AtIndex)
VariationObjects.SetNum(AtIndex + 1);
if (VariationTransformOffsets.Num() <= AtIndex)
VariationTransformOffsets.SetNum(AtIndex + 1);
UObject* CurrentObject = VariationObjects[AtIndex].LoadSynchronous();
if (CurrentObject == InObject)
return;
VariationObjects[AtIndex] = InObject;
}
bool
FHoudiniInstancedOutput::SetTransformOffsetAt(const float& Value, const int32& AtIndex, const int32& PosRotScaleIndex, const int32& XYZIndex)
{
FTransform* Transform = VariationTransformOffsets.IsValidIndex(AtIndex) ? &VariationTransformOffsets[AtIndex] : nullptr;
if (!Transform)
return false;
if (PosRotScaleIndex == 0)
{
FVector Position = Transform->GetLocation();
if (Position[XYZIndex] == Value)
return false;
Position[XYZIndex] = Value;
Transform->SetLocation(Position);
}
else if (PosRotScaleIndex == 1)
{
FRotator Rotator = Transform->Rotator();
switch (XYZIndex)
{
case 0:
{
if (Rotator.Roll == Value)
return false;
Rotator.Roll = Value;
break;
}
case 1:
{
if (Rotator.Pitch == Value)
return false;
Rotator.Pitch = Value;
break;
}
case 2:
{
if (Rotator.Yaw == Value)
return false;
Rotator.Yaw = Value;
break;
}
}
Transform->SetRotation(Rotator.Quaternion());
}
else if (PosRotScaleIndex == 2)
{
FVector Scale = Transform->GetScale3D();
if (Scale[XYZIndex] == Value)
return false;
Scale[XYZIndex] = Value;
Transform->SetScale3D(Scale);
}
MarkChanged(true);
return true;
}
float
FHoudiniInstancedOutput::GetTransformOffsetAt(const int32& AtIndex, const int32& PosRotScaleIndex, const int32& XYZIndex)
{
FTransform* Transform = VariationTransformOffsets.IsValidIndex(AtIndex) ? &VariationTransformOffsets[AtIndex] : nullptr;
if (!Transform)
return 0.0f;
if (PosRotScaleIndex == 0)
{
FVector Position = Transform->GetLocation();
return Position[XYZIndex];
}
else if (PosRotScaleIndex == 1)
{
FRotator Rotator = Transform->Rotator();
switch (XYZIndex)
{
case 0:
{
return Rotator.Roll;
}
case 1:
{
return Rotator.Pitch;
}
case 2:
{
return Rotator.Yaw;
}
}
}
else if (PosRotScaleIndex == 2)
{
FVector Scale = Transform->GetScale3D();
return Scale[XYZIndex];
}
return 0.0f;
}
// ----------------------------------------------------
// FHoudiniOutputObjectIdentifier
// ----------------------------------------------------
FHoudiniOutputObjectIdentifier::FHoudiniOutputObjectIdentifier()
{
ObjectId = -1;
GeoId = -1;
PartId = -1;
SplitIdentifier = FString();
PartName = FString();
}
FHoudiniOutputObjectIdentifier::FHoudiniOutputObjectIdentifier(
const int32& InObjectId, const int32& InGeoId, const int32& InPartId, const FString& InSplitIdentifier)
{
ObjectId = InObjectId;
GeoId = InGeoId;
PartId = InPartId;
SplitIdentifier = InSplitIdentifier;
}
uint32
FHoudiniOutputObjectIdentifier::GetTypeHash() const
{
int32 HashBuffer[3] = { ObjectId, GeoId, PartId };
int32 Hash = FCrc::MemCrc32((void *)&HashBuffer[0], sizeof(HashBuffer));
return FCrc::StrCrc32(*SplitIdentifier, Hash);
}
bool
FHoudiniOutputObjectIdentifier::operator==(const FHoudiniOutputObjectIdentifier& InOutputObjectIdentifier) const
{
// Object/Geo/Part IDs must match
bool bMatchingIds = true;
if (ObjectId != InOutputObjectIdentifier.ObjectId
|| GeoId != InOutputObjectIdentifier.GeoId
|| PartId != InOutputObjectIdentifier.PartId)
bMatchingIds = false;
if ((bLoaded && !InOutputObjectIdentifier.bLoaded)
|| (!bLoaded && InOutputObjectIdentifier.bLoaded))
{
// If one of the two identifier is loaded,
// we can simply compare the part names
if (PartName.Equals(InOutputObjectIdentifier.PartName)
&& SplitIdentifier.Equals(InOutputObjectIdentifier.SplitIdentifier))
return true;
}
if (!bMatchingIds)
{
return false;
}
// If split ID and name match, we're equal...
if (SplitIdentifier.Equals(InOutputObjectIdentifier.SplitIdentifier))
return true;
// ... if not we're different
return false;
}
bool
FHoudiniOutputObjectIdentifier::Matches(const FHoudiniGeoPartObject& InHGPO) const
{
// Object/Geo/Part IDs must match
bool bMatchingIds = true;
if (ObjectId != InHGPO.ObjectId
|| GeoId != InHGPO.GeoId
|| PartId != InHGPO.PartId)
bMatchingIds = false;
if ((bLoaded && !InHGPO.bLoaded) || (!bLoaded && InHGPO.bLoaded))
{
// If either the HGPO or the Identifer is nmarked as loaded,
// we can simply compare the part names
if (PartName.Equals(InHGPO.PartName))
return true;
}
if (!bMatchingIds)
{
return false;
}
// If the HGPO has our split identifier
//if (InHGPO.SplitGroups.Contains(SplitIdentifier))
// return true;
//
return true;
}
// ----------------------------------------------------
// FHoudiniBakedOutputObjectIdentifier
// ----------------------------------------------------
FHoudiniBakedOutputObjectIdentifier::FHoudiniBakedOutputObjectIdentifier()
{
PartId = -1;
SplitIdentifier = FString();
}
FHoudiniBakedOutputObjectIdentifier::FHoudiniBakedOutputObjectIdentifier(
const int32& InPartId, const FString& InSplitIdentifier)
{
PartId = InPartId;
SplitIdentifier = InSplitIdentifier;
}
FHoudiniBakedOutputObjectIdentifier::FHoudiniBakedOutputObjectIdentifier(const FHoudiniOutputObjectIdentifier& InIdentifier)
{
PartId = InIdentifier.PartId;
SplitIdentifier = InIdentifier.SplitIdentifier;
}
uint32
FHoudiniBakedOutputObjectIdentifier::GetTypeHash() const
{
const int32 HashBuffer = PartId;
const int32 Hash = FCrc::MemCrc32((void *)&HashBuffer, sizeof(HashBuffer));
return FCrc::StrCrc32(*SplitIdentifier, Hash);
}
uint32
GetTypeHash(const FHoudiniBakedOutputObjectIdentifier& InIdentifier)
{
return InIdentifier.GetTypeHash();
}
bool
FHoudiniBakedOutputObjectIdentifier::operator==(const FHoudiniBakedOutputObjectIdentifier& InIdentifier) const
{
return (InIdentifier.PartId == PartId && InIdentifier.SplitIdentifier.Equals(SplitIdentifier));
}
// ----------------------------------------------------
// FHoudiniBakedOutputObject
// ----------------------------------------------------
FHoudiniBakedOutputObject::FHoudiniBakedOutputObject()
: Actor()
, ActorBakeName(NAME_None)
, BakedObject()
, BakedComponent()
{
}
FHoudiniBakedOutputObject::FHoudiniBakedOutputObject(AActor* InActor, FName InActorBakeName, UObject* InBakeObject, UObject* InBakedComponent)
: Actor(FSoftObjectPath(InActor).ToString())
, ActorBakeName(InActorBakeName)
, BakedObject(FSoftObjectPath(InBakeObject).ToString())
, BakedComponent(FSoftObjectPath(InBakedComponent).ToString())
{
}
AActor*
FHoudiniBakedOutputObject::GetActorIfValid(bool bInTryLoad) const
{
const FSoftObjectPath ActorPath(Actor);
if (!ActorPath.IsValid())
return nullptr;
UObject* Object = ActorPath.ResolveObject();
if (!Object && bInTryLoad)
Object = ActorPath.TryLoad();
if (!IsValid(Object))
return nullptr;
return Cast<AActor>(Object);
}
UObject*
FHoudiniBakedOutputObject::GetBakedObjectIfValid(bool bInTryLoad) const
{
const FSoftObjectPath ObjectPath(BakedObject);
if (!ObjectPath.IsValid())
return nullptr;
UObject* Object = ObjectPath.ResolveObject();
if (!Object && bInTryLoad)
Object = ObjectPath.TryLoad();
if (!IsValid(Object))
return nullptr;
return Object;
}
UObject*
FHoudiniBakedOutputObject::GetBakedComponentIfValid(bool bInTryLoad) const
{
const FSoftObjectPath ComponentPath(BakedComponent);
if (!ComponentPath.IsValid())
return nullptr;
UObject* Object = ComponentPath.ResolveObject();
if (!Object && bInTryLoad)
Object = ComponentPath.TryLoad();
if (!IsValid(Object))
return nullptr;
return Object;
}
UBlueprint*
FHoudiniBakedOutputObject::GetBlueprintIfValid(bool bInTryLoad) const
{
const FSoftObjectPath BlueprintPath(Blueprint);
if (!BlueprintPath.IsValid())
return nullptr;
UObject* Object = BlueprintPath.ResolveObject();
if (!Object && bInTryLoad)
Object = BlueprintPath.TryLoad();
if (!IsValid(Object))
return nullptr;
return Cast<UBlueprint>(Object);
}
ULandscapeLayerInfoObject*
FHoudiniBakedOutputObject::GetLandscapeLayerInfoIfValid(const FName& InLayerName, const bool bInTryLoad) const
{
if (!LandscapeLayers.Contains(InLayerName))
return nullptr;
const FString& LayerInfoPathStr = LandscapeLayers.FindChecked(InLayerName);
const FSoftObjectPath LayerInfoPath(LayerInfoPathStr);
if (!LayerInfoPath.IsValid())
return nullptr;
UObject* Object = LayerInfoPath.ResolveObject();
if (!Object && bInTryLoad)
Object = LayerInfoPath.TryLoad();
if (!IsValid(Object))
return nullptr;
return Cast<ULandscapeLayerInfoObject>(Object);
}
UHoudiniOutput::UHoudiniOutput(const FObjectInitializer & ObjectInitializer)
: Super(ObjectInitializer)
, Type(EHoudiniOutputType::Invalid)
, StaleCount(0)
, bLandscapeWorldComposition(false)
, bIsEditableNode(false)
, bHasEditableNodeBuilt(false)
, bCanDeleteHoudiniNodes(true)
{
}
UHoudiniOutput::~UHoudiniOutput()
{
Type = EHoudiniOutputType::Invalid;
StaleCount = 0;
bIsUpdating = false;
HoudiniGeoPartObjects.Empty();
OutputObjects.Empty();
InstancedOutputs.Empty();
AssignementMaterials.Empty();
ReplacementMaterials.Empty();
}
void
UHoudiniOutput::BeginDestroy()
{
Super::BeginDestroy();
}
FBox
UHoudiniOutput::GetBounds() const
{
FBox BoxBounds(ForceInitToZero);
switch (GetType())
{
case EHoudiniOutputType::Mesh:
{
for (auto & CurPair : OutputObjects)
{
const FHoudiniOutputObject& CurObj = CurPair.Value;
UMeshComponent* MeshComp = nullptr;
if (CurObj.bProxyIsCurrent)
{
MeshComp = Cast<UMeshComponent>(CurObj.ProxyComponent);
}
else
{
MeshComp = Cast<UMeshComponent>(CurObj.OutputComponent);
}
if (!MeshComp || MeshComp->IsPendingKill())
continue;
BoxBounds += MeshComp->Bounds.GetBox();
}
}
break;
case EHoudiniOutputType::Landscape:
{
for (auto & CurPair : OutputObjects)
{
const FHoudiniOutputObject& CurObj = CurPair.Value;
UHoudiniLandscapePtr* CurLandscapeObj = Cast<UHoudiniLandscapePtr>(CurObj.OutputObject);
if (!CurLandscapeObj || CurLandscapeObj->IsPendingKill())
continue;
ALandscapeProxy* Landscape = Cast<ALandscapeProxy>(CurLandscapeObj->GetRawPtr());
if (!Landscape || Landscape->IsPendingKill())
continue;
FVector Origin, Extent;
Landscape->GetActorBounds(false, Origin, Extent);
FBox LandscapeBounds = FBox::BuildAABB(Origin, Extent);
BoxBounds += LandscapeBounds;
}
}
break;
case EHoudiniOutputType::Instancer:
{
for (auto & CurPair : OutputObjects)
{
const FHoudiniOutputObject& CurObj = CurPair.Value;
USceneComponent* InstancedComp = Cast<USceneComponent>(CurObj.OutputObject);
if (!InstancedComp || InstancedComp->IsPendingKill())
continue;
BoxBounds += InstancedComp->Bounds.GetBox();
}
}
break;
case EHoudiniOutputType::Curve:
{
for (auto & CurPair : OutputObjects)
{
const FHoudiniOutputObject& CurObj = CurPair.Value;
UHoudiniSplineComponent* CurHoudiniSplineComp = Cast<UHoudiniSplineComponent>(CurObj.OutputComponent);
if (!CurHoudiniSplineComp || CurHoudiniSplineComp->IsPendingKill())
continue;
FBox CurCurveBound(ForceInitToZero);
for (auto & Trans : CurHoudiniSplineComp->CurvePoints)
{
CurCurveBound += Trans.GetLocation();
}
UHoudiniAssetComponent* OuterHAC = Cast<UHoudiniAssetComponent>(GetOuter());
if (OuterHAC && !OuterHAC->IsPendingKill())
BoxBounds += CurCurveBound.MoveTo(OuterHAC->GetComponentLocation());
}
}
break;
case EHoudiniOutputType::Skeletal:
case EHoudiniOutputType::Invalid:
break;
default:
break;
}
return BoxBounds;
}
void
UHoudiniOutput::Clear()
{
StaleCount = 0;
HoudiniGeoPartObjects.Empty();
for (auto& CurrentOutputObject : OutputObjects)
{
// Clear the output component
USceneComponent* SceneComp = Cast<USceneComponent>(CurrentOutputObject.Value.OutputComponent);
if (SceneComp && !SceneComp->IsPendingKill())
{
SceneComp->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
SceneComp->UnregisterComponent();
SceneComp->DestroyComponent();
}
// Also destroy proxy components
USceneComponent* ProxyComp = Cast<USceneComponent>(CurrentOutputObject.Value.ProxyComponent);
if (ProxyComp && !ProxyComp->IsPendingKill())
{
ProxyComp->DetachFromComponent(FDetachmentTransformRules::KeepRelativeTransform);
ProxyComp->UnregisterComponent();
ProxyComp->DestroyComponent();
}
if (Type == EHoudiniOutputType::Landscape && !bLandscapeWorldComposition && !IsGarbageCollecting())
{
// NOTE: We cannot resolve soft pointers during garbage collection. Any Get() or IsValid() call
// will result in a StaticFindObject() call which will raise an exception during GC.
UHoudiniLandscapePtr* LandscapePtr = Cast<UHoudiniLandscapePtr>(CurrentOutputObject.Value.OutputObject);
ALandscapeProxy* LandscapeProxy = LandscapePtr ? LandscapePtr->GetRawPtr() : nullptr;
if (LandscapeProxy && !LandscapeProxy->IsPendingKill())
{
LandscapeProxy->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
LandscapeProxy->ConditionalBeginDestroy();
LandscapeProxy->Destroy();
}
}
}
OutputObjects.Empty();
InstancedOutputs.Empty();
AssignementMaterials.Empty();
ReplacementMaterials.Empty();
Type = EHoudiniOutputType::Invalid;
}
bool
UHoudiniOutput::ShouldDeferClear() const
{
if (Type == EHoudiniOutputType::Landscape)
return true;
return false;
}
const bool
UHoudiniOutput::HasGeoChanged() const
{
for (auto currentHGPO : HoudiniGeoPartObjects)
{
if (currentHGPO.bHasGeoChanged)
return true;
}
return false;
}
const bool
UHoudiniOutput::HasTransformChanged() const
{
for (auto currentHGPO : HoudiniGeoPartObjects)
{
if (currentHGPO.bHasTransformChanged)
return true;
}
return false;
}
const bool
UHoudiniOutput::HasMaterialsChanged() const
{
for (auto currentHGPO : HoudiniGeoPartObjects)
{
if (currentHGPO.bHasMaterialsChanged)
return true;
}
return false;
}
const bool
UHoudiniOutput::HasHoudiniGeoPartObject(const FHoudiniGeoPartObject& InHGPO) const
{
return HoudiniGeoPartObjects.Find(InHGPO) != INDEX_NONE;
}
const bool
UHoudiniOutput::HeightfieldMatch(const FHoudiniGeoPartObject& InHGPO, const bool& bVolumeNameShouldMatch) const
{
if (InHGPO.Type != EHoudiniPartType::Volume)
return false;
if (InHGPO.VolumeName.IsEmpty())
return false;
for (auto& currentHGPO : HoudiniGeoPartObjects)
{
// Asset/Object/Geo IDs should match
if (currentHGPO.AssetId != InHGPO.AssetId
|| currentHGPO.ObjectId != InHGPO.ObjectId
|| currentHGPO.GeoId != InHGPO.GeoId)
{
continue;
}
// Both HGPO type should be volumes
if (currentHGPO.Type != EHoudiniPartType::Volume)
{
continue;
}
// Volume tile index should match
if (currentHGPO.VolumeTileIndex != InHGPO.VolumeTileIndex)
{
continue;
}
// We've specified if we want the name to match/to be different:
// when looking in previous outputs, we want the name to match
// when looking in newly created outputs, we want to be sure the names are different
bool bNameMatch = InHGPO.VolumeName.Equals(currentHGPO.VolumeName, ESearchCase::IgnoreCase);
if (bNameMatch != bVolumeNameShouldMatch)
continue;
return true;
}
return false;
}
void
UHoudiniOutput::MarkAllHGPOsAsStale(const bool& bInStale)
{
// Since objects can only be added to this array,
// Simply keep track of the current number of HoudiniGeoPartObject
StaleCount = bInStale ? HoudiniGeoPartObjects.Num() : 0;
}
void
UHoudiniOutput::DeleteAllStaleHGPOs()
{
// Simply delete the first "StaleCount" objects and reset the stale marker
HoudiniGeoPartObjects.RemoveAt(0, StaleCount);
StaleCount = 0;
}
void
UHoudiniOutput::AddNewHGPO(const FHoudiniGeoPartObject& InHGPO)
{
HoudiniGeoPartObjects.Add(InHGPO);
}
void
UHoudiniOutput::UpdateOutputType()
{
int32 MeshCount = 0;
int32 CurveCount = 0;
int32 VolumeCount = 0;
int32 InstancerCount = 0;
for (auto& HGPO : HoudiniGeoPartObjects)
{
switch (HGPO.Type)
{
case EHoudiniPartType::Mesh:
MeshCount++;
break;
case EHoudiniPartType::Curve:
CurveCount++;
break;
case EHoudiniPartType::Volume:
VolumeCount++;
break;
case EHoudiniPartType::Instancer:
InstancerCount++;
break;
default:
case EHoudiniPartType::Invalid:
break;
}
}
if (VolumeCount > 0)
{
// If we have a volume, we're a landscape
Type = EHoudiniOutputType::Landscape;
}
else if (InstancerCount > 0)
{
// if we have at least an instancer, we're one
Type = EHoudiniOutputType::Instancer;
}
else if (MeshCount > 0)
{
Type = EHoudiniOutputType::Mesh;
}
else if (CurveCount > 0)
{
Type = EHoudiniOutputType::Curve;
}
else
{
// No valid HGPO detected...
Type = EHoudiniOutputType::Invalid;
}
}
UHoudiniOutput*
UHoudiniOutput::DuplicateAndCopyProperties(UObject* DestOuter, FName NewName)
{
UHoudiniOutput* NewOutput = Cast<UHoudiniOutput>(StaticDuplicateObject(this, DestOuter, NewName));
NewOutput->CopyPropertiesFrom(this, false);
return NewOutput;
}
void
UHoudiniOutput::CopyPropertiesFrom(UHoudiniOutput* InInput, bool bCopyAllProperties)
{
// Copy the state of this UHoudiniInput object.
if (bCopyAllProperties)
{
// Stash all the data that we want to preserve, and re-apply after property copy took place
// (similar to Get/Apply component instance data). This is typically only needed
// for certain properties that require cleanup when being replaced / removed.
TMap<FHoudiniOutputObjectIdentifier, FHoudiniOutputObject> PrevOutputObjects = OutputObjects;
TMap<FHoudiniOutputObjectIdentifier, FHoudiniInstancedOutput> PrevInstancedOutputs = InstancedOutputs;
UEngine::FCopyPropertiesForUnrelatedObjectsParams Params;
Params.bDoDelta = false; // Perform a deep copy
Params.bClearReferences = false; // References will be replaced afterwards.
UEngine::CopyPropertiesForUnrelatedObjects(InInput, this, Params);
// Restore the desired properties.
OutputObjects = PrevOutputObjects;
InstancedOutputs = PrevInstancedOutputs;
}
// Copy any additional DuplicateTransient properties.
bHasEditableNodeBuilt = InInput->bHasEditableNodeBuilt;
}
void
UHoudiniOutput::SetCanDeleteHoudiniNodes(bool bInCanDeleteNodes)
{
bCanDeleteHoudiniNodes = bInCanDeleteNodes;
}
FString
UHoudiniOutput::OutputTypeToString(const EHoudiniOutputType& InOutputType)
{
FString OutputTypeStr;
switch (InOutputType)
{
case EHoudiniOutputType::Mesh:
OutputTypeStr = TEXT("Mesh");
break;
case EHoudiniOutputType::Instancer:
OutputTypeStr = TEXT("Instancer");
break;
case EHoudiniOutputType::Landscape:
OutputTypeStr = TEXT("Landscape");
break;
case EHoudiniOutputType::Curve:
OutputTypeStr = TEXT("Curve");
break;
case EHoudiniOutputType::Skeletal:
OutputTypeStr = TEXT("Skeletal");
break;
default:
case EHoudiniOutputType::Invalid:
OutputTypeStr = TEXT("Invalid");
break;
}
return OutputTypeStr;
}
void
UHoudiniOutput::MarkAsLoaded(const bool& InLoaded)
{
// Mark all HGPO as loaded
for (auto& HGPO : HoudiniGeoPartObjects)
{
HGPO.bLoaded = InLoaded;
}
// Mark all output object's identifier as loaded
for (auto& Iter : OutputObjects)
{
FHoudiniOutputObjectIdentifier& Identifier = Iter.Key;
Identifier.bLoaded = InLoaded;
}
// Instanced outputs
for (auto& Iter : InstancedOutputs)
{
FHoudiniOutputObjectIdentifier& Identifier = Iter.Key;
Identifier.bLoaded = InLoaded;
}
}
const bool
UHoudiniOutput::HasAnyProxy() const
{
for (const auto& Pair : OutputObjects)
{
UObject* FoundProxy = Pair.Value.ProxyObject;
if (FoundProxy && !FoundProxy->IsPendingKill())
{
return true;
}
}
return false;
}
const bool
UHoudiniOutput::HasProxy(const FHoudiniOutputObjectIdentifier& InIdentifier) const
{
const FHoudiniOutputObject* FoundOutputObject = OutputObjects.Find(InIdentifier);
if (!FoundOutputObject)
return false;
UObject* FoundProxy = FoundOutputObject->ProxyObject;
if (!FoundProxy || FoundProxy->IsPendingKill())
return false;
return true;
}
const bool
UHoudiniOutput::HasAnyCurrentProxy() const
{
for (const auto& Pair : OutputObjects)
{
UObject* FoundProxy = Pair.Value.ProxyObject;
if (FoundProxy && !FoundProxy->IsPendingKill())
{
if(Pair.Value.bProxyIsCurrent)
{
return true;
}
}
}
return false;
}
const bool
UHoudiniOutput::IsProxyCurrent(const FHoudiniOutputObjectIdentifier &InIdentifier) const
{
if (!HasProxy(InIdentifier))
return false;
const FHoudiniOutputObject* FoundOutputObject = OutputObjects.Find(InIdentifier);
if (!FoundOutputObject)
return false;
return FoundOutputObject->bProxyIsCurrent;
}