2021-09-13 17:51:19 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "BakeMultiMeshAttributeMapsTool.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/MeshPropertyMapEvaluator.h"
# include "Sampling/MeshResampleImageEvaluator.h"
# include "ImageUtils.h"
# include "AssetUtils/Texture2DUtil.h"
2021-11-02 13:58:07 -04:00
# include "EngineAnalytics.h"
2021-09-13 17:51:19 -04:00
# include "TargetInterfaces/MaterialProvider.h"
# include "TargetInterfaces/MeshDescriptionProvider.h"
# include "TargetInterfaces/PrimitiveComponentBackedTarget.h"
# include "TargetInterfaces/StaticMeshBackedTarget.h"
# include "ToolTargetManager.h"
# include "ModelingToolTargetUtil.h"
// required to pass UStaticMesh asset so we can save at same location
# include "Engine/Classes/Engine/StaticMesh.h"
using namespace UE : : Geometry ;
# define LOCTEXT_NAMESPACE "UBakeMultiMeshAttributeMapsTool"
/*
* ToolBuilder
*/
const FToolTargetTypeRequirements & UBakeMultiMeshAttributeMapsToolBuilder : : GetTargetRequirements ( ) const
{
static FToolTargetTypeRequirements TypeRequirements ( {
UMeshDescriptionProvider : : StaticClass ( ) ,
UPrimitiveComponentBackedTarget : : StaticClass ( ) ,
UMaterialProvider : : StaticClass ( )
} ) ;
return TypeRequirements ;
}
bool UBakeMultiMeshAttributeMapsToolBuilder : : CanBuildTool ( const FToolBuilderState & SceneState ) const
{
const int32 NumTargets = SceneState . TargetManager - > CountSelectedAndTargetable ( SceneState , GetTargetRequirements ( ) ) ;
2022-04-20 16:57:45 -04:00
if ( NumTargets > 1 )
{
// FMeshSceneAdapter currently only supports StaticMesh targets.
// Restrict source targets to StaticMesh.
bool bValidTargets = true ;
int TargetId = 0 ;
SceneState . TargetManager - > EnumerateSelectedAndTargetableComponents ( SceneState , GetTargetRequirements ( ) ,
[ & bValidTargets , & TargetId ] ( UActorComponent * Component )
{
if ( TargetId > 0 )
{
const UStaticMeshComponent * StaticMesh = Cast < UStaticMeshComponent > ( Component ) ;
bValidTargets = bValidTargets & & StaticMesh ;
}
+ + TargetId ;
} ) ;
return bValidTargets ;
}
return false ;
2021-09-13 17:51:19 -04:00
}
2021-11-23 09:42:40 -05:00
UMultiSelectionMeshEditingTool * UBakeMultiMeshAttributeMapsToolBuilder : : CreateNewTool ( const FToolBuilderState & SceneState ) const
2021-09-13 17:51:19 -04:00
{
2021-11-23 09:42:40 -05:00
return NewObject < UBakeMultiMeshAttributeMapsTool > ( SceneState . ToolManager ) ;
2021-09-13 17:51:19 -04:00
}
/**
* MeshSceneAdapter bake detail sampler for baking N detail meshes to 1 target mesh .
*/
class FMeshBakerMeshSceneSampler : public IMeshBakerDetailSampler
{
public :
using FDetailTextureMap = TMap < void * , FBakeDetailTexture > ;
2022-02-18 15:34:48 -05:00
using FDetailNormalMap = TMap < void * , FBakeDetailNormalTexture > ;
2021-09-13 17:51:19 -04:00
public :
/**
* Constructor .
*
* Input FMeshSceneAdapter ' s spatial evaluation cache is assumed to be built .
*/
FMeshBakerMeshSceneSampler ( FMeshSceneAdapter * Scene )
: MeshScene ( Scene )
{
}
// Begin IMeshBakerDetailSampler interface
virtual void ProcessMeshes ( TFunctionRef < void ( const void * ) > ProcessFn ) const override
{
auto ProcessChildMesh = [ ProcessFn ] ( const FActorAdapter * , const FActorChildMesh * ChildMesh )
{
if ( ChildMesh )
{
2022-04-21 13:18:38 -04:00
ProcessFn ( ChildMesh ) ;
2021-09-13 17:51:19 -04:00
}
} ;
MeshScene - > ProcessActorChildMeshes ( ProcessChildMesh ) ;
}
2021-11-02 13:58:07 -04:00
virtual int32 GetTriangleCount ( const void * Mesh ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
return ChildMesh - > MeshSpatial - > GetTriangleCount ( ) ;
2021-11-02 13:58:07 -04:00
}
2021-11-24 19:19:10 -05:00
virtual void SetTextureMap ( const void * Mesh , const FBakeDetailTexture & Map ) override
2021-09-13 17:51:19 -04:00
{
2021-11-24 19:19:10 -05:00
DetailTextureMaps [ Mesh ] = Map ;
2021-09-13 17:51:19 -04:00
}
virtual void SetNormalMap ( const void * Mesh , const FBakeDetailTexture & Map ) override
2022-02-18 15:34:48 -05:00
{
DetailNormalMaps [ Mesh ] = FBakeDetailNormalTexture ( Map . Key , Map . Value , EBakeDetailNormalSpace : : Tangent ) ;
}
virtual void SetNormalTextureMap ( const void * Mesh , const FBakeDetailNormalTexture & Map ) override
2021-09-13 17:51:19 -04:00
{
DetailNormalMaps [ Mesh ] = Map ;
}
2021-11-24 19:19:10 -05:00
virtual const FBakeDetailTexture * GetTextureMap ( const void * Mesh ) const override
2021-09-13 17:51:19 -04:00
{
2021-11-24 19:19:10 -05:00
return DetailTextureMaps . Find ( Mesh ) ;
2021-09-13 17:51:19 -04:00
}
virtual const FBakeDetailTexture * GetNormalMap ( const void * Mesh ) const override
2022-02-18 15:34:48 -05:00
{
return nullptr ;
}
virtual const FBakeDetailNormalTexture * GetNormalTextureMap ( const void * Mesh ) const override
2021-09-13 17:51:19 -04:00
{
return DetailNormalMaps . Find ( Mesh ) ;
}
virtual bool SupportsIdentityCorrespondence ( ) const override
{
return false ;
}
virtual bool SupportsNearestPointCorrespondence ( ) const override
{
return false ;
}
virtual bool SupportsRaycastCorrespondence ( ) const override
{
return true ;
}
virtual const void * FindNearestHitTriangle (
2021-11-17 21:06:46 -05:00
const FRay3d & Ray ,
2021-09-13 17:51:19 -04:00
double & NearestT ,
int & TriId ,
FVector3d & TriBaryCoords ,
const IMeshSpatial : : FQueryOptions & Options = IMeshSpatial : : FQueryOptions ( ) ) const override
{
2022-04-21 13:18:38 -04:00
// Transform ray to world space
const FRay3d WorldRay = BaseToWorldTransform . TransformRay ( Ray ) ;
2021-09-13 17:51:19 -04:00
// TODO: Pass-through max distance to FMeshSceneAdapter query
FMeshSceneRayHit HitResult ;
2022-04-21 13:18:38 -04:00
const bool bHit = MeshScene - > FindNearestRayIntersection ( WorldRay , HitResult ) ;
2021-09-13 17:51:19 -04:00
const void * HitMesh = nullptr ;
2021-11-10 23:27:16 -05:00
// Use TNumericLimits<float>::Max() for consistency with MeshAABBTree3.
NearestT = ( Options . MaxDistance < TNumericLimits < float > : : Max ( ) ) ? Options . MaxDistance : TNumericLimits < float > : : Max ( ) ;
if ( bHit & & HitResult . RayDistance < Options . MaxDistance )
2021-09-13 17:51:19 -04:00
{
TriId = HitResult . HitMeshTriIndex ;
NearestT = HitResult . RayDistance ;
TriBaryCoords = HitResult . HitMeshBaryCoords ;
2022-04-21 13:18:38 -04:00
// Use the component and component element index to identify the actual instance
// (FActorChildMesh) that was hit.
if ( const FActorAdapter * const * FoundActor = ActorComponentMap . Find ( HitResult . HitComponent ) ; FoundActor ! = nullptr )
{
if ( HitResult . HitComponentElementIndex < ( * FoundActor ) - > ChildMeshes . Num ( ) )
{
HitMesh = ( * FoundActor ) - > ChildMeshes [ FMath : : Max ( 0 , HitResult . HitComponentElementIndex ) ] . Get ( ) ;
}
}
2021-09-13 17:51:19 -04:00
}
return HitMesh ;
}
virtual bool TestAnyHitTriangle (
2021-11-17 21:06:46 -05:00
const FRay3d & Ray ,
2021-09-13 17:51:19 -04:00
const IMeshSpatial : : FQueryOptions & Options = IMeshSpatial : : FQueryOptions ( ) ) const override
{
// TODO: Add proper test any hit triangle support (for Occlusion)
// TODO: Pass through max distance to FMeshSceneAdapter query
double NearestT = TNumericLimits < double > : : Max ( ) ;
int TriId = IndexConstants : : InvalidID ;
FVector3d TriBaryCoords ;
2021-11-18 14:37:34 -05:00
return FindNearestHitTriangle ( Ray , NearestT , TriId , TriBaryCoords , Options ) ! = nullptr ;
2021-09-13 17:51:19 -04:00
}
virtual FAxisAlignedBox3d GetBounds ( ) const override
{
return MeshScene - > GetBoundingBox ( ) ;
}
virtual bool IsTriangle ( const void * Mesh , const int TriId ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
return ChildMesh - > MeshSpatial - > IsTriangle ( TriId ) ;
2021-09-13 17:51:19 -04:00
}
virtual FIndex3i GetTriangle ( const void * Mesh , const int TriId ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
return ChildMesh - > MeshSpatial - > GetTriangle ( TriId ) ;
2021-09-13 17:51:19 -04:00
}
virtual FVector3d GetTriNormal ( const void * Mesh , const int TriId ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
return FVector3d : : Zero ( ) ;
}
virtual int32 GetMaterialID ( const void * Mesh , const int TriId ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
return IndexConstants : : InvalidID ;
}
virtual bool HasNormals ( const void * Mesh ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
return ChildMesh - > MeshSpatial - > HasNormals ( ) ;
2021-09-13 17:51:19 -04:00
}
virtual bool HasUVs ( const void * Mesh , const int UVLayer = 0 ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
return ChildMesh - > MeshSpatial - > HasUVs ( UVLayer ) ;
2021-09-13 17:51:19 -04:00
}
virtual bool HasTangents ( const void * Mesh ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
return false ;
}
virtual bool HasColors ( const void * Mesh ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
return false ;
}
virtual FVector3d TriBaryInterpolatePoint (
const void * Mesh ,
const int32 TriId ,
const FVector3d & BaryCoords ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
const FVector3d Point = ChildMesh - > MeshSpatial - > TriBaryInterpolatePoint ( TriId , BaryCoords ) ;
const FVector3d WorldPoint = ChildMesh - > WorldTransform . TransformPosition ( Point ) ;
return WorldPoint ;
2021-09-13 17:51:19 -04:00
}
virtual bool TriBaryInterpolateNormal (
const void * Mesh ,
const int32 TriId ,
const FVector3d & BaryCoords ,
FVector3f & NormalOut ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
const bool bSuccess = ChildMesh - > MeshSpatial - > TriBaryInterpolateNormal ( TriId , BaryCoords , NormalOut ) ;
const FVector3d NormalOut3d ( NormalOut ) ;
// As a stop-gap fix, transform our normal to the local space of the Base mesh here.
// TODO: Handle WorldToBase transformation in the evaluator.
2022-05-12 12:08:26 -04:00
NormalOut = BaseToWorldTransform . InverseTransformNormal ( FVector3f ( ChildMesh - > WorldTransform . TransformNormal ( NormalOut3d ) ) ) ;
2022-04-21 13:18:38 -04:00
return bSuccess ;
2021-09-13 17:51:19 -04:00
}
virtual bool TriBaryInterpolateUV (
const void * Mesh ,
const int32 TriId ,
const FVector3d & BaryCoords ,
const int UVLayer ,
FVector2f & UVOut ) const override
{
2022-04-21 13:18:38 -04:00
const FActorChildMesh * ChildMesh = static_cast < const FActorChildMesh * > ( Mesh ) ;
return ChildMesh - > MeshSpatial - > TriBaryInterpolateUV ( TriId , BaryCoords , UVLayer , UVOut ) ;
2021-09-13 17:51:19 -04:00
}
virtual bool TriBaryInterpolateColor (
const void * Mesh ,
const int32 TriId ,
const FVector3d & BaryCoords ,
FVector4f & ColorOut ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
return false ;
}
virtual bool TriBaryInterpolateTangents (
const void * Mesh ,
const int32 TriId ,
const FVector3d & BaryCoords ,
FVector3d & TangentX ,
FVector3d & TangentY ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
return false ;
}
virtual void GetCurvature (
const void * Mesh ,
FMeshVertexCurvatureCache & CurvatureCache ) const override
{
// TODO
2021-11-18 14:37:34 -05:00
checkSlow ( false ) ;
2021-09-13 17:51:19 -04:00
}
// End IMeshBakerDetailSampler interface
/** Initialize the mesh to color textures map */
2021-11-24 19:19:10 -05:00
void SetTextureMaps ( const FDetailTextureMap & Map )
2021-09-13 17:51:19 -04:00
{
2021-11-24 19:19:10 -05:00
DetailTextureMaps = Map ;
2021-09-13 17:51:19 -04:00
}
/** Initialize the mesh to normal textures map */
2022-02-18 15:34:48 -05:00
void SetNormalMaps ( const FDetailNormalMap & Map )
2021-09-13 17:51:19 -04:00
{
DetailNormalMaps = Map ;
}
2022-04-21 13:18:38 -04:00
public :
FTransformSRT3d BaseToWorldTransform ;
TMap < UActorComponent * , const FActorAdapter * > ActorComponentMap ;
2021-09-13 17:51:19 -04:00
protected :
FMeshSceneAdapter * MeshScene = nullptr ;
2021-11-24 19:19:10 -05:00
FDetailTextureMap DetailTextureMaps ;
2022-02-18 15:34:48 -05:00
FDetailNormalMap DetailNormalMaps ;
2021-09-13 17:51:19 -04:00
} ;
/*
* Operators
*/
class FMultiMeshMapBakerOp : public TGenericDataOperator < FMeshMapBaker >
{
public :
// General bake settings
2022-02-24 23:37:17 -05:00
TSharedPtr < FMeshSceneAdapter , ESPMode : : ThreadSafe > DetailMeshScene ;
2022-01-31 12:52:55 -05:00
UE : : Geometry : : FDynamicMesh3 * BaseMesh = nullptr ;
2021-09-13 17:51:19 -04:00
TSharedPtr < UE : : Geometry : : FMeshTangentsd , ESPMode : : ThreadSafe > BaseMeshTangents ;
TUniquePtr < UE : : Geometry : : FMeshMapBaker > Baker ;
2021-11-24 12:49:18 -05:00
UBakeMultiMeshAttributeMapsTool : : FBakeSettings BakeSettings ;
2022-01-31 12:52:55 -05:00
TSharedPtr < TArray < int32 > , ESPMode : : ThreadSafe > BaseMeshUVCharts ;
2022-04-12 18:19:08 -04:00
TSharedPtr < UE : : Geometry : : TImageBuilder < FVector4f > > SampleFilterMask ;
2021-09-13 17:51:19 -04:00
2022-04-21 13:18:38 -04:00
FTransformSRT3d BaseToWorldTransform ;
2021-09-13 17:51:19 -04:00
// Detail bake data
TArray < TSharedPtr < UE : : Geometry : : TImageBuilder < FVector4f > > > CachedColorImages ;
UBakeMultiMeshAttributeMapsTool : : FTextureImageMap CachedMeshToColorImageMap ;
// Begin TGenericDataOperator interface
virtual void CalculateResult ( FProgressCancel * Progress ) override
{
Baker = MakeUnique < FMeshMapBaker > ( ) ;
Baker - > CancelF = [ Progress ] ( ) {
return Progress & & Progress - > Cancelled ( ) ;
} ;
Baker - > SetTargetMesh ( BaseMesh ) ;
2021-11-24 12:49:18 -05:00
Baker - > SetTargetMeshUVLayer ( BakeSettings . TargetUVLayer ) ;
Baker - > SetDimensions ( BakeSettings . Dimensions ) ;
Baker - > SetProjectionDistance ( BakeSettings . ProjectionDistance ) ;
Baker - > SetSamplesPerPixel ( BakeSettings . SamplesPerPixel ) ;
2021-09-13 17:51:19 -04:00
Baker - > SetTargetMeshTangents ( BaseMeshTangents ) ;
2022-01-31 12:52:55 -05:00
Baker - > SetTargetMeshUVCharts ( BaseMeshUVCharts . Get ( ) ) ;
2022-04-12 18:19:08 -04:00
if ( SampleFilterMask )
{
Baker - > SampleFilterF = [ this ] ( const FVector2i & ImageCoords , const FVector2d & UV , int32 TriID )
{
2022-04-13 21:13:17 -04:00
const FVector4f Mask = SampleFilterMask - > BilinearSampleUV < float > ( UV , FVector4f : : One ( ) ) ;
return ( Mask . X + Mask . Y + Mask . Z ) / 3 ;
2022-04-12 18:19:08 -04:00
} ;
}
2021-09-13 17:51:19 -04:00
2022-02-24 23:37:17 -05:00
FMeshBakerMeshSceneSampler DetailSampler ( DetailMeshScene . Get ( ) ) ;
2022-04-21 13:18:38 -04:00
// TODO: Precompute the ActorComponent to ActorChildMesh map in the tool.
auto ProcessChildMesh = [ & DetailSampler ] ( const FActorAdapter * Actor , const FActorChildMesh * ChildMesh )
{
2022-04-22 16:50:50 -04:00
if ( Actor & & ChildMesh )
2022-04-21 13:18:38 -04:00
{
2022-04-22 16:50:50 -04:00
// Here we rely on the fact that we populated the MeshSceneAdapter using AddComponents.
// This guarantees that each component has its own ActorAdapter. So we build a map
// of source component to actor adapter.
const FActorAdapter * * FoundActor = DetailSampler . ActorComponentMap . Find ( ChildMesh - > SourceComponent ) ;
if ( ! FoundActor )
{
DetailSampler . ActorComponentMap . Add ( ChildMesh - > SourceComponent , Actor ) ;
}
2022-04-21 13:18:38 -04:00
}
} ;
DetailMeshScene - > ProcessActorChildMeshes ( ProcessChildMesh ) ;
DetailSampler . BaseToWorldTransform = BaseToWorldTransform ;
2021-09-13 17:51:19 -04:00
Baker - > SetDetailSampler ( & DetailSampler ) ;
2021-11-26 21:57:52 -05:00
for ( const EBakeMapType MapType : ENUM_EBAKEMAPTYPE_ALL )
2021-09-13 17:51:19 -04:00
{
2021-11-24 12:49:18 -05:00
switch ( BakeSettings . BakeMapTypes & MapType )
2021-09-13 17:51:19 -04:00
{
2021-10-21 10:53:58 -04:00
case EBakeMapType : : TangentSpaceNormal :
2021-09-13 17:51:19 -04:00
{
2021-09-15 20:13:36 -04:00
TSharedPtr < FMeshNormalMapEvaluator , ESPMode : : ThreadSafe > NormalEval = MakeShared < FMeshNormalMapEvaluator , ESPMode : : ThreadSafe > ( ) ;
Baker - > AddEvaluator ( NormalEval ) ;
2021-09-13 17:51:19 -04:00
break ;
}
2022-04-22 10:35:38 -04:00
case EBakeMapType : : Position :
{
TSharedPtr < FMeshPropertyMapEvaluator , ESPMode : : ThreadSafe > PropertyEval = MakeShared < FMeshPropertyMapEvaluator , ESPMode : : ThreadSafe > ( ) ;
PropertyEval - > Property = EMeshPropertyMapType : : Position ;
Baker - > AddEvaluator ( PropertyEval ) ;
break ;
}
case EBakeMapType : : ObjectSpaceNormal :
{
TSharedPtr < FMeshPropertyMapEvaluator , ESPMode : : ThreadSafe > PropertyEval = MakeShared < FMeshPropertyMapEvaluator , ESPMode : : ThreadSafe > ( ) ;
PropertyEval - > Property = EMeshPropertyMapType : : Normal ;
Baker - > AddEvaluator ( PropertyEval ) ;
}
2021-10-21 10:53:58 -04:00
case EBakeMapType : : Texture :
2021-09-13 17:51:19 -04:00
{
2021-09-15 20:13:36 -04:00
TSharedPtr < FMeshResampleImageEvaluator , ESPMode : : ThreadSafe > TextureEval = MakeShared < FMeshResampleImageEvaluator , ESPMode : : ThreadSafe > ( ) ;
2021-11-24 19:19:10 -05:00
DetailSampler . SetTextureMaps ( CachedMeshToColorImageMap ) ;
2021-09-15 20:13:36 -04:00
Baker - > AddEvaluator ( TextureEval ) ;
2021-09-13 17:51:19 -04:00
break ;
}
default :
break ;
}
}
Baker - > Bake ( ) ;
SetResult ( MoveTemp ( Baker ) ) ;
}
// End TGenericDataOperator interface
} ;
/*
* Tool
*/
void UBakeMultiMeshAttributeMapsTool : : Setup ( )
{
2021-11-08 21:54:53 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( UBakeMultiMeshAttributeMapsTool : : Setup ) ;
2021-09-13 17:51:19 -04:00
Super : : Setup ( ) ;
// Initialize base mesh
2022-04-21 13:18:38 -04:00
PreviewMesh - > ProcessMesh ( [ this ] ( const FDynamicMesh3 & Mesh )
2021-09-13 17:51:19 -04:00
{
2021-11-24 12:49:18 -05:00
TargetMesh . Copy ( Mesh ) ;
TargetMeshTangents = MakeShared < FMeshTangentsd , ESPMode : : ThreadSafe > ( & TargetMesh ) ;
TargetMeshTangents - > CopyTriVertexTangents ( Mesh ) ;
TargetSpatial . SetMesh ( & TargetMesh , true ) ;
2021-09-13 17:51:19 -04:00
} ) ;
// Initialize detail sampler
const int NumTargets = Targets . Num ( ) ;
TArray < UActorComponent * > DetailComponents ;
for ( int Idx = 1 ; Idx < NumTargets ; + + Idx )
{
2021-11-18 14:37:34 -05:00
if ( UPrimitiveComponent * Component = UE : : ToolTarget : : GetTargetComponent ( Targets [ Idx ] ) )
2021-09-13 17:51:19 -04:00
{
DetailComponents . Add ( Component ) ;
}
}
2022-02-24 23:37:17 -05:00
DetailMeshScene = MakeShared < FMeshSceneAdapter , ESPMode : : ThreadSafe > ( ) ;
2022-04-21 13:18:38 -04:00
// FMultiMeshMapBakerOp depends on adding meshes via AddComponents.
// If this changed, ensure the logic for generating the ActorComponentMap
// is updated in FMultiMeshMapBakerOp.
2022-02-24 23:37:17 -05:00
DetailMeshScene - > AddComponents ( DetailComponents ) ;
DetailMeshScene - > Build ( FMeshSceneAdapterBuildOptions ( ) ) ;
DetailMeshScene - > BuildSpatialEvaluationCache ( ) ;
2021-09-13 17:51:19 -04:00
2021-11-18 14:37:34 -05:00
UToolTarget * Target = Targets [ 0 ] ;
2021-09-13 17:51:19 -04:00
// Setup tool property sets
2021-11-30 16:34:23 -05:00
2021-09-13 17:51:19 -04:00
Settings = NewObject < UBakeMultiMeshAttributeMapsToolProperties > ( this ) ;
Settings - > RestoreProperties ( this ) ;
AddToolPropertySource ( Settings ) ;
2021-11-03 21:48:49 -04:00
Settings - > WatchProperty ( Settings - > MapTypes , [ this ] ( int32 ) { OpState | = EBakeOpState : : Evaluate ; UpdateOnModeChange ( ) ; } ) ;
2021-10-21 10:53:58 -04:00
Settings - > WatchProperty ( Settings - > MapPreview , [ this ] ( FString ) { UpdateVisualization ( ) ; GetToolManager ( ) - > PostInvalidation ( ) ; } ) ;
2021-11-03 21:48:49 -04:00
Settings - > WatchProperty ( Settings - > Resolution , [ this ] ( EBakeTextureResolution ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
2021-11-18 14:37:34 -05:00
Settings - > WatchProperty ( Settings - > BitDepth , [ this ] ( EBakeTextureBitDepth ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
Settings - > WatchProperty ( Settings - > SamplesPerPixel , [ this ] ( EBakeTextureSamplesPerPixel ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
2022-04-12 18:19:08 -04:00
Settings - > WatchProperty ( Settings - > SampleFilterMask , [ this ] ( UTexture2D * ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
2021-11-30 16:34:23 -05:00
InputMeshSettings = NewObject < UBakeMultiMeshInputToolProperties > ( this ) ;
InputMeshSettings - > RestoreProperties ( this ) ;
AddToolPropertySource ( InputMeshSettings ) ;
InputMeshSettings - > TargetStaticMesh = GetStaticMeshTarget ( Target ) ;
2022-04-20 21:32:46 -04:00
InputMeshSettings - > TargetSkeletalMesh = GetSkeletalMeshTarget ( Target ) ;
InputMeshSettings - > TargetDynamicMesh = GetDynamicMeshTarget ( Target ) ;
2021-11-30 16:34:23 -05:00
UpdateUVLayerNames ( InputMeshSettings - > TargetUVLayer , InputMeshSettings - > TargetUVLayerNamesList , TargetMesh ) ;
InputMeshSettings - > WatchProperty ( InputMeshSettings - > TargetUVLayer , [ this ] ( FString ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
InputMeshSettings - > WatchProperty ( InputMeshSettings - > ProjectionDistance , [ this ] ( float ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
ResultSettings = NewObject < UBakeMeshAttributeMapsResultToolProperties > ( this ) ;
ResultSettings - > RestoreProperties ( this ) ;
AddToolPropertySource ( ResultSettings ) ;
SetToolPropertySourceEnabled ( ResultSettings , true ) ;
2021-09-13 17:51:19 -04:00
// Pre-populate detail mesh data
TArray < UTexture2D * > DetailColorTextures ;
for ( int Idx = 1 ; Idx < NumTargets ; + + Idx )
{
UToolTarget * DetailTarget = Targets [ Idx ] ;
// Hide each of our detail targets since this baker operates solely in world space
// which will occlude the preview of the target mesh.
UE : : ToolTarget : : HideSourceObject ( DetailTarget ) ;
const UPrimitiveComponent * Component = UE : : ToolTarget : : GetTargetComponent ( Targets [ Idx ] ) ;
UTexture2D * DetailColorTexture = nullptr ;
2021-11-18 14:37:34 -05:00
ProcessComponentTextures ( Component , [ & DetailColorTexture ] ( const int NumMaterials , const int MaterialID , const TArray < UTexture * > & Textures )
2021-09-13 17:51:19 -04:00
{
// TODO: Support multiple materialIDs per detail mesh
if ( MaterialID = = 0 )
{
const int SelectedTextureIndex = SelectColorTextureToBake ( Textures ) ;
if ( SelectedTextureIndex > = 0 )
{
DetailColorTexture = Cast < UTexture2D > ( Textures [ SelectedTextureIndex ] ) ;
}
}
} ) ;
2021-11-18 14:37:34 -05:00
FBakeMultiMeshDetailProperties SourceMeshProp ;
SourceMeshProp . SourceMesh = GetStaticMeshTarget ( DetailTarget ) ;
SourceMeshProp . SourceTexture = DetailColorTexture ;
2021-11-24 19:19:10 -05:00
InputMeshSettings - > SourceMeshes . Add ( SourceMeshProp ) ;
InputMeshSettings - > WatchProperty ( InputMeshSettings - > SourceMeshes [ Idx - 1 ] . SourceTexture , [ this ] ( UTexture2D * ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
InputMeshSettings - > WatchProperty ( InputMeshSettings - > SourceMeshes [ Idx - 1 ] . SourceTextureUVLayer , [ this ] ( int ) { OpState | = EBakeOpState : : Evaluate ; } ) ;
2021-09-13 17:51:19 -04:00
}
UpdateOnModeChange ( ) ;
2021-11-03 21:48:49 -04:00
OpState | = EBakeOpState : : Evaluate ;
2021-09-13 17:51:19 -04:00
SetToolDisplayName ( LOCTEXT ( " ToolName " , " Bake Textures " ) ) ;
GetToolManager ( ) - > DisplayMessage (
LOCTEXT ( " OnStartTool " , " Bake Maps. Select Bake Mesh (LowPoly) first, then select Detail Meshes (HiPoly) to bake. Texture Assets will be created on Accept. " ) ,
EToolMessageLevel : : UserNotification ) ;
2021-11-02 13:58:07 -04:00
PostSetup ( ) ;
2021-09-13 17:51:19 -04:00
}
bool UBakeMultiMeshAttributeMapsTool : : CanAccept ( ) const
{
2021-11-18 14:37:34 -05:00
const bool bValidOp = ( OpState & EBakeOpState : : Invalid ) ! = EBakeOpState : : Invalid ;
bool bCanAccept = bValidOp & & Compute ? Compute - > HaveValidResult ( ) : false ;
2021-09-13 17:51:19 -04:00
if ( bCanAccept )
{
// Allow Accept if all non-None types have valid results.
2021-11-30 16:34:23 -05:00
for ( const TTuple < EBakeMapType , TObjectPtr < UTexture2D > > & Result : ResultSettings - > Result )
2021-09-13 17:51:19 -04:00
{
2021-10-21 10:53:58 -04:00
bCanAccept = bCanAccept & & Result . Get < 1 > ( ) ;
2021-09-13 17:51:19 -04:00
}
}
return bCanAccept ;
}
TUniquePtr < UE : : Geometry : : TGenericDataOperator < FMeshMapBaker > > UBakeMultiMeshAttributeMapsTool : : MakeNewOperator ( )
{
TUniquePtr < FMultiMeshMapBakerOp > Op = MakeUnique < FMultiMeshMapBakerOp > ( ) ;
2022-02-24 23:37:17 -05:00
Op - > DetailMeshScene = DetailMeshScene ;
2021-11-24 12:49:18 -05:00
Op - > BaseMesh = & TargetMesh ;
2022-01-31 12:52:55 -05:00
Op - > BaseMeshUVCharts = TargetMeshUVCharts ;
2022-04-21 13:18:38 -04:00
Op - > BaseToWorldTransform = UE : : ToolTarget : : GetLocalToWorldTransform ( Targets [ 0 ] ) ;
2021-11-24 12:49:18 -05:00
Op - > BakeSettings = CachedBakeSettings ;
2022-04-12 18:19:08 -04:00
Op - > SampleFilterMask = CachedSampleFilterMask ;
2021-09-13 17:51:19 -04:00
2021-10-21 10:53:58 -04:00
constexpr EBakeMapType RequiresTangents = EBakeMapType : : TangentSpaceNormal | EBakeMapType : : BentNormal ;
2021-11-24 12:49:18 -05:00
if ( static_cast < bool > ( CachedBakeSettings . BakeMapTypes & RequiresTangents ) )
2021-09-13 17:51:19 -04:00
{
2021-11-24 12:49:18 -05:00
Op - > BaseMeshTangents = TargetMeshTangents ;
2021-09-13 17:51:19 -04:00
}
2021-11-24 12:49:18 -05:00
if ( static_cast < bool > ( CachedBakeSettings . BakeMapTypes & EBakeMapType : : Texture ) )
2021-09-13 17:51:19 -04:00
{
Op - > CachedColorImages = CachedColorImages ;
Op - > CachedMeshToColorImageMap = CachedMeshToColorImagesMap ;
}
return Op ;
}
2022-01-28 18:40:54 -05:00
void UBakeMultiMeshAttributeMapsTool : : OnShutdown ( EToolShutdownType ShutdownType )
2021-09-13 17:51:19 -04:00
{
2021-11-08 21:54:53 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE ( UBakeMultiMeshAttributeMapsTool : : Shutdown ) ;
2022-01-28 18:40:54 -05:00
Super : : OnShutdown ( ShutdownType ) ;
2021-09-13 17:51:19 -04:00
Settings - > SaveProperties ( this ) ;
2021-11-24 19:19:10 -05:00
InputMeshSettings - > SaveProperties ( this ) ;
2021-09-13 17:51:19 -04:00
if ( Compute )
{
Compute - > Shutdown ( ) ;
}
2022-02-24 23:37:17 -05:00
DetailMeshScene = nullptr ;
2021-09-13 17:51:19 -04:00
// Restore visibility of detail targets
const int NumTargets = Targets . Num ( ) ;
for ( int Idx = 1 ; Idx < NumTargets ; + + Idx )
{
UE : : ToolTarget : : ShowSourceObject ( Targets [ Idx ] ) ;
}
if ( ShutdownType = = EToolShutdownType : : Accept )
{
2021-11-02 13:58:07 -04:00
IStaticMeshBackedTarget * StaticMeshTarget = Cast < IStaticMeshBackedTarget > ( Targets [ 0 ] ) ;
UObject * SourceAsset = StaticMeshTarget ? StaticMeshTarget - > GetStaticMesh ( ) : nullptr ;
const UPrimitiveComponent * SourceComponent = UE : : ToolTarget : : GetTargetComponent ( Targets [ 0 ] ) ;
2021-11-30 16:34:23 -05:00
CreateTextureAssets ( ResultSettings - > Result , SourceComponent - > GetWorld ( ) , SourceAsset ) ;
2021-09-13 17:51:19 -04:00
}
}
void UBakeMultiMeshAttributeMapsTool : : UpdateResult ( )
{
2021-11-03 21:48:49 -04:00
if ( OpState = = EBakeOpState : : Clean )
2021-09-13 17:51:19 -04:00
{
return ;
}
// clear warning (ugh)
GetToolManager ( ) - > DisplayMessage ( FText ( ) , EToolMessageLevel : : UserWarning ) ;
2021-11-18 14:37:34 -05:00
const int32 ImageSize = static_cast < int32 > ( Settings - > Resolution ) ;
2021-09-13 17:51:19 -04:00
const FImageDimensions Dimensions ( ImageSize , ImageSize ) ;
2021-11-24 12:49:18 -05:00
FBakeSettings BakeSettings ;
BakeSettings . Dimensions = Dimensions ;
BakeSettings . BitDepth = Settings - > BitDepth ;
2021-11-24 19:19:10 -05:00
BakeSettings . TargetUVLayer = InputMeshSettings - > TargetUVLayerNamesList . IndexOfByKey ( InputMeshSettings - > TargetUVLayer ) ;
BakeSettings . ProjectionDistance = InputMeshSettings - > ProjectionDistance ;
2021-11-24 12:49:18 -05:00
BakeSettings . bProjectionInWorldSpace = true ; // Always world space
BakeSettings . SamplesPerPixel = static_cast < int32 > ( Settings - > SamplesPerPixel ) ;
2021-09-13 17:51:19 -04:00
2021-11-02 13:58:07 -04:00
// Record the original map types and process the raw bitfield which may add
// additional targets.
2021-11-24 12:49:18 -05:00
BakeSettings . SourceBakeMapTypes = static_cast < EBakeMapType > ( Settings - > MapTypes ) ;
BakeSettings . BakeMapTypes = GetMapTypes ( Settings - > MapTypes ) ;
2021-09-13 17:51:19 -04:00
// update bake cache settings
2021-11-24 12:49:18 -05:00
if ( ! ( CachedBakeSettings = = BakeSettings ) )
2021-09-13 17:51:19 -04:00
{
2021-11-24 12:49:18 -05:00
CachedBakeSettings = BakeSettings ;
2021-09-13 17:51:19 -04:00
CachedDetailSettings = FBakeMultiMeshDetailSettings ( ) ;
}
// Clear our invalid bitflag to check again for valid inputs.
2021-11-03 21:48:49 -04:00
OpState & = ~ EBakeOpState : : Invalid ;
2021-09-13 17:51:19 -04:00
2021-12-13 13:43:18 -05:00
OpState | = UpdateResult_TargetMeshTangents ( CachedBakeSettings . BakeMapTypes ) ;
2022-04-12 18:19:08 -04:00
OpState | = UpdateResult_SampleFilterMask ( Settings - > SampleFilterMask ) ;
2021-09-13 17:51:19 -04:00
// Update map type settings
OpState | = UpdateResult_DetailMeshes ( ) ;
// Early exit if op input parameters are invalid.
2021-11-18 14:37:34 -05:00
if ( static_cast < bool > ( OpState & EBakeOpState : : Invalid ) )
2021-09-13 17:51:19 -04:00
{
2021-10-19 15:13:28 -04:00
InvalidateResults ( ) ;
2021-09-13 17:51:19 -04:00
return ;
}
// This should be the only point of compute invalidation to
// minimize synchronization issues.
2021-11-02 13:58:07 -04:00
InvalidateCompute ( ) ;
2021-09-13 17:51:19 -04:00
}
EBakeOpState UBakeMultiMeshAttributeMapsTool : : UpdateResult_DetailMeshes ( )
{
FBakeMultiMeshDetailSettings NewSettings ;
2021-11-24 19:19:10 -05:00
// Iterate through our detail properties to build our detail mesh data.
const int32 NumDetail = InputMeshSettings - > SourceMeshes . Num ( ) ;
2021-09-13 17:51:19 -04:00
CachedColorImages . SetNum ( NumDetail ) ;
CachedColorUVLayers . SetNum ( NumDetail ) ;
TMap < UActorComponent * , int > ActorToDataMap ;
for ( int Idx = 0 ; Idx < NumDetail ; + + Idx )
{
UActorComponent * ActorComponent = UE : : ToolTarget : : GetTargetComponent ( Targets [ Idx + 1 ] ) ;
ActorToDataMap . Emplace ( ActorComponent , Idx ) ;
// Color map data
2021-11-24 12:49:18 -05:00
if ( static_cast < bool > ( CachedBakeSettings . BakeMapTypes & EBakeMapType : : Texture ) )
2021-09-13 17:51:19 -04:00
{
2021-11-24 19:19:10 -05:00
UTexture2D * ColorMapSourceTexture = InputMeshSettings - > SourceMeshes [ Idx ] . SourceTexture ;
const int ColorMapUVLayer = InputMeshSettings - > SourceMeshes [ Idx ] . SourceTextureUVLayer ;
2021-09-13 17:51:19 -04:00
if ( ! ColorMapSourceTexture )
{
GetToolManager ( ) - > DisplayMessage ( LOCTEXT ( " InvalidTextureWarning " , " The Source Texture is not valid " ) , EToolMessageLevel : : UserWarning ) ;
return EBakeOpState : : Invalid ;
}
TSharedPtr < UE : : Geometry : : TImageBuilder < FVector4f > , ESPMode : : ThreadSafe > ColorTextureImage = MakeShared < UE : : Geometry : : TImageBuilder < FVector4f > , ESPMode : : ThreadSafe > ( ) ;
if ( ! UE : : AssetUtils : : ReadTexture ( ColorMapSourceTexture , * ColorTextureImage , bPreferPlatformData ) )
{
GetToolManager ( ) - > DisplayMessage ( LOCTEXT ( " CannotReadTextureWarning " , " Cannot read from the source texture " ) , EToolMessageLevel : : UserWarning ) ;
return EBakeOpState : : Invalid ;
}
CachedColorImages [ Idx ] = ColorTextureImage ;
CachedColorUVLayers [ Idx ] = ColorMapUVLayer ;
}
}
// Iterate through mesh scene adapter and build mesh to data maps.
CachedMeshToColorImagesMap . Empty ( ) ;
auto BuildMeshToDataMaps = [ this , ActorToDataMap ] ( const FActorAdapter * , const FActorChildMesh * ChildMesh )
{
if ( ChildMesh )
{
if ( const int * DataIndex = ActorToDataMap . Find ( ChildMesh - > SourceComponent ) )
{
2021-11-24 12:49:18 -05:00
if ( static_cast < bool > ( CachedBakeSettings . BakeMapTypes & EBakeMapType : : Texture ) )
2021-09-13 17:51:19 -04:00
{
CachedMeshToColorImagesMap . Emplace (
2022-04-21 13:18:38 -04:00
( void * ) ChildMesh ,
2021-09-13 17:51:19 -04:00
FTextureImageData ( CachedColorImages [ * DataIndex ] . Get ( ) , CachedColorUVLayers [ * DataIndex ] ) ) ;
}
}
}
} ;
2022-02-24 23:37:17 -05:00
DetailMeshScene - > ProcessActorChildMeshes ( BuildMeshToDataMaps ) ;
2021-11-03 21:48:49 -04:00
// This method will always force a re-evaluation.
return EBakeOpState : : Evaluate ;
2021-09-13 17:51:19 -04:00
}
void UBakeMultiMeshAttributeMapsTool : : UpdateVisualization ( )
{
PreviewMesh - > SetOverrideRenderMaterial ( PreviewMaterial ) ;
2021-10-21 10:53:58 -04:00
// Populate Settings->Result from CachedMaps
for ( const TTuple < EBakeMapType , TObjectPtr < UTexture2D > > & Map : CachedMaps )
2021-09-13 17:51:19 -04:00
{
2021-11-30 16:34:23 -05:00
if ( ResultSettings - > Result . Contains ( Map . Get < 0 > ( ) ) )
2021-10-21 10:53:58 -04:00
{
2021-11-30 16:34:23 -05:00
ResultSettings - > Result [ Map . Get < 0 > ( ) ] = Map . Get < 1 > ( ) ;
2021-10-21 10:53:58 -04:00
}
2021-09-13 17:51:19 -04:00
}
2021-11-30 16:34:23 -05:00
UpdatePreview ( Settings - > MapPreview , Settings - > MapPreviewNamesMap ) ;
2021-09-13 17:51:19 -04:00
}
void UBakeMultiMeshAttributeMapsTool : : UpdateOnModeChange ( )
{
2021-11-30 16:34:23 -05:00
OnMapTypesUpdated (
static_cast < EBakeMapType > ( Settings - > MapTypes ) ,
ResultSettings - > Result ,
Settings - > MapPreview ,
Settings - > MapPreviewNamesList ,
Settings - > MapPreviewNamesMap ) ;
2021-10-19 15:13:28 -04:00
}
2021-11-02 13:58:07 -04:00
2021-10-19 15:13:28 -04:00
void UBakeMultiMeshAttributeMapsTool : : InvalidateResults ( )
{
2021-11-30 16:34:23 -05:00
for ( TTuple < EBakeMapType , TObjectPtr < UTexture2D > > & Result : ResultSettings - > Result )
2021-10-21 10:53:58 -04:00
{
Result . Get < 1 > ( ) = nullptr ;
}
2021-09-13 17:51:19 -04:00
}
2021-11-02 13:58:07 -04:00
void UBakeMultiMeshAttributeMapsTool : : GatherAnalytics ( FBakeAnalytics : : FMeshSettings & Data )
{
if ( FEngineAnalytics : : IsAvailable ( ) )
{
2021-11-24 12:49:18 -05:00
Data . NumTargetMeshTris = TargetMesh . TriangleCount ( ) ;
2021-11-02 13:58:07 -04:00
Data . NumDetailMesh = 0 ;
Data . NumDetailMeshTris = 0 ;
2022-02-24 23:37:17 -05:00
DetailMeshScene - > ProcessActorChildMeshes ( [ & Data ] ( const FActorAdapter * ActorAdapter , const FActorChildMesh * ChildMesh )
2021-11-02 13:58:07 -04:00
{
if ( ChildMesh )
{
+ + Data . NumDetailMesh ;
Data . NumDetailMeshTris + = ChildMesh - > MeshSpatial - > GetTriangleCount ( ) ;
}
} ) ;
}
}
2021-10-19 15:13:28 -04:00
2021-09-13 17:51:19 -04:00
# undef LOCTEXT_NAMESPACE