void UActorRecording::GetSceneComponents(TArray<USceneComponent*>& OutArray, bool bIncludeNonCDO/*=true*/)
{
	// it is not enough to just go through the owned components array here
	// we need to traverse the scene component hierarchy as well, as some components may be 
	// owned by other actors (e.g. for pooling) and some may not be part of the hierarchy
	if(GetActorToRecord() != nullptr)
	{
		USceneComponent* RootComponent = GetActorToRecord()->GetRootComponent();
		if(RootComponent)
		{
			// note: GetChildrenComponents clears array!
			RootComponent->GetChildrenComponents(true, OutArray);
			OutArray.Add(RootComponent);
		}

		// add owned components that are *not* part of the hierarchy
		TInlineComponentArray<USceneComponent*> OwnedComponents(GetActorToRecord());
		for(USceneComponent* OwnedComponent : OwnedComponents)
		{
			check(OwnedComponent);
			if(OwnedComponent->GetAttachParent() == nullptr && OwnedComponent != RootComponent)
			{
				OutArray.Add(OwnedComponent);
			}
		}

		if(!bIncludeNonCDO)
		{
			AActor* CDO = Cast<AActor>(GetActorToRecord()->GetClass()->GetDefaultObject());

			auto ShouldRemovePredicate = [&](UActorComponent* PossiblyRemovedComponent)
				{
					// try to find a component with this name in the CDO
					for (UActorComponent* SearchComponent : CDO->GetComponents())
					{
						if (SearchComponent->GetClass() == PossiblyRemovedComponent->GetClass() &&
							SearchComponent->GetFName() == PossiblyRemovedComponent->GetFName())
						{
							return false;
						}
					}

					// remove if its not found
					return true;
				};

			OutArray.RemoveAllSwap(ShouldRemovePredicate);
		}
	}
}