You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Merging //UE4/Dev-Main to Dev-Networking (//UE4/Dev-Networking)
#rb none #rnx [CL 4344260 by Ryan Gerleve in Dev-Networking branch]
This commit is contained in:
+818
-638
File diff suppressed because it is too large
Load Diff
@@ -106,6 +106,8 @@ EditorBrushMaterialName=/Engine/EngineMaterials/EditorBrushMaterial.EditorBrushM
|
||||
DefaultPhysMaterialName=/Engine/EngineMaterials/DefaultPhysicalMaterial.DefaultPhysicalMaterial
|
||||
DefaultDeferredDecalMaterialName=/Engine/EngineMaterials/DefaultDeferredDecalMaterial.DefaultDeferredDecalMaterial
|
||||
DefaultPostProcessMaterialName=/Engine/EngineMaterials/DefaultPostProcessMaterial.DefaultPostProcessMaterial
|
||||
TimecodeProviderClassName=None
|
||||
DefaultTimecodeProviderClassName=/Script/Engine.SystemTimeTimecodeProvider
|
||||
TextureStreamingBoundsMaterialName=/Engine/EditorMaterials/Utilities/TextureStreamingBounds_MATInst.TextureStreamingBounds_MATInst
|
||||
ArrowMaterialName=/Engine/EditorMaterials/GizmoMaterial.GizmoMaterial
|
||||
bAllowHostMigration=false
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -95,3 +95,16 @@ void ULiveLinkComponent::GetSubjectDataAtWorldTime(const FName SubjectName, cons
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ULiveLinkComponent::GetSubjectDataAtSceneTime(const FName SubjectName, const FTimecode& SceneTime, bool& bSuccess, FSubjectFrameHandle& SubjectFrameHandle)
|
||||
{
|
||||
bSuccess = false;
|
||||
if (HasLiveLinkClient())
|
||||
{
|
||||
if (const FLiveLinkSubjectFrame* SubjectFrame = LiveLinkClient->GetSubjectDataAtSceneTime(SubjectName, SceneTime))
|
||||
{
|
||||
SubjectFrameHandle.SetCachedFrame(MakeShared<FCachedSubjectFrame>(SubjectFrame));
|
||||
bSuccess = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "LiveLinkTimeSynchronizationSource.h"
|
||||
#include "LiveLinkClient.h"
|
||||
#include "Features/IModularFeatures.h"
|
||||
#include "Math/NumericLimits.h"
|
||||
|
||||
ULiveLinkTimeSynchronizationSource::ULiveLinkTimeSynchronizationSource()
|
||||
{
|
||||
if (!HasAnyFlags(RF_ArchetypeObject | RF_ClassDefaultObject))
|
||||
{
|
||||
IModularFeatures& ModularFeatures = IModularFeatures::Get();
|
||||
ModularFeatures.OnModularFeatureRegistered().AddUObject(this, &ThisClass::OnModularFeatureRegistered);
|
||||
ModularFeatures.OnModularFeatureUnregistered().AddUObject(this, &ThisClass::OnModularFeatureUnregistered);
|
||||
|
||||
if (ModularFeatures.IsModularFeatureAvailable(ILiveLinkClient::ModularFeatureName))
|
||||
{
|
||||
LiveLinkClient = &ModularFeatures.GetModularFeature<FLiveLinkClient>(ILiveLinkClient::ModularFeatureName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FFrameTime ULiveLinkTimeSynchronizationSource::GetNewestSampleTime() const
|
||||
{
|
||||
UpdateCachedState();
|
||||
return CachedData.NewestSampleTime + FrameOffset;
|
||||
}
|
||||
|
||||
FFrameTime ULiveLinkTimeSynchronizationSource::GetOldestSampleTime() const
|
||||
{
|
||||
UpdateCachedState();
|
||||
return CachedData.OldestSampleTime + FrameOffset;
|
||||
}
|
||||
|
||||
FFrameRate ULiveLinkTimeSynchronizationSource::GetFrameRate() const
|
||||
{
|
||||
UpdateCachedState();
|
||||
return CachedData.Settings.FrameRate;
|
||||
}
|
||||
|
||||
bool ULiveLinkTimeSynchronizationSource::IsReady() const
|
||||
{
|
||||
UpdateCachedState();
|
||||
return LiveLinkClient && CachedData.bIsValid && (ESyncState::NotSynced == State || LastUpdateGuid == CachedData.SkeletonGuid);
|
||||
}
|
||||
|
||||
bool ULiveLinkTimeSynchronizationSource::Open(const FTimeSynchronizationOpenData& OpenData)
|
||||
{
|
||||
UE_LOG(LogLiveLink, Log, TEXT("ULiveLinkTimeSynchronizationSource::Open %s"), *SubjectName.ToString());
|
||||
if (ensure(LiveLinkClient != nullptr) && IsReady())
|
||||
{
|
||||
State = ESyncState::Opened;
|
||||
LastUpdateGuid = CachedData.SkeletonGuid;
|
||||
LiveLinkClient->OnStartSynchronization(SubjectName, OpenData, FrameOffset);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
State = ESyncState::NotSynced;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void ULiveLinkTimeSynchronizationSource::Start(const FTimeSynchronizationStartData& StartData)
|
||||
{
|
||||
UE_LOG(LogLiveLink, Log, TEXT("ULiveLinkTimeSynchronizationSource::Start %s"), *SubjectName.ToString());
|
||||
if (ensure(LiveLinkClient != nullptr))
|
||||
{
|
||||
State = ESyncState::Synced;
|
||||
LiveLinkClient->OnSynchronizationEstablished(SubjectName, StartData);
|
||||
}
|
||||
else
|
||||
{
|
||||
State = ESyncState::NotSynced;
|
||||
}
|
||||
}
|
||||
|
||||
void ULiveLinkTimeSynchronizationSource::Close()
|
||||
{
|
||||
UE_LOG(LogLiveLink, Log, TEXT("ULiveLinkTimeSynchronizationSource::Close %s"), *SubjectName.ToString());
|
||||
if (ensure(LiveLinkClient != nullptr))
|
||||
{
|
||||
LiveLinkClient->OnStopSynchronization(SubjectName);
|
||||
}
|
||||
|
||||
State = ESyncState::NotSynced;
|
||||
}
|
||||
|
||||
FString ULiveLinkTimeSynchronizationSource::GetDisplayName() const
|
||||
{
|
||||
return SubjectName.ToString();
|
||||
}
|
||||
|
||||
void ULiveLinkTimeSynchronizationSource::OnModularFeatureRegistered(const FName& FeatureName, class IModularFeature* Feature)
|
||||
{
|
||||
if (FeatureName == ILiveLinkClient::ModularFeatureName)
|
||||
{
|
||||
LiveLinkClient = static_cast<FLiveLinkClient*>(Feature);
|
||||
}
|
||||
}
|
||||
|
||||
void ULiveLinkTimeSynchronizationSource::OnModularFeatureUnregistered(const FName& FeatureName, class IModularFeature* Feature)
|
||||
{
|
||||
if (FeatureName == ILiveLinkClient::ModularFeatureName && (LiveLinkClient != nullptr) && ensure(Feature == LiveLinkClient))
|
||||
{
|
||||
LiveLinkClient = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ULiveLinkTimeSynchronizationSource::UpdateCachedState() const
|
||||
{
|
||||
if (LastUpdateFrame != GFrameCounter && LiveLinkClient != nullptr)
|
||||
{
|
||||
LastUpdateFrame = GFrameCounter;
|
||||
CachedData = LiveLinkClient->GetTimeSyncData(SubjectName);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "TimeSynchronizationSource.h"
|
||||
#include "LiveLinkClient.h"
|
||||
#include "Misc/Guid.h"
|
||||
#include "LiveLinkTimeSynchronizationSource.generated.h"
|
||||
|
||||
UCLASS(EditInlineNew)
|
||||
class ULiveLinkTimeSynchronizationSource : public UTimeSynchronizationSource
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
private:
|
||||
|
||||
UPROPERTY(EditAnywhere, Category="LiveLink")
|
||||
FName SubjectName;
|
||||
|
||||
FLiveLinkClient* LiveLinkClient;
|
||||
|
||||
enum class ESyncState
|
||||
{
|
||||
NotSynced,
|
||||
Opened,
|
||||
Synced
|
||||
};
|
||||
|
||||
mutable ESyncState State = ESyncState::NotSynced;
|
||||
mutable int64 LastUpdateFrame;
|
||||
mutable FLiveLinkSubjectTimeSyncData CachedData;
|
||||
mutable FGuid LastUpdateGuid;
|
||||
|
||||
public:
|
||||
|
||||
ULiveLinkTimeSynchronizationSource();
|
||||
|
||||
//~ Begin TimeSynchronizationSource API
|
||||
virtual FFrameTime GetNewestSampleTime() const override;
|
||||
virtual FFrameTime GetOldestSampleTime() const override;
|
||||
virtual FFrameRate GetFrameRate() const override;
|
||||
virtual bool IsReady() const override;
|
||||
virtual bool Open(const FTimeSynchronizationOpenData& OpenData) override;
|
||||
virtual void Start(const FTimeSynchronizationStartData& StartData) override;
|
||||
virtual void Close() override;
|
||||
virtual FString GetDisplayName() const override;
|
||||
//~ End TimeSynchronizationSource API
|
||||
|
||||
private:
|
||||
|
||||
void OnModularFeatureRegistered(const FName& FeatureName, class IModularFeature* Feature);
|
||||
void OnModularFeatureUnregistered(const FName& FeatureName, class IModularFeature* Feature);
|
||||
void UpdateCachedState() const;
|
||||
};
|
||||
@@ -17,15 +17,22 @@
|
||||
// Live Link Log Category
|
||||
DECLARE_LOG_CATEGORY_EXTERN(LogLiveLink, Log, All);
|
||||
|
||||
|
||||
struct FLiveLinkSubjectTimeSyncData
|
||||
{
|
||||
bool bIsValid = false;
|
||||
FGuid SkeletonGuid;
|
||||
FFrameTime OldestSampleTime;
|
||||
FFrameTime NewestSampleTime;
|
||||
FLiveLinkTimeSynchronizationSettings Settings;
|
||||
};
|
||||
|
||||
struct FLiveLinkSubject
|
||||
{
|
||||
// Key for storing curve data (Names)
|
||||
FLiveLinkCurveKey CurveKeyData;
|
||||
FLiveLinkCurveKey CurveKeyData;
|
||||
|
||||
// Subject data frames that we have received (transforms and curve values)
|
||||
TArray<FLiveLinkFrame> Frames;
|
||||
TArray<FLiveLinkFrame> Frames;
|
||||
|
||||
// Time difference between current system time and TimeCode times
|
||||
double SubjectTimeOffset;
|
||||
@@ -40,42 +47,110 @@ struct FLiveLinkSubject
|
||||
// Guid to track the last live link source that modified us
|
||||
FGuid LastModifier;
|
||||
|
||||
// Connection settings specified by user
|
||||
FLiveLinkInterpolationSettings CachedInterpolationSettings;
|
||||
|
||||
FLiveLinkSubject(const FLiveLinkRefSkeleton& InRefSkeleton)
|
||||
: RefSkeleton(InRefSkeleton)
|
||||
FLiveLinkSubject(const FLiveLinkRefSkeleton& InRefSkeleton, FName InName)
|
||||
: Name(InName)
|
||||
, RefSkeleton(InRefSkeleton)
|
||||
, RefSkeletonGuid(FGuid::NewGuid())
|
||||
{}
|
||||
|
||||
FLiveLinkSubject()
|
||||
{}
|
||||
|
||||
// Add a frame of data from a FLiveLinkFrameData
|
||||
void AddFrame(const FLiveLinkFrameData& FrameData, FGuid FrameSource, bool bSaveFrame);
|
||||
|
||||
// Populate OutFrame with a frame based off of the supplied time and our own offsets
|
||||
void GetFrameAtWorldTime(const double InSeconds, FLiveLinkSubjectFrame& OutFrame);
|
||||
|
||||
// Populate OutFrame with a frame based off of the supplied scene time.
|
||||
void GetFrameAtSceneTime(const FQualifiedFrameTime& InSceneTime, FLiveLinkSubjectFrame& OutFrame);
|
||||
|
||||
// Get this subjects ref skeleton
|
||||
const FLiveLinkRefSkeleton& GetRefSkeleton() const { return RefSkeleton; }
|
||||
|
||||
// Handling setting a new ref skeleton
|
||||
void SetRefSkeleton(const FLiveLinkRefSkeleton& InRefSkeleton) { RefSkeleton = InRefSkeleton; RefSkeletonGuid = FGuid::NewGuid(); }
|
||||
|
||||
// Free all subject data frames.
|
||||
void ClearFrames();
|
||||
|
||||
void CacheSourceSettings(const ULiveLinkSourceSettings* DataToCache);
|
||||
|
||||
FName GetName() const { return Name; }
|
||||
|
||||
ELiveLinkSourceMode GetMode() const { return CachedSettings.SourceMode; }
|
||||
|
||||
FLiveLinkSubjectTimeSyncData GetTimeSyncData();
|
||||
|
||||
void OnStartSynchronization(const struct FTimeSynchronizationOpenData& OpenData, const int32 FrameOffset);
|
||||
void OnSynchronizationEstablished(const struct FTimeSynchronizationStartData& StartData);
|
||||
void OnStopSynchronization();
|
||||
|
||||
private:
|
||||
|
||||
// Copy a frame from the buffer to a FLiveLinkSubjectFrame
|
||||
static void CopyFrameData(const FLiveLinkFrame& InFrame, FLiveLinkSubjectFrame& OutFrame);
|
||||
|
||||
// Blend two frames from the buffer and copy the result to a FLiveLinkSubjectFrame
|
||||
static void CopyFrameDataBlended(const FLiveLinkFrame& PreFrame, const FLiveLinkFrame& PostFrame, float BlendWeight, FLiveLinkSubjectFrame& OutFrame);
|
||||
|
||||
void ResetFrame(FLiveLinkSubjectFrame& OutFrame) const;;
|
||||
|
||||
int32 AddFrame_Default(const FLiveLinkWorldTime& FrameTime, bool bSaveFrame);
|
||||
int32 AddFrame_Interpolated(const FLiveLinkWorldTime& FrameTime, bool bSaveFrame);
|
||||
int32 AddFrame_TimeSynchronized(const FFrameTime& FrameTime, bool bSaveFrame);
|
||||
|
||||
template<bool bWithRollover>
|
||||
int32 AddFrame_TimeSynchronized(const FFrameTime& FrameTime, bool bSaveFrame);
|
||||
|
||||
void GetFrameAtWorldTime_Default(const double InSeconds, FLiveLinkSubjectFrame& OutFrame);
|
||||
void GetFrameAtWorldTime_Interpolated(const double InSeconds, FLiveLinkSubjectFrame& OutFrame);
|
||||
|
||||
template<bool bWithRollover>
|
||||
void GetFrameAtSceneTime_TimeSynchronized(const FFrameTime& FrameTime, FLiveLinkSubjectFrame& OutFrame);
|
||||
|
||||
template<bool bForInsert, bool bWithRollover>
|
||||
int32 FindFrameIndex_TimeSynchronized(const FFrameTime& FrameTime);
|
||||
|
||||
FName Name;
|
||||
|
||||
struct FLiveLinkCachedSettings
|
||||
{
|
||||
ELiveLinkSourceMode SourceMode = ELiveLinkSourceMode::Default;
|
||||
TOptional<FLiveLinkInterpolationSettings> InterpolationSettings;
|
||||
TOptional<FLiveLinkTimeSynchronizationSettings> TimeSynchronizationSettings;
|
||||
};
|
||||
|
||||
// Connection settings specified by user
|
||||
// May only store settings relevant to the current mode (ELiveLinkSourceMode).
|
||||
FLiveLinkCachedSettings CachedSettings;
|
||||
|
||||
// Ref Skeleton for transforms
|
||||
FLiveLinkRefSkeleton RefSkeleton;
|
||||
|
||||
// Allow us to track changes to the ref skeleton
|
||||
FGuid RefSkeletonGuid;
|
||||
|
||||
// Copy a frame from the buffer to a FLiveLinkSubjectFrame
|
||||
void CopyFrameData(const FLiveLinkFrame& InFrame, FLiveLinkSubjectFrame& OutFrame);
|
||||
struct FLiveLinkTimeSynchronizationData
|
||||
{
|
||||
// Whether or not synchronization has been established.
|
||||
bool bHasEstablishedSync = false;
|
||||
|
||||
// The frame in our buffer where a rollover was detected. Only applicable for time synchronized sources.
|
||||
int32 RolloverFrame = INDEX_NONE;
|
||||
|
||||
// Frame offset that will be used for this source.
|
||||
int32 Offset = 0;
|
||||
|
||||
// Frame Time value modulus. When this value is not set, we assume no rollover occurs.
|
||||
TOptional<FFrameTime> RolloverModulus;
|
||||
|
||||
// Frame rate used as the base for synchronization.
|
||||
FFrameRate SyncFrameRate;
|
||||
|
||||
// Frame time that synchronization was established (relative to SynchronizationFrameRate).
|
||||
FFrameTime SyncStartTime;
|
||||
};
|
||||
|
||||
TOptional<FLiveLinkTimeSynchronizationData> TimeSyncData;
|
||||
|
||||
// Blend two frames from the buffer and copy the result to a FLiveLinkSubjectFrame
|
||||
void CopyFrameDataBlended(const FLiveLinkFrame& PreFrame, const FLiveLinkFrame& PostFrame, float BlendWeight, FLiveLinkSubjectFrame& OutFrame);
|
||||
};
|
||||
|
||||
// Structure that identifies an individual subject
|
||||
@@ -149,6 +224,7 @@ public:
|
||||
virtual const FLiveLinkSubjectFrame* GetSubjectData(FName SubjectName) override;
|
||||
|
||||
const FLiveLinkSubjectFrame* GetSubjectDataAtWorldTime(FName SubjectName, double WorldTime) override;
|
||||
const FLiveLinkSubjectFrame* GetSubjectDataAtSceneTime(FName SubjectName, const FTimecode& SceneTime) override;
|
||||
|
||||
const TArray<FGuid>& GetSourceEntries() const { return SourceGuids; }
|
||||
const TArray<FLiveLinkFrame>* GetSubjectRawFrames(FName SubjectName) override;
|
||||
@@ -161,6 +237,10 @@ public:
|
||||
// Get a list of currently active subjects
|
||||
TArray<FLiveLinkSubjectKey> GetSubjects();
|
||||
|
||||
FLiveLinkSubjectTimeSyncData GetTimeSyncData(FName SubjectName);
|
||||
|
||||
FGuid GetCurrentSubjectOwner(FName SubjectName) const;
|
||||
|
||||
// Populates an array with in-use subject names
|
||||
virtual void GetSubjectNames(TArray<FName>& SubjectNames) override;
|
||||
|
||||
@@ -196,8 +276,19 @@ public:
|
||||
FDelegateHandle RegisterSubjectsChangedHandle(const FSimpleMulticastDelegate::FDelegate& SubjectsChanged);
|
||||
void UnregisterSubjectsChangedHandle(FDelegateHandle Handle);
|
||||
|
||||
/** Called when time synchronization is starting for a subject. */
|
||||
void OnStartSynchronization(FName SubjectName, const struct FTimeSynchronizationOpenData& OpenData, const int32 FrameOffset);
|
||||
|
||||
/** Called when time synchronization has been established for a subject. */
|
||||
void OnSynchronizationEstablished(FName SubjectName, const struct FTimeSynchronizationStartData& StartData);
|
||||
|
||||
/** Called when time synchronization has been stopped for a subject. */
|
||||
void OnStopSynchronization(FName SubjectName);
|
||||
|
||||
private:
|
||||
|
||||
void RemoveSource(int32 SourceIndex);
|
||||
|
||||
// Setup the source for virtual subjects
|
||||
void AddVirtualSubjectSource();
|
||||
|
||||
|
||||
@@ -48,6 +48,10 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "LiveLink")
|
||||
void GetSubjectDataAtWorldTime(const FName SubjectName, const float WorldTime, bool& bSuccess, FSubjectFrameHandle& SubjectFrameHandle);
|
||||
|
||||
// Returns a handle to the frame of data in LiveLink for a given subject at the specified time along with a boolean for whether a frame was found.
|
||||
// Returns a handle to an empty frame if no frame of data is found.
|
||||
UFUNCTION(BlueprintCallable, Category = "LiveLink")
|
||||
void GetSubjectDataAtSceneTime(const FName SubjectName, const FTimecode& SceneTime, bool& bSuccess, FSubjectFrameHandle& SubjectFrameHandle);
|
||||
private:
|
||||
bool HasLiveLinkClient();
|
||||
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ UMovieSceneComposurePostMoveSettingsTrack::UMovieSceneComposurePostMoveSettingsT
|
||||
|
||||
UMovieSceneSection* UMovieSceneComposurePostMoveSettingsTrack::CreateNewSection()
|
||||
{
|
||||
return NewObject<UMovieSceneSection>(this, UMovieSceneComposurePostMoveSettingsSection::StaticClass(), NAME_None, RF_Transactional);
|
||||
return NewObject<UMovieSceneComposurePostMoveSettingsSection>(this, NAME_None, RF_Transactional);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,12 +21,5 @@
|
||||
"Type" : "Editor",
|
||||
"LoadingPhase" : "Default"
|
||||
}
|
||||
],
|
||||
"Plugins" :
|
||||
[
|
||||
{
|
||||
"Name" : "PythonScriptPlugin",
|
||||
"Enabled" : true
|
||||
}
|
||||
]
|
||||
}
|
||||
+3
-7
@@ -21,18 +21,14 @@ namespace UnrealBuildTool.Rules
|
||||
"AssetTools",
|
||||
"EditorStyle",
|
||||
"MainFrame",
|
||||
"MeshDescription",
|
||||
"MeshDescriptionOperations",
|
||||
"RawMesh",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"MeshDescription",
|
||||
"UnrealEd",
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"PythonScriptPlugin",
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+111
-15
@@ -12,6 +12,7 @@
|
||||
#include "Editor/UnrealEdEngine.h"
|
||||
#include "EngineUtils.h"
|
||||
#include "Engine/Brush.h"
|
||||
#include "Engine/MapBuildDataRegistry.h"
|
||||
#include "Engine/Selection.h"
|
||||
#include "Engine/StaticMesh.h"
|
||||
#include "Engine/StaticMeshActor.h"
|
||||
@@ -23,7 +24,8 @@
|
||||
#include "Kismet2/ComponentEditorUtils.h"
|
||||
#include "Layers/ILayers.h"
|
||||
#include "LevelEditorViewport.h"
|
||||
#include "Engine/MapBuildDataRegistry.h"
|
||||
#include "Materials/Material.h"
|
||||
#include "Materials/MaterialInterface.h"
|
||||
#include "MeshMergeModule.h"
|
||||
#include "ScopedTransaction.h"
|
||||
#include "UnrealEdGlobals.h"
|
||||
@@ -914,11 +916,6 @@ namespace InternalEditorLevelLibrary
|
||||
ActorsToTest.RemoveAtSwap(Index);
|
||||
}
|
||||
}
|
||||
if (ActorsToTest.Num() < 2)
|
||||
{
|
||||
OutFailureReason = TEXT("A merge operation requires at least 2 Actors.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// All actors need to come from the same World
|
||||
UWorld* CurrentWorld = ActorsToTest[0]->GetWorld();
|
||||
@@ -983,12 +980,6 @@ namespace InternalEditorLevelLibrary
|
||||
}
|
||||
}
|
||||
|
||||
if (OutValidActor.Num() < 2)
|
||||
{
|
||||
OutFailureReason = TEXT("A merge operation requires at least 2 valid Actors.");
|
||||
return false;
|
||||
}
|
||||
|
||||
OutAverageLocation = PivotLocation / OutValidActor.Num();
|
||||
|
||||
return true;
|
||||
@@ -1034,7 +1025,13 @@ AActor* UEditorLevelLibrary::JoinStaticMeshActors(const TArray<AStaticMeshActor*
|
||||
FString FailureReason;
|
||||
if (!InternalEditorLevelLibrary::FindValidActorAndComponents(ActorsToMerge, AllActors, AllComponents, PivotLocation, FailureReason))
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("JoinStaticMeshSctors failed. %s"), *FailureReason);
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("JoinStaticMeshActors failed. %s"), *FailureReason);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (AllActors.Num() < 2)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("JoinStaticMeshActors failed. A merge operation requires at least 2 valid Actors."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1044,7 +1041,7 @@ AActor* UEditorLevelLibrary::JoinStaticMeshActors(const TArray<AStaticMeshActor*
|
||||
AActor* NewActor = AllActors[0]->GetWorld()->SpawnActor<AActor>(PivotLocation, FRotator::ZeroRotator, Params);
|
||||
if (!NewActor)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("JoinStaticMeshSctors failed. Internal error while creating the join actor."));
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("JoinStaticMeshActors failed. Internal error while creating the join actor."));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1129,7 +1126,7 @@ bool UEditorLevelLibrary::MergeStaticMeshActors(const TArray<AStaticMeshActor*>&
|
||||
FVector MergedActorLocation;
|
||||
TArray<UObject*> CreatedAssets;
|
||||
const float ScreenAreaSize = TNumericLimits<float>::Max();
|
||||
MeshUtilities.MergeComponentsToStaticMesh(AllComponents, AllActors[0]->GetWorld(), MergeOptions.MeshMergingSettings, nullptr, nullptr, MergeOptions.BasePackageName, CreatedAssets, MergedActorLocation, ScreenAreaSize, true);
|
||||
MeshUtilities.MergeComponentsToStaticMesh(AllComponents, AllActors[0]->GetWorld(), MergeOptions.MeshMergingSettings, nullptr, nullptr, PackageName, CreatedAssets, MergedActorLocation, ScreenAreaSize, true);
|
||||
|
||||
UStaticMesh* MergedMesh = nullptr;
|
||||
if (!CreatedAssets.FindItemByClass(&MergedMesh))
|
||||
@@ -1184,4 +1181,103 @@ bool UEditorLevelLibrary::MergeStaticMeshActors(const TArray<AStaticMeshActor*>&
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UEditorLevelLibrary::CreateProxyMeshActor(const TArray<class AStaticMeshActor*>& ActorsToMerge, const FEditorScriptingCreateProxyMeshActorOptions& MergeOptions, class AStaticMeshActor*& OutMergedActor)
|
||||
{
|
||||
// See FMeshProxyTool::RunMerge (Engine\Source\Editor\MergeActors\Private\MeshProxyTool\MeshProxyTool.cpp)
|
||||
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
|
||||
|
||||
OutMergedActor = nullptr;
|
||||
|
||||
if (!EditorScriptingUtils::CheckIfInEditorAndPIE())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup actors
|
||||
TArray<AStaticMeshActor*> StaticMeshActors;
|
||||
TArray<UPrimitiveComponent*> AllComponents_UNUSED;
|
||||
FVector PivotLocation;
|
||||
FString FailureReason;
|
||||
if (!InternalEditorLevelLibrary::FindValidActorAndComponents(ActorsToMerge, StaticMeshActors, AllComponents_UNUSED, PivotLocation, FailureReason))
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("MergeStaticMeshActors failed. %s"), *FailureReason);
|
||||
return false;
|
||||
}
|
||||
TArray<AActor*> AllActors(StaticMeshActors);
|
||||
|
||||
const IMeshMergeUtilities& MeshUtilities = FModuleManager::Get().LoadModuleChecked<IMeshMergeModule>("MeshMergeUtilities").GetUtilities();
|
||||
|
||||
FCreateProxyDelegate ProxyDelegate;
|
||||
TArray<UObject*> CreatedAssets;
|
||||
ProxyDelegate.BindLambda([&CreatedAssets](const FGuid Guid, TArray<UObject*>& InAssetsToSync){CreatedAssets.Append(InAssetsToSync);});
|
||||
|
||||
MeshUtilities.CreateProxyMesh(
|
||||
AllActors, // List of Actors to merge
|
||||
MergeOptions.MeshProxySettings, // Merge settings
|
||||
nullptr, // Base Material used for final proxy material. Note: nullptr for default impl: /Engine/EngineMaterials/BaseFlattenMaterial.BaseFlattenMaterial
|
||||
nullptr, // Package for generated assets. Note: if nullptr, BasePackageName is used
|
||||
MergeOptions.BasePackageName, // Will be used for naming generated assets, in case InOuter is not specified ProxyBasePackageName will be used as long package name for creating new packages
|
||||
FGuid::NewGuid(), // Identify a job, First argument of the ProxyDelegate
|
||||
ProxyDelegate // Called back on asset creation
|
||||
);
|
||||
|
||||
UStaticMesh* MergedMesh = nullptr;
|
||||
if (!CreatedAssets.FindItemByClass(&MergedMesh))
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("CreateProxyMeshActor failed. No mesh created."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update the asset registry that a new static mesh and material has been created
|
||||
FAssetRegistryModule& AssetRegistry = FModuleManager::Get().LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
|
||||
for (UObject* Asset : CreatedAssets)
|
||||
{
|
||||
AssetRegistry.AssetCreated(Asset);
|
||||
GEditor->BroadcastObjectReimported(Asset);
|
||||
}
|
||||
|
||||
// Also notify the content browser that the new assets exists
|
||||
FContentBrowserModule& ContentBrowserModule = FModuleManager::Get().LoadModuleChecked<FContentBrowserModule>("ContentBrowser");
|
||||
ContentBrowserModule.Get().SyncBrowserToAssets(CreatedAssets, true);
|
||||
|
||||
// Place new mesh in the world
|
||||
UWorld* ActorWorld = AllActors[0]->GetWorld();
|
||||
ULevel* ActorLevel = AllActors[0]->GetLevel();
|
||||
if (MergeOptions.bSpawnMergedActor)
|
||||
{
|
||||
FActorSpawnParameters Params;
|
||||
Params.OverrideLevel = ActorLevel;
|
||||
OutMergedActor = ActorWorld->SpawnActor<AStaticMeshActor>(FVector::ZeroVector, FRotator::ZeroRotator, Params);
|
||||
if (!OutMergedActor)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("CreateProxyMeshActor failed. Internal error while creating the merged actor."));
|
||||
return false;
|
||||
}
|
||||
|
||||
OutMergedActor->GetStaticMeshComponent()->SetStaticMesh(MergedMesh);
|
||||
OutMergedActor->SetActorLabel(MergeOptions.NewActorLabel);
|
||||
ActorWorld->UpdateCullDistanceVolumes(OutMergedActor, OutMergedActor->GetStaticMeshComponent());
|
||||
}
|
||||
|
||||
// Remove source actors
|
||||
if (MergeOptions.bDestroySourceActors)
|
||||
{
|
||||
for (AActor* Actor : AllActors)
|
||||
{
|
||||
GEditor->Layers->DisassociateActorFromLayers(Actor);
|
||||
ActorWorld->EditorDestroyActor(Actor, true);
|
||||
}
|
||||
}
|
||||
|
||||
//Select newly created actor
|
||||
if (OutMergedActor)
|
||||
{
|
||||
GEditor->SelectNone(false, true, false);
|
||||
GEditor->SelectActor(OutMergedActor, true, false); // don't notify but manually call NoteSelectionChange ?
|
||||
GEditor->NoteSelectionChange();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
+8
-12
@@ -1,7 +1,6 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "EditorPythonExecuter.h"
|
||||
#include "IPythonScriptPlugin.h"
|
||||
|
||||
#include "AssetRegistryModule.h"
|
||||
#include "Editor.h"
|
||||
@@ -164,7 +163,12 @@ namespace InternalEditorPythonRunner
|
||||
if (!AssetRegistryModule.Get().IsLoadingAssets())
|
||||
{
|
||||
bIsRunning = true;
|
||||
IPythonScriptPlugin::Get()->ExecPythonCommand(*FileName);
|
||||
|
||||
// Try and run the command
|
||||
if (!GEngine->Exec(GWorld, *FString::Printf(TEXT("PY \"%s\""), *FileName), *GLog))
|
||||
{
|
||||
UE_LOG(LogEditorPythonExecuter, Error, TEXT("-ExecutePythonScript cannot be used without a valid Python Script Plugin. Ensure the plugin is enabled and wasn't compiled with Python support stubbed out."));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -212,16 +216,8 @@ void FEditorPythonExecuter::OnStartupModule()
|
||||
}
|
||||
else
|
||||
{
|
||||
IPythonScriptPlugin* PythonScriptPlugin = IPythonScriptPlugin::Get();
|
||||
if (PythonScriptPlugin && PythonScriptPlugin->IsPythonAvailable())
|
||||
{
|
||||
InternalEditorPythonRunner::Executer = new InternalEditorPythonRunner::FExecuterTickable(MoveTemp(FileValue));
|
||||
InternalEditorPythonRunner::SExecutingDialog::OpenDialog();
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogEditorPythonExecuter, Error, TEXT("-ExecutePythonScript cannot be used without a valid Python Script Plugin. Ensure the plugin is enabled and wasn't compiled with Python support stubbed out."));
|
||||
}
|
||||
InternalEditorPythonRunner::Executer = new InternalEditorPythonRunner::FExecuterTickable(MoveTemp(FileValue));
|
||||
InternalEditorPythonRunner::SExecutingDialog::OpenDialog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-31
@@ -42,14 +42,15 @@ namespace EditorScriptingUtils
|
||||
}
|
||||
|
||||
// Test for invalid characters
|
||||
bool IsAValidPath(const FString& Path, FString& OutFailureReason)
|
||||
bool IsAValidPath(const FString& Path, const TCHAR* InvalidChar, FString& OutFailureReason)
|
||||
{
|
||||
// Like !FName::IsValidGroupName(Path)), but with INVALID_OBJECTPATH_CHARACTERS and no conversion to from FName
|
||||
const int32 StrLen = FCString::Strlen(INVALID_OBJECTPATH_CHARACTERS);
|
||||
// Like !FName::IsValidGroupName(Path)), but with another list and no conversion to from FName
|
||||
// InvalidChar may be INVALID_OBJECTPATH_CHARACTERS or INVALID_LONGPACKAGE_CHARACTERS or ...
|
||||
const int32 StrLen = FCString::Strlen(InvalidChar);
|
||||
for (int32 Index = 0; Index < StrLen; ++Index)
|
||||
{
|
||||
int32 FoundIndex = 0;
|
||||
if (Path.FindChar(INVALID_OBJECTPATH_CHARACTERS[Index], FoundIndex))
|
||||
if (Path.FindChar(InvalidChar[Index], FoundIndex))
|
||||
{
|
||||
OutFailureReason = FString::Printf(TEXT("Can't convert the path %s because it contains invalid characters."), *Path);
|
||||
return false;
|
||||
@@ -185,34 +186,35 @@ namespace EditorScriptingUtils
|
||||
TextPath.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
|
||||
FPaths::RemoveDuplicateSlashes(TextPath);
|
||||
|
||||
// Test for invalid characters
|
||||
if (!IsAValidPath(TextPath, OutFailureReason))
|
||||
{
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Get asset full name, i.e."PackageName.ObjectName:InnerAssetName.2ndInnerAssetName" from "/Game/Folder/PackageName.ObjectName:InnerAssetName.2ndInnerAssetName"
|
||||
FString AssetFullName = FPackageName::GetShortName(TextPath);
|
||||
|
||||
// Remove possible ':' character from asset full name
|
||||
FString AssetFullName;
|
||||
{
|
||||
int32 IndexOfSemiColumn;
|
||||
if (AssetFullName.FindChar(TEXT(':'), IndexOfSemiColumn))
|
||||
// Get everything after the last slash
|
||||
int32 IndexOfLastSlash = INDEX_NONE;
|
||||
TextPath.FindLastChar('/', IndexOfLastSlash);
|
||||
|
||||
FString Folders = TextPath.Left(IndexOfLastSlash);
|
||||
// Test for invalid characters
|
||||
if (!IsAValidPath(Folders, INVALID_LONGPACKAGE_CHARACTERS, OutFailureReason))
|
||||
{
|
||||
AssetFullName = AssetFullName.Left(IndexOfSemiColumn);
|
||||
return FString();
|
||||
}
|
||||
|
||||
AssetFullName = TextPath.Mid(IndexOfLastSlash + 1);
|
||||
}
|
||||
|
||||
// Get the object name
|
||||
FString ObjectName = FPackageName::ObjectPathToObjectName(AssetFullName);
|
||||
FString ObjectName = FPackageName::ObjectPathToPackageName(AssetFullName);
|
||||
if (ObjectName.IsEmpty())
|
||||
{
|
||||
ObjectName = FPackageName::ObjectPathToPackageName(AssetFullName);
|
||||
if (ObjectName.IsEmpty())
|
||||
{
|
||||
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it doesn't contain an asset name."), *AnyAssetPath);
|
||||
return FString();
|
||||
}
|
||||
OutFailureReason = FString::Printf(TEXT("Can't convert the path '%s' because it doesn't contain an asset name."), *AnyAssetPath);
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Test for invalid characters
|
||||
if (!IsAValidPath(ObjectName, INVALID_OBJECTNAME_CHARACTERS, OutFailureReason))
|
||||
{
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Confirm that we have a valid Root Package and get the valid PackagePath /Game/MyFolder/MyAsset
|
||||
@@ -234,7 +236,7 @@ namespace EditorScriptingUtils
|
||||
return FString();
|
||||
}
|
||||
|
||||
FString ObjectPath = FString::Printf(TEXT("%s.%s"), *PackagePath, *ObjectName);
|
||||
FString ObjectPath = FString::Printf(TEXT("%s.%s"), *PackagePath, *ObjectName); // #todo-ueent should be asset name, not object name (as ObjectName == PackageName)
|
||||
|
||||
if (FPackageName::IsScriptPackage(ObjectPath))
|
||||
{
|
||||
@@ -281,13 +283,6 @@ namespace EditorScriptingUtils
|
||||
TextPath.ReplaceInline(TEXT("\\"), TEXT("/"), ESearchCase::CaseSensitive);
|
||||
FPaths::RemoveDuplicateSlashes(TextPath);
|
||||
|
||||
// Test for invalid characters
|
||||
if (!IsAValidPath(TextPath, OutFailureReason))
|
||||
{
|
||||
return FString();
|
||||
}
|
||||
|
||||
//ObjectName = FPackageName::ObjectPathToObjectName(ObjectName);
|
||||
{
|
||||
// Remove .
|
||||
int32 ObjectDelimiterIdx;
|
||||
@@ -303,6 +298,12 @@ namespace EditorScriptingUtils
|
||||
}
|
||||
}
|
||||
|
||||
// Test for invalid characters
|
||||
if (!IsAValidPath(TextPath, INVALID_LONGPACKAGE_CHARACTERS, OutFailureReason))
|
||||
{
|
||||
return FString();
|
||||
}
|
||||
|
||||
// Confirm that we have a valid Root Package and get the valid PackagePath /Game/MyFolder
|
||||
FString PackagePath;
|
||||
if (!FPackageName::TryConvertFilenameToLongPackageName(TextPath, PackagePath, &OutFailureReason))
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ namespace EditorScriptingUtils
|
||||
/*
|
||||
* Check if the Path is a valid ContentBrowser Path
|
||||
*/
|
||||
bool IsAValidPath(const FString& Path, FString& OutFailureReason);
|
||||
bool IsAValidPath(const FString& Path, const TCHAR* InvalidChar, FString& OutFailureReason);
|
||||
|
||||
/*
|
||||
* Check if the AssetPath can be used to create a new asset
|
||||
|
||||
+164
-4
@@ -24,16 +24,18 @@
|
||||
#include "Layers/ILayers.h"
|
||||
#include "LevelEditorViewport.h"
|
||||
#include "Engine/MapBuildDataRegistry.h"
|
||||
#include "MeshAttributes.h"
|
||||
#include "MeshAttributeArray.h"
|
||||
#include "MeshDescription.h"
|
||||
#include "MeshDescriptionOperations.h"
|
||||
#include "MeshMergeModule.h"
|
||||
#include "PhysicsEngine/BodySetup.h"
|
||||
#include "RawMesh.h"
|
||||
#include "ScopedTransaction.h"
|
||||
#include "Toolkits/AssetEditorManager.h"
|
||||
#include "UnrealEdGlobals.h"
|
||||
#include "UnrealEd/Private/GeomFitUtils.h"
|
||||
#include "UnrealEd/Private/ConvexDecompTool.h"
|
||||
#include "MeshDescription.h"
|
||||
#include "MeshAttributes.h"
|
||||
#include "MeshAttributeArray.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "EditorStaticMeshLibrary"
|
||||
|
||||
@@ -114,6 +116,37 @@ namespace InternalEditorMeshLibrary
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsUVChannelValid(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex)
|
||||
{
|
||||
if (StaticMesh == nullptr)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("The StaticMesh is null."));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (LODIndex >= StaticMesh->GetNumLODs() || LODIndex < 0)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("The StaticMesh doesn't have LOD %d."), LODIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
FMeshDescription* MeshDescription = StaticMesh->GetOriginalMeshDescription(LODIndex);
|
||||
if (!MeshDescription)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("No mesh description for LOD %d."), LODIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 NumUVChannels = StaticMesh->GetNumUVChannels(LODIndex);
|
||||
if (UVChannelIndex < 0 || UVChannelIndex >= NumUVChannels)
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("The given UV channel index %d is out of bounds."), UVChannelIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int32 UEditorStaticMeshLibrary::SetLods(UStaticMesh* StaticMesh, const FEditorScriptingMeshReductionOptions& ReductionOptions)
|
||||
@@ -196,6 +229,68 @@ int32 UEditorStaticMeshLibrary::SetLods(UStaticMesh* StaticMesh, const FEditorSc
|
||||
return LODIndex;
|
||||
}
|
||||
|
||||
int32 UEditorStaticMeshLibrary::SetLodFromStaticMesh(UStaticMesh* DestinationStaticMesh, int32 DestinationLodIndex, UStaticMesh* SourceStaticMesh, int32 SourceLodIndex)
|
||||
{
|
||||
TGuardValue<bool> UnattendedScriptGuard( GIsRunningUnattendedScript, true );
|
||||
|
||||
if ( !EditorScriptingUtils::CheckIfInEditorAndPIE( ))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( DestinationStaticMesh == nullptr )
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("SetLodFromStaticMesh: The DestinationStaticMesh is null."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( SourceStaticMesh == nullptr )
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("SetLodFromStaticMesh: The SourceStaticMesh is null."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( !SourceStaticMesh->SourceModels.IsValidIndex( SourceLodIndex ) )
|
||||
{
|
||||
UE_LOG(LogEditorScripting, Error, TEXT("SetLodFromStaticMesh: SourceLodIndex is invalid."));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Close the mesh editor to prevent crashing. Reopen it after the mesh has been built.
|
||||
FAssetEditorManager& AssetEditorManager = FAssetEditorManager::Get();
|
||||
bool bStaticMeshIsEdited = false;
|
||||
if ( AssetEditorManager.FindEditorForAsset( DestinationStaticMesh, false ) )
|
||||
{
|
||||
AssetEditorManager.CloseAllEditorsForAsset( DestinationStaticMesh );
|
||||
bStaticMeshIsEdited = true;
|
||||
}
|
||||
|
||||
DestinationStaticMesh->Modify();
|
||||
|
||||
if ( DestinationStaticMesh->SourceModels.Num() < DestinationLodIndex + 1 )
|
||||
{
|
||||
// Add one LOD
|
||||
DestinationStaticMesh->AddSourceModel();
|
||||
|
||||
DestinationLodIndex = DestinationStaticMesh->SourceModels.Num() - 1;
|
||||
}
|
||||
|
||||
FRawMesh SourceRawMesh;
|
||||
SourceStaticMesh->SourceModels[ SourceLodIndex ].LoadRawMesh( SourceRawMesh );
|
||||
|
||||
DestinationStaticMesh->SourceModels[ DestinationLodIndex ].SaveRawMesh( SourceRawMesh );
|
||||
|
||||
DestinationStaticMesh->PostEditChange();
|
||||
|
||||
// Reopen MeshEditor on this mesh if the MeshEditor was previously opened in it
|
||||
if ( bStaticMeshIsEdited )
|
||||
{
|
||||
AssetEditorManager.OpenEditorForAsset( DestinationStaticMesh );
|
||||
}
|
||||
|
||||
return DestinationLodIndex;
|
||||
}
|
||||
|
||||
int32 UEditorStaticMeshLibrary::GetLodCount(UStaticMesh* StaticMesh)
|
||||
{
|
||||
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
|
||||
@@ -935,5 +1030,70 @@ bool UEditorStaticMeshLibrary::RemoveUVChannel(UStaticMesh* StaticMesh, int32 LO
|
||||
return StaticMesh->RemoveUVChannel(LODIndex, UVChannelIndex);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
bool UEditorStaticMeshLibrary::GeneratePlanarUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex, const FUVMapSettings& UVSettings)
|
||||
{
|
||||
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
|
||||
|
||||
if (!EditorScriptingUtils::CheckIfInEditorAndPIE())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InternalEditorMeshLibrary::IsUVChannelValid(StaticMesh, LODIndex, UVChannelIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FMeshDescription* MeshDescription = StaticMesh->GetOriginalMeshDescription(LODIndex);
|
||||
|
||||
TArray<FVector2D> TexCoords;
|
||||
FMeshDescriptionOperations::GeneratePlanarUV(*MeshDescription, UVSettings, TexCoords);
|
||||
|
||||
return StaticMesh->SetUVChannel(LODIndex, UVChannelIndex, TexCoords);
|
||||
}
|
||||
|
||||
bool UEditorStaticMeshLibrary::GenerateCylindricalUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex, const FUVMapSettings& UVSettings)
|
||||
{
|
||||
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
|
||||
|
||||
if (!EditorScriptingUtils::CheckIfInEditorAndPIE())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InternalEditorMeshLibrary::IsUVChannelValid(StaticMesh, LODIndex, UVChannelIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FMeshDescription* MeshDescription = StaticMesh->GetOriginalMeshDescription(LODIndex);
|
||||
|
||||
TArray<FVector2D> TexCoords;
|
||||
FMeshDescriptionOperations::GenerateCylindricalUV(*MeshDescription, UVSettings, TexCoords);
|
||||
|
||||
return StaticMesh->SetUVChannel(LODIndex, UVChannelIndex, TexCoords);
|
||||
}
|
||||
|
||||
bool UEditorStaticMeshLibrary::GenerateBoxUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex, const FUVMapSettings& UVSettings)
|
||||
{
|
||||
TGuardValue<bool> UnattendedScriptGuard(GIsRunningUnattendedScript, true);
|
||||
|
||||
if (!EditorScriptingUtils::CheckIfInEditorAndPIE())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InternalEditorMeshLibrary::IsUVChannelValid(StaticMesh, LODIndex, UVChannelIndex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FMeshDescription* MeshDescription = StaticMesh->GetOriginalMeshDescription(LODIndex);
|
||||
|
||||
TArray<FVector2D> TexCoords;
|
||||
FMeshDescriptionOperations::GenerateBoxUV(*MeshDescription, UVSettings, TexCoords);
|
||||
|
||||
return StaticMesh->SetUVChannel(LODIndex, UVChannelIndex, TexCoords);
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
+35
-4
@@ -12,7 +12,7 @@
|
||||
USTRUCT(BlueprintType)
|
||||
struct FEditorScriptingJoinStaticMeshActorsOptions
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
GENERATED_BODY()
|
||||
|
||||
FEditorScriptingJoinStaticMeshActorsOptions()
|
||||
: bDestroySourceActors(true)
|
||||
@@ -35,7 +35,7 @@ struct FEditorScriptingJoinStaticMeshActorsOptions
|
||||
USTRUCT(BlueprintType)
|
||||
struct FEditorScriptingMergeStaticMeshActorsOptions : public FEditorScriptingJoinStaticMeshActorsOptions
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
GENERATED_BODY()
|
||||
|
||||
FEditorScriptingMergeStaticMeshActorsOptions()
|
||||
: bSpawnMergedActor(true)
|
||||
@@ -53,6 +53,27 @@ struct FEditorScriptingMergeStaticMeshActorsOptions : public FEditorScriptingJoi
|
||||
FMeshMergingSettings MeshMergingSettings;
|
||||
};
|
||||
|
||||
USTRUCT(BlueprintType)
|
||||
struct FEditorScriptingCreateProxyMeshActorOptions : public FEditorScriptingJoinStaticMeshActorsOptions
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
FEditorScriptingCreateProxyMeshActorOptions()
|
||||
: bSpawnMergedActor(true)
|
||||
{ }
|
||||
|
||||
// Spawn the new merged actors
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Options)
|
||||
bool bSpawnMergedActor;
|
||||
|
||||
// The package path you want to save to. ie: /Game/MyFolder
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Options)
|
||||
FString BasePackageName;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Options)
|
||||
FMeshProxySettings MeshProxySettings;
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility class to do most of the common functionalities in the World Editor.
|
||||
* The editor should not be in play in editor mode.
|
||||
@@ -243,12 +264,22 @@ public:
|
||||
* Merge the meshes into a unique mesh with the provided StaticMeshActors. There are multiple options on how to merge the meshes and their materials.
|
||||
* The ActorsToMerge need to be in the same Level.
|
||||
* This may have a high impact on performance depending of the MeshMergingSettings options.
|
||||
* @param ActorsToMerge List of Actors to join.
|
||||
* @param MergeOptions Options on how to join the actors.
|
||||
* @param ActorsToMerge List of Actors to merge.
|
||||
* @param MergeOptions Options on how to merge the actors.
|
||||
* @param OutMergedActor The new created actor, if requested.
|
||||
* @return if the operation is successful.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataPrep")
|
||||
static bool MergeStaticMeshActors(const TArray<class AStaticMeshActor*>& ActorsToMerge, const FEditorScriptingMergeStaticMeshActorsOptions& MergeOptions, class AStaticMeshActor*& OutMergedActor);
|
||||
|
||||
/**
|
||||
* Build a proxy mesh actor that can replace a set of mesh actors.
|
||||
* @param ActorsToMerge List of actors to build a proxy for.
|
||||
* @param MergeOptions
|
||||
* @param OutMergedActor generated actor if requested
|
||||
* @return Success of the proxy creation
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | DataPrep")
|
||||
static bool CreateProxyMeshActor(const TArray<class AStaticMeshActor*>& ActorsToMerge, const FEditorScriptingCreateProxyMeshActorOptions& MergeOptions, class AStaticMeshActor*& OutMergedActor);
|
||||
};
|
||||
|
||||
|
||||
+46
-1
@@ -9,6 +9,7 @@
|
||||
#include "Engine/MeshMerging.h"
|
||||
#include "GameFramework/Actor.h"
|
||||
#include "PhysicsEngine/BodySetupEnums.h"
|
||||
#include "UVMapSettings.h"
|
||||
|
||||
#include "EditorStaticMeshLibrary.generated.h"
|
||||
|
||||
@@ -90,6 +91,18 @@ public:
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | StaticMesh")
|
||||
static int32 SetLods(UStaticMesh* StaticMesh, const FEditorScriptingMeshReductionOptions& ReductionOptions);
|
||||
|
||||
/**
|
||||
* Adds or create a LOD at DestinationLodIndex using the geometry from SourceStaticMesh SourceLodIndex
|
||||
* @param DestinationStaticMesh The static mesh to set the LOD in.
|
||||
* @param DestinationLodIndex The index of the LOD to set.
|
||||
* @param SourceStaticMesh The static mesh to get the LOD from.
|
||||
* @param SourceLodIndex The index of the LOD to get.
|
||||
* @return The index of the LOD that was set. It can be different than DestinationLodIndex if it wasn't a valid index.
|
||||
* A negative value indicates that the LOD was not set. See log for explanation.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | StaticMesh")
|
||||
static int32 SetLodFromStaticMesh(UStaticMesh* DestinationStaticMesh, int32 DestinationLodIndex, UStaticMesh* SourceStaticMesh, int32 SourceLodIndex);
|
||||
|
||||
/**
|
||||
* Get number of LODs present on a static mesh.
|
||||
* @param StaticMesh Mesh to process.
|
||||
@@ -266,5 +279,37 @@ public:
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | StaticMesh")
|
||||
static bool RemoveUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates planar UV mapping in the specified UV channel on the given LOD of a StaticMesh.
|
||||
* @param StaticMesh Static mesh on which to generate the UV mapping.
|
||||
* @param LODIndex Index of the StaticMesh LOD.
|
||||
* @param UVChannelIndex Channel where to save the UV mapping.
|
||||
* @param UVSettings The settings to use to generate the UV mapping.
|
||||
* @return true if the UV mapping was generated.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | StaticMesh")
|
||||
static bool GeneratePlanarUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex, const FUVMapSettings& UVSettings);
|
||||
|
||||
/**
|
||||
* Generates cylindrical UV mapping in the specified UV channel on the given LOD of a StaticMesh.
|
||||
* @param StaticMesh Static mesh on which to generate the UV mapping.
|
||||
* @param LODIndex Index of the StaticMesh LOD.
|
||||
* @param UVChannelIndex Channel where to save the UV mapping.
|
||||
* @param UVSettings The settings to use to generate the UV mapping.
|
||||
* @return true if the UV mapping was generated.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | StaticMesh")
|
||||
static bool GenerateCylindricalUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex, const FUVMapSettings& UVSettings);
|
||||
|
||||
/**
|
||||
* Generates box UV mapping in the specified UV channel on the given LOD of a StaticMesh.
|
||||
* @param StaticMesh Static mesh on which to generate the UV mapping.
|
||||
* @param LODIndex Index of the StaticMesh LOD.
|
||||
* @param UVChannelIndex Channel where to save the UV mapping.
|
||||
* @param UVSettings The settings to use to generate the UV mapping.
|
||||
* @return true if the UV mapping was generated.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "Editor Scripting | StaticMesh")
|
||||
static bool GenerateBoxUVChannel(UStaticMesh* StaticMesh, int32 LODIndex, int32 UVChannelIndex, const FUVMapSettings& UVSettings);
|
||||
};
|
||||
|
||||
+7
@@ -16,6 +16,13 @@ namespace UnrealBuildTool.Rules
|
||||
"Engine"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Landscape"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
|
||||
|
||||
#include "ObjectTemplates/DatasmithLandscapeTemplate.h"
|
||||
|
||||
#include "Landscape.h"
|
||||
|
||||
void UDatasmithLandscapeTemplate::Apply( UObject* Destination, bool bForce )
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
ALandscape* Landscape = Cast< ALandscape >( Destination );
|
||||
|
||||
if( !Landscape )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UDatasmithLandscapeTemplate* PreviousTemplate = !bForce ? FDatasmithObjectTemplateUtils::GetObjectTemplate< UDatasmithLandscapeTemplate >( Destination ) : nullptr;
|
||||
|
||||
DATASMITHOBJECTTEMPLATE_CONDITIONALSET(LandscapeMaterial, Landscape, PreviousTemplate);
|
||||
DATASMITHOBJECTTEMPLATE_CONDITIONALSET(StaticLightingLOD, Landscape, PreviousTemplate);
|
||||
|
||||
FDatasmithObjectTemplateUtils::SetObjectTemplate( Landscape->GetRootComponent(), this );
|
||||
#endif // #if WITH_EDITORONLY_DATA
|
||||
}
|
||||
|
||||
void UDatasmithLandscapeTemplate::Load( const UObject* Source )
|
||||
{
|
||||
#if WITH_EDITORONLY_DATA
|
||||
const ALandscape* Landscape = Cast< ALandscape >( Source );
|
||||
|
||||
if( !Landscape )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LandscapeMaterial = Landscape->LandscapeMaterial;
|
||||
StaticLightingLOD = Landscape->StaticLightingLOD;
|
||||
#endif // #if WITH_EDITORONLY_DATA
|
||||
}
|
||||
|
||||
bool UDatasmithLandscapeTemplate::Equals( const UDatasmithObjectTemplate* Other ) const
|
||||
{
|
||||
const UDatasmithLandscapeTemplate* TypedOther = Cast< UDatasmithLandscapeTemplate >( Other );
|
||||
|
||||
if ( !TypedOther )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bEquals = ( LandscapeMaterial == TypedOther->LandscapeMaterial );
|
||||
bEquals = bEquals && ( StaticLightingLOD == TypedOther->StaticLightingLOD );
|
||||
|
||||
return bEquals;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user