230 lines
7.7 KiB
C++
230 lines
7.7 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 "HoudiniInstancedActorComponent.h"
|
|
|
|
#include "HoudiniApi.h"
|
|
#include "HoudiniMeshSplitInstancerComponent.h"
|
|
#include "HoudiniEngineRuntimePrivatePCH.h"
|
|
|
|
#include "Components/InstancedStaticMeshComponent.h"
|
|
#include "Components/HierarchicalInstancedStaticMeshComponent.h"
|
|
#if WITH_EDITOR
|
|
#include "LevelEditorViewport.h"
|
|
#endif
|
|
#include "Internationalization/Internationalization.h"
|
|
|
|
#define LOCTEXT_NAMESPACE HOUDINI_LOCTEXT_NAMESPACE
|
|
|
|
UHoudiniInstancedActorComponent::UHoudiniInstancedActorComponent( const FObjectInitializer& ObjectInitializer )
|
|
: Super( ObjectInitializer )
|
|
, InstancedAsset( nullptr )
|
|
{
|
|
}
|
|
|
|
|
|
void UHoudiniInstancedActorComponent::OnComponentDestroyed( bool bDestroyingHierarchy )
|
|
{
|
|
ClearInstances();
|
|
Super::OnComponentDestroyed( bDestroyingHierarchy );
|
|
}
|
|
|
|
void
|
|
UHoudiniInstancedActorComponent::Serialize( FArchive & Ar )
|
|
{
|
|
Super::Serialize( Ar );
|
|
Ar.UsingCustomVersion( FHoudiniCustomSerializationVersion::GUID );
|
|
|
|
Ar << InstancedAsset;
|
|
Ar << Instances;
|
|
}
|
|
|
|
void
|
|
UHoudiniInstancedActorComponent::AddReferencedObjects( UObject * InThis, FReferenceCollector & Collector )
|
|
{
|
|
UHoudiniInstancedActorComponent * ThisHIAC = Cast< UHoudiniInstancedActorComponent >(InThis);
|
|
if ( ThisHIAC && !ThisHIAC->IsPendingKill() )
|
|
{
|
|
if ( ThisHIAC->InstancedAsset && !ThisHIAC->InstancedAsset->IsPendingKill() )
|
|
Collector.AddReferencedObject( ThisHIAC->InstancedAsset, ThisHIAC );
|
|
|
|
Collector.AddReferencedObjects(ThisHIAC->Instances, ThisHIAC );
|
|
}
|
|
}
|
|
|
|
void
|
|
UHoudiniInstancedActorComponent::SetInstances( const TArray<FTransform>& InstanceTransforms )
|
|
{
|
|
#if WITH_EDITOR
|
|
if ( Instances.Num() || InstanceTransforms.Num() )
|
|
{
|
|
const FScopedTransaction Transaction( LOCTEXT( "UpdateInstances", "Update Instances" ) );
|
|
GetOwner()->Modify();
|
|
ClearInstances();
|
|
|
|
if( InstancedAsset && !InstancedAsset->IsPendingKill() )
|
|
{
|
|
for( const FTransform& InstanceTransform : InstanceTransforms )
|
|
{
|
|
AddInstance( InstanceTransform );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
HOUDINI_LOG_ERROR( TEXT( "%s: Null InstancedAsset for instanced actor override" ), *GetOwner()->GetName() );
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int32
|
|
UHoudiniInstancedActorComponent::AddInstance( const FTransform& InstanceTransform )
|
|
{
|
|
AActor * NewActor = SpawnInstancedActor(InstanceTransform);
|
|
if ( NewActor && !NewActor->IsPendingKill() )
|
|
{
|
|
NewActor->AttachToComponent( this, FAttachmentTransformRules::KeepRelativeTransform );
|
|
NewActor->SetActorRelativeTransform( InstanceTransform );
|
|
return Instances.Add( NewActor );
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
AActor*
|
|
UHoudiniInstancedActorComponent::SpawnInstancedActor( const FTransform& InstancedTransform ) const
|
|
{
|
|
#if WITH_EDITOR
|
|
if (InstancedAsset && !InstancedAsset->IsPendingKill())
|
|
{
|
|
GEditor->ClickLocation = InstancedTransform.GetTranslation();
|
|
GEditor->ClickPlane = FPlane(GEditor->ClickLocation, FVector::UpVector);
|
|
TArray<AActor*> NewActors = FLevelEditorViewportClient::TryPlacingActorFromObject(GetOwner()->GetLevel(), InstancedAsset, false, RF_Transactional, nullptr);
|
|
|
|
if (NewActors.Num() > 0)
|
|
{
|
|
if ( NewActors[0] && !NewActors[0]->IsPendingKill() )
|
|
return NewActors[0];
|
|
}
|
|
}
|
|
#endif
|
|
return nullptr;
|
|
}
|
|
|
|
void
|
|
UHoudiniInstancedActorComponent::ClearInstances()
|
|
{
|
|
for ( AActor* Instance : Instances )
|
|
{
|
|
if ( Instance && !Instance->IsPendingKill() )
|
|
Instance->Destroy();
|
|
}
|
|
Instances.Empty();
|
|
}
|
|
|
|
|
|
void
|
|
UHoudiniInstancedActorComponent::OnComponentCreated()
|
|
{
|
|
Super::OnComponentCreated();
|
|
|
|
// If our instances are parented to another actor we should duplicate them
|
|
bool bNeedDuplicate = false;
|
|
for (auto CurrentInstance : Instances)
|
|
{
|
|
if ( !CurrentInstance || CurrentInstance->IsPendingKill() )
|
|
continue;
|
|
|
|
if ( CurrentInstance->GetAttachParentActor() != GetOwner() )
|
|
bNeedDuplicate = true;
|
|
}
|
|
|
|
if ( !bNeedDuplicate )
|
|
return;
|
|
|
|
// We need to duplicate our instances
|
|
TArray< AActor* > SourceInstances = Instances;
|
|
Instances.Empty();
|
|
for ( AActor* CurrentInstance : SourceInstances )
|
|
{
|
|
if ( !CurrentInstance || CurrentInstance->IsPendingKill() )
|
|
continue;
|
|
|
|
FTransform InstanceTransform;
|
|
if ( CurrentInstance->GetRootComponent() )
|
|
InstanceTransform = CurrentInstance->GetRootComponent()->GetRelativeTransform();
|
|
|
|
AddInstance( InstanceTransform );
|
|
}
|
|
}
|
|
|
|
void UHoudiniInstancedActorComponent::UpdateInstancerComponentInstances(
|
|
USceneComponent * Component,
|
|
const TArray< FTransform > & ProcessedTransforms, const TArray<FLinearColor> & InstancedColors )
|
|
{
|
|
UInstancedStaticMeshComponent* ISMC = Cast<UInstancedStaticMeshComponent>( Component );
|
|
UHierarchicalInstancedStaticMeshComponent* HISMC = Cast<UHierarchicalInstancedStaticMeshComponent>(Component);
|
|
UHoudiniInstancedActorComponent* IAC = Cast<UHoudiniInstancedActorComponent>( Component );
|
|
UHoudiniMeshSplitInstancerComponent* MSIC = Cast<UHoudiniMeshSplitInstancerComponent>( Component );
|
|
|
|
if(!ISMC && !IAC && !MSIC)
|
|
return;
|
|
|
|
if( ISMC && !ISMC->IsPendingKill() )
|
|
{
|
|
ISMC->ClearInstances();
|
|
if( HISMC )
|
|
{
|
|
// HISM need a special treatment as calling AddInstance multiple times on them can cause crashes:
|
|
// see UE4 bug UE-68582
|
|
// Calling UHierarchicalInstancedStaticMeshComponent::AddInstance multiple times causes
|
|
// multiple BuildTrees to be created and run asynchronously at the same time.
|
|
bool bAautoRebuildState = HISMC->bAutoRebuildTreeOnInstanceChanges;
|
|
HISMC->bAutoRebuildTreeOnInstanceChanges = false;
|
|
|
|
for( int32 InstanceIdx = 0; InstanceIdx < ProcessedTransforms.Num(); ++InstanceIdx )
|
|
{
|
|
HISMC->AddInstance(ProcessedTransforms[InstanceIdx]);
|
|
}
|
|
|
|
HISMC->bAutoRebuildTreeOnInstanceChanges = bAautoRebuildState;
|
|
HISMC->BuildTreeIfOutdated(true, true);
|
|
}
|
|
else
|
|
{
|
|
for( int32 InstanceIdx = 0; InstanceIdx < ProcessedTransforms.Num(); ++InstanceIdx )
|
|
{
|
|
ISMC->AddInstance(ProcessedTransforms[InstanceIdx]);
|
|
}
|
|
}
|
|
}
|
|
else if( IAC && !IAC->IsPendingKill() )
|
|
{
|
|
IAC->SetInstances(ProcessedTransforms);
|
|
}
|
|
else if( MSIC && !MSIC->IsPendingKill() )
|
|
{
|
|
MSIC->SetInstances(ProcessedTransforms, InstancedColors );
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |