You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
#rb trivial #rnx #jira none #preflight 616e0e96e0de460001f0c86a #ROBOMERGE-AUTHOR: lonnie.li #ROBOMERGE-SOURCE: CL 17854840 in //UE5/Release-5.0/... via CL 17854854 #ROBOMERGE-BOT: STARSHIP (Release-Engine-Staging -> Release-Engine-Test) (v883-17842818) #ROBOMERGE[STARSHIP]: UE5-Main [CL 17854858 by lonnie li in ue5-release-engine-test branch]
1004 lines
36 KiB
C++
1004 lines
36 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "BakeMeshAttributeMapsTool.h"
|
|
#include "InteractiveToolManager.h"
|
|
#include "ToolBuilderUtil.h"
|
|
|
|
#include "DynamicMesh/DynamicMesh3.h"
|
|
#include "DynamicMesh/DynamicMeshAttributeSet.h"
|
|
#include "DynamicMesh/MeshTransforms.h"
|
|
#include "MeshDescriptionToDynamicMesh.h"
|
|
#include "Sampling/MeshNormalMapEvaluator.h"
|
|
#include "Sampling/MeshOcclusionMapEvaluator.h"
|
|
#include "Sampling/MeshCurvatureMapEvaluator.h"
|
|
#include "Sampling/MeshPropertyMapEvaluator.h"
|
|
#include "Sampling/MeshResampleImageEvaluator.h"
|
|
|
|
#include "ImageUtils.h"
|
|
|
|
#include "AssetUtils/Texture2DUtil.h"
|
|
#include "ModelingObjectsCreationAPI.h"
|
|
|
|
#include "TargetInterfaces/MaterialProvider.h"
|
|
#include "TargetInterfaces/MeshDescriptionProvider.h"
|
|
#include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
|
|
#include "TargetInterfaces/StaticMeshBackedTarget.h"
|
|
#include "TargetInterfaces/SkeletalMeshBackedTarget.h"
|
|
#include "ToolTargetManager.h"
|
|
#include "ModelingToolTargetUtil.h"
|
|
|
|
// required to pass UStaticMesh asset so we can save at same location
|
|
#include "Engine/StaticMesh.h"
|
|
#include "Engine/SkeletalMesh.h"
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Components/SkeletalMeshComponent.h"
|
|
#include "Components/DynamicMeshComponent.h"
|
|
|
|
#include "ExplicitUseGeometryMathTypes.h" // using UE::Geometry::(math types)
|
|
using namespace UE::Geometry;
|
|
|
|
#define LOCTEXT_NAMESPACE "UBakeMeshAttributeMapsTool"
|
|
|
|
|
|
/*
|
|
* ToolBuilder
|
|
*/
|
|
|
|
const FToolTargetTypeRequirements& UBakeMeshAttributeMapsToolBuilder::GetTargetRequirements() const
|
|
{
|
|
static FToolTargetTypeRequirements TypeRequirements({
|
|
UMeshDescriptionProvider::StaticClass(),
|
|
UPrimitiveComponentBackedTarget::StaticClass(),
|
|
UMaterialProvider::StaticClass()
|
|
});
|
|
return TypeRequirements;
|
|
}
|
|
|
|
bool UBakeMeshAttributeMapsToolBuilder::CanBuildTool(const FToolBuilderState& SceneState) const
|
|
{
|
|
const int32 NumTargets = SceneState.TargetManager->CountSelectedAndTargetable(SceneState, GetTargetRequirements());
|
|
if (NumTargets == 1 || NumTargets == 2)
|
|
{
|
|
bool bValidTargets = true;
|
|
SceneState.TargetManager->EnumerateSelectedAndTargetableComponents(SceneState, GetTargetRequirements(),
|
|
[&bValidTargets](UActorComponent* Component)
|
|
{
|
|
UStaticMeshComponent* StaticMesh = Cast<UStaticMeshComponent>(Component);
|
|
USkeletalMeshComponent* SkeletalMesh = Cast<USkeletalMeshComponent>(Component);
|
|
UDynamicMeshComponent* DynMesh = Cast<UDynamicMeshComponent>(Component);
|
|
bValidTargets = bValidTargets && (StaticMesh || SkeletalMesh || DynMesh);
|
|
});
|
|
return bValidTargets;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
UInteractiveTool* UBakeMeshAttributeMapsToolBuilder::BuildTool(const FToolBuilderState& SceneState) const
|
|
{
|
|
UBakeMeshAttributeMapsTool* NewTool = NewObject<UBakeMeshAttributeMapsTool>(SceneState.ToolManager);
|
|
|
|
TArray<TObjectPtr<UToolTarget>> Targets = SceneState.TargetManager->BuildAllSelectedTargetable(SceneState, GetTargetRequirements());
|
|
NewTool->SetTargets(MoveTemp(Targets));
|
|
NewTool->SetWorld(SceneState.World);
|
|
return NewTool;
|
|
}
|
|
|
|
|
|
|
|
|
|
TArray<FString> UBakeMeshAttributeMapsToolProperties::GetUVLayerNamesFunc()
|
|
{
|
|
return UVLayerNamesList;
|
|
}
|
|
|
|
|
|
/*
|
|
* Operators
|
|
*/
|
|
|
|
class FMeshMapBakerOp : public TGenericDataOperator<FMeshMapBaker>
|
|
{
|
|
public:
|
|
using ImagePtr = TSharedPtr<UE::Geometry::TImageBuilder<FVector4f>, ESPMode::ThreadSafe>;
|
|
|
|
// General bake settings
|
|
TSharedPtr<UE::Geometry::FDynamicMesh3, ESPMode::ThreadSafe> DetailMesh;
|
|
TSharedPtr<UE::Geometry::FDynamicMeshAABBTree3, ESPMode::ThreadSafe> DetailSpatial;
|
|
TSharedPtr<UE::Geometry::TMeshTangents<double>, ESPMode::ThreadSafe> DetailMeshTangents;
|
|
UE::Geometry::FDynamicMesh3* BaseMesh;
|
|
TUniquePtr<UE::Geometry::FMeshMapBaker> Baker;
|
|
UBakeMeshAttributeMapsTool::FBakeCacheSettings BakeCacheSettings;
|
|
TSharedPtr<UE::Geometry::TMeshTangents<double>, ESPMode::ThreadSafe> BaseMeshTangents;
|
|
|
|
// Map Type settings
|
|
EBakeMapType Maps;
|
|
FNormalMapSettings NormalSettings;
|
|
FOcclusionMapSettings OcclusionSettings;
|
|
FCurvatureMapSettings CurvatureSettings;
|
|
FMeshPropertyMapSettings PropertySettings;
|
|
FTexture2DImageSettings TextureSettings;
|
|
|
|
// NormalMap settings
|
|
ImagePtr DetailMeshNormalMap;
|
|
int32 DetailMeshNormalUVLayer = 0;
|
|
|
|
// Texture2DImage & MultiTexture settings
|
|
ImagePtr TextureImage;
|
|
TMap<int32, ImagePtr> MaterialToTextureImageMap;
|
|
|
|
// Begin TGenericDataOperator interface
|
|
virtual void CalculateResult(FProgressCancel* Progress) override
|
|
{
|
|
Baker = MakeUnique<FMeshMapBaker>();
|
|
Baker->CancelF = [Progress]() {
|
|
return Progress && Progress->Cancelled();
|
|
};
|
|
Baker->SetTargetMesh(BaseMesh);
|
|
Baker->SetDimensions(BakeCacheSettings.Dimensions);
|
|
Baker->SetUVLayer(BakeCacheSettings.UVLayer);
|
|
Baker->SetThickness(BakeCacheSettings.Thickness);
|
|
Baker->SetMultisampling(BakeCacheSettings.Multisampling);
|
|
Baker->SetTargetMeshTangents(BaseMeshTangents);
|
|
|
|
FMeshBakerDynamicMeshSampler DetailSampler(DetailMesh.Get(), DetailSpatial.Get(), DetailMeshTangents.Get());
|
|
Baker->SetDetailSampler(&DetailSampler);
|
|
|
|
for (const EBakeMapType MapType : ALL_BAKE_MAP_TYPES)
|
|
{
|
|
switch (BakeCacheSettings.BakeMapTypes & MapType)
|
|
{
|
|
case EBakeMapType::TangentSpaceNormalMap:
|
|
{
|
|
TSharedPtr<FMeshNormalMapEvaluator, ESPMode::ThreadSafe> NormalEval = MakeShared<FMeshNormalMapEvaluator, ESPMode::ThreadSafe>();
|
|
DetailSampler.SetNormalMap(DetailMesh.Get(), IMeshBakerDetailSampler::FBakeDetailTexture(DetailMeshNormalMap.Get(), DetailMeshNormalUVLayer));
|
|
Baker->AddEvaluator(NormalEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::AmbientOcclusion:
|
|
case EBakeMapType::BentNormal:
|
|
case EBakeMapType::Occlusion:
|
|
{
|
|
TSharedPtr<FMeshOcclusionMapEvaluator> OcclusionEval = MakeShared<FMeshOcclusionMapEvaluator>();
|
|
OcclusionEval->OcclusionType = EMeshOcclusionMapType::None;
|
|
if ((bool)(BakeCacheSettings.BakeMapTypes & EBakeMapType::AmbientOcclusion))
|
|
{
|
|
OcclusionEval->OcclusionType |= EMeshOcclusionMapType::AmbientOcclusion;
|
|
}
|
|
if ((bool)(BakeCacheSettings.BakeMapTypes & EBakeMapType::BentNormal))
|
|
{
|
|
OcclusionEval->OcclusionType |= EMeshOcclusionMapType::BentNormal;
|
|
}
|
|
OcclusionEval->NumOcclusionRays = OcclusionSettings.OcclusionRays;
|
|
OcclusionEval->MaxDistance = OcclusionSettings.MaxDistance;
|
|
OcclusionEval->SpreadAngle = OcclusionSettings.SpreadAngle;
|
|
OcclusionEval->BiasAngleDeg = OcclusionSettings.BiasAngle;
|
|
|
|
Baker->AddEvaluator(OcclusionEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::Curvature:
|
|
{
|
|
TSharedPtr<FMeshCurvatureMapEvaluator, ESPMode::ThreadSafe> CurvatureEval = MakeShared<FMeshCurvatureMapEvaluator, ESPMode::ThreadSafe>();
|
|
CurvatureEval->RangeScale = FMathd::Clamp(CurvatureSettings.RangeMultiplier, 0.0001, 1000.0);
|
|
CurvatureEval->MinRangeScale = FMathd::Clamp(CurvatureSettings.MinRangeMultiplier, 0.0, 1.0);
|
|
CurvatureEval->UseCurvatureType = (FMeshCurvatureMapEvaluator::ECurvatureType)CurvatureSettings.CurvatureType;
|
|
CurvatureEval->UseColorMode = (FMeshCurvatureMapEvaluator::EColorMode)CurvatureSettings.ColorMode;
|
|
CurvatureEval->UseClampMode = (FMeshCurvatureMapEvaluator::EClampMode)CurvatureSettings.ClampMode;
|
|
Baker->AddEvaluator(CurvatureEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::NormalImage:
|
|
{
|
|
TSharedPtr<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe> PropertyEval = MakeShared<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe>();
|
|
PropertyEval->Property = EMeshPropertyMapType::Normal;
|
|
DetailSampler.SetNormalMap(DetailMesh.Get(), IMeshBakerDetailSampler::FBakeDetailTexture(DetailMeshNormalMap.Get(), DetailMeshNormalUVLayer));
|
|
Baker->AddEvaluator(PropertyEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::FaceNormalImage:
|
|
{
|
|
TSharedPtr<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe> PropertyEval = MakeShared<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe>();
|
|
PropertyEval->Property = EMeshPropertyMapType::FacetNormal;
|
|
Baker->AddEvaluator(PropertyEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::PositionImage:
|
|
{
|
|
TSharedPtr<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe> PropertyEval = MakeShared<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe>();
|
|
PropertyEval->Property = EMeshPropertyMapType::Position;
|
|
Baker->AddEvaluator(PropertyEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::MaterialID:
|
|
{
|
|
TSharedPtr<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe> PropertyEval = MakeShared<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe>();
|
|
PropertyEval->Property = EMeshPropertyMapType::MaterialID;
|
|
Baker->AddEvaluator(PropertyEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::VertexColorImage:
|
|
{
|
|
TSharedPtr<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe> PropertyEval = MakeShared<FMeshPropertyMapEvaluator, ESPMode::ThreadSafe>();
|
|
PropertyEval->Property = EMeshPropertyMapType::VertexColor;
|
|
Baker->AddEvaluator(PropertyEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::Texture2DImage:
|
|
{
|
|
TSharedPtr<FMeshResampleImageEvaluator, ESPMode::ThreadSafe> TextureEval = MakeShared<FMeshResampleImageEvaluator, ESPMode::ThreadSafe>();
|
|
DetailSampler.SetColorMap(DetailMesh.Get(), IMeshBakerDetailSampler::FBakeDetailTexture(TextureImage.Get(), TextureSettings.UVLayer));
|
|
Baker->AddEvaluator(TextureEval);
|
|
break;
|
|
}
|
|
case EBakeMapType::MultiTexture:
|
|
{
|
|
TSharedPtr<FMeshMultiResampleImageEvaluator, ESPMode::ThreadSafe> TextureEval = MakeShared<FMeshMultiResampleImageEvaluator, ESPMode::ThreadSafe>();
|
|
TextureEval->DetailUVLayer = TextureSettings.UVLayer;
|
|
TextureEval->MultiTextures = MaterialToTextureImageMap;
|
|
Baker->AddEvaluator(TextureEval);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
Baker->Bake();
|
|
SetResult(MoveTemp(Baker));
|
|
}
|
|
// End TGenericDataOperator interface
|
|
};
|
|
|
|
/*
|
|
* Tool
|
|
*/
|
|
|
|
void UBakeMeshAttributeMapsTool::Setup()
|
|
{
|
|
Super::Setup();
|
|
|
|
// Initialize preview mesh
|
|
bIsBakeToSelf = (Targets.Num() == 1);
|
|
|
|
PreviewMesh->ProcessMesh([this](const FDynamicMesh3& Mesh)
|
|
{
|
|
BaseMesh.Copy(Mesh);
|
|
BaseSpatial.SetMesh(&BaseMesh, true);
|
|
BaseMeshTangents = MakeShared<FMeshTangentsd, ESPMode::ThreadSafe>(&BaseMesh);
|
|
BaseMeshTangents->CopyTriVertexTangents(Mesh);
|
|
});
|
|
|
|
// Setup tool property sets
|
|
Settings = NewObject<UBakeMeshAttributeMapsToolProperties>(this);
|
|
Settings->RestoreProperties(this);
|
|
Settings->UVLayerNamesList.Reset();
|
|
int32 FoundIndex = -1;
|
|
for (int32 k = 0; k < BaseMesh.Attributes()->NumUVLayers(); ++k)
|
|
{
|
|
Settings->UVLayerNamesList.Add(FString::FromInt(k));
|
|
if (Settings->UVLayer == Settings->UVLayerNamesList.Last())
|
|
{
|
|
FoundIndex = k;
|
|
}
|
|
}
|
|
if (FoundIndex == -1)
|
|
{
|
|
Settings->UVLayer = Settings->UVLayerNamesList[0];
|
|
}
|
|
AddToolPropertySource(Settings);
|
|
|
|
Settings->WatchProperty(Settings->MapTypes, [this](int32) { bInputsDirty = true; UpdateOnModeChange(); });
|
|
Settings->WatchProperty(Settings->MapPreview, [this](int32) { UpdateVisualization(); GetToolManager()->PostInvalidation(); });
|
|
Settings->WatchProperty(Settings->Resolution, [this](EBakeTextureResolution) { bInputsDirty = true; });
|
|
Settings->WatchProperty(Settings->SourceFormat, [this](EBakeTextureFormat) { bInputsDirty = true; });
|
|
Settings->WatchProperty(Settings->UVLayer, [this](FString) { bInputsDirty = true; });
|
|
Settings->WatchProperty(Settings->bUseWorldSpace, [this](bool) { bDetailMeshValid = false; bInputsDirty = true; });
|
|
Settings->WatchProperty(Settings->Thickness, [this](float) { bInputsDirty = true; });
|
|
Settings->WatchProperty(Settings->Multisampling, [this](EBakeMultisampling) { bInputsDirty = true; });
|
|
|
|
|
|
UToolTarget* DetailTarget = Targets[bIsBakeToSelf ? 0 : 1];
|
|
IStaticMeshBackedTarget* DetailStaticMeshTarget = Cast<IStaticMeshBackedTarget>(DetailTarget);
|
|
UStaticMesh* DetailStaticMesh = DetailStaticMeshTarget ? DetailStaticMeshTarget->GetStaticMesh() : nullptr;
|
|
ISkeletalMeshBackedTarget* DetailSkeletalMeshTarget = Cast<ISkeletalMeshBackedTarget>(DetailTarget);
|
|
USkeletalMesh* DetailSkeletalMesh = DetailSkeletalMeshTarget ? DetailSkeletalMeshTarget->GetSkeletalMesh() : nullptr;
|
|
|
|
DetailMeshProps = NewObject<UDetailMeshToolProperties>(this);
|
|
AddToolPropertySource(DetailMeshProps);
|
|
SetToolPropertySourceEnabled(DetailMeshProps, true);
|
|
DetailMeshProps->DetailStaticMesh = DetailStaticMesh;
|
|
DetailMeshProps->DetailSkeletalMesh = DetailSkeletalMesh;
|
|
DetailMeshProps->DetailMeshNormalMap = nullptr;
|
|
DetailMeshProps->WatchProperty(DetailMeshProps->DetailNormalUVLayer, [this](int) { bInputsDirty = true; });
|
|
DetailMeshProps->WatchProperty(DetailMeshProps->DetailMeshNormalMap, [this](UTexture2D*)
|
|
{
|
|
// Only invalidate detail mesh if we need to recompute tangents.
|
|
if (!DetailMeshTangents)
|
|
{
|
|
bDetailMeshValid = false;
|
|
}
|
|
bInputsDirty = true;
|
|
});
|
|
|
|
|
|
NormalMapProps = NewObject<UBakedNormalMapToolProperties>(this);
|
|
NormalMapProps->RestoreProperties(this);
|
|
AddToolPropertySource(NormalMapProps);
|
|
SetToolPropertySourceEnabled(NormalMapProps, false);
|
|
|
|
|
|
OcclusionMapProps = NewObject<UBakedOcclusionMapToolProperties>(this);
|
|
OcclusionMapProps->RestoreProperties(this);
|
|
AddToolPropertySource(OcclusionMapProps);
|
|
SetToolPropertySourceEnabled(OcclusionMapProps, false);
|
|
OcclusionMapProps->WatchProperty(OcclusionMapProps->OcclusionRays, [this](int32) { bInputsDirty = true; });
|
|
OcclusionMapProps->WatchProperty(OcclusionMapProps->MaxDistance, [this](float) { bInputsDirty = true; });
|
|
OcclusionMapProps->WatchProperty(OcclusionMapProps->SpreadAngle, [this](float) { bInputsDirty = true; });
|
|
OcclusionMapProps->WatchProperty(OcclusionMapProps->BiasAngle, [this](float) { bInputsDirty = true; });
|
|
|
|
|
|
CurvatureMapProps = NewObject<UBakedCurvatureMapToolProperties>(this);
|
|
CurvatureMapProps->RestoreProperties(this);
|
|
AddToolPropertySource(CurvatureMapProps);
|
|
SetToolPropertySourceEnabled(CurvatureMapProps, false);
|
|
CurvatureMapProps->WatchProperty(CurvatureMapProps->RangeMultiplier, [this](float) { bInputsDirty = true; });
|
|
CurvatureMapProps->WatchProperty(CurvatureMapProps->MinRangeMultiplier, [this](float) { bInputsDirty = true; });
|
|
CurvatureMapProps->WatchProperty(CurvatureMapProps->CurvatureType, [this](EBakedCurvatureTypeMode) { bInputsDirty = true; });
|
|
CurvatureMapProps->WatchProperty(CurvatureMapProps->ColorMode, [this](EBakedCurvatureColorMode) { bInputsDirty = true; });
|
|
CurvatureMapProps->WatchProperty(CurvatureMapProps->Clamping, [this](EBakedCurvatureClampMode) { bInputsDirty = true; });
|
|
|
|
|
|
Texture2DProps = NewObject<UBakedTexture2DImageProperties>(this);
|
|
Texture2DProps->RestoreProperties(this);
|
|
AddToolPropertySource(Texture2DProps);
|
|
SetToolPropertySourceEnabled(Texture2DProps, false);
|
|
Texture2DProps->WatchProperty(Texture2DProps->UVLayer, [this](float) { bInputsDirty = true; });
|
|
Texture2DProps->WatchProperty(Texture2DProps->SourceTexture, [this](UTexture2D*) { bInputsDirty = true; });
|
|
|
|
MultiTextureProps = NewObject<UBakedMultiTexture2DImageProperties>(this);
|
|
MultiTextureProps->RestoreProperties(this);
|
|
AddToolPropertySource(MultiTextureProps);
|
|
SetToolPropertySourceEnabled(MultiTextureProps, false);
|
|
|
|
auto SetDirtyCallback = [this](decltype(MultiTextureProps->MaterialIDSourceTextureMap)) { bInputsDirty = true; };
|
|
auto NotEqualsCallback = [](const decltype(MultiTextureProps->MaterialIDSourceTextureMap)& A, const decltype(MultiTextureProps->MaterialIDSourceTextureMap)& B) -> bool { return !(A.OrderIndependentCompareEqual(B)); };
|
|
MultiTextureProps->WatchProperty(MultiTextureProps->MaterialIDSourceTextureMap, SetDirtyCallback, NotEqualsCallback);
|
|
MultiTextureProps->WatchProperty(MultiTextureProps->UVLayer, [this](float) { bInputsDirty = true; });
|
|
|
|
UpdateOnModeChange();
|
|
|
|
bInputsDirty = true;
|
|
bDetailMeshValid = false;
|
|
|
|
SetToolDisplayName(LOCTEXT("ToolName", "Bake Textures"));
|
|
GetToolManager()->DisplayMessage(
|
|
LOCTEXT("OnStartTool", "Bake Maps. Select Bake Mesh (LowPoly) first, then (optionally) Detail Mesh second. Texture Assets will be created on Accept. "),
|
|
EToolMessageLevel::UserNotification);
|
|
|
|
SetupBaseToolProperties();
|
|
}
|
|
|
|
|
|
|
|
|
|
bool UBakeMeshAttributeMapsTool::CanAccept() const
|
|
{
|
|
bool bCanAccept = Compute ? Compute->HaveValidResult() && Settings->MapTypes != (int) EBakeMapType::None : false;
|
|
if (bCanAccept)
|
|
{
|
|
// Allow Accept if all non-None types have valid results.
|
|
const int NumResults = Settings->Result.Num();
|
|
for (int ResultIdx = 0; ResultIdx < NumResults; ++ResultIdx)
|
|
{
|
|
bCanAccept = bCanAccept && Settings->Result[ResultIdx];
|
|
}
|
|
}
|
|
return bCanAccept;
|
|
}
|
|
|
|
|
|
TUniquePtr<UE::Geometry::TGenericDataOperator<FMeshMapBaker>> UBakeMeshAttributeMapsTool::MakeNewOperator()
|
|
{
|
|
TUniquePtr<FMeshMapBakerOp> Op = MakeUnique<FMeshMapBakerOp>();
|
|
Op->DetailMesh = DetailMesh;
|
|
Op->DetailSpatial = DetailSpatial;
|
|
Op->BaseMesh = &BaseMesh;
|
|
Op->BakeCacheSettings = CachedBakeCacheSettings;
|
|
|
|
constexpr EBakeMapType RequiresTangents = EBakeMapType::TangentSpaceNormalMap | EBakeMapType::BentNormal;
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & RequiresTangents))
|
|
{
|
|
Op->BaseMeshTangents = BaseMeshTangents;
|
|
}
|
|
|
|
if (CachedDetailNormalMap)
|
|
{
|
|
Op->DetailMeshTangents = DetailMeshTangents;
|
|
Op->DetailMeshNormalMap = CachedDetailNormalMap;
|
|
Op->DetailMeshNormalUVLayer = CachedDetailMeshSettings.UVLayer;
|
|
}
|
|
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::TangentSpaceNormalMap))
|
|
{
|
|
Op->NormalSettings = CachedNormalMapSettings;
|
|
}
|
|
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::AmbientOcclusion) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::BentNormal))
|
|
{
|
|
Op->OcclusionSettings = CachedOcclusionMapSettings;
|
|
}
|
|
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::Curvature))
|
|
{
|
|
Op->CurvatureSettings = CachedCurvatureMapSettings;
|
|
}
|
|
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::NormalImage) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::FaceNormalImage) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::PositionImage) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::MaterialID) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::VertexColorImage))
|
|
{
|
|
Op->PropertySettings = CachedMeshPropertyMapSettings;
|
|
}
|
|
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::Texture2DImage))
|
|
{
|
|
Op->TextureSettings = CachedTexture2DImageSettings;
|
|
Op->TextureImage = CachedTextureImage;
|
|
}
|
|
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::MultiTexture))
|
|
{
|
|
Op->TextureSettings = CachedTexture2DImageSettings;
|
|
Op->MaterialToTextureImageMap = CachedMultiTextures;
|
|
}
|
|
|
|
return Op;
|
|
}
|
|
|
|
|
|
void UBakeMeshAttributeMapsTool::Shutdown(EToolShutdownType ShutdownType)
|
|
{
|
|
Super::Shutdown(ShutdownType);
|
|
|
|
Settings->SaveProperties(this);
|
|
OcclusionMapProps->SaveProperties(this);
|
|
NormalMapProps->SaveProperties(this);
|
|
CurvatureMapProps->SaveProperties(this);
|
|
Texture2DProps->SaveProperties(this);
|
|
MultiTextureProps->SaveProperties(this);
|
|
|
|
if (Compute)
|
|
{
|
|
Compute->Shutdown();
|
|
}
|
|
if (ShutdownType == EToolShutdownType::Accept)
|
|
{
|
|
// Check if we have a source asset to identify a location to store the texture assets.
|
|
IStaticMeshBackedTarget* StaticMeshTarget = Cast<IStaticMeshBackedTarget>(Targets[0]);
|
|
UObject* SourceAsset = StaticMeshTarget ? StaticMeshTarget->GetStaticMesh() : nullptr;
|
|
if (!SourceAsset)
|
|
{
|
|
// Check if our target is a Skeletal Mesh Asset
|
|
ISkeletalMeshBackedTarget* SkeletalMeshTarget = Cast<ISkeletalMeshBackedTarget>(Targets[0]);
|
|
SourceAsset = SkeletalMeshTarget ? SkeletalMeshTarget->GetSkeletalMesh() : nullptr;
|
|
}
|
|
const UPrimitiveComponent* SourceComponent = UE::ToolTarget::GetTargetComponent(Targets[0]);
|
|
const FString BaseName = UE::ToolTarget::GetTargetActor(Targets[0])->GetName();
|
|
|
|
bool bCreatedAssetOK = true;
|
|
const int NumResults = Settings->Result.Num();
|
|
for (int ResultIdx = 0; ResultIdx < NumResults; ++ResultIdx)
|
|
{
|
|
FString TexName;
|
|
GetTextureName(ResultTypes[ResultIdx], BaseName, TexName);
|
|
bCreatedAssetOK = bCreatedAssetOK && UE::Modeling::CreateTextureObject(GetToolManager(), FCreateTextureObjectParams{ 0, SourceComponent->GetWorld(), SourceAsset, TexName, Settings->Result[ResultIdx] }).IsOK();
|
|
}
|
|
ensure(bCreatedAssetOK);
|
|
}
|
|
}
|
|
|
|
|
|
void UBakeMeshAttributeMapsTool::UpdateDetailMesh()
|
|
{
|
|
UToolTarget* DetailTarget = Targets[bIsBakeToSelf ? 0 : 1];
|
|
|
|
const bool bWantMeshTangents = (DetailMeshProps->DetailMeshNormalMap != nullptr);
|
|
DetailMesh = MakeShared<FDynamicMesh3, ESPMode::ThreadSafe>(UE::ToolTarget::GetDynamicMeshCopy(DetailTarget, bWantMeshTangents));
|
|
|
|
if (Settings->bUseWorldSpace && bIsBakeToSelf == false)
|
|
{
|
|
using FTransform3d = UE::Geometry::FTransform3d;
|
|
const FTransform3d DetailToWorld = UE::ToolTarget::GetLocalToWorldTransform(DetailTarget);
|
|
MeshTransforms::ApplyTransform(*DetailMesh, DetailToWorld);
|
|
const FTransform3d WorldToBase = UE::ToolTarget::GetLocalToWorldTransform(Targets[0]);
|
|
MeshTransforms::ApplyTransform(*DetailMesh, WorldToBase.Inverse());
|
|
}
|
|
|
|
DetailSpatial = MakeShared<FDynamicMeshAABBTree3, ESPMode::ThreadSafe>();
|
|
DetailSpatial->SetMesh(DetailMesh.Get(), true);
|
|
|
|
// Extract tangents if a DetailMesh normal map was provided.
|
|
if (bWantMeshTangents)
|
|
{
|
|
DetailMeshTangents = MakeShared<FMeshTangentsd, ESPMode::ThreadSafe>(DetailMesh.Get());
|
|
DetailMeshTangents->CopyTriVertexTangents(*DetailMesh);
|
|
}
|
|
else
|
|
{
|
|
DetailMeshTangents = nullptr;
|
|
}
|
|
|
|
ProcessComponentTextures(UE::ToolTarget::GetTargetComponent(DetailTarget), [this](const int MaterialID, const TArray<UTexture*>& Textures)
|
|
{
|
|
for (UTexture* Tex : Textures)
|
|
{
|
|
UTexture2D* Tex2D = Cast<UTexture2D>(Tex);
|
|
if (Tex2D)
|
|
{
|
|
MultiTextureProps->AllSourceTextures.Add(Tex2D);
|
|
}
|
|
}
|
|
|
|
constexpr bool bGuessAtTextures = true;
|
|
if (bGuessAtTextures)
|
|
{
|
|
const int SelectedTextureIndex = SelectColorTextureToBake(Textures);
|
|
if (SelectedTextureIndex >= 0)
|
|
{
|
|
UTexture2D* Tex2D = Cast<UTexture2D>(Textures[SelectedTextureIndex]);
|
|
|
|
// if cast fails, this will set the value to nullptr, which is fine
|
|
MultiTextureProps->MaterialIDSourceTextureMap.Add(MaterialID, Tex2D);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MultiTextureProps->MaterialIDSourceTextureMap.Add(MaterialID, nullptr);
|
|
}
|
|
});
|
|
|
|
bInputsDirty = true;
|
|
DetailMeshTimestamp++;
|
|
}
|
|
|
|
|
|
void UBakeMeshAttributeMapsTool::UpdateResult()
|
|
{
|
|
if (bDetailMeshValid == false)
|
|
{
|
|
UpdateDetailMesh();
|
|
bDetailMeshValid = true;
|
|
CachedBakeCacheSettings = FBakeCacheSettings();
|
|
}
|
|
|
|
// bInputsDirty ensures that we only validate parameters once per param
|
|
// change. Parameter validation can be expensive (ex. UpdateResult_Texture2DImage).
|
|
if (!bInputsDirty)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// clear warning (ugh)
|
|
GetToolManager()->DisplayMessage(FText(), EToolMessageLevel::UserWarning);
|
|
|
|
int32 ImageSize = (int32)Settings->Resolution;
|
|
FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FBakeCacheSettings BakeCacheSettings;
|
|
BakeCacheSettings.Dimensions = Dimensions;
|
|
BakeCacheSettings.SourceFormat = Settings->SourceFormat;
|
|
BakeCacheSettings.UVLayer = FCString::Atoi(*Settings->UVLayer);
|
|
BakeCacheSettings.DetailTimestamp = this->DetailMeshTimestamp;
|
|
BakeCacheSettings.Thickness = Settings->Thickness;
|
|
BakeCacheSettings.Multisampling = (int32)Settings->Multisampling;
|
|
|
|
// process the raw bitfield before caching which may add additional targets.
|
|
BakeCacheSettings.BakeMapTypes = GetMapTypes(Settings->MapTypes);
|
|
|
|
// update bake cache settings
|
|
if (!(CachedBakeCacheSettings == BakeCacheSettings))
|
|
{
|
|
CachedBakeCacheSettings = BakeCacheSettings;
|
|
|
|
CachedNormalMapSettings = FNormalMapSettings();
|
|
CachedOcclusionMapSettings = FOcclusionMapSettings();
|
|
CachedCurvatureMapSettings = FCurvatureMapSettings();
|
|
CachedMeshPropertyMapSettings = FMeshPropertyMapSettings();
|
|
CachedTexture2DImageSettings = FTexture2DImageSettings();
|
|
}
|
|
|
|
// Clear our invalid bitflag to check again for valid inputs.
|
|
OpState = EBakeOpState::Evaluate;
|
|
|
|
OpState |= UpdateResult_DetailNormalMap();
|
|
|
|
// Update map type settings
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::TangentSpaceNormalMap))
|
|
{
|
|
OpState |= UpdateResult_Normal();
|
|
}
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::AmbientOcclusion) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::BentNormal))
|
|
{
|
|
OpState |= UpdateResult_Occlusion();
|
|
}
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::Curvature))
|
|
{
|
|
OpState |= UpdateResult_Curvature();
|
|
}
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::NormalImage) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::FaceNormalImage) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::PositionImage) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::MaterialID) ||
|
|
(bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::VertexColorImage))
|
|
{
|
|
OpState |= UpdateResult_MeshProperty();
|
|
}
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::Texture2DImage))
|
|
{
|
|
OpState |= UpdateResult_Texture2DImage();
|
|
}
|
|
if ((bool)(CachedBakeCacheSettings.BakeMapTypes & EBakeMapType::MultiTexture))
|
|
{
|
|
OpState |= UpdateResult_MultiTexture();
|
|
}
|
|
|
|
// Early exit if op input parameters are invalid.
|
|
if ((bool)(OpState & EBakeOpState::Invalid))
|
|
{
|
|
return;
|
|
}
|
|
|
|
// This should be the only point of compute invalidation to
|
|
// minimize synchronization issues.
|
|
bool bInvalidate = bInputsDirty || (bool)(OpState & EBakeOpState::Evaluate);
|
|
if (!Compute)
|
|
{
|
|
Compute = MakeUnique<TGenericDataBackgroundCompute<FMeshMapBaker>>();
|
|
Compute->Setup(this);
|
|
Compute->OnResultUpdated.AddLambda([this](const TUniquePtr<FMeshMapBaker>& NewResult) { OnMapsUpdated(NewResult, CachedBakeCacheSettings.SourceFormat); });
|
|
Compute->InvalidateResult();
|
|
}
|
|
else if (bInvalidate)
|
|
{
|
|
Compute->InvalidateResult();
|
|
}
|
|
bInputsDirty = false;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_DetailNormalMap()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const FDynamicMeshUVOverlay* UVOverlay = DetailMesh->Attributes()->GetUVLayer(DetailMeshProps->DetailNormalUVLayer);
|
|
if (UVOverlay == nullptr)
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidUVWarning", "The Detail Mesh does not have the selected UV layer"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
|
|
UTexture2D* DetailMeshNormalMap = DetailMeshProps->DetailMeshNormalMap;
|
|
if (DetailMeshNormalMap)
|
|
{
|
|
CachedDetailNormalMap = MakeShared<UE::Geometry::TImageBuilder<FVector4f>, ESPMode::ThreadSafe>();
|
|
if (!UE::AssetUtils::ReadTexture(DetailMeshNormalMap, *CachedDetailNormalMap, bPreferPlatformData))
|
|
{
|
|
// Report the failed texture read as a warning, but permit the bake to continue.
|
|
GetToolManager()->DisplayMessage(LOCTEXT("CannotReadTextureWarning", "Cannot read from the detail normal map texture"), EToolMessageLevel::UserWarning);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CachedDetailNormalMap = nullptr;
|
|
}
|
|
|
|
FDetailMeshSettings DetailMeshSettings;
|
|
DetailMeshSettings.UVLayer = DetailMeshProps->DetailNormalUVLayer;
|
|
|
|
if (!(CachedDetailMeshSettings == DetailMeshSettings))
|
|
{
|
|
CachedDetailMeshSettings = DetailMeshSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_Normal()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const int32 ImageSize = (int32)Settings->Resolution;
|
|
const FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FNormalMapSettings NormalMapSettings;
|
|
NormalMapSettings.Dimensions = Dimensions;
|
|
|
|
if (!(CachedNormalMapSettings == NormalMapSettings))
|
|
{
|
|
CachedNormalMapSettings = NormalMapSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_Occlusion()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const int32 ImageSize = (int32)Settings->Resolution;
|
|
const FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FOcclusionMapSettings OcclusionMapSettings;
|
|
OcclusionMapSettings.Dimensions = Dimensions;
|
|
OcclusionMapSettings.MaxDistance = (OcclusionMapProps->MaxDistance == 0) ? TNumericLimits<float>::Max() : OcclusionMapProps->MaxDistance;
|
|
OcclusionMapSettings.OcclusionRays = OcclusionMapProps->OcclusionRays;
|
|
OcclusionMapSettings.SpreadAngle = OcclusionMapProps->SpreadAngle;
|
|
OcclusionMapSettings.BiasAngle = OcclusionMapProps->BiasAngle;
|
|
|
|
if ( !(CachedOcclusionMapSettings == OcclusionMapSettings) )
|
|
{
|
|
CachedOcclusionMapSettings = OcclusionMapSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_Curvature()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const int32 ImageSize = (int32)Settings->Resolution;
|
|
const FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FCurvatureMapSettings CurvatureMapSettings;
|
|
CurvatureMapSettings.Dimensions = Dimensions;
|
|
CurvatureMapSettings.RangeMultiplier = CurvatureMapProps->RangeMultiplier;
|
|
CurvatureMapSettings.MinRangeMultiplier = CurvatureMapProps->MinRangeMultiplier;
|
|
switch (CurvatureMapProps->CurvatureType)
|
|
{
|
|
default:
|
|
case EBakedCurvatureTypeMode::MeanAverage:
|
|
CurvatureMapSettings.CurvatureType = (int32)FMeshCurvatureMapEvaluator::ECurvatureType::Mean;
|
|
break;
|
|
case EBakedCurvatureTypeMode::Gaussian:
|
|
CurvatureMapSettings.CurvatureType = (int32)FMeshCurvatureMapEvaluator::ECurvatureType::Gaussian;
|
|
break;
|
|
case EBakedCurvatureTypeMode::Max:
|
|
CurvatureMapSettings.CurvatureType = (int32)FMeshCurvatureMapEvaluator::ECurvatureType::MaxPrincipal;
|
|
break;
|
|
case EBakedCurvatureTypeMode::Min:
|
|
CurvatureMapSettings.CurvatureType = (int32)FMeshCurvatureMapEvaluator::ECurvatureType::MinPrincipal;
|
|
break;
|
|
}
|
|
switch (CurvatureMapProps->ColorMode)
|
|
{
|
|
default:
|
|
case EBakedCurvatureColorMode::Grayscale:
|
|
CurvatureMapSettings.ColorMode = (int32)FMeshCurvatureMapEvaluator::EColorMode::BlackGrayWhite;
|
|
break;
|
|
case EBakedCurvatureColorMode::RedBlue:
|
|
CurvatureMapSettings.ColorMode = (int32)FMeshCurvatureMapEvaluator::EColorMode::RedBlue;
|
|
break;
|
|
case EBakedCurvatureColorMode::RedGreenBlue:
|
|
CurvatureMapSettings.ColorMode = (int32)FMeshCurvatureMapEvaluator::EColorMode::RedGreenBlue;
|
|
break;
|
|
}
|
|
switch (CurvatureMapProps->Clamping)
|
|
{
|
|
default:
|
|
case EBakedCurvatureClampMode::None:
|
|
CurvatureMapSettings.ClampMode = (int32)FMeshCurvatureMapEvaluator::EClampMode::FullRange;
|
|
break;
|
|
case EBakedCurvatureClampMode::Positive:
|
|
CurvatureMapSettings.ClampMode = (int32)FMeshCurvatureMapEvaluator::EClampMode::Positive;
|
|
break;
|
|
case EBakedCurvatureClampMode::Negative:
|
|
CurvatureMapSettings.ClampMode = (int32)FMeshCurvatureMapEvaluator::EClampMode::Negative;
|
|
break;
|
|
}
|
|
|
|
if (!(CachedCurvatureMapSettings == CurvatureMapSettings))
|
|
{
|
|
CachedCurvatureMapSettings = CurvatureMapSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_MeshProperty()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const int32 ImageSize = (int32)Settings->Resolution;
|
|
const FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FMeshPropertyMapSettings MeshPropertyMapSettings;
|
|
MeshPropertyMapSettings.Dimensions = Dimensions;
|
|
|
|
if (!(CachedMeshPropertyMapSettings == MeshPropertyMapSettings))
|
|
{
|
|
CachedMeshPropertyMapSettings = MeshPropertyMapSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_Texture2DImage()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const int32 ImageSize = (int32)Settings->Resolution;
|
|
const FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FTexture2DImageSettings NewSettings;
|
|
NewSettings.Dimensions = Dimensions;
|
|
NewSettings.UVLayer = 0;
|
|
|
|
const FDynamicMeshUVOverlay* UVOverlay = DetailMesh->Attributes()->GetUVLayer(NewSettings.UVLayer);
|
|
if (UVOverlay == nullptr)
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidUVWarning", "The Source Mesh does not have the selected UV layer"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
|
|
if (Texture2DProps->SourceTexture == nullptr)
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidTextureWarning", "The Source Texture is not valid"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
|
|
{
|
|
CachedTextureImage = MakeShared<UE::Geometry::TImageBuilder<FVector4f>, ESPMode::ThreadSafe>();
|
|
if (!UE::AssetUtils::ReadTexture(Texture2DProps->SourceTexture, *CachedTextureImage, bPreferPlatformData))
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("CannotReadTextureWarning", "Cannot read from the source texture"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
}
|
|
|
|
if (!(CachedTexture2DImageSettings == NewSettings))
|
|
{
|
|
CachedTexture2DImageSettings = NewSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
EBakeOpState UBakeMeshAttributeMapsTool::UpdateResult_MultiTexture()
|
|
{
|
|
EBakeOpState ResultState = EBakeOpState::Complete;
|
|
|
|
const int32 ImageSize = (int32)Settings->Resolution;
|
|
const FImageDimensions Dimensions(ImageSize, ImageSize);
|
|
|
|
FTexture2DImageSettings NewSettings;
|
|
NewSettings.Dimensions = Dimensions;
|
|
NewSettings.UVLayer = MultiTextureProps->UVLayer;
|
|
|
|
const FDynamicMeshUVOverlay* UVOverlay = DetailMesh->Attributes()->GetUVLayer(NewSettings.UVLayer);
|
|
if (UVOverlay == nullptr)
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidUVWarning", "The Source Mesh does not have the selected UV layer"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
|
|
for (auto& InputTexture : MultiTextureProps->MaterialIDSourceTextureMap)
|
|
{
|
|
if (InputTexture.Value == nullptr)
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidTextureWarning", "The Source Texture is not valid"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
}
|
|
|
|
CachedMultiTextures.Reset();
|
|
|
|
for ( auto& InputTexture : MultiTextureProps->MaterialIDSourceTextureMap)
|
|
{
|
|
UTexture2D* Texture = InputTexture.Value;
|
|
if (!ensure(Texture != nullptr))
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidTextureWarning", "The Source Texture is not valid"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
|
|
int32 MaterialID = InputTexture.Key;
|
|
CachedMultiTextures.Add(MaterialID, MakeShared<UE::Geometry::TImageBuilder<FVector4f>, ESPMode::ThreadSafe>());
|
|
if (!UE::AssetUtils::ReadTexture(Texture, *CachedMultiTextures[MaterialID], bPreferPlatformData))
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("CannotReadTextureWarning", "Cannot read from the source texture"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
}
|
|
if (CachedMultiTextures.Num() == 0)
|
|
{
|
|
GetToolManager()->DisplayMessage(LOCTEXT("InvalidTextureWarning", "The Source Texture is not valid"), EToolMessageLevel::UserWarning);
|
|
return EBakeOpState::Invalid;
|
|
}
|
|
|
|
if (!(CachedTexture2DImageSettings == NewSettings))
|
|
{
|
|
CachedTexture2DImageSettings = NewSettings;
|
|
ResultState = EBakeOpState::Evaluate;
|
|
}
|
|
return ResultState;
|
|
}
|
|
|
|
|
|
void UBakeMeshAttributeMapsTool::UpdateVisualization()
|
|
{
|
|
PreviewMesh->SetOverrideRenderMaterial(PreviewMaterial);
|
|
|
|
// Map CachedMaps to Settings->Result
|
|
int NumResults = Settings->Result.Num();
|
|
for (int ResultIdx = 0; ResultIdx < NumResults; ResultIdx++)
|
|
{
|
|
Settings->Result[ResultIdx] = CachedMaps[CachedMapIndices[ResultTypes[ResultIdx]]];
|
|
}
|
|
|
|
// Set the preview material according to the preview index.
|
|
if (Settings->MapPreview >= 0 && Settings->MapPreview < Settings->Result.Num())
|
|
{
|
|
UpdatePreview(Settings->MapPreview);
|
|
}
|
|
}
|
|
|
|
|
|
void UBakeMeshAttributeMapsTool::UpdateOnModeChange()
|
|
{
|
|
OnMapTypesUpdated(Settings->MapTypes);
|
|
|
|
SetToolPropertySourceEnabled(NormalMapProps, false);
|
|
SetToolPropertySourceEnabled(OcclusionMapProps, false);
|
|
SetToolPropertySourceEnabled(CurvatureMapProps, false);
|
|
SetToolPropertySourceEnabled(Texture2DProps, false);
|
|
SetToolPropertySourceEnabled(MultiTextureProps, false);
|
|
|
|
for (const EBakeMapType MapType : ALL_BAKE_MAP_TYPES)
|
|
{
|
|
switch ((EBakeMapType)Settings->MapTypes & MapType)
|
|
{
|
|
case EBakeMapType::TangentSpaceNormalMap:
|
|
SetToolPropertySourceEnabled(NormalMapProps, true);
|
|
break;
|
|
case EBakeMapType::AmbientOcclusion:
|
|
case EBakeMapType::BentNormal:
|
|
case EBakeMapType::Occlusion:
|
|
SetToolPropertySourceEnabled(OcclusionMapProps, true);
|
|
break;
|
|
case EBakeMapType::Curvature:
|
|
SetToolPropertySourceEnabled(CurvatureMapProps, true);
|
|
break;
|
|
case EBakeMapType::NormalImage:
|
|
case EBakeMapType::FaceNormalImage:
|
|
case EBakeMapType::PositionImage:
|
|
case EBakeMapType::MaterialID:
|
|
case EBakeMapType::VertexColorImage:
|
|
break;
|
|
case EBakeMapType::Texture2DImage:
|
|
SetToolPropertySourceEnabled(Texture2DProps, true);
|
|
break;
|
|
case EBakeMapType::MultiTexture:
|
|
SetToolPropertySourceEnabled(MultiTextureProps, true);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
Settings->Result.Empty();
|
|
Settings->Result.SetNum(ResultTypes.Num());
|
|
}
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|