2019-01-17 18:54:05 -05:00
// Copyright 1998-2019 Epic Games, Inc. All Rights Reserved.
2017-06-30 12:21:06 -04:00
# include "MeshMergeUtilities.h"
# include "Engine/MapBuildDataRegistry.h"
# include "Engine/MeshMerging.h"
# include "MaterialOptions.h"
# include "IMaterialBakingModule.h"
# include "Misc/PackageName.h"
# include "MaterialUtilities.h"
# include "Components/SkeletalMeshComponent.h"
# include "Components/SplineMeshComponent.h"
# include "Components/SkinnedMeshComponent.h"
# include "Components/ShapeComponent.h"
# include "SkeletalMeshTypes.h"
# include "SkeletalRenderPublic.h"
# include "UObject/UObjectBaseUtility.h"
# include "UObject/Package.h"
# include "Materials/Material.h"
# include "Misc/ScopedSlowTask.h"
# include "Modules/ModuleManager.h"
# include "HierarchicalLODUtilitiesModule.h"
# include "MeshMergeData.h"
# include "IHierarchicalLODUtilities.h"
# include "Engine/MeshMergeCullingVolume.h"
# include "Landscape.h"
# include "LandscapeProxy.h"
# include "Editor.h"
# include "ProxyGenerationProcessor.h"
# include "Editor/EditorPerProjectUserSettings.h"
# include "ProxyMaterialUtilities.h"
# include "Engine/StaticMesh.h"
# include "PhysicsEngine/ConvexElem.h"
# include "PhysicsEngine/BodySetup.h"
# include "MeshUtilities.h"
# include "ImageUtils.h"
# include "LandscapeHeightfieldCollisionComponent.h"
# include "IMeshReductionManagerModule.h"
2018-02-14 14:13:42 -05:00
# include "IMeshReductionInterfaces.h"
2017-06-30 12:21:06 -04:00
# include "ProxyGenerationProcessor.h"
# include "IMaterialBakingAdapter.h"
# include "StaticMeshComponentAdapter.h"
# include "SkeletalMeshAdapter.h"
# include "StaticMeshAdapter.h"
# include "MeshMergeEditorExtensions.h"
# include "MeshMergeDataTracker.h"
# include "Misc/FileHelper.h"
# include "MeshMergeHelpers.h"
# include "Settings/EditorExperimentalSettings.h"
# include "MaterialBakingStructures.h"
# include "Async/ParallelFor.h"
2018-03-06 13:26:20 -05:00
# include "ScopedTransaction.h"
# include "Components/InstancedStaticMeshComponent.h"
# include "Engine/LODActor.h"
# include "HierarchicalLODVolume.h"
# include "Engine/Selection.h"
# include "MaterialBakingHelpers.h"
# include "IMeshMergeExtension.h"
2019-01-17 18:54:05 -05:00
# include "RawMesh.h"
# include "MeshDescription.h"
# include "MeshAttributes.h"
# include "MeshAttributeArray.h"
# include "MeshDescriptionOperations.h"
2018-03-06 13:26:20 -05:00
# if WITH_EDITOR
# include "Widgets/Notifications/SNotificationList.h"
# include "Framework/Notifications/NotificationManager.h"
# endif // WITH_EDITOR
2017-06-30 12:21:06 -04:00
# define LOCTEXT_NAMESPACE "MeshMergeUtils"
DEFINE_LOG_CATEGORY ( LogMeshMerging ) ;
FMeshMergeUtilities : : FMeshMergeUtilities ( )
{
2018-05-23 21:04:31 -04:00
Processor = new FProxyGenerationProcessor ( this ) ;
2017-06-30 12:21:06 -04:00
2018-02-14 14:13:42 -05:00
// Add callback for registering editor extensions with Skeletal/Static mesh editor
ModuleLoadedDelegateHandle = FModuleManager : : Get ( ) . OnModulesChanged ( ) . AddStatic ( & FMeshMergeEditorExtensions : : OnModulesChanged ) ;
2017-06-30 12:21:06 -04:00
}
FMeshMergeUtilities : : ~ FMeshMergeUtilities ( )
{
FModuleManager : : Get ( ) . OnModulesChanged ( ) . Remove ( ModuleLoadedDelegateHandle ) ;
FMeshMergeEditorExtensions : : RemoveExtenders ( ) ;
}
void FMeshMergeUtilities : : BakeMaterialsForComponent ( TArray < TWeakObjectPtr < UObject > > & OptionObjects , IMaterialBakingAdapter * Adapter ) const
{
// Try and find material (merge) options from provided set of objects
TWeakObjectPtr < UObject > * MaterialOptionsObject = OptionObjects . FindByPredicate ( [ ] ( TWeakObjectPtr < UObject > Object )
{
return Cast < UMaterialOptions > ( Object . Get ( ) ) ! = nullptr ;
} ) ;
TWeakObjectPtr < UObject > * MaterialMergeOptionsObject = OptionObjects . FindByPredicate ( [ ] ( TWeakObjectPtr < UObject > Object )
{
return Cast < UMaterialMergeOptions > ( Object . Get ( ) ) ! = nullptr ;
} ) ;
UMaterialOptions * MaterialOptions = MaterialOptionsObject ? Cast < UMaterialOptions > ( MaterialOptionsObject - > Get ( ) ) : nullptr ;
checkf ( MaterialOptions , TEXT ( " No valid material options found " ) ) ;
UMaterialMergeOptions * MaterialMergeOptions = MaterialMergeOptionsObject ? Cast < UMaterialMergeOptions > ( MaterialMergeOptionsObject - > Get ( ) ) : nullptr ;
// Mesh / LOD index
2019-01-17 18:54:05 -05:00
TMap < uint32 , FMeshDescription > RawMeshLODs ;
2017-06-30 12:21:06 -04:00
// LOD index, <original section index, unique section index>
TMultiMap < uint32 , TPair < uint32 , uint32 > > UniqueSectionIndexPerLOD ;
// Unique set of sections in mesh
TArray < FSectionInfo > UniqueSections ;
TArray < FSectionInfo > Sections ;
int32 NumLODs = Adapter - > GetNumberOfLODs ( ) ;
// Retrieve raw mesh data and unique sections
for ( int32 LODIndex = 0 ; LODIndex < NumLODs ; + + LODIndex )
{
// Reset section for reuse
Sections . SetNum ( 0 , false ) ;
// Extract raw mesh data
const bool bProcessedLOD = MaterialOptions - > LODIndices . Contains ( LODIndex ) ;
if ( bProcessedLOD )
{
2019-01-17 18:54:05 -05:00
FMeshDescription & RawMesh = RawMeshLODs . Add ( LODIndex ) ;
UStaticMesh : : RegisterMeshAttributes ( RawMesh ) ;
2017-06-30 12:21:06 -04:00
Adapter - > RetrieveRawMeshData ( LODIndex , RawMesh , MaterialOptions - > bUseMeshData ) ;
}
// Extract sections for given LOD index from the mesh
Adapter - > RetrieveMeshSections ( LODIndex , Sections ) ;
for ( int32 SectionIndex = 0 ; SectionIndex < Sections . Num ( ) ; + + SectionIndex )
{
FSectionInfo & Section = Sections [ SectionIndex ] ;
Section . bProcessed = bProcessedLOD ;
const int32 UniqueIndex = UniqueSections . AddUnique ( Section ) ;
UniqueSectionIndexPerLOD . Add ( LODIndex , TPair < uint32 , uint32 > ( SectionIndex , UniqueIndex ) ) ;
}
}
TArray < UMaterialInterface * > UniqueMaterials ;
TMap < UMaterialInterface * , int32 > MaterialIndices ;
TMultiMap < uint32 , uint32 > SectionToMaterialMap ;
// Populate list of unique materials and store section mappings
for ( int32 SectionIndex = 0 ; SectionIndex < UniqueSections . Num ( ) ; + + SectionIndex )
{
FSectionInfo & Section = UniqueSections [ SectionIndex ] ;
const int32 UniqueIndex = UniqueMaterials . AddUnique ( Section . Material ) ;
SectionToMaterialMap . Add ( UniqueIndex , SectionIndex ) ;
}
2017-09-11 10:43:35 -04:00
TArray < bool > bMaterialUsesVertexData ;
DetermineMaterialVertexDataUsage ( bMaterialUsesVertexData , UniqueMaterials , MaterialOptions ) ;
2017-06-30 12:21:06 -04:00
TArray < FMeshData > GlobalMeshSettings ;
TArray < FMaterialData > GlobalMaterialSettings ;
TMultiMap < uint32 , TPair < uint32 , uint32 > > OutputMaterialsMap ;
for ( int32 MaterialIndex = 0 ; MaterialIndex < UniqueMaterials . Num ( ) ; + + MaterialIndex )
{
UMaterialInterface * Material = UniqueMaterials [ MaterialIndex ] ;
2017-09-11 10:43:35 -04:00
const bool bDoesMaterialUseVertexData = bMaterialUsesVertexData [ MaterialIndex ] ;
2017-06-30 12:21:06 -04:00
// Retrieve all sections using this material
TArray < uint32 > SectionIndices ;
SectionToMaterialMap . MultiFind ( MaterialIndex , SectionIndices ) ;
if ( MaterialOptions - > bUseMeshData )
{
for ( const int32 LODIndex : MaterialOptions - > LODIndices )
{
TArray < TPair < uint32 , uint32 > > IndexPairs ;
UniqueSectionIndexPerLOD . MultiFind ( LODIndex , IndexPairs ) ;
FMeshData MeshSettings ;
2019-01-17 18:54:05 -05:00
MeshSettings . RawMeshDescription = nullptr ;
2017-06-30 12:21:06 -04:00
// Add material indices used for rendering out material
for ( const TPair < uint32 , uint32 > & Pair : IndexPairs )
{
if ( SectionIndices . Contains ( Pair . Value ) )
{
MeshSettings . MaterialIndices . Add ( Pair . Key ) ;
}
}
if ( MeshSettings . MaterialIndices . Num ( ) )
{
// Retrieve raw mesh
2019-01-17 18:54:05 -05:00
MeshSettings . RawMeshDescription = RawMeshLODs . Find ( LODIndex ) ;
//Should not be using mesh data if there is no mesh
check ( MeshSettings . RawMeshDescription ) ;
2017-06-30 12:21:06 -04:00
MeshSettings . TextureCoordinateBox = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
2019-01-17 18:54:05 -05:00
const bool bUseVertexColor = FMeshDescriptionOperations : : HasVertexColor ( * ( MeshSettings . RawMeshDescription ) ) ;
2017-09-11 10:43:35 -04:00
if ( MaterialOptions - > bUseSpecificUVIndex )
{
MeshSettings . TextureCoordinateIndex = MaterialOptions - > TextureCoordinateIndex ;
}
// if you use vertex color, we can't rely on overlapping UV channel, so use light map UV to unwrap UVs
else if ( bUseVertexColor )
{
MeshSettings . TextureCoordinateIndex = Adapter - > LightmapUVIndex ( ) ;
}
else
{
MeshSettings . TextureCoordinateIndex = 0 ;
}
2017-06-30 12:21:06 -04:00
Adapter - > ApplySettings ( LODIndex , MeshSettings ) ;
// In case part of the UVs is not within the 0-1 range try to use the lightmap UVs
2019-01-17 18:54:05 -05:00
const bool bNeedsUniqueUVs = FMeshMergeHelpers : : CheckWrappingUVs ( * ( MeshSettings . RawMeshDescription ) , MeshSettings . TextureCoordinateIndex ) ;
2017-06-30 12:21:06 -04:00
const int32 LightMapUVIndex = Adapter - > LightmapUVIndex ( ) ;
2019-01-17 18:54:05 -05:00
TVertexInstanceAttributesConstRef < FVector2D > VertexInstanceUVs = MeshSettings . RawMeshDescription - > VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
if ( bNeedsUniqueUVs & & MeshSettings . TextureCoordinateIndex ! = LightMapUVIndex & & VertexInstanceUVs . GetNumElements ( ) > 0 & & VertexInstanceUVs . GetNumIndices ( ) > LightMapUVIndex )
2017-06-30 12:21:06 -04:00
{
MeshSettings . TextureCoordinateIndex = LightMapUVIndex ;
}
FMaterialData MaterialSettings ;
MaterialSettings . Material = Material ;
// Add all user defined properties for baking out
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( ! Entry . bUseConstantValue & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
2018-03-27 16:13:55 -04:00
int32 NumTextureCoordinates ;
bool bUsesVertexData ;
Material - > AnalyzeMaterialProperty ( Entry . Property , NumTextureCoordinates , bUsesVertexData ) ;
2017-06-30 12:21:06 -04:00
MaterialSettings . PropertySizes . Add ( Entry . Property , Entry . bUseCustomSize ? Entry . CustomSize : MaterialOptions - > TextureSize ) ;
}
}
// For each original material index add an entry to the corresponding LOD and bake output index
for ( int32 Index : MeshSettings . MaterialIndices )
{
OutputMaterialsMap . Add ( LODIndex , TPair < uint32 , uint32 > ( Index , GlobalMeshSettings . Num ( ) ) ) ;
}
GlobalMeshSettings . Add ( MeshSettings ) ;
GlobalMaterialSettings . Add ( MaterialSettings ) ;
}
}
}
else
{
// If we are not using the mesh data we aren't doing anything special, just bake out uv range
FMeshData MeshSettings ;
for ( int32 LODIndex : MaterialOptions - > LODIndices )
{
TArray < TPair < uint32 , uint32 > > IndexPairs ;
UniqueSectionIndexPerLOD . MultiFind ( LODIndex , IndexPairs ) ;
for ( const TPair < uint32 , uint32 > & Pair : IndexPairs )
{
if ( SectionIndices . Contains ( Pair . Value ) )
{
MeshSettings . MaterialIndices . Add ( Pair . Key ) ;
}
}
}
if ( MeshSettings . MaterialIndices . Num ( ) )
{
2019-01-17 18:54:05 -05:00
MeshSettings . RawMeshDescription = nullptr ;
2017-06-30 12:21:06 -04:00
MeshSettings . TextureCoordinateBox = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
MeshSettings . TextureCoordinateIndex = 0 ;
FMaterialData MaterialSettings ;
MaterialSettings . Material = Material ;
// Add all user defined properties for baking out
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( ! Entry . bUseConstantValue & & Material - > IsPropertyActive ( Entry . Property ) & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
MaterialSettings . PropertySizes . Add ( Entry . Property , Entry . bUseCustomSize ? Entry . CustomSize : MaterialOptions - > TextureSize ) ;
}
}
for ( int32 LODIndex : MaterialOptions - > LODIndices )
{
TArray < TPair < uint32 , uint32 > > IndexPairs ;
UniqueSectionIndexPerLOD . MultiFind ( LODIndex , IndexPairs ) ;
for ( const TPair < uint32 , uint32 > & Pair : IndexPairs )
{
if ( SectionIndices . Contains ( Pair . Value ) )
{
/// For each original material index add an entry to the corresponding LOD and bake output index
OutputMaterialsMap . Add ( LODIndex , TPair < uint32 , uint32 > ( Pair . Key , GlobalMeshSettings . Num ( ) ) ) ;
}
}
}
GlobalMeshSettings . Add ( MeshSettings ) ;
GlobalMaterialSettings . Add ( MaterialSettings ) ;
}
}
}
TArray < FMeshData * > MeshSettingPtrs ;
for ( int32 SettingsIndex = 0 ; SettingsIndex < GlobalMeshSettings . Num ( ) ; + + SettingsIndex )
{
MeshSettingPtrs . Add ( & GlobalMeshSettings [ SettingsIndex ] ) ;
}
TArray < FMaterialData * > MaterialSettingPtrs ;
for ( int32 SettingsIndex = 0 ; SettingsIndex < GlobalMaterialSettings . Num ( ) ; + + SettingsIndex )
{
MaterialSettingPtrs . Add ( & GlobalMaterialSettings [ SettingsIndex ] ) ;
}
TArray < FBakeOutput > BakeOutputs ;
IMaterialBakingModule & Module = FModuleManager : : Get ( ) . LoadModuleChecked < IMaterialBakingModule > ( " MaterialBaking " ) ;
Module . BakeMaterials ( MaterialSettingPtrs , MeshSettingPtrs , BakeOutputs ) ;
// Append constant properties which did not require baking out
TArray < FColor > ConstantData ;
FIntPoint ConstantSize ( 1 , 1 ) ;
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( Entry . bUseConstantValue & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
ConstantData . SetNum ( 1 , false ) ;
ConstantData [ 0 ] = FColor ( Entry . ConstantValue * 255.0f , Entry . ConstantValue * 255.0f , Entry . ConstantValue * 255.0f ) ;
for ( FBakeOutput & Ouput : BakeOutputs )
{
Ouput . PropertyData . Add ( Entry . Property , ConstantData ) ;
Ouput . PropertySizes . Add ( Entry . Property , ConstantSize ) ;
}
}
}
TArray < UMaterialInterface * > NewMaterials ;
FString PackageName = Adapter - > GetBaseName ( ) ;
const FGuid NameGuid = FGuid : : NewGuid ( ) ;
for ( int32 OutputIndex = 0 ; OutputIndex < BakeOutputs . Num ( ) ; + + OutputIndex )
{
// Create merged material asset
FString MaterialAssetName = TEXT ( " M_ " ) + FPackageName : : GetShortName ( PackageName ) + TEXT ( " _ " ) + MaterialSettingPtrs [ OutputIndex ] - > Material - > GetName ( ) + TEXT ( " _ " ) + NameGuid . ToString ( ) ;
FString MaterialPackageName = FPackageName : : GetLongPackagePath ( PackageName ) + TEXT ( " / " ) + MaterialAssetName ;
FBakeOutput & Output = BakeOutputs [ OutputIndex ] ;
// Optimize output
for ( auto DataPair : Output . PropertyData )
{
FMaterialUtilities : : OptimizeSampleArray ( DataPair . Value , Output . PropertySizes [ DataPair . Key ] ) ;
}
UMaterialInterface * Material = nullptr ;
if ( Adapter - > GetOuter ( ) )
{
Material = FMaterialUtilities : : CreateProxyMaterialAndTextures ( Adapter - > GetOuter ( ) , MaterialAssetName , Output , * MeshSettingPtrs [ OutputIndex ] , * MaterialSettingPtrs [ OutputIndex ] , MaterialOptions ) ;
}
else
{
Material = FMaterialUtilities : : CreateProxyMaterialAndTextures ( MaterialPackageName , MaterialAssetName , Output , * MeshSettingPtrs [ OutputIndex ] , * MaterialSettingPtrs [ OutputIndex ] , MaterialOptions ) ;
}
NewMaterials . Add ( Material ) ;
}
// Retrieve material indices which were not baked out and should still be part of the final asset
TArray < int32 > NonReplaceMaterialIndices ;
for ( int32 MaterialIndex = 0 ; MaterialIndex < NewMaterials . Num ( ) ; + + MaterialIndex )
{
TArray < uint32 > SectionIndices ;
SectionToMaterialMap . MultiFind ( MaterialIndex , SectionIndices ) ;
for ( int32 LODIndex = 0 ; LODIndex < NumLODs ; + + LODIndex )
{
const bool bProcessedLOD = MaterialOptions - > LODIndices . Contains ( LODIndex ) ;
if ( ! bProcessedLOD )
{
TArray < TPair < uint32 , uint32 > > IndexPairs ;
UniqueSectionIndexPerLOD . MultiFind ( LODIndex , IndexPairs ) ;
for ( TPair < uint32 , uint32 > & Pair : IndexPairs )
{
NonReplaceMaterialIndices . AddUnique ( Adapter - > GetMaterialIndex ( LODIndex , Pair . Key ) ) ;
}
}
}
}
// Remap all baked out materials to their new material indices
TMap < uint32 , uint32 > NewMaterialRemap ;
for ( int32 LODIndex : MaterialOptions - > LODIndices )
{
TArray < TPair < uint32 , uint32 > > IndexPairs ;
OutputMaterialsMap . MultiFind ( LODIndex , IndexPairs ) ;
// Key == original section index, Value == unique material index
for ( auto Pair : IndexPairs )
{
int32 SetIndex = Adapter - > GetMaterialIndex ( LODIndex , Pair . Key ) ;
if ( ! NonReplaceMaterialIndices . Contains ( SetIndex ) )
{
Adapter - > SetMaterial ( SetIndex , NewMaterials [ Pair . Value ] ) ;
}
else
{
const FSectionInfo & SectionInfo = UniqueSections [ Pair . Key ] ;
// Check if this material was processed and a new entry already exists
if ( uint32 * ExistingIndex = NewMaterialRemap . Find ( Pair . Value ) )
{
Adapter - > RemapMaterialIndex ( LODIndex , Pair . Key , * ExistingIndex ) ;
}
else
{
// Add new material
const int32 NewMaterialIndex = Adapter - > AddMaterial ( NewMaterials [ Pair . Value ] ) ;
NewMaterialRemap . Add ( Pair . Value , NewMaterialIndex ) ;
Adapter - > RemapMaterialIndex ( LODIndex , Pair . Key , NewMaterialIndex ) ;
}
}
}
}
2017-09-11 10:43:35 -04:00
Adapter - > UpdateUVChannelData ( ) ;
2019-01-17 18:54:05 -05:00
GlobalMeshSettings . Empty ( ) ;
2017-06-30 12:21:06 -04:00
}
void FMeshMergeUtilities : : BakeMaterialsForComponent ( USkeletalMeshComponent * SkeletalMeshComponent ) const
{
// Retrieve settings object
UMaterialOptions * MaterialOptions = DuplicateObject ( GetMutableDefault < UMaterialOptions > ( ) , GetTransientPackage ( ) ) ;
UAssetBakeOptions * AssetOptions = GetMutableDefault < UAssetBakeOptions > ( ) ;
UMaterialMergeOptions * MergeOptions = GetMutableDefault < UMaterialMergeOptions > ( ) ;
TArray < TWeakObjectPtr < UObject > > Objects { MergeOptions , AssetOptions , MaterialOptions } ;
2018-03-06 13:26:20 -05:00
const int32 NumLODs = SkeletalMeshComponent - > SkeletalMesh - > GetLODNum ( ) ;
2017-06-30 12:21:06 -04:00
IMaterialBakingModule & Module = FModuleManager : : Get ( ) . LoadModuleChecked < IMaterialBakingModule > ( " MaterialBaking " ) ;
if ( ! Module . SetupMaterialBakeSettings ( Objects , NumLODs ) )
{
return ;
}
// Bake out materials for skeletal mesh
FSkeletalMeshComponentAdapter Adapter ( SkeletalMeshComponent ) ;
BakeMaterialsForComponent ( Objects , & Adapter ) ;
SkeletalMeshComponent - > MarkRenderStateDirty ( ) ;
}
void FMeshMergeUtilities : : BakeMaterialsForComponent ( UStaticMeshComponent * StaticMeshComponent ) const
{
// Retrieve settings object
UMaterialOptions * MaterialOptions = DuplicateObject ( GetMutableDefault < UMaterialOptions > ( ) , GetTransientPackage ( ) ) ;
UAssetBakeOptions * AssetOptions = GetMutableDefault < UAssetBakeOptions > ( ) ;
UMaterialMergeOptions * MergeOptions = GetMutableDefault < UMaterialMergeOptions > ( ) ;
TArray < TWeakObjectPtr < UObject > > Objects { MergeOptions , AssetOptions , MaterialOptions } ;
const int32 NumLODs = StaticMeshComponent - > GetStaticMesh ( ) - > GetNumLODs ( ) ;
IMaterialBakingModule & Module = FModuleManager : : Get ( ) . LoadModuleChecked < IMaterialBakingModule > ( " MaterialBaking " ) ;
if ( ! Module . SetupMaterialBakeSettings ( Objects , NumLODs ) )
{
return ;
}
// Bake out materials for static mesh component
FStaticMeshComponentAdapter Adapter ( StaticMeshComponent ) ;
BakeMaterialsForComponent ( Objects , & Adapter ) ;
StaticMeshComponent - > MarkRenderStateDirty ( ) ;
}
void FMeshMergeUtilities : : BakeMaterialsForMesh ( UStaticMesh * StaticMesh ) const
{
// Retrieve settings object
UMaterialOptions * MaterialOptions = DuplicateObject ( GetMutableDefault < UMaterialOptions > ( ) , GetTransientPackage ( ) ) ;
UAssetBakeOptions * AssetOptions = GetMutableDefault < UAssetBakeOptions > ( ) ;
UMaterialMergeOptions * MergeOptions = GetMutableDefault < UMaterialMergeOptions > ( ) ;
TArray < TWeakObjectPtr < UObject > > Objects { MergeOptions , AssetOptions , MaterialOptions } ;
const int32 NumLODs = StaticMesh - > GetNumLODs ( ) ;
IMaterialBakingModule & Module = FModuleManager : : Get ( ) . LoadModuleChecked < IMaterialBakingModule > ( " MaterialBaking " ) ;
if ( ! Module . SetupMaterialBakeSettings ( Objects , NumLODs ) )
{
return ;
}
// Bake out materials for static mesh asset
FStaticMeshAdapter Adapter ( StaticMesh ) ;
BakeMaterialsForComponent ( Objects , & Adapter ) ;
}
void FMeshMergeUtilities : : DetermineMaterialVertexDataUsage ( TArray < bool > & InOutMaterialUsesVertexData , const TArray < UMaterialInterface * > & UniqueMaterials , const UMaterialOptions * MaterialOptions ) const
{
2017-09-11 10:43:35 -04:00
InOutMaterialUsesVertexData . SetNum ( UniqueMaterials . Num ( ) ) ;
2017-06-30 12:21:06 -04:00
for ( int32 MaterialIndex = 0 ; MaterialIndex < UniqueMaterials . Num ( ) ; + + MaterialIndex )
{
UMaterialInterface * Material = UniqueMaterials [ MaterialIndex ] ;
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
// Don't have to check a property if the result is going to be constant anyway
2017-09-11 10:43:35 -04:00
if ( ! Entry . bUseConstantValue & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
int32 NumTextureCoordinates ;
bool bUsesVertexData ;
Material - > AnalyzeMaterialProperty ( Entry . Property , NumTextureCoordinates , bUsesVertexData ) ;
if ( bUsesVertexData | | NumTextureCoordinates > 1 )
{
InOutMaterialUsesVertexData [ MaterialIndex ] = true ;
break ;
}
}
}
}
}
void FMeshMergeUtilities : : ConvertOutputToFlatMaterials ( const TArray < FBakeOutput > & BakeOutputs , const TArray < FMaterialData > & MaterialData , TArray < FFlattenMaterial > & FlattenedMaterials ) const
{
for ( int32 OutputIndex = 0 ; OutputIndex < BakeOutputs . Num ( ) ; + + OutputIndex )
{
const FBakeOutput & Output = BakeOutputs [ OutputIndex ] ;
const FMaterialData & MaterialInfo = MaterialData [ OutputIndex ] ;
FFlattenMaterial Material ;
for ( TPair < EMaterialProperty , FIntPoint > SizePair : Output . PropertySizes )
{
EFlattenMaterialProperties OldProperty = NewToOldProperty ( SizePair . Key ) ;
Material . SetPropertySize ( OldProperty , SizePair . Value ) ;
Material . GetPropertySamples ( OldProperty ) . Append ( Output . PropertyData [ SizePair . Key ] ) ;
}
Material . bDitheredLODTransition = MaterialInfo . Material - > IsDitheredLODTransition ( ) ;
Material . BlendMode = BLEND_Opaque ;
Material . bTwoSided = MaterialInfo . Material - > IsTwoSided ( ) ;
Material . EmissiveScale = Output . EmissiveScale ;
FlattenedMaterials . Add ( Material ) ;
}
}
EFlattenMaterialProperties FMeshMergeUtilities : : NewToOldProperty ( int32 NewProperty ) const
{
const EFlattenMaterialProperties Remap [ MP_Refraction ] =
{
EFlattenMaterialProperties : : Emissive ,
EFlattenMaterialProperties : : Opacity ,
EFlattenMaterialProperties : : OpacityMask ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : Diffuse ,
EFlattenMaterialProperties : : Metallic ,
EFlattenMaterialProperties : : Specular ,
EFlattenMaterialProperties : : Roughness ,
EFlattenMaterialProperties : : Normal ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : NumFlattenMaterialProperties ,
EFlattenMaterialProperties : : AmbientOcclusion
} ;
return Remap [ NewProperty ] ;
}
UMaterialOptions * FMeshMergeUtilities : : PopulateMaterialOptions ( const FMaterialProxySettings & MaterialSettings ) const
{
UMaterialOptions * MaterialOptions = DuplicateObject ( GetMutableDefault < UMaterialOptions > ( ) , GetTransientPackage ( ) ) ;
MaterialOptions - > Properties . Empty ( ) ;
MaterialOptions - > TextureSize = MaterialSettings . TextureSize ;
const bool bCustomSizes = MaterialSettings . TextureSizingType = = TextureSizingType_UseManualOverrideTextureSize ;
FPropertyEntry Property ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_BaseColor , Property ) ;
2017-06-30 12:21:06 -04:00
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_Specular , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bSpecularMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_Roughness , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bRoughnessMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_Metallic , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bMetallicMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_Normal , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bNormalMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_Opacity , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bOpacityMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_OpacityMask , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bOpacityMaskMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_EmissiveColor , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bEmissiveMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
PopulatePropertyEntry ( MaterialSettings , MP_AmbientOcclusion , Property ) ;
2017-06-30 12:21:06 -04:00
if ( MaterialSettings . bAmbientOcclusionMap )
MaterialOptions - > Properties . Add ( Property ) ;
2017-09-11 10:43:35 -04:00
2017-06-30 12:21:06 -04:00
return MaterialOptions ;
}
2017-09-11 10:43:35 -04:00
void FMeshMergeUtilities : : PopulatePropertyEntry ( const FMaterialProxySettings & MaterialSettings , EMaterialProperty MaterialProperty , FPropertyEntry & InOutPropertyEntry ) const
{
InOutPropertyEntry . Property = MaterialProperty ;
switch ( MaterialSettings . TextureSizingType )
{
/** Set property output size to unique per-property user set sizes */
case TextureSizingType_UseManualOverrideTextureSize :
{
InOutPropertyEntry . bUseCustomSize = true ;
InOutPropertyEntry . CustomSize = [ MaterialSettings , MaterialProperty ] ( ) - > FIntPoint
{
switch ( MaterialProperty )
{
case MP_BaseColor : return MaterialSettings . DiffuseTextureSize ;
case MP_Specular : return MaterialSettings . SpecularTextureSize ;
case MP_Roughness : return MaterialSettings . RoughnessTextureSize ;
case MP_Metallic : return MaterialSettings . MetallicTextureSize ;
case MP_Normal : return MaterialSettings . NormalTextureSize ;
case MP_Opacity : return MaterialSettings . OpacityTextureSize ;
case MP_OpacityMask : return MaterialSettings . OpacityMaskTextureSize ;
case MP_EmissiveColor : return MaterialSettings . EmissiveTextureSize ;
case MP_AmbientOcclusion : return MaterialSettings . AmbientOcclusionTextureSize ;
default :
{
checkf ( false , TEXT ( " Invalid Material Property " ) ) ;
return FIntPoint ( ) ;
}
}
} ( ) ;
break ;
}
/** Set property output size to biased values off the TextureSize value (Normal at fullres, Diffuse at halfres, and anything else at quarter res */
case TextureSizingType_UseAutomaticBiasedSizes :
{
const FIntPoint FullRes = MaterialSettings . TextureSize ;
const FIntPoint HalfRes = FIntPoint ( FMath : : Max ( 8 , FullRes . X > > 1 ) , FMath : : Max ( 8 , FullRes . Y > > 1 ) ) ;
const FIntPoint QuarterRes = FIntPoint ( FMath : : Max ( 4 , FullRes . X > > 2 ) , FMath : : Max ( 4 , FullRes . Y > > 2 ) ) ;
InOutPropertyEntry . bUseCustomSize = true ;
InOutPropertyEntry . CustomSize = [ FullRes , HalfRes , QuarterRes , MaterialSettings , MaterialProperty ] ( ) - > FIntPoint
{
switch ( MaterialProperty )
{
case MP_Normal : return FullRes ;
case MP_BaseColor : return HalfRes ;
case MP_Specular : return QuarterRes ;
case MP_Roughness : return QuarterRes ;
case MP_Metallic : return QuarterRes ;
case MP_Opacity : return QuarterRes ;
case MP_OpacityMask : return QuarterRes ;
case MP_EmissiveColor : return QuarterRes ;
case MP_AmbientOcclusion : return QuarterRes ;
default :
{
checkf ( false , TEXT ( " Invalid Material Property " ) ) ;
return FIntPoint ( ) ;
}
}
} ( ) ;
break ;
}
/** Set all sizes to TextureSize */
case TextureSizingType_UseSingleTextureSize :
case TextureSizingType_UseSimplygonAutomaticSizing :
{
InOutPropertyEntry . bUseCustomSize = false ;
InOutPropertyEntry . CustomSize = MaterialSettings . TextureSize ;
break ;
}
}
/** Check whether or not a constant value should be used for this property */
InOutPropertyEntry . bUseConstantValue = [ MaterialSettings , MaterialProperty ] ( ) - > bool
{
switch ( MaterialProperty )
{
case MP_BaseColor : return false ;
case MP_Normal : return ! MaterialSettings . bNormalMap ;
case MP_Specular : return ! MaterialSettings . bSpecularMap ;
case MP_Roughness : return ! MaterialSettings . bRoughnessMap ;
case MP_Metallic : return ! MaterialSettings . bMetallicMap ;
case MP_Opacity : return ! MaterialSettings . bOpacityMap ;
case MP_OpacityMask : return ! MaterialSettings . bOpacityMaskMap ;
case MP_EmissiveColor : return ! MaterialSettings . bEmissiveMap ;
case MP_AmbientOcclusion : return ! MaterialSettings . bAmbientOcclusionMap ;
default :
{
checkf ( false , TEXT ( " Invalid Material Property " ) ) ;
return false ;
}
}
} ( ) ;
/** Set the value if a constant value should be used for this property */
InOutPropertyEntry . ConstantValue = [ MaterialSettings , MaterialProperty ] ( ) - > float
{
switch ( MaterialProperty )
{
case MP_BaseColor : return 1.0f ;
case MP_Normal : return 1.0f ;
case MP_Specular : return MaterialSettings . SpecularConstant ;
case MP_Roughness : return MaterialSettings . RoughnessConstant ;
case MP_Metallic : return MaterialSettings . MetallicConstant ;
case MP_Opacity : return MaterialSettings . OpacityConstant ;
case MP_OpacityMask : return MaterialSettings . OpacityMaskConstant ;
case MP_EmissiveColor : return 0.0f ;
case MP_AmbientOcclusion : return MaterialSettings . AmbientOcclusionConstant ;
default :
{
checkf ( false , TEXT ( " Invalid Material Property " ) ) ;
return 1.0f ;
}
}
} ( ) ;
}
2018-05-23 21:04:31 -04:00
void FMeshMergeUtilities : : CopyTextureRect ( const FColor * Src , const FIntPoint & SrcSize , FColor * Dst , const FIntPoint & DstSize , const FIntPoint & DstPos , bool bCopyOnlyMaskedPixels ) const
2017-06-30 12:21:06 -04:00
{
const int32 RowLength = SrcSize . X * sizeof ( FColor ) ;
FColor * RowDst = Dst + DstSize . X * DstPos . Y ;
const FColor * RowSrc = Src ;
2018-05-23 21:04:31 -04:00
if ( bCopyOnlyMaskedPixels )
2017-06-30 12:21:06 -04:00
{
2018-05-23 21:04:31 -04:00
for ( int32 RowIdx = 0 ; RowIdx < SrcSize . Y ; + + RowIdx )
{
for ( int32 ColIdx = 0 ; ColIdx < SrcSize . X ; + + ColIdx )
{
if ( RowSrc [ ColIdx ] ! = FColor : : Magenta )
{
RowDst [ DstPos . X + ColIdx ] = RowSrc [ ColIdx ] ;
}
}
RowDst + = DstSize . X ;
RowSrc + = SrcSize . X ;
}
}
else
{
for ( int32 RowIdx = 0 ; RowIdx < SrcSize . Y ; + + RowIdx )
{
FMemory : : Memcpy ( RowDst + DstPos . X , RowSrc , RowLength ) ;
RowDst + = DstSize . X ;
RowSrc + = SrcSize . X ;
}
2017-06-30 12:21:06 -04:00
}
}
void FMeshMergeUtilities : : SetTextureRect ( const FColor & ColorValue , const FIntPoint & SrcSize , FColor * Dst , const FIntPoint & DstSize , const FIntPoint & DstPos ) const
{
FColor * RowDst = Dst + DstSize . X * DstPos . Y ;
for ( int32 RowIdx = 0 ; RowIdx < SrcSize . Y ; + + RowIdx )
{
for ( int32 ColIdx = 0 ; ColIdx < SrcSize . X ; + + ColIdx )
{
RowDst [ DstPos . X + ColIdx ] = ColorValue ;
}
RowDst + = DstSize . X ;
}
}
FIntPoint FMeshMergeUtilities : : ConditionalImageResize ( const FIntPoint & SrcSize , const FIntPoint & DesiredSize , TArray < FColor > & InOutImage , bool bLinearSpace ) const
{
const int32 NumDesiredSamples = DesiredSize . X * DesiredSize . Y ;
if ( InOutImage . Num ( ) & & InOutImage . Num ( ) ! = NumDesiredSamples )
{
check ( InOutImage . Num ( ) = = SrcSize . X * SrcSize . Y ) ;
TArray < FColor > OutImage ;
if ( NumDesiredSamples > 0 )
{
FImageUtils : : ImageResize ( SrcSize . X , SrcSize . Y , InOutImage , DesiredSize . X , DesiredSize . Y , OutImage , bLinearSpace ) ;
}
Exchange ( InOutImage , OutImage ) ;
return DesiredSize ;
}
return SrcSize ;
}
2018-03-06 13:26:20 -05:00
void FMeshMergeUtilities : : MergeFlattenedMaterials ( TArray < struct FFlattenMaterial > & InMaterialList , int32 InGutter , FFlattenMaterial & OutMergedMaterial , TArray < FUVOffsetScalePair > & OutUVTransforms ) const
2017-06-30 12:21:06 -04:00
{
OutUVTransforms . Reserve ( InMaterialList . Num ( ) ) ;
// Fill output UV transforms with invalid values
for ( auto Material : InMaterialList )
{
// Invalid UV transform
FUVOffsetScalePair UVTransform ;
UVTransform . Key = FVector2D : : ZeroVector ;
UVTransform . Value = FVector2D : : ZeroVector ;
OutUVTransforms . Add ( UVTransform ) ;
}
const int32 AtlasGridSize = FMath : : CeilToInt ( FMath : : Sqrt ( InMaterialList . Num ( ) ) ) ;
OutMergedMaterial . EmissiveScale = FlattenEmissivescale ( InMaterialList ) ;
for ( int32 PropertyIndex = 0 ; PropertyIndex < ( int32 ) EFlattenMaterialProperties : : NumFlattenMaterialProperties ; + + PropertyIndex )
{
const EFlattenMaterialProperties Property = ( EFlattenMaterialProperties ) PropertyIndex ;
if ( OutMergedMaterial . ShouldGenerateDataForProperty ( Property ) )
{
const FIntPoint AtlasTextureSize = OutMergedMaterial . GetPropertySize ( Property ) ;
const FIntPoint ExportTextureSize = AtlasTextureSize / AtlasGridSize ;
const int32 AtlasNumSamples = AtlasTextureSize . X * AtlasTextureSize . Y ;
check ( OutMergedMaterial . GetPropertySize ( Property ) = = AtlasTextureSize ) ;
TArray < FColor > & Samples = OutMergedMaterial . GetPropertySamples ( Property ) ;
2018-03-06 13:26:20 -05:00
Samples . SetNumUninitialized ( AtlasNumSamples ) ;
// Fill with magenta (as we will be box blurring this later)
for ( FColor & SampleColor : Samples )
{
SampleColor = FColor ( 255 , 0 , 255 ) ;
}
2017-06-30 12:21:06 -04:00
}
}
int32 AtlasRowIdx = 0 ;
int32 AtlasColIdx = 0 ;
2018-03-06 13:26:20 -05:00
FIntPoint Gutter ( InGutter , InGutter ) ;
FIntPoint DoubleGutter ( InGutter * 2 , InGutter * 2 ) ;
FIntPoint GlobalAtlasTargetPos = Gutter ;
2017-06-30 12:21:06 -04:00
bool bSamplesWritten [ ( uint32 ) EFlattenMaterialProperties : : NumFlattenMaterialProperties ] ;
FMemory : : Memset ( bSamplesWritten , 0 ) ;
// Used to calculate UV transforms
const FIntPoint GlobalAtlasTextureSize = OutMergedMaterial . GetPropertySize ( EFlattenMaterialProperties : : Diffuse ) ;
2018-03-06 13:26:20 -05:00
const FIntPoint GlobalExportTextureSize = ( GlobalAtlasTextureSize / AtlasGridSize ) - DoubleGutter ;
const FIntPoint GlobalExportEntrySize = ( GlobalAtlasTextureSize / AtlasGridSize ) ;
2017-06-30 12:21:06 -04:00
// Flatten all materials and merge them into one material using texture atlases
for ( int32 MatIdx = 0 ; MatIdx < InMaterialList . Num ( ) ; + + MatIdx )
{
FFlattenMaterial & FlatMaterial = InMaterialList [ MatIdx ] ;
OutMergedMaterial . bTwoSided | = FlatMaterial . bTwoSided ;
OutMergedMaterial . bDitheredLODTransition = FlatMaterial . bDitheredLODTransition ;
2018-03-06 13:26:20 -05:00
2017-06-30 12:21:06 -04:00
for ( int32 PropertyIndex = 0 ; PropertyIndex < ( int32 ) EFlattenMaterialProperties : : NumFlattenMaterialProperties ; + + PropertyIndex )
{
const EFlattenMaterialProperties Property = ( EFlattenMaterialProperties ) PropertyIndex ;
2018-03-06 13:26:20 -05:00
const FIntPoint PropertyTextureSize = OutMergedMaterial . GetPropertySize ( Property ) ;
2017-06-30 12:21:06 -04:00
const int32 NumPropertySamples = PropertyTextureSize . X * PropertyTextureSize . Y ;
2018-03-06 13:26:20 -05:00
const FIntPoint PropertyAtlasTextureSize = ( PropertyTextureSize / AtlasGridSize ) - DoubleGutter ;
const FIntPoint PropertyAtlasEntrySize = ( PropertyTextureSize / AtlasGridSize ) ;
const FIntPoint AtlasTargetPos ( ( AtlasColIdx * PropertyAtlasEntrySize . X ) + InGutter , ( AtlasRowIdx * PropertyAtlasEntrySize . Y ) + InGutter ) ;
2017-06-30 12:21:06 -04:00
if ( OutMergedMaterial . ShouldGenerateDataForProperty ( Property ) & & FlatMaterial . DoesPropertyContainData ( Property ) )
{
TArray < FColor > & SourceSamples = FlatMaterial . GetPropertySamples ( Property ) ;
TArray < FColor > & TargetSamples = OutMergedMaterial . GetPropertySamples ( Property ) ;
if ( FlatMaterial . IsPropertyConstant ( Property ) )
{
SetTextureRect ( SourceSamples [ 0 ] , PropertyAtlasTextureSize , TargetSamples . GetData ( ) , PropertyTextureSize , AtlasTargetPos ) ;
}
else
{
FIntPoint PropertySize = FlatMaterial . GetPropertySize ( Property ) ;
PropertySize = ConditionalImageResize ( PropertySize , PropertyAtlasTextureSize , SourceSamples , false ) ;
CopyTextureRect ( SourceSamples . GetData ( ) , PropertyAtlasTextureSize , TargetSamples . GetData ( ) , PropertyTextureSize , AtlasTargetPos ) ;
FlatMaterial . SetPropertySize ( Property , PropertySize ) ;
}
bSamplesWritten [ PropertyIndex ] | = true ;
}
}
check ( OutUVTransforms . IsValidIndex ( MatIdx ) ) ;
2018-03-06 13:26:20 -05:00
// Offset
2017-06-30 12:21:06 -04:00
OutUVTransforms [ MatIdx ] . Key = FVector2D (
( float ) GlobalAtlasTargetPos . X / GlobalAtlasTextureSize . X ,
( float ) GlobalAtlasTargetPos . Y / GlobalAtlasTextureSize . Y ) ;
2018-03-06 13:26:20 -05:00
// Scale
2017-06-30 12:21:06 -04:00
OutUVTransforms [ MatIdx ] . Value = FVector2D (
( float ) GlobalExportTextureSize . X / GlobalAtlasTextureSize . X ,
( float ) GlobalExportTextureSize . Y / GlobalAtlasTextureSize . Y ) ;
AtlasColIdx + + ;
if ( AtlasColIdx > = AtlasGridSize )
{
AtlasColIdx = 0 ;
AtlasRowIdx + + ;
}
2018-03-06 13:26:20 -05:00
GlobalAtlasTargetPos = FIntPoint ( ( AtlasColIdx * GlobalExportEntrySize . X ) + InGutter , ( AtlasRowIdx * GlobalExportEntrySize . Y ) + InGutter ) ;
2017-06-30 12:21:06 -04:00
}
// Check if some properties weren't populated with data (which means we can empty them out)
for ( int32 PropertyIndex = 0 ; PropertyIndex < ( int32 ) EFlattenMaterialProperties : : NumFlattenMaterialProperties ; + + PropertyIndex )
{
2018-03-06 13:26:20 -05:00
EFlattenMaterialProperties Property = ( EFlattenMaterialProperties ) PropertyIndex ;
2017-06-30 12:21:06 -04:00
if ( ! bSamplesWritten [ PropertyIndex ] )
2018-03-06 13:26:20 -05:00
{
2017-06-30 12:21:06 -04:00
OutMergedMaterial . GetPropertySamples ( Property ) . Empty ( ) ;
OutMergedMaterial . SetPropertySize ( Property , FIntPoint ( 0 , 0 ) ) ;
}
2018-03-06 13:26:20 -05:00
else
{
// Smear borders
const FIntPoint PropertySize = OutMergedMaterial . GetPropertySize ( Property ) ;
2018-05-23 21:04:31 -04:00
FMaterialBakingHelpers : : PerformUVBorderSmear ( OutMergedMaterial . GetPropertySamples ( Property ) , PropertySize . X , PropertySize . Y ) ;
2018-03-06 13:26:20 -05:00
}
2017-06-30 12:21:06 -04:00
}
}
2018-05-23 21:04:31 -04:00
void FMeshMergeUtilities : : FlattenBinnedMaterials ( TArray < struct FFlattenMaterial > & InMaterialList , const TArray < FBox2D > & InMaterialBoxes , int32 InGutter , bool bCopyOnlyMaskedPixels , FFlattenMaterial & OutMergedMaterial , TArray < FUVOffsetScalePair > & OutUVTransforms ) const
2017-06-30 12:21:06 -04:00
{
OutUVTransforms . AddZeroed ( InMaterialList . Num ( ) ) ;
// Flatten emissive scale across all incoming materials
OutMergedMaterial . EmissiveScale = FlattenEmissivescale ( InMaterialList ) ;
// Merge all material properties
for ( int32 Index = 0 ; Index < ( int32 ) EFlattenMaterialProperties : : NumFlattenMaterialProperties ; + + Index )
{
const EFlattenMaterialProperties Property = ( EFlattenMaterialProperties ) Index ;
const FIntPoint & OutTextureSize = OutMergedMaterial . GetPropertySize ( Property ) ;
if ( OutTextureSize ! = FIntPoint : : ZeroValue )
{
TArray < FColor > & OutSamples = OutMergedMaterial . GetPropertySamples ( Property ) ;
OutSamples . Reserve ( OutTextureSize . X * OutTextureSize . Y ) ;
2018-03-06 13:26:20 -05:00
OutSamples . SetNumUninitialized ( OutTextureSize . X * OutTextureSize . Y ) ;
2017-06-30 12:21:06 -04:00
2018-03-06 13:26:20 -05:00
// Fill with magenta (as we will be box blurring this later)
for ( FColor & SampleColor : OutSamples )
{
SampleColor = FColor ( 255 , 0 , 255 ) ;
}
FVector2D Gutter2D ( ( float ) InGutter , ( float ) InGutter ) ;
2017-06-30 12:21:06 -04:00
bool bMaterialsWritten = false ;
for ( int32 MaterialIndex = 0 ; MaterialIndex < InMaterialList . Num ( ) ; + + MaterialIndex )
{
// Determine output size and offset
FFlattenMaterial & FlatMaterial = InMaterialList [ MaterialIndex ] ;
OutMergedMaterial . bDitheredLODTransition | = FlatMaterial . bDitheredLODTransition ;
OutMergedMaterial . bTwoSided | = FlatMaterial . bTwoSided ;
if ( FlatMaterial . DoesPropertyContainData ( Property ) )
{
const FBox2D MaterialBox = InMaterialBoxes [ MaterialIndex ] ;
const FIntPoint & InputSize = FlatMaterial . GetPropertySize ( Property ) ;
TArray < FColor > & InputSamples = FlatMaterial . GetPropertySamples ( Property ) ;
// Resize material to match output (area) size
2018-03-06 13:26:20 -05:00
FIntPoint OutputSize = FIntPoint ( ( OutTextureSize . X * MaterialBox . GetSize ( ) . X ) - ( InGutter * 2 ) , ( OutTextureSize . Y * MaterialBox . GetSize ( ) . Y ) - ( InGutter * 2 ) ) ;
2017-06-30 12:21:06 -04:00
ConditionalImageResize ( InputSize , OutputSize , InputSamples , false ) ;
// Copy material data to the merged 'atlas' texture
2018-03-06 13:26:20 -05:00
FIntPoint OutputPosition = FIntPoint ( ( OutTextureSize . X * MaterialBox . Min . X ) + InGutter , ( OutTextureSize . Y * MaterialBox . Min . Y ) + InGutter ) ;
2018-05-23 21:04:31 -04:00
CopyTextureRect ( InputSamples . GetData ( ) , OutputSize , OutSamples . GetData ( ) , OutTextureSize , OutputPosition , bCopyOnlyMaskedPixels ) ;
2017-06-30 12:21:06 -04:00
// Set the UV tranforms only once
if ( Index = = 0 )
{
FUVOffsetScalePair & UVTransform = OutUVTransforms [ MaterialIndex ] ;
2018-03-06 13:26:20 -05:00
UVTransform . Key = MaterialBox . Min + ( Gutter2D / FVector2D ( OutTextureSize ) ) ;
UVTransform . Value = MaterialBox . GetSize ( ) - ( ( Gutter2D * 2.0f ) / FVector2D ( OutTextureSize ) ) ;
2017-06-30 12:21:06 -04:00
}
bMaterialsWritten = true ;
}
}
if ( ! bMaterialsWritten )
{
OutSamples . Empty ( ) ;
OutMergedMaterial . SetPropertySize ( Property , FIntPoint ( 0 , 0 ) ) ;
}
2018-03-06 13:26:20 -05:00
else
{
// Smear borders
const FIntPoint PropertySize = OutMergedMaterial . GetPropertySize ( Property ) ;
2018-05-23 21:04:31 -04:00
FMaterialBakingHelpers : : PerformUVBorderSmear ( OutSamples , PropertySize . X , PropertySize . Y ) ;
2018-03-06 13:26:20 -05:00
}
2017-06-30 12:21:06 -04:00
}
}
}
float FMeshMergeUtilities : : FlattenEmissivescale ( TArray < struct FFlattenMaterial > & InMaterialList ) const
{
// Find maximum emissive scaling value across materials
float MaxScale = 0.0f ;
for ( const FFlattenMaterial & Material : InMaterialList )
{
MaxScale = FMath : : Max ( MaxScale , Material . EmissiveScale ) ;
}
// Renormalize samples
const float Multiplier = 1.0f / MaxScale ;
const int32 NumThreads = [ & ] ( )
{
return FPlatformProcess : : SupportsMultithreading ( ) ? FPlatformMisc : : NumberOfCores ( ) : 1 ;
} ( ) ;
const int32 MaterialsPerThread = FMath : : CeilToInt ( ( float ) InMaterialList . Num ( ) / ( float ) NumThreads ) ;
ParallelFor ( NumThreads , [ & InMaterialList , MaterialsPerThread , Multiplier , MaxScale ]
( int32 Index )
{
int32 StartIndex = FMath : : CeilToInt ( ( Index ) * MaterialsPerThread ) ;
const int32 EndIndex = FMath : : Min ( FMath : : CeilToInt ( ( Index + 1 ) * MaterialsPerThread ) , InMaterialList . Num ( ) ) ;
for ( ; StartIndex < EndIndex ; + + StartIndex )
{
FFlattenMaterial & Material = InMaterialList [ StartIndex ] ;
if ( Material . EmissiveScale ! = MaxScale )
{
for ( FColor & Sample : Material . GetPropertySamples ( EFlattenMaterialProperties : : Emissive ) )
{
2019-04-26 22:56:07 -04:00
if ( Sample ! = FColor : : Magenta )
{
Sample . R = Sample . R * Multiplier ;
Sample . G = Sample . G * Multiplier ;
Sample . B = Sample . B * Multiplier ;
Sample . A = Sample . A * Multiplier ;
}
2017-06-30 12:21:06 -04:00
}
}
}
} , NumThreads = = 1 ) ;
return MaxScale ;
}
void FMeshMergeUtilities : : CreateProxyMesh ( const TArray < AActor * > & InActors , const struct FMeshProxySettings & InMeshProxySettings , UPackage * InOuter , const FString & InProxyBasePackageName , const FGuid InGuid , const FCreateProxyDelegate & InProxyCreatedDelegate , const bool bAllowAsync , const float ScreenSize ) const
2018-03-06 13:26:20 -05:00
{
UMaterial * BaseMaterial = LoadObject < UMaterial > ( NULL , TEXT ( " /Engine/EngineMaterials/BaseFlattenMaterial.BaseFlattenMaterial " ) , NULL , LOAD_None , NULL ) ;
check ( BaseMaterial ) ;
CreateProxyMesh ( InActors , InMeshProxySettings , BaseMaterial , InOuter , InProxyBasePackageName , InGuid , InProxyCreatedDelegate , bAllowAsync , ScreenSize ) ;
}
void FMeshMergeUtilities : : CreateProxyMesh ( const TArray < UStaticMeshComponent * > & InStaticMeshComps , const struct FMeshProxySettings & InMeshProxySettings , UPackage * InOuter , const FString & InProxyBasePackageName , const FGuid InGuid , const FCreateProxyDelegate & InProxyCreatedDelegate , const bool bAllowAsync , const float ScreenSize ) const
{
UMaterial * BaseMaterial = LoadObject < UMaterial > ( NULL , TEXT ( " /Engine/EngineMaterials/BaseFlattenMaterial.BaseFlattenMaterial " ) , NULL , LOAD_None , NULL ) ;
check ( BaseMaterial ) ;
CreateProxyMesh ( InStaticMeshComps , InMeshProxySettings , BaseMaterial , InOuter , InProxyBasePackageName , InGuid , InProxyCreatedDelegate , bAllowAsync , ScreenSize ) ;
}
void FMeshMergeUtilities : : CreateProxyMesh ( const TArray < AActor * > & InActors , const struct FMeshProxySettings & InMeshProxySettings , UMaterialInterface * InBaseMaterial , UPackage * InOuter , const FString & InProxyBasePackageName , const FGuid InGuid , const FCreateProxyDelegate & InProxyCreatedDelegate , const bool bAllowAsync /*= false*/ , const float ScreenSize /*= 1.0f*/ ) const
2017-06-30 12:21:06 -04:00
{
// No actors given as input
if ( InActors . Num ( ) = = 0 )
{
UE_LOG ( LogMeshMerging , Log , TEXT ( " No actors specified to generate a proxy mesh for " ) ) ;
return ;
}
--------------------------------------
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise [at] 4341740)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 4280523 by Patrick.Boutot
Add option in AjaCustomTimeStep to wait until the frame to be ready. Previously, the frame was there but not yet processed so it was possible that it was not ready by the time we wanted to read it. It won't work with interlaced because the 2 fields are processed at the same time. In interlaced, will get a 30fps behaviour when we actually want a 60fps.
Fix bug that didn't set and reset bIsOwned properly when it was first initialized as not owned.
Change 4280526 by Patrick.Boutot
Add accessor to get the leaf media source or output.
Change 4280624 by Patrick.Boutot
Add timecode acessor to media samples
Change 4280626 by Patrick.Boutot
Rework the timing for AJA Media Player. Previously, we took the timing of the frame. That was a bad idea because if 2 incomings video frames were coming a the same time, you would only show one. Making the buffering system useless.
That affects the Custom Time Step since it was waiting for the interrupt signal and in some behavior we would like the frame to be ready to be used by UE. Same the timecode in the MediaSample because we may not used it to stamps the frame.
Change 4283022 by Patrick.Boutot
[EditorScriptingUtilitites] Check folder names invalid characters separatly from the object's name.
#jira UE-59886, UE-62333
Change 4283112 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Rename TimemanagemenetEditor module names.
Change 4283426 by JeanLuc.Corenthin
Fix crash with FBX file
#jira UE-62501
Change 4284940 by Patrick.Boutot
A widget that let you select a single permutation from a list. It groups the values into categories and removes duplicates inside that category.
Change 4285471 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Change 4286925 by Patrick.Boutot
[AJA] Add support to read LTC from the reference In.
Add more detail on video format and the device.
MediaSource use the Permutations Selection widget to select his mode and device.
Remove debugging option to trigger an AJA custom time step and timecode provider.
Remove the UYVY pixel option from AJA. It's better do to the conversion on the AJA card that on the GPU.
Change the tooltip and category for some AjaMediaSource properties.
Change 4287026 by Julien.StJean
Modifed the file STimeCodeProviderTab.cpp to fix the position of a SComboButton that wasn't properly place.
Change 4287663 by Jon.Nabozny
Add timecode messages into nDisplay, and sync those between Master and Slave
Change 4287884 by Jon.Nabozny
Create a TimecodeProvider for SystemTime and introduce a notion for DefaultTimecodeProvider in Engine.
Change 4288050 by Jon.Nabozny
Rework the TimeSynchronization implementation for usability and functionality.
Change 4288283 by Jon.Nabozny
Fixed swapped MetaClass and DisplayName options on UEngine::DefaultTimecodeProviderClassName;
Change 4288352 by Jon.Nabozny
Set TimecodeProviderClassName and DefaultTimecodeProviderClassName in BaseEngine.ini
Change 4288378 by Jon.Nabozny
Fixup some issues in TimecodeSynchronizer where code was reset improperly due to multiple unshelves / resolves.
Change 4288394 by Jon.Nabozny
Add TimeSync functionality into LiveLink. Also add test cases for this. This should allow us to easily synchronize multiple LiveLink sources together, as well as synchronize those to anything else using the sync system (Relies on CL-4235417)
Change 4288899 by Patrick.Boutot
Fix initialization order of FMediaIOCorePlayerBase variables
Change 4289157 by Patrick.Boutot
Allow the user to change the source of a capture without stopping the current capture.
[AJA] AjaMediaCapture, add support for UpdateSceneViewport & UpdateRenderTarget
[at]made by julien.stjean
Change 4291328 by Jon.Nabozny
Report the Skeleton Guid with TimeSyncData and track sync state in LiveLinkTimeSynchronizationSource.
This prevents a crash that can happen if a source is quickly cleared and reset before the next tick of Time Synchronization.
Change 4296294 by Jon.Nabozny
Fixup errors when TimecodeProviderClassName is empty. It's valid to leave this empty.
Change 4297122 by Patrick.Boutot
Media Profile with timecode provider & custom time step
Change 4301855 by Austin.Crismore
Fix for movment scaling and virtual joystick controls. Movement scaling in for truck and dolly is locked to the world xy plane, and virtual joysticks use their own method for movement scaling now.
#jira UE-61762, UE-62187
Change 4301856 by Austin.Crismore
Virtual sequence level controller now listens to on object spawned, so that it can intercept the camera actor and disable attatching to HMD to prevent camera movement that isn't from the level sequence
#jira UE-61766
Change 4301860 by Austin.Crismore
Fix for touch scrubbing. Added default values back in. Added logic to only allow scrubbing when touch focus was off.
#jira UE-61865
Change 4302294 by Jamie.Dale
Added functions to get your the localized spoken and subtitle text from a dialogue wave
Change 4304393 by Jamie.Dale
Added support for BlueprintAssignable properties in Python
Change 4305852 by Jamie.Dale
Removed hard-dependency between EditorScriptingUtilities and PythonScriptPlugin
Backed-out changelist 4259264 and query Python availability based on whether anything is available to handle the command
#jira UE-62318
Change 4308550 by Jamie.Dale
Fixed crash when passing a null world to Python actor iterators
Change 4311867 by Homam.Bahnassi
Revit master material with exposed parameters matching the API when possible.
Change 4314428 by Francis.Hurteau
Made the usage of the bBuildDeveloperTools switch independent of the bCompileAgainstEngine switch.
Changed bBuildDeveloperTools TargetRule in UnrealBuildTool to a nullable to keep the old behavior in case where bBuildDeveloperTools wasn't explicitly set in TargetRules
Change 4315134 by Jamie.Dale
Defer editable text focus selection until mouse-up to allow the user to make an initial selection
#jira UE-58086
Change 4318615 by Johan.Duparc
EditorFactories: consistent return values after asset import.
Change 4322459 by Jamie.Dale
Made SequencerScripting an Editor plugin as it depends on PythonScriptPlugin which is an Editor plugin
This was causing issues at runtime when SequencerScripting was enabled, as it failed to load PythonScriptPlugin (which hadn't been built).
Change 4323341 by Francis.Hurteau
Implement proper message bus protocol version negociation with static nodes
Change 4323733 by Francis.Hurteau
Fix VR Pausing Sequence Scrubbing just setting playback speed to 0.0
Change 4324319 by Jamie.Dale
Exposed transactions to Blueprints
Change 4325847 by Alistair.White
Copying //Tasks/UE4/Private-PixelStreaming[at]4325566 to Dev-Enterprise-Minimal (//UE4/Dev-Enterprise-Minimal)
This adds the new experimental PixelStreaming plugin to allow streaming of an Unreal client's audio & video stream to a browser through the WebRTC protocol to support new uses for enterprise customers.
Change 4326282 by Simon.Tourangeau
nDisplay native present handler
Change 4326581 by Jamie.Dale
Replacing FDateTime with int64 Ticks value to workaround UE-63485
Change 4326599 by Homam.Bahnassi
Moving texture coords outside UVEdit function to allow using different UV channels.
Change 4333250 by Francis.Hurteau
Small TFuture changes:
* cleans up TFuture::Then with usage of TUniqueFunction
* added TFuture::Reset to invalidate it and remove continuation from a future shared state
Change 4333359 by Homam.Bahnassi
Support scaling and rotating UVs around arbitrary pivot
Change 4333566 by Johan.Duparc
Expose ProxyLOD functionalities to Scripting
#jira UEENT-1788
Change 4333988 by Jamie.Dale
Allow UHT to parse FText default parameter values
INVTEXT, NSLOCTEXT, LOCTABLE, and FText::GetEmpty() are supported. LOCTEXT isn't as it relies on an external macro that is known to C++ but not to UHT (NSLOCTEXT can easily be used instead).
Change 4335020 by Francis.Hurteau
Uncomment MessageBus::Send deprecation notice for 4.21
Update MessageBus Send usage to new API
Change 4335195 by JeanMichel.Dignard
Add a SetLodFromStaticMesh script utility function
#jira UEENT-1789
Change 4335231 by Anousack.Kitisa
Added functions to generate planar, cylindrical, box UV mapping.
#jira UEENT-1598
Change 4335373 by Jamie.Dale
Cleaned up some places creating empty literal texts
Change 4335458 by Jamie.Dale
Allow UHT to parse FText() as an alias of FText::GetEmpty() when processing default values
Change 4335875 by Max.Chen
Sequencer: Clear RF_Transient on pasted tracks/sections
#jira UE-63537
Change 4336497 by Johan.Duparc
ProxyLOD: Fix progress bar issue
- removed duplicated code
- removed duplicated LongTask object
#jira UEENT-1788
Change 4336723 by Jamie.Dale
Ensure that Python generated types create their CDO at the correct point
#jira UE-62895
Change 4340594 by Ben.Marsh
Fix manifest being invalidated when building two enterprise targets in a row. Fixes CIS error.
#jira UE-63644
#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4342443 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 4342474 by jason bestimt in Dev-VR branch]
2018-09-04 16:42:27 -04:00
// Collect components to merge
2017-06-30 12:21:06 -04:00
TArray < UStaticMeshComponent * > ComponentsToMerge ;
--------------------------------------
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise [at] 4341740)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 4280523 by Patrick.Boutot
Add option in AjaCustomTimeStep to wait until the frame to be ready. Previously, the frame was there but not yet processed so it was possible that it was not ready by the time we wanted to read it. It won't work with interlaced because the 2 fields are processed at the same time. In interlaced, will get a 30fps behaviour when we actually want a 60fps.
Fix bug that didn't set and reset bIsOwned properly when it was first initialized as not owned.
Change 4280526 by Patrick.Boutot
Add accessor to get the leaf media source or output.
Change 4280624 by Patrick.Boutot
Add timecode acessor to media samples
Change 4280626 by Patrick.Boutot
Rework the timing for AJA Media Player. Previously, we took the timing of the frame. That was a bad idea because if 2 incomings video frames were coming a the same time, you would only show one. Making the buffering system useless.
That affects the Custom Time Step since it was waiting for the interrupt signal and in some behavior we would like the frame to be ready to be used by UE. Same the timecode in the MediaSample because we may not used it to stamps the frame.
Change 4283022 by Patrick.Boutot
[EditorScriptingUtilitites] Check folder names invalid characters separatly from the object's name.
#jira UE-59886, UE-62333
Change 4283112 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Rename TimemanagemenetEditor module names.
Change 4283426 by JeanLuc.Corenthin
Fix crash with FBX file
#jira UE-62501
Change 4284940 by Patrick.Boutot
A widget that let you select a single permutation from a list. It groups the values into categories and removes duplicates inside that category.
Change 4285471 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Change 4286925 by Patrick.Boutot
[AJA] Add support to read LTC from the reference In.
Add more detail on video format and the device.
MediaSource use the Permutations Selection widget to select his mode and device.
Remove debugging option to trigger an AJA custom time step and timecode provider.
Remove the UYVY pixel option from AJA. It's better do to the conversion on the AJA card that on the GPU.
Change the tooltip and category for some AjaMediaSource properties.
Change 4287026 by Julien.StJean
Modifed the file STimeCodeProviderTab.cpp to fix the position of a SComboButton that wasn't properly place.
Change 4287663 by Jon.Nabozny
Add timecode messages into nDisplay, and sync those between Master and Slave
Change 4287884 by Jon.Nabozny
Create a TimecodeProvider for SystemTime and introduce a notion for DefaultTimecodeProvider in Engine.
Change 4288050 by Jon.Nabozny
Rework the TimeSynchronization implementation for usability and functionality.
Change 4288283 by Jon.Nabozny
Fixed swapped MetaClass and DisplayName options on UEngine::DefaultTimecodeProviderClassName;
Change 4288352 by Jon.Nabozny
Set TimecodeProviderClassName and DefaultTimecodeProviderClassName in BaseEngine.ini
Change 4288378 by Jon.Nabozny
Fixup some issues in TimecodeSynchronizer where code was reset improperly due to multiple unshelves / resolves.
Change 4288394 by Jon.Nabozny
Add TimeSync functionality into LiveLink. Also add test cases for this. This should allow us to easily synchronize multiple LiveLink sources together, as well as synchronize those to anything else using the sync system (Relies on CL-4235417)
Change 4288899 by Patrick.Boutot
Fix initialization order of FMediaIOCorePlayerBase variables
Change 4289157 by Patrick.Boutot
Allow the user to change the source of a capture without stopping the current capture.
[AJA] AjaMediaCapture, add support for UpdateSceneViewport & UpdateRenderTarget
[at]made by julien.stjean
Change 4291328 by Jon.Nabozny
Report the Skeleton Guid with TimeSyncData and track sync state in LiveLinkTimeSynchronizationSource.
This prevents a crash that can happen if a source is quickly cleared and reset before the next tick of Time Synchronization.
Change 4296294 by Jon.Nabozny
Fixup errors when TimecodeProviderClassName is empty. It's valid to leave this empty.
Change 4297122 by Patrick.Boutot
Media Profile with timecode provider & custom time step
Change 4301855 by Austin.Crismore
Fix for movment scaling and virtual joystick controls. Movement scaling in for truck and dolly is locked to the world xy plane, and virtual joysticks use their own method for movement scaling now.
#jira UE-61762, UE-62187
Change 4301856 by Austin.Crismore
Virtual sequence level controller now listens to on object spawned, so that it can intercept the camera actor and disable attatching to HMD to prevent camera movement that isn't from the level sequence
#jira UE-61766
Change 4301860 by Austin.Crismore
Fix for touch scrubbing. Added default values back in. Added logic to only allow scrubbing when touch focus was off.
#jira UE-61865
Change 4302294 by Jamie.Dale
Added functions to get your the localized spoken and subtitle text from a dialogue wave
Change 4304393 by Jamie.Dale
Added support for BlueprintAssignable properties in Python
Change 4305852 by Jamie.Dale
Removed hard-dependency between EditorScriptingUtilities and PythonScriptPlugin
Backed-out changelist 4259264 and query Python availability based on whether anything is available to handle the command
#jira UE-62318
Change 4308550 by Jamie.Dale
Fixed crash when passing a null world to Python actor iterators
Change 4311867 by Homam.Bahnassi
Revit master material with exposed parameters matching the API when possible.
Change 4314428 by Francis.Hurteau
Made the usage of the bBuildDeveloperTools switch independent of the bCompileAgainstEngine switch.
Changed bBuildDeveloperTools TargetRule in UnrealBuildTool to a nullable to keep the old behavior in case where bBuildDeveloperTools wasn't explicitly set in TargetRules
Change 4315134 by Jamie.Dale
Defer editable text focus selection until mouse-up to allow the user to make an initial selection
#jira UE-58086
Change 4318615 by Johan.Duparc
EditorFactories: consistent return values after asset import.
Change 4322459 by Jamie.Dale
Made SequencerScripting an Editor plugin as it depends on PythonScriptPlugin which is an Editor plugin
This was causing issues at runtime when SequencerScripting was enabled, as it failed to load PythonScriptPlugin (which hadn't been built).
Change 4323341 by Francis.Hurteau
Implement proper message bus protocol version negociation with static nodes
Change 4323733 by Francis.Hurteau
Fix VR Pausing Sequence Scrubbing just setting playback speed to 0.0
Change 4324319 by Jamie.Dale
Exposed transactions to Blueprints
Change 4325847 by Alistair.White
Copying //Tasks/UE4/Private-PixelStreaming[at]4325566 to Dev-Enterprise-Minimal (//UE4/Dev-Enterprise-Minimal)
This adds the new experimental PixelStreaming plugin to allow streaming of an Unreal client's audio & video stream to a browser through the WebRTC protocol to support new uses for enterprise customers.
Change 4326282 by Simon.Tourangeau
nDisplay native present handler
Change 4326581 by Jamie.Dale
Replacing FDateTime with int64 Ticks value to workaround UE-63485
Change 4326599 by Homam.Bahnassi
Moving texture coords outside UVEdit function to allow using different UV channels.
Change 4333250 by Francis.Hurteau
Small TFuture changes:
* cleans up TFuture::Then with usage of TUniqueFunction
* added TFuture::Reset to invalidate it and remove continuation from a future shared state
Change 4333359 by Homam.Bahnassi
Support scaling and rotating UVs around arbitrary pivot
Change 4333566 by Johan.Duparc
Expose ProxyLOD functionalities to Scripting
#jira UEENT-1788
Change 4333988 by Jamie.Dale
Allow UHT to parse FText default parameter values
INVTEXT, NSLOCTEXT, LOCTABLE, and FText::GetEmpty() are supported. LOCTEXT isn't as it relies on an external macro that is known to C++ but not to UHT (NSLOCTEXT can easily be used instead).
Change 4335020 by Francis.Hurteau
Uncomment MessageBus::Send deprecation notice for 4.21
Update MessageBus Send usage to new API
Change 4335195 by JeanMichel.Dignard
Add a SetLodFromStaticMesh script utility function
#jira UEENT-1789
Change 4335231 by Anousack.Kitisa
Added functions to generate planar, cylindrical, box UV mapping.
#jira UEENT-1598
Change 4335373 by Jamie.Dale
Cleaned up some places creating empty literal texts
Change 4335458 by Jamie.Dale
Allow UHT to parse FText() as an alias of FText::GetEmpty() when processing default values
Change 4335875 by Max.Chen
Sequencer: Clear RF_Transient on pasted tracks/sections
#jira UE-63537
Change 4336497 by Johan.Duparc
ProxyLOD: Fix progress bar issue
- removed duplicated code
- removed duplicated LongTask object
#jira UEENT-1788
Change 4336723 by Jamie.Dale
Ensure that Python generated types create their CDO at the correct point
#jira UE-62895
Change 4340594 by Ben.Marsh
Fix manifest being invalidated when building two enterprise targets in a row. Fixes CIS error.
#jira UE-63644
#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4342443 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 4342474 by jason bestimt in Dev-VR branch]
2018-09-04 16:42:27 -04:00
for ( AActor * Actor : InActors )
2017-06-30 12:21:06 -04:00
{
--------------------------------------
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise [at] 4341740)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 4280523 by Patrick.Boutot
Add option in AjaCustomTimeStep to wait until the frame to be ready. Previously, the frame was there but not yet processed so it was possible that it was not ready by the time we wanted to read it. It won't work with interlaced because the 2 fields are processed at the same time. In interlaced, will get a 30fps behaviour when we actually want a 60fps.
Fix bug that didn't set and reset bIsOwned properly when it was first initialized as not owned.
Change 4280526 by Patrick.Boutot
Add accessor to get the leaf media source or output.
Change 4280624 by Patrick.Boutot
Add timecode acessor to media samples
Change 4280626 by Patrick.Boutot
Rework the timing for AJA Media Player. Previously, we took the timing of the frame. That was a bad idea because if 2 incomings video frames were coming a the same time, you would only show one. Making the buffering system useless.
That affects the Custom Time Step since it was waiting for the interrupt signal and in some behavior we would like the frame to be ready to be used by UE. Same the timecode in the MediaSample because we may not used it to stamps the frame.
Change 4283022 by Patrick.Boutot
[EditorScriptingUtilitites] Check folder names invalid characters separatly from the object's name.
#jira UE-59886, UE-62333
Change 4283112 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Rename TimemanagemenetEditor module names.
Change 4283426 by JeanLuc.Corenthin
Fix crash with FBX file
#jira UE-62501
Change 4284940 by Patrick.Boutot
A widget that let you select a single permutation from a list. It groups the values into categories and removes duplicates inside that category.
Change 4285471 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Change 4286925 by Patrick.Boutot
[AJA] Add support to read LTC from the reference In.
Add more detail on video format and the device.
MediaSource use the Permutations Selection widget to select his mode and device.
Remove debugging option to trigger an AJA custom time step and timecode provider.
Remove the UYVY pixel option from AJA. It's better do to the conversion on the AJA card that on the GPU.
Change the tooltip and category for some AjaMediaSource properties.
Change 4287026 by Julien.StJean
Modifed the file STimeCodeProviderTab.cpp to fix the position of a SComboButton that wasn't properly place.
Change 4287663 by Jon.Nabozny
Add timecode messages into nDisplay, and sync those between Master and Slave
Change 4287884 by Jon.Nabozny
Create a TimecodeProvider for SystemTime and introduce a notion for DefaultTimecodeProvider in Engine.
Change 4288050 by Jon.Nabozny
Rework the TimeSynchronization implementation for usability and functionality.
Change 4288283 by Jon.Nabozny
Fixed swapped MetaClass and DisplayName options on UEngine::DefaultTimecodeProviderClassName;
Change 4288352 by Jon.Nabozny
Set TimecodeProviderClassName and DefaultTimecodeProviderClassName in BaseEngine.ini
Change 4288378 by Jon.Nabozny
Fixup some issues in TimecodeSynchronizer where code was reset improperly due to multiple unshelves / resolves.
Change 4288394 by Jon.Nabozny
Add TimeSync functionality into LiveLink. Also add test cases for this. This should allow us to easily synchronize multiple LiveLink sources together, as well as synchronize those to anything else using the sync system (Relies on CL-4235417)
Change 4288899 by Patrick.Boutot
Fix initialization order of FMediaIOCorePlayerBase variables
Change 4289157 by Patrick.Boutot
Allow the user to change the source of a capture without stopping the current capture.
[AJA] AjaMediaCapture, add support for UpdateSceneViewport & UpdateRenderTarget
[at]made by julien.stjean
Change 4291328 by Jon.Nabozny
Report the Skeleton Guid with TimeSyncData and track sync state in LiveLinkTimeSynchronizationSource.
This prevents a crash that can happen if a source is quickly cleared and reset before the next tick of Time Synchronization.
Change 4296294 by Jon.Nabozny
Fixup errors when TimecodeProviderClassName is empty. It's valid to leave this empty.
Change 4297122 by Patrick.Boutot
Media Profile with timecode provider & custom time step
Change 4301855 by Austin.Crismore
Fix for movment scaling and virtual joystick controls. Movement scaling in for truck and dolly is locked to the world xy plane, and virtual joysticks use their own method for movement scaling now.
#jira UE-61762, UE-62187
Change 4301856 by Austin.Crismore
Virtual sequence level controller now listens to on object spawned, so that it can intercept the camera actor and disable attatching to HMD to prevent camera movement that isn't from the level sequence
#jira UE-61766
Change 4301860 by Austin.Crismore
Fix for touch scrubbing. Added default values back in. Added logic to only allow scrubbing when touch focus was off.
#jira UE-61865
Change 4302294 by Jamie.Dale
Added functions to get your the localized spoken and subtitle text from a dialogue wave
Change 4304393 by Jamie.Dale
Added support for BlueprintAssignable properties in Python
Change 4305852 by Jamie.Dale
Removed hard-dependency between EditorScriptingUtilities and PythonScriptPlugin
Backed-out changelist 4259264 and query Python availability based on whether anything is available to handle the command
#jira UE-62318
Change 4308550 by Jamie.Dale
Fixed crash when passing a null world to Python actor iterators
Change 4311867 by Homam.Bahnassi
Revit master material with exposed parameters matching the API when possible.
Change 4314428 by Francis.Hurteau
Made the usage of the bBuildDeveloperTools switch independent of the bCompileAgainstEngine switch.
Changed bBuildDeveloperTools TargetRule in UnrealBuildTool to a nullable to keep the old behavior in case where bBuildDeveloperTools wasn't explicitly set in TargetRules
Change 4315134 by Jamie.Dale
Defer editable text focus selection until mouse-up to allow the user to make an initial selection
#jira UE-58086
Change 4318615 by Johan.Duparc
EditorFactories: consistent return values after asset import.
Change 4322459 by Jamie.Dale
Made SequencerScripting an Editor plugin as it depends on PythonScriptPlugin which is an Editor plugin
This was causing issues at runtime when SequencerScripting was enabled, as it failed to load PythonScriptPlugin (which hadn't been built).
Change 4323341 by Francis.Hurteau
Implement proper message bus protocol version negociation with static nodes
Change 4323733 by Francis.Hurteau
Fix VR Pausing Sequence Scrubbing just setting playback speed to 0.0
Change 4324319 by Jamie.Dale
Exposed transactions to Blueprints
Change 4325847 by Alistair.White
Copying //Tasks/UE4/Private-PixelStreaming[at]4325566 to Dev-Enterprise-Minimal (//UE4/Dev-Enterprise-Minimal)
This adds the new experimental PixelStreaming plugin to allow streaming of an Unreal client's audio & video stream to a browser through the WebRTC protocol to support new uses for enterprise customers.
Change 4326282 by Simon.Tourangeau
nDisplay native present handler
Change 4326581 by Jamie.Dale
Replacing FDateTime with int64 Ticks value to workaround UE-63485
Change 4326599 by Homam.Bahnassi
Moving texture coords outside UVEdit function to allow using different UV channels.
Change 4333250 by Francis.Hurteau
Small TFuture changes:
* cleans up TFuture::Then with usage of TUniqueFunction
* added TFuture::Reset to invalidate it and remove continuation from a future shared state
Change 4333359 by Homam.Bahnassi
Support scaling and rotating UVs around arbitrary pivot
Change 4333566 by Johan.Duparc
Expose ProxyLOD functionalities to Scripting
#jira UEENT-1788
Change 4333988 by Jamie.Dale
Allow UHT to parse FText default parameter values
INVTEXT, NSLOCTEXT, LOCTABLE, and FText::GetEmpty() are supported. LOCTEXT isn't as it relies on an external macro that is known to C++ but not to UHT (NSLOCTEXT can easily be used instead).
Change 4335020 by Francis.Hurteau
Uncomment MessageBus::Send deprecation notice for 4.21
Update MessageBus Send usage to new API
Change 4335195 by JeanMichel.Dignard
Add a SetLodFromStaticMesh script utility function
#jira UEENT-1789
Change 4335231 by Anousack.Kitisa
Added functions to generate planar, cylindrical, box UV mapping.
#jira UEENT-1598
Change 4335373 by Jamie.Dale
Cleaned up some places creating empty literal texts
Change 4335458 by Jamie.Dale
Allow UHT to parse FText() as an alias of FText::GetEmpty() when processing default values
Change 4335875 by Max.Chen
Sequencer: Clear RF_Transient on pasted tracks/sections
#jira UE-63537
Change 4336497 by Johan.Duparc
ProxyLOD: Fix progress bar issue
- removed duplicated code
- removed duplicated LongTask object
#jira UEENT-1788
Change 4336723 by Jamie.Dale
Ensure that Python generated types create their CDO at the correct point
#jira UE-62895
Change 4340594 by Ben.Marsh
Fix manifest being invalidated when building two enterprise targets in a row. Fixes CIS error.
#jira UE-63644
#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4342443 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 4342474 by jason bestimt in Dev-VR branch]
2018-09-04 16:42:27 -04:00
TInlineComponentArray < UStaticMeshComponent * > Components ;
Actor - > GetComponents < UStaticMeshComponent > ( Components ) ;
ComponentsToMerge . Append ( Components ) ;
2017-06-30 12:21:06 -04:00
}
2018-03-06 13:26:20 -05:00
CreateProxyMesh ( ComponentsToMerge , InMeshProxySettings , InBaseMaterial , InOuter , InProxyBasePackageName , InGuid , InProxyCreatedDelegate , bAllowAsync , ScreenSize ) ;
2018-02-14 14:13:42 -05:00
}
2018-03-06 13:26:20 -05:00
void FMeshMergeUtilities : : CreateProxyMesh ( const TArray < UStaticMeshComponent * > & InComponentsToMerge , const struct FMeshProxySettings & InMeshProxySettings , UMaterialInterface * InBaseMaterial ,
2018-02-14 14:13:42 -05:00
UPackage * InOuter , const FString & InProxyBasePackageName , const FGuid InGuid , const FCreateProxyDelegate & InProxyCreatedDelegate , const bool bAllowAsync , const float ScreenSize ) const
{
// The MeshReductionInterface manages the choice mesh reduction plugins, Unreal native vs third party (e.g. Simplygon)
IMeshReductionModule & ReductionModule = FModuleManager : : Get ( ) . LoadModuleChecked < IMeshReductionModule > ( " MeshReductionInterface " ) ;
--------------------------------------
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise [at] 4341740)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 4280523 by Patrick.Boutot
Add option in AjaCustomTimeStep to wait until the frame to be ready. Previously, the frame was there but not yet processed so it was possible that it was not ready by the time we wanted to read it. It won't work with interlaced because the 2 fields are processed at the same time. In interlaced, will get a 30fps behaviour when we actually want a 60fps.
Fix bug that didn't set and reset bIsOwned properly when it was first initialized as not owned.
Change 4280526 by Patrick.Boutot
Add accessor to get the leaf media source or output.
Change 4280624 by Patrick.Boutot
Add timecode acessor to media samples
Change 4280626 by Patrick.Boutot
Rework the timing for AJA Media Player. Previously, we took the timing of the frame. That was a bad idea because if 2 incomings video frames were coming a the same time, you would only show one. Making the buffering system useless.
That affects the Custom Time Step since it was waiting for the interrupt signal and in some behavior we would like the frame to be ready to be used by UE. Same the timecode in the MediaSample because we may not used it to stamps the frame.
Change 4283022 by Patrick.Boutot
[EditorScriptingUtilitites] Check folder names invalid characters separatly from the object's name.
#jira UE-59886, UE-62333
Change 4283112 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Rename TimemanagemenetEditor module names.
Change 4283426 by JeanLuc.Corenthin
Fix crash with FBX file
#jira UE-62501
Change 4284940 by Patrick.Boutot
A widget that let you select a single permutation from a list. It groups the values into categories and removes duplicates inside that category.
Change 4285471 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Change 4286925 by Patrick.Boutot
[AJA] Add support to read LTC from the reference In.
Add more detail on video format and the device.
MediaSource use the Permutations Selection widget to select his mode and device.
Remove debugging option to trigger an AJA custom time step and timecode provider.
Remove the UYVY pixel option from AJA. It's better do to the conversion on the AJA card that on the GPU.
Change the tooltip and category for some AjaMediaSource properties.
Change 4287026 by Julien.StJean
Modifed the file STimeCodeProviderTab.cpp to fix the position of a SComboButton that wasn't properly place.
Change 4287663 by Jon.Nabozny
Add timecode messages into nDisplay, and sync those between Master and Slave
Change 4287884 by Jon.Nabozny
Create a TimecodeProvider for SystemTime and introduce a notion for DefaultTimecodeProvider in Engine.
Change 4288050 by Jon.Nabozny
Rework the TimeSynchronization implementation for usability and functionality.
Change 4288283 by Jon.Nabozny
Fixed swapped MetaClass and DisplayName options on UEngine::DefaultTimecodeProviderClassName;
Change 4288352 by Jon.Nabozny
Set TimecodeProviderClassName and DefaultTimecodeProviderClassName in BaseEngine.ini
Change 4288378 by Jon.Nabozny
Fixup some issues in TimecodeSynchronizer where code was reset improperly due to multiple unshelves / resolves.
Change 4288394 by Jon.Nabozny
Add TimeSync functionality into LiveLink. Also add test cases for this. This should allow us to easily synchronize multiple LiveLink sources together, as well as synchronize those to anything else using the sync system (Relies on CL-4235417)
Change 4288899 by Patrick.Boutot
Fix initialization order of FMediaIOCorePlayerBase variables
Change 4289157 by Patrick.Boutot
Allow the user to change the source of a capture without stopping the current capture.
[AJA] AjaMediaCapture, add support for UpdateSceneViewport & UpdateRenderTarget
[at]made by julien.stjean
Change 4291328 by Jon.Nabozny
Report the Skeleton Guid with TimeSyncData and track sync state in LiveLinkTimeSynchronizationSource.
This prevents a crash that can happen if a source is quickly cleared and reset before the next tick of Time Synchronization.
Change 4296294 by Jon.Nabozny
Fixup errors when TimecodeProviderClassName is empty. It's valid to leave this empty.
Change 4297122 by Patrick.Boutot
Media Profile with timecode provider & custom time step
Change 4301855 by Austin.Crismore
Fix for movment scaling and virtual joystick controls. Movement scaling in for truck and dolly is locked to the world xy plane, and virtual joysticks use their own method for movement scaling now.
#jira UE-61762, UE-62187
Change 4301856 by Austin.Crismore
Virtual sequence level controller now listens to on object spawned, so that it can intercept the camera actor and disable attatching to HMD to prevent camera movement that isn't from the level sequence
#jira UE-61766
Change 4301860 by Austin.Crismore
Fix for touch scrubbing. Added default values back in. Added logic to only allow scrubbing when touch focus was off.
#jira UE-61865
Change 4302294 by Jamie.Dale
Added functions to get your the localized spoken and subtitle text from a dialogue wave
Change 4304393 by Jamie.Dale
Added support for BlueprintAssignable properties in Python
Change 4305852 by Jamie.Dale
Removed hard-dependency between EditorScriptingUtilities and PythonScriptPlugin
Backed-out changelist 4259264 and query Python availability based on whether anything is available to handle the command
#jira UE-62318
Change 4308550 by Jamie.Dale
Fixed crash when passing a null world to Python actor iterators
Change 4311867 by Homam.Bahnassi
Revit master material with exposed parameters matching the API when possible.
Change 4314428 by Francis.Hurteau
Made the usage of the bBuildDeveloperTools switch independent of the bCompileAgainstEngine switch.
Changed bBuildDeveloperTools TargetRule in UnrealBuildTool to a nullable to keep the old behavior in case where bBuildDeveloperTools wasn't explicitly set in TargetRules
Change 4315134 by Jamie.Dale
Defer editable text focus selection until mouse-up to allow the user to make an initial selection
#jira UE-58086
Change 4318615 by Johan.Duparc
EditorFactories: consistent return values after asset import.
Change 4322459 by Jamie.Dale
Made SequencerScripting an Editor plugin as it depends on PythonScriptPlugin which is an Editor plugin
This was causing issues at runtime when SequencerScripting was enabled, as it failed to load PythonScriptPlugin (which hadn't been built).
Change 4323341 by Francis.Hurteau
Implement proper message bus protocol version negociation with static nodes
Change 4323733 by Francis.Hurteau
Fix VR Pausing Sequence Scrubbing just setting playback speed to 0.0
Change 4324319 by Jamie.Dale
Exposed transactions to Blueprints
Change 4325847 by Alistair.White
Copying //Tasks/UE4/Private-PixelStreaming[at]4325566 to Dev-Enterprise-Minimal (//UE4/Dev-Enterprise-Minimal)
This adds the new experimental PixelStreaming plugin to allow streaming of an Unreal client's audio & video stream to a browser through the WebRTC protocol to support new uses for enterprise customers.
Change 4326282 by Simon.Tourangeau
nDisplay native present handler
Change 4326581 by Jamie.Dale
Replacing FDateTime with int64 Ticks value to workaround UE-63485
Change 4326599 by Homam.Bahnassi
Moving texture coords outside UVEdit function to allow using different UV channels.
Change 4333250 by Francis.Hurteau
Small TFuture changes:
* cleans up TFuture::Then with usage of TUniqueFunction
* added TFuture::Reset to invalidate it and remove continuation from a future shared state
Change 4333359 by Homam.Bahnassi
Support scaling and rotating UVs around arbitrary pivot
Change 4333566 by Johan.Duparc
Expose ProxyLOD functionalities to Scripting
#jira UEENT-1788
Change 4333988 by Jamie.Dale
Allow UHT to parse FText default parameter values
INVTEXT, NSLOCTEXT, LOCTABLE, and FText::GetEmpty() are supported. LOCTEXT isn't as it relies on an external macro that is known to C++ but not to UHT (NSLOCTEXT can easily be used instead).
Change 4335020 by Francis.Hurteau
Uncomment MessageBus::Send deprecation notice for 4.21
Update MessageBus Send usage to new API
Change 4335195 by JeanMichel.Dignard
Add a SetLodFromStaticMesh script utility function
#jira UEENT-1789
Change 4335231 by Anousack.Kitisa
Added functions to generate planar, cylindrical, box UV mapping.
#jira UEENT-1598
Change 4335373 by Jamie.Dale
Cleaned up some places creating empty literal texts
Change 4335458 by Jamie.Dale
Allow UHT to parse FText() as an alias of FText::GetEmpty() when processing default values
Change 4335875 by Max.Chen
Sequencer: Clear RF_Transient on pasted tracks/sections
#jira UE-63537
Change 4336497 by Johan.Duparc
ProxyLOD: Fix progress bar issue
- removed duplicated code
- removed duplicated LongTask object
#jira UEENT-1788
Change 4336723 by Jamie.Dale
Ensure that Python generated types create their CDO at the correct point
#jira UE-62895
Change 4340594 by Ben.Marsh
Fix manifest being invalidated when building two enterprise targets in a row. Fixes CIS error.
#jira UE-63644
#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4342443 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 4342474 by jason bestimt in Dev-VR branch]
2018-09-04 16:42:27 -04:00
2018-02-14 14:13:42 -05:00
// Error/warning checking for input
if ( ReductionModule . GetMeshMergingInterface ( ) = = nullptr )
{
UE_LOG ( LogMeshMerging , Log , TEXT ( " No automatic mesh merging module available " ) ) ;
return ;
}
// Check that the delegate has a func-ptr bound to it
if ( ! InProxyCreatedDelegate . IsBound ( ) )
{
UE_LOG ( LogMeshMerging , Log , TEXT ( " Invalid (unbound) delegate for returning generated proxy mesh " ) ) ;
return ;
}
2018-03-06 13:26:20 -05:00
TArray < UStaticMeshComponent * > ComponentsToMerge = InComponentsToMerge ;
--------------------------------------
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise [at] 4341740)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 4280523 by Patrick.Boutot
Add option in AjaCustomTimeStep to wait until the frame to be ready. Previously, the frame was there but not yet processed so it was possible that it was not ready by the time we wanted to read it. It won't work with interlaced because the 2 fields are processed at the same time. In interlaced, will get a 30fps behaviour when we actually want a 60fps.
Fix bug that didn't set and reset bIsOwned properly when it was first initialized as not owned.
Change 4280526 by Patrick.Boutot
Add accessor to get the leaf media source or output.
Change 4280624 by Patrick.Boutot
Add timecode acessor to media samples
Change 4280626 by Patrick.Boutot
Rework the timing for AJA Media Player. Previously, we took the timing of the frame. That was a bad idea because if 2 incomings video frames were coming a the same time, you would only show one. Making the buffering system useless.
That affects the Custom Time Step since it was waiting for the interrupt signal and in some behavior we would like the frame to be ready to be used by UE. Same the timecode in the MediaSample because we may not used it to stamps the frame.
Change 4283022 by Patrick.Boutot
[EditorScriptingUtilitites] Check folder names invalid characters separatly from the object's name.
#jira UE-59886, UE-62333
Change 4283112 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Rename TimemanagemenetEditor module names.
Change 4283426 by JeanLuc.Corenthin
Fix crash with FBX file
#jira UE-62501
Change 4284940 by Patrick.Boutot
A widget that let you select a single permutation from a list. It groups the values into categories and removes duplicates inside that category.
Change 4285471 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Change 4286925 by Patrick.Boutot
[AJA] Add support to read LTC from the reference In.
Add more detail on video format and the device.
MediaSource use the Permutations Selection widget to select his mode and device.
Remove debugging option to trigger an AJA custom time step and timecode provider.
Remove the UYVY pixel option from AJA. It's better do to the conversion on the AJA card that on the GPU.
Change the tooltip and category for some AjaMediaSource properties.
Change 4287026 by Julien.StJean
Modifed the file STimeCodeProviderTab.cpp to fix the position of a SComboButton that wasn't properly place.
Change 4287663 by Jon.Nabozny
Add timecode messages into nDisplay, and sync those between Master and Slave
Change 4287884 by Jon.Nabozny
Create a TimecodeProvider for SystemTime and introduce a notion for DefaultTimecodeProvider in Engine.
Change 4288050 by Jon.Nabozny
Rework the TimeSynchronization implementation for usability and functionality.
Change 4288283 by Jon.Nabozny
Fixed swapped MetaClass and DisplayName options on UEngine::DefaultTimecodeProviderClassName;
Change 4288352 by Jon.Nabozny
Set TimecodeProviderClassName and DefaultTimecodeProviderClassName in BaseEngine.ini
Change 4288378 by Jon.Nabozny
Fixup some issues in TimecodeSynchronizer where code was reset improperly due to multiple unshelves / resolves.
Change 4288394 by Jon.Nabozny
Add TimeSync functionality into LiveLink. Also add test cases for this. This should allow us to easily synchronize multiple LiveLink sources together, as well as synchronize those to anything else using the sync system (Relies on CL-4235417)
Change 4288899 by Patrick.Boutot
Fix initialization order of FMediaIOCorePlayerBase variables
Change 4289157 by Patrick.Boutot
Allow the user to change the source of a capture without stopping the current capture.
[AJA] AjaMediaCapture, add support for UpdateSceneViewport & UpdateRenderTarget
[at]made by julien.stjean
Change 4291328 by Jon.Nabozny
Report the Skeleton Guid with TimeSyncData and track sync state in LiveLinkTimeSynchronizationSource.
This prevents a crash that can happen if a source is quickly cleared and reset before the next tick of Time Synchronization.
Change 4296294 by Jon.Nabozny
Fixup errors when TimecodeProviderClassName is empty. It's valid to leave this empty.
Change 4297122 by Patrick.Boutot
Media Profile with timecode provider & custom time step
Change 4301855 by Austin.Crismore
Fix for movment scaling and virtual joystick controls. Movement scaling in for truck and dolly is locked to the world xy plane, and virtual joysticks use their own method for movement scaling now.
#jira UE-61762, UE-62187
Change 4301856 by Austin.Crismore
Virtual sequence level controller now listens to on object spawned, so that it can intercept the camera actor and disable attatching to HMD to prevent camera movement that isn't from the level sequence
#jira UE-61766
Change 4301860 by Austin.Crismore
Fix for touch scrubbing. Added default values back in. Added logic to only allow scrubbing when touch focus was off.
#jira UE-61865
Change 4302294 by Jamie.Dale
Added functions to get your the localized spoken and subtitle text from a dialogue wave
Change 4304393 by Jamie.Dale
Added support for BlueprintAssignable properties in Python
Change 4305852 by Jamie.Dale
Removed hard-dependency between EditorScriptingUtilities and PythonScriptPlugin
Backed-out changelist 4259264 and query Python availability based on whether anything is available to handle the command
#jira UE-62318
Change 4308550 by Jamie.Dale
Fixed crash when passing a null world to Python actor iterators
Change 4311867 by Homam.Bahnassi
Revit master material with exposed parameters matching the API when possible.
Change 4314428 by Francis.Hurteau
Made the usage of the bBuildDeveloperTools switch independent of the bCompileAgainstEngine switch.
Changed bBuildDeveloperTools TargetRule in UnrealBuildTool to a nullable to keep the old behavior in case where bBuildDeveloperTools wasn't explicitly set in TargetRules
Change 4315134 by Jamie.Dale
Defer editable text focus selection until mouse-up to allow the user to make an initial selection
#jira UE-58086
Change 4318615 by Johan.Duparc
EditorFactories: consistent return values after asset import.
Change 4322459 by Jamie.Dale
Made SequencerScripting an Editor plugin as it depends on PythonScriptPlugin which is an Editor plugin
This was causing issues at runtime when SequencerScripting was enabled, as it failed to load PythonScriptPlugin (which hadn't been built).
Change 4323341 by Francis.Hurteau
Implement proper message bus protocol version negociation with static nodes
Change 4323733 by Francis.Hurteau
Fix VR Pausing Sequence Scrubbing just setting playback speed to 0.0
Change 4324319 by Jamie.Dale
Exposed transactions to Blueprints
Change 4325847 by Alistair.White
Copying //Tasks/UE4/Private-PixelStreaming[at]4325566 to Dev-Enterprise-Minimal (//UE4/Dev-Enterprise-Minimal)
This adds the new experimental PixelStreaming plugin to allow streaming of an Unreal client's audio & video stream to a browser through the WebRTC protocol to support new uses for enterprise customers.
Change 4326282 by Simon.Tourangeau
nDisplay native present handler
Change 4326581 by Jamie.Dale
Replacing FDateTime with int64 Ticks value to workaround UE-63485
Change 4326599 by Homam.Bahnassi
Moving texture coords outside UVEdit function to allow using different UV channels.
Change 4333250 by Francis.Hurteau
Small TFuture changes:
* cleans up TFuture::Then with usage of TUniqueFunction
* added TFuture::Reset to invalidate it and remove continuation from a future shared state
Change 4333359 by Homam.Bahnassi
Support scaling and rotating UVs around arbitrary pivot
Change 4333566 by Johan.Duparc
Expose ProxyLOD functionalities to Scripting
#jira UEENT-1788
Change 4333988 by Jamie.Dale
Allow UHT to parse FText default parameter values
INVTEXT, NSLOCTEXT, LOCTABLE, and FText::GetEmpty() are supported. LOCTEXT isn't as it relies on an external macro that is known to C++ but not to UHT (NSLOCTEXT can easily be used instead).
Change 4335020 by Francis.Hurteau
Uncomment MessageBus::Send deprecation notice for 4.21
Update MessageBus Send usage to new API
Change 4335195 by JeanMichel.Dignard
Add a SetLodFromStaticMesh script utility function
#jira UEENT-1789
Change 4335231 by Anousack.Kitisa
Added functions to generate planar, cylindrical, box UV mapping.
#jira UEENT-1598
Change 4335373 by Jamie.Dale
Cleaned up some places creating empty literal texts
Change 4335458 by Jamie.Dale
Allow UHT to parse FText() as an alias of FText::GetEmpty() when processing default values
Change 4335875 by Max.Chen
Sequencer: Clear RF_Transient on pasted tracks/sections
#jira UE-63537
Change 4336497 by Johan.Duparc
ProxyLOD: Fix progress bar issue
- removed duplicated code
- removed duplicated LongTask object
#jira UEENT-1788
Change 4336723 by Jamie.Dale
Ensure that Python generated types create their CDO at the correct point
#jira UE-62895
Change 4340594 by Ben.Marsh
Fix manifest being invalidated when building two enterprise targets in a row. Fixes CIS error.
#jira UE-63644
#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4342443 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 4342474 by jason bestimt in Dev-VR branch]
2018-09-04 16:42:27 -04:00
// Remove anything non-regular or non-spline static mesh components
2018-03-06 13:26:20 -05:00
ComponentsToMerge . RemoveAll ( [ ] ( UStaticMeshComponent * Val )
{
if ( Val - > GetClass ( ) ! = UStaticMeshComponent : : StaticClass ( ) & & Val - > GetClass ( ) ! = UInstancedStaticMeshComponent : : StaticClass ( ) & & ! Val - > IsA ( USplineMeshComponent : : StaticClass ( ) ) )
{
return true ;
}
if ( Val - > GetStaticMesh ( ) = = nullptr )
{
return true ;
}
return false ;
} ) ;
2018-02-14 14:13:42 -05:00
// No actors given as input
if ( ComponentsToMerge . Num ( ) = = 0 )
{
UE_LOG ( LogMeshMerging , Log , TEXT ( " No static mesh specified to generate a proxy mesh for " ) ) ;
2019-03-13 15:19:08 -04:00
TArray < UObject * > OutAssetsToSync ;
InProxyCreatedDelegate . ExecuteIfBound ( InGuid , OutAssetsToSync ) ;
2018-02-14 14:13:42 -05:00
return ;
}
// Base asset name for a new assets
// In case outer is null ProxyBasePackageName has to be long package name
if ( InOuter = = nullptr & & FPackageName : : IsShortPackageName ( InProxyBasePackageName ) )
{
UE_LOG ( LogMeshMerging , Warning , TEXT ( " Invalid long package name: '%s'. " ) , * InProxyBasePackageName ) ;
return ;
}
FScopedSlowTask SlowTask ( 100.f , ( LOCTEXT ( " CreateProxyMesh_CreateMesh " , " Creating Mesh Proxy " ) ) ) ;
SlowTask . MakeDialog ( ) ;
2017-06-30 12:21:06 -04:00
TArray < FRawMeshExt > SourceMeshes ;
TMap < FMeshIdAndLOD , TArray < int32 > > GlobalMaterialMap ;
static const int32 ProxyMeshTargetLODLevel = 0 ;
FBoxSphereBounds EstimatedBounds ( ForceInitToZero ) ;
for ( const UStaticMeshComponent * StaticMeshComponent : ComponentsToMerge )
{
EstimatedBounds = EstimatedBounds + StaticMeshComponent - > Bounds ;
}
--------------------------------------
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise [at] 4341740)
#lockdown Nick.Penwarden
#rb none
============================
MAJOR FEATURES & CHANGES
============================
Change 4280523 by Patrick.Boutot
Add option in AjaCustomTimeStep to wait until the frame to be ready. Previously, the frame was there but not yet processed so it was possible that it was not ready by the time we wanted to read it. It won't work with interlaced because the 2 fields are processed at the same time. In interlaced, will get a 30fps behaviour when we actually want a 60fps.
Fix bug that didn't set and reset bIsOwned properly when it was first initialized as not owned.
Change 4280526 by Patrick.Boutot
Add accessor to get the leaf media source or output.
Change 4280624 by Patrick.Boutot
Add timecode acessor to media samples
Change 4280626 by Patrick.Boutot
Rework the timing for AJA Media Player. Previously, we took the timing of the frame. That was a bad idea because if 2 incomings video frames were coming a the same time, you would only show one. Making the buffering system useless.
That affects the Custom Time Step since it was waiting for the interrupt signal and in some behavior we would like the frame to be ready to be used by UE. Same the timecode in the MediaSample because we may not used it to stamps the frame.
Change 4283022 by Patrick.Boutot
[EditorScriptingUtilitites] Check folder names invalid characters separatly from the object's name.
#jira UE-59886, UE-62333
Change 4283112 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Rename TimemanagemenetEditor module names.
Change 4283426 by JeanLuc.Corenthin
Fix crash with FBX file
#jira UE-62501
Change 4284940 by Patrick.Boutot
A widget that let you select a single permutation from a list. It groups the values into categories and removes duplicates inside that category.
Change 4285471 by Patrick.Boutot
Remove MediaFrameworkUtilititesModule dependency to the Settings module at runtime.
Change 4286925 by Patrick.Boutot
[AJA] Add support to read LTC from the reference In.
Add more detail on video format and the device.
MediaSource use the Permutations Selection widget to select his mode and device.
Remove debugging option to trigger an AJA custom time step and timecode provider.
Remove the UYVY pixel option from AJA. It's better do to the conversion on the AJA card that on the GPU.
Change the tooltip and category for some AjaMediaSource properties.
Change 4287026 by Julien.StJean
Modifed the file STimeCodeProviderTab.cpp to fix the position of a SComboButton that wasn't properly place.
Change 4287663 by Jon.Nabozny
Add timecode messages into nDisplay, and sync those between Master and Slave
Change 4287884 by Jon.Nabozny
Create a TimecodeProvider for SystemTime and introduce a notion for DefaultTimecodeProvider in Engine.
Change 4288050 by Jon.Nabozny
Rework the TimeSynchronization implementation for usability and functionality.
Change 4288283 by Jon.Nabozny
Fixed swapped MetaClass and DisplayName options on UEngine::DefaultTimecodeProviderClassName;
Change 4288352 by Jon.Nabozny
Set TimecodeProviderClassName and DefaultTimecodeProviderClassName in BaseEngine.ini
Change 4288378 by Jon.Nabozny
Fixup some issues in TimecodeSynchronizer where code was reset improperly due to multiple unshelves / resolves.
Change 4288394 by Jon.Nabozny
Add TimeSync functionality into LiveLink. Also add test cases for this. This should allow us to easily synchronize multiple LiveLink sources together, as well as synchronize those to anything else using the sync system (Relies on CL-4235417)
Change 4288899 by Patrick.Boutot
Fix initialization order of FMediaIOCorePlayerBase variables
Change 4289157 by Patrick.Boutot
Allow the user to change the source of a capture without stopping the current capture.
[AJA] AjaMediaCapture, add support for UpdateSceneViewport & UpdateRenderTarget
[at]made by julien.stjean
Change 4291328 by Jon.Nabozny
Report the Skeleton Guid with TimeSyncData and track sync state in LiveLinkTimeSynchronizationSource.
This prevents a crash that can happen if a source is quickly cleared and reset before the next tick of Time Synchronization.
Change 4296294 by Jon.Nabozny
Fixup errors when TimecodeProviderClassName is empty. It's valid to leave this empty.
Change 4297122 by Patrick.Boutot
Media Profile with timecode provider & custom time step
Change 4301855 by Austin.Crismore
Fix for movment scaling and virtual joystick controls. Movement scaling in for truck and dolly is locked to the world xy plane, and virtual joysticks use their own method for movement scaling now.
#jira UE-61762, UE-62187
Change 4301856 by Austin.Crismore
Virtual sequence level controller now listens to on object spawned, so that it can intercept the camera actor and disable attatching to HMD to prevent camera movement that isn't from the level sequence
#jira UE-61766
Change 4301860 by Austin.Crismore
Fix for touch scrubbing. Added default values back in. Added logic to only allow scrubbing when touch focus was off.
#jira UE-61865
Change 4302294 by Jamie.Dale
Added functions to get your the localized spoken and subtitle text from a dialogue wave
Change 4304393 by Jamie.Dale
Added support for BlueprintAssignable properties in Python
Change 4305852 by Jamie.Dale
Removed hard-dependency between EditorScriptingUtilities and PythonScriptPlugin
Backed-out changelist 4259264 and query Python availability based on whether anything is available to handle the command
#jira UE-62318
Change 4308550 by Jamie.Dale
Fixed crash when passing a null world to Python actor iterators
Change 4311867 by Homam.Bahnassi
Revit master material with exposed parameters matching the API when possible.
Change 4314428 by Francis.Hurteau
Made the usage of the bBuildDeveloperTools switch independent of the bCompileAgainstEngine switch.
Changed bBuildDeveloperTools TargetRule in UnrealBuildTool to a nullable to keep the old behavior in case where bBuildDeveloperTools wasn't explicitly set in TargetRules
Change 4315134 by Jamie.Dale
Defer editable text focus selection until mouse-up to allow the user to make an initial selection
#jira UE-58086
Change 4318615 by Johan.Duparc
EditorFactories: consistent return values after asset import.
Change 4322459 by Jamie.Dale
Made SequencerScripting an Editor plugin as it depends on PythonScriptPlugin which is an Editor plugin
This was causing issues at runtime when SequencerScripting was enabled, as it failed to load PythonScriptPlugin (which hadn't been built).
Change 4323341 by Francis.Hurteau
Implement proper message bus protocol version negociation with static nodes
Change 4323733 by Francis.Hurteau
Fix VR Pausing Sequence Scrubbing just setting playback speed to 0.0
Change 4324319 by Jamie.Dale
Exposed transactions to Blueprints
Change 4325847 by Alistair.White
Copying //Tasks/UE4/Private-PixelStreaming[at]4325566 to Dev-Enterprise-Minimal (//UE4/Dev-Enterprise-Minimal)
This adds the new experimental PixelStreaming plugin to allow streaming of an Unreal client's audio & video stream to a browser through the WebRTC protocol to support new uses for enterprise customers.
Change 4326282 by Simon.Tourangeau
nDisplay native present handler
Change 4326581 by Jamie.Dale
Replacing FDateTime with int64 Ticks value to workaround UE-63485
Change 4326599 by Homam.Bahnassi
Moving texture coords outside UVEdit function to allow using different UV channels.
Change 4333250 by Francis.Hurteau
Small TFuture changes:
* cleans up TFuture::Then with usage of TUniqueFunction
* added TFuture::Reset to invalidate it and remove continuation from a future shared state
Change 4333359 by Homam.Bahnassi
Support scaling and rotating UVs around arbitrary pivot
Change 4333566 by Johan.Duparc
Expose ProxyLOD functionalities to Scripting
#jira UEENT-1788
Change 4333988 by Jamie.Dale
Allow UHT to parse FText default parameter values
INVTEXT, NSLOCTEXT, LOCTABLE, and FText::GetEmpty() are supported. LOCTEXT isn't as it relies on an external macro that is known to C++ but not to UHT (NSLOCTEXT can easily be used instead).
Change 4335020 by Francis.Hurteau
Uncomment MessageBus::Send deprecation notice for 4.21
Update MessageBus Send usage to new API
Change 4335195 by JeanMichel.Dignard
Add a SetLodFromStaticMesh script utility function
#jira UEENT-1789
Change 4335231 by Anousack.Kitisa
Added functions to generate planar, cylindrical, box UV mapping.
#jira UEENT-1598
Change 4335373 by Jamie.Dale
Cleaned up some places creating empty literal texts
Change 4335458 by Jamie.Dale
Allow UHT to parse FText() as an alias of FText::GetEmpty() when processing default values
Change 4335875 by Max.Chen
Sequencer: Clear RF_Transient on pasted tracks/sections
#jira UE-63537
Change 4336497 by Johan.Duparc
ProxyLOD: Fix progress bar issue
- removed duplicated code
- removed duplicated LongTask object
#jira UEENT-1788
Change 4336723 by Jamie.Dale
Ensure that Python generated types create their CDO at the correct point
#jira UE-62895
Change 4340594 by Ben.Marsh
Fix manifest being invalidated when building two enterprise targets in a row. Fixes CIS error.
#jira UE-63644
#ROBOMERGE-OWNER: jason.bestimt
#ROBOMERGE-SOURCE: CL 4342443 in //UE4/Main/...
#ROBOMERGE-BOT: DEVVR (Main -> Dev-VR)
[CL 4342474 by jason bestimt in Dev-VR branch]
2018-09-04 16:42:27 -04:00
static const float FOVRad = FMath : : DegreesToRadians ( 45.0f ) ;
2017-06-30 12:21:06 -04:00
static const FMatrix ProjectionMatrix = FPerspectiveMatrix ( FOVRad , 1920 , 1080 , 0.01f ) ;
FHierarchicalLODUtilitiesModule & HLODModule = FModuleManager : : LoadModuleChecked < FHierarchicalLODUtilitiesModule > ( " HierarchicalLODUtilities " ) ;
IHierarchicalLODUtilities * Utilities = HLODModule . GetUtilities ( ) ;
float EstimatedDistance = Utilities - > CalculateDrawDistanceFromScreenSize ( EstimatedBounds . SphereRadius , ScreenSize , ProjectionMatrix ) ;
SlowTask . EnterProgressFrame ( 5.0f , LOCTEXT ( " CreateProxyMesh_CollectingMeshes " , " Collecting Input Static Meshes " ) ) ;
// Mesh / LOD index
2019-01-17 18:54:05 -05:00
TMap < uint32 , FMeshDescription * > RawMeshLODs ;
2017-06-30 12:21:06 -04:00
2019-01-17 18:54:05 -05:00
TArray < FMeshDescription * > RawMeshData ;
2017-06-30 12:21:06 -04:00
// LOD index, <original section index, unique section index>
TMultiMap < uint32 , TPair < uint32 , uint32 > > UniqueSectionIndexPerLOD ;
// Unique set of sections in mesh
TArray < FSectionInfo > UniqueSections ;
TArray < FSectionInfo > Sections ;
TMultiMap < uint32 , uint32 > SectionToMesh ;
2017-09-11 10:43:35 -04:00
int32 SummedLightmapPixels = 0 ;
2018-03-06 13:26:20 -05:00
TArray < const UStaticMeshComponent * > ImposterMeshComponents ;
2017-06-30 12:21:06 -04:00
for ( const UStaticMeshComponent * StaticMeshComponent : ComponentsToMerge )
{
2018-03-06 13:26:20 -05:00
int32 NumInstances = 1 ;
if ( StaticMeshComponent - > bUseMaxLODAsImposter )
2017-06-30 12:21:06 -04:00
{
2018-03-06 13:26:20 -05:00
ImposterMeshComponents . Add ( StaticMeshComponent ) ;
2017-06-30 12:21:06 -04:00
}
2018-03-06 13:26:20 -05:00
else
{
const int32 ScreenSizeBasedLODLevel = Utilities - > GetLODLevelForScreenSize ( StaticMeshComponent , Utilities - > CalculateScreenSizeFromDrawDistance ( StaticMeshComponent - > Bounds . SphereRadius , ProjectionMatrix , EstimatedDistance ) ) ;
const int32 LODIndex = InMeshProxySettings . bCalculateCorrectLODModel ? ScreenSizeBasedLODLevel : 0 ;
static const bool bPropagateVertexColours = true ;
2019-01-17 18:54:05 -05:00
// Retrieve mesh data in FMeshDescription form
FMeshDescription * RawMesh = new FMeshDescription ( ) ;
UStaticMesh : : RegisterMeshAttributes ( * RawMesh ) ;
2018-03-06 13:26:20 -05:00
FMeshMergeHelpers : : RetrieveMesh ( StaticMeshComponent , LODIndex , * RawMesh , bPropagateVertexColours ) ;
const int32 MeshIndex = RawMeshData . Add ( RawMesh ) ;
// Reset section array for reuse
Sections . SetNum ( 0 , false ) ;
// Extract sections for given LOD index from the mesh
FMeshMergeHelpers : : ExtractSections ( StaticMeshComponent , LODIndex , Sections ) ;
for ( int32 SectionIndex = 0 ; SectionIndex < Sections . Num ( ) ; + + SectionIndex )
{
FSectionInfo & Section = Sections [ SectionIndex ] ;
const int32 UniqueIndex = UniqueSections . AddUnique ( Section ) ;
UniqueSectionIndexPerLOD . Add ( MeshIndex , TPair < uint32 , uint32 > ( UniqueIndex , Section . MaterialIndex ) ) ;
SectionToMesh . Add ( UniqueIndex , MeshIndex ) ;
}
// If the component is an ISMC then we need to duplicate the vertex data
if ( StaticMeshComponent - > GetClass ( ) = = UInstancedStaticMeshComponent : : StaticClass ( ) )
{
const UInstancedStaticMeshComponent * InstancedStaticMeshComponent = Cast < UInstancedStaticMeshComponent > ( StaticMeshComponent ) ;
FMeshMergeHelpers : : ExpandInstances ( InstancedStaticMeshComponent , * RawMesh , Sections ) ;
NumInstances = InstancedStaticMeshComponent - > PerInstanceSMData . Num ( ) ;
}
}
2017-09-11 10:43:35 -04:00
int32 LightMapWidth , LightMapHeight ;
StaticMeshComponent - > GetLightMapResolution ( LightMapWidth , LightMapHeight ) ;
// Make sure we at least have some lightmap space allocated in case the static mesh is set up with invalid input
2018-03-06 13:26:20 -05:00
SummedLightmapPixels + = FMath : : Max ( 16 , LightMapHeight * LightMapWidth * NumInstances ) ;
2017-06-30 12:21:06 -04:00
}
TArray < UMaterialInterface * > UniqueMaterials ;
TMultiMap < uint32 , uint32 > SectionToMaterialMap ;
for ( int32 SectionIndex = 0 ; SectionIndex < UniqueSections . Num ( ) ; + + SectionIndex )
{
FSectionInfo & Section = UniqueSections [ SectionIndex ] ;
const int32 UniqueIndex = UniqueMaterials . AddUnique ( Section . Material ) ;
SectionToMaterialMap . Add ( UniqueIndex , SectionIndex ) ;
}
TArray < FMeshData > GlobalMeshSettings ;
TArray < FMaterialData > GlobalMaterialSettings ;
UMaterialOptions * Options = PopulateMaterialOptions ( InMeshProxySettings . MaterialSettings ) ;
TArray < EMaterialProperty > MaterialProperties ;
2018-02-14 14:13:42 -05:00
for ( const FPropertyEntry & Entry : Options - > Properties )
2017-06-30 12:21:06 -04:00
{
2017-09-11 10:43:35 -04:00
if ( Entry . Property ! = MP_MAX )
{
MaterialProperties . Add ( Entry . Property ) ;
}
2017-06-30 12:21:06 -04:00
}
// Mesh index / ( Mesh relative section index / output index )
TMultiMap < uint32 , TPair < uint32 , uint32 > > OutputMaterialsMap ;
for ( int32 MaterialIndex = 0 ; MaterialIndex < UniqueMaterials . Num ( ) ; + + MaterialIndex )
{
UMaterialInterface * Material = UniqueMaterials [ MaterialIndex ] ;
TArray < uint32 > SectionIndices ;
SectionToMaterialMap . MultiFind ( MaterialIndex , SectionIndices ) ;
// Check whether or not this material requires mesh data
int32 NumTexCoords = 0 ;
bool bUseVertexData = false ;
FMaterialUtilities : : AnalyzeMaterial ( Material , MaterialProperties , NumTexCoords , bUseVertexData ) ;
FMaterialData MaterialSettings ;
MaterialSettings . Material = Material ;
for ( const FPropertyEntry & Entry : Options - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( ! Entry . bUseConstantValue & & Material - > IsPropertyActive ( Entry . Property ) & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
MaterialSettings . PropertySizes . Add ( Entry . Property , Entry . bUseCustomSize ? Entry . CustomSize : Options - > TextureSize ) ;
}
}
if ( bUseVertexData | | NumTexCoords ! = 0 )
{
for ( uint32 SectionIndex : SectionIndices )
{
TArray < uint32 > MeshIndices ;
SectionToMesh . MultiFind ( SectionIndex , MeshIndices ) ;
for ( uint32 MeshIndex : MeshIndices )
{
FMeshData MeshSettings ;
2019-01-17 18:54:05 -05:00
// Retrieve raw mesh
FMeshDescription * MeshDescription = RawMeshData [ MeshIndex ] ;
MeshSettings . RawMeshDescription = MeshDescription ;
2017-06-30 12:21:06 -04:00
2019-01-17 18:54:05 -05:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = MeshSettings . RawMeshDescription - > VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
2017-06-30 12:21:06 -04:00
// If we already have lightmap uvs generated or the lightmap coordinate index != 0 and available we can reuse those instead of having to generate new ones
2019-01-17 18:54:05 -05:00
if ( InMeshProxySettings . bReuseMeshLightmapUVs
& & ( ComponentsToMerge [ MeshIndex ] - > GetStaticMesh ( ) - > SourceModels [ 0 ] . BuildSettings . bGenerateLightmapUVs
| | ( ComponentsToMerge [ MeshIndex ] - > GetStaticMesh ( ) - > LightMapCoordinateIndex ! = 0 & & VertexInstanceUVs . GetNumElements ( ) > 0 & & VertexInstanceUVs . GetNumIndices ( ) > ComponentsToMerge [ MeshIndex ] - > GetStaticMesh ( ) - > LightMapCoordinateIndex ) ) )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
MeshSettings . CustomTextureCoordinates . Reset ( VertexInstanceUVs . GetNumElements ( ) ) ;
int32 LightMapCoordinateIndex = ComponentsToMerge [ MeshIndex ] - > GetStaticMesh ( ) - > LightMapCoordinateIndex ;
for ( const FVertexInstanceID VertexInstanceID : MeshSettings . RawMeshDescription - > VertexInstances ( ) . GetElementIDs ( ) )
{
MeshSettings . CustomTextureCoordinates . Add ( VertexInstanceUVs . Get ( VertexInstanceID , LightMapCoordinateIndex ) ) ;
}
2017-06-30 12:21:06 -04:00
ScaleTextureCoordinatesToBox ( FBox2D ( FVector2D : : ZeroVector , FVector2D ( 1 , 1 ) ) , MeshSettings . CustomTextureCoordinates ) ;
}
else
{
// Generate unique UVs for mesh (should only be done if needed)
2019-01-17 18:54:05 -05:00
FMeshDescriptionOperations : : GenerateUniqueUVsForStaticMesh ( * MeshDescription , Options - > TextureSize . GetMax ( ) , false , MeshSettings . CustomTextureCoordinates ) ;
2017-06-30 12:21:06 -04:00
ScaleTextureCoordinatesToBox ( FBox2D ( FVector2D : : ZeroVector , FVector2D ( 1 , 1 ) ) , MeshSettings . CustomTextureCoordinates ) ;
}
MeshSettings . TextureCoordinateBox = FBox2D ( MeshSettings . CustomTextureCoordinates ) ;
// Section index is a unique one so we need to map it to the mesh's equivalent(s)
TArray < TPair < uint32 , uint32 > > UniqueToMeshSectionIndices ;
UniqueSectionIndexPerLOD . MultiFind ( MeshIndex , UniqueToMeshSectionIndices ) ;
for ( const TPair < uint32 , uint32 > IndexPair : UniqueToMeshSectionIndices )
{
if ( IndexPair . Key = = SectionIndex )
{
MeshSettings . MaterialIndices . Add ( IndexPair . Value ) ;
}
}
// Retrieve lightmap for usage of lightmap data
const UStaticMeshComponent * StaticMeshComponent = ComponentsToMerge [ MeshIndex ] ;
if ( StaticMeshComponent - > LODData . IsValidIndex ( 0 ) )
{
const FStaticMeshComponentLODInfo & ComponentLODInfo = StaticMeshComponent - > LODData [ 0 ] ;
const FMeshMapBuildData * MeshMapBuildData = StaticMeshComponent - > GetMeshMapBuildData ( ComponentLODInfo ) ;
if ( MeshMapBuildData )
{
MeshSettings . LightMap = MeshMapBuildData - > LightMap ;
MeshSettings . LightMapIndex = StaticMeshComponent - > GetStaticMesh ( ) - > LightMapCoordinateIndex ;
}
}
// For each original material index add an entry to the corresponding LOD and bake output index
for ( int32 Index : MeshSettings . MaterialIndices )
{
OutputMaterialsMap . Add ( MeshIndex , TPair < uint32 , uint32 > ( Index , GlobalMeshSettings . Num ( ) ) ) ;
}
GlobalMeshSettings . Add ( MeshSettings ) ;
GlobalMaterialSettings . Add ( MaterialSettings ) ;
}
}
}
else
{
// Add simple bake entry
FMeshData MeshSettings ;
2019-01-17 18:54:05 -05:00
MeshSettings . RawMeshDescription = nullptr ;
2017-06-30 12:21:06 -04:00
MeshSettings . TextureCoordinateBox = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
MeshSettings . TextureCoordinateIndex = 0 ;
// For each original material index add an entry to the corresponding LOD and bake output index
for ( uint32 SectionIndex : SectionIndices )
{
TArray < uint32 > MeshIndices ;
SectionToMesh . MultiFind ( SectionIndex , MeshIndices ) ;
for ( uint32 MeshIndex : MeshIndices )
{
TArray < TPair < uint32 , uint32 > > UniqueToMeshSectionIndices ;
UniqueSectionIndexPerLOD . MultiFind ( MeshIndex , UniqueToMeshSectionIndices ) ;
for ( const TPair < uint32 , uint32 > IndexPair : UniqueToMeshSectionIndices )
{
if ( IndexPair . Key = = SectionIndex )
{
OutputMaterialsMap . Add ( MeshIndex , TPair < uint32 , uint32 > ( IndexPair . Value , GlobalMeshSettings . Num ( ) ) ) ;
}
}
}
}
GlobalMeshSettings . Add ( MeshSettings ) ;
GlobalMaterialSettings . Add ( MaterialSettings ) ;
}
}
2018-02-14 14:13:42 -05:00
TArray < FFlattenMaterial > FlattenedMaterials ;
IMaterialBakingModule & MaterialBakingModule = FModuleManager : : Get ( ) . LoadModuleChecked < IMaterialBakingModule > ( " MaterialBaking " ) ;
2017-06-30 12:21:06 -04:00
2018-02-14 14:13:42 -05:00
auto MaterialFlattenLambda =
[ this , & Options , & GlobalMeshSettings , & GlobalMaterialSettings , & RawMeshData , & OutputMaterialsMap , & MaterialBakingModule ] ( TArray < FFlattenMaterial > & FlattenedMaterialArray )
2017-06-30 12:21:06 -04:00
{
2018-02-14 14:13:42 -05:00
TArray < FMeshData * > MeshSettingPtrs ;
for ( int32 SettingsIndex = 0 ; SettingsIndex < GlobalMeshSettings . Num ( ) ; + + SettingsIndex )
2017-06-30 12:21:06 -04:00
{
2018-02-14 14:13:42 -05:00
MeshSettingPtrs . Add ( & GlobalMeshSettings [ SettingsIndex ] ) ;
}
TArray < FMaterialData * > MaterialSettingPtrs ;
for ( int32 SettingsIndex = 0 ; SettingsIndex < GlobalMaterialSettings . Num ( ) ; + + SettingsIndex )
{
MaterialSettingPtrs . Add ( & GlobalMaterialSettings [ SettingsIndex ] ) ;
}
TArray < FBakeOutput > BakeOutputs ;
MaterialBakingModule . BakeMaterials ( MaterialSettingPtrs , MeshSettingPtrs , BakeOutputs ) ;
// Append constant properties ?
TArray < FColor > ConstantData ;
FIntPoint ConstantSize ( 1 , 1 ) ;
for ( const FPropertyEntry & Entry : Options - > Properties )
{
if ( Entry . bUseConstantValue & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
2018-02-14 14:13:42 -05:00
ConstantData . SetNum ( 1 , false ) ;
ConstantData [ 0 ] = FColor ( Entry . ConstantValue * 255.0f , Entry . ConstantValue * 255.0f , Entry . ConstantValue * 255.0f ) ;
for ( FBakeOutput & Ouput : BakeOutputs )
{
Ouput . PropertyData . Add ( Entry . Property , ConstantData ) ;
Ouput . PropertySizes . Add ( Entry . Property , ConstantSize ) ;
}
2017-06-30 12:21:06 -04:00
}
}
2018-02-14 14:13:42 -05:00
ConvertOutputToFlatMaterials ( BakeOutputs , GlobalMaterialSettings , FlattenedMaterialArray ) ;
2017-06-30 12:21:06 -04:00
2018-02-14 14:13:42 -05:00
// Now have the baked out material data, need to have a map or actually remap the raw mesh data to baked material indices
for ( int32 MeshIndex = 0 ; MeshIndex < RawMeshData . Num ( ) ; + + MeshIndex )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
FMeshDescription & RawMesh = * RawMeshData [ MeshIndex ] ;
2017-06-30 12:21:06 -04:00
2018-02-14 14:13:42 -05:00
TArray < TPair < uint32 , uint32 > > SectionAndOutputIndices ;
OutputMaterialsMap . MultiFind ( MeshIndex , SectionAndOutputIndices ) ;
2019-04-12 12:57:00 -04:00
//Make sure the section index are in the correct order
SectionAndOutputIndices . Sort ( [ ] ( const TPair < uint32 , uint32 > & A , const TPair < uint32 , uint32 > & B ) { return ( A . Key < B . Key ) ; } ) ;
2018-02-14 14:13:42 -05:00
TArray < int32 > Remap ;
2019-03-29 17:00:56 -04:00
TArray < int32 > UniqueMaterialIndexes ;
2018-02-14 14:13:42 -05:00
// Reorder loops
for ( const TPair < uint32 , uint32 > & IndexPair : SectionAndOutputIndices )
2017-06-30 12:21:06 -04:00
{
2019-03-29 17:00:56 -04:00
//We are not using the IndexPair.Key since we want to keep the polygon group
//We instead find the section index by looking at unique IndexPair.Value
2018-02-14 14:13:42 -05:00
const int32 NewIndex = IndexPair . Value ;
2019-03-29 17:00:56 -04:00
const int32 SectionIndex = UniqueMaterialIndexes . AddUnique ( NewIndex ) ;
2018-02-14 14:13:42 -05:00
if ( Remap . Num ( ) < ( SectionIndex + 1 ) )
{
Remap . SetNum ( SectionIndex + 1 ) ;
}
Remap [ SectionIndex ] = NewIndex ;
2017-06-30 12:21:06 -04:00
}
2019-01-17 18:54:05 -05:00
TMap < FPolygonGroupID , FPolygonGroupID > RemapPolygonGroup ;
for ( const FPolygonGroupID & PolygonGroupID : RawMesh . PolygonGroups ( ) . GetElementIDs ( ) )
2018-02-14 14:13:42 -05:00
{
2019-01-17 18:54:05 -05:00
checkf ( Remap . IsValidIndex ( PolygonGroupID . GetValue ( ) ) , TEXT ( " Missing material bake output index entry for mesh(section) " ) ) ;
int32 RemapID = Remap [ PolygonGroupID . GetValue ( ) ] ;
RemapPolygonGroup . Add ( PolygonGroupID , FPolygonGroupID ( RemapID ) ) ;
}
2019-03-13 14:47:20 -04:00
FMeshDescriptionOperations : : RemapPolygonGroups ( RawMesh , RemapPolygonGroup ) ;
2017-06-30 12:21:06 -04:00
}
2018-02-14 14:13:42 -05:00
} ;
2017-06-30 12:21:06 -04:00
2019-01-24 15:35:51 -05:00
// Landscape culling. NB these are temporary copies of the culling data and should be deleted after use.
2019-01-17 18:54:05 -05:00
TArray < FMeshDescription * > CullingRawMeshes ;
2017-06-30 12:21:06 -04:00
if ( InMeshProxySettings . bUseLandscapeCulling )
{
SlowTask . EnterProgressFrame ( 5.0f , LOCTEXT ( " CreateProxyMesh_LandscapeCulling " , " Applying Landscape Culling " ) ) ;
2018-02-14 14:13:42 -05:00
UWorld * InWorld = ComponentsToMerge [ 0 ] - > GetWorld ( ) ;
2017-06-30 12:21:06 -04:00
FMeshMergeHelpers : : RetrieveCullingLandscapeAndVolumes ( InWorld , EstimatedBounds , InMeshProxySettings . LandscapeCullingPrecision , CullingRawMeshes ) ;
}
// Allocate merge complete data
FMergeCompleteData * Data = new FMergeCompleteData ( ) ;
Data - > InOuter = InOuter ;
Data - > InProxySettings = InMeshProxySettings ;
Data - > ProxyBasePackageName = InProxyBasePackageName ;
Data - > CallbackDelegate = InProxyCreatedDelegate ;
2018-03-06 13:26:20 -05:00
Data - > ImposterComponents = ImposterMeshComponents ;
2018-05-23 21:04:31 -04:00
Data - > StaticMeshComponents = ComponentsToMerge ;
Data - > StaticMeshComponents . RemoveAll ( [ & ] ( UStaticMeshComponent * Component ) { return ImposterMeshComponents . Contains ( Component ) ; } ) ;
2018-03-06 13:26:20 -05:00
Data - > BaseMaterial = InBaseMaterial ;
2017-06-30 12:21:06 -04:00
2017-09-11 10:43:35 -04:00
// Lightmap resolution
if ( InMeshProxySettings . bComputeLightMapResolution )
{
Data - > InProxySettings . LightMapResolution = FMath : : CeilToInt ( FMath : : Sqrt ( SummedLightmapPixels ) ) ;
}
2017-06-30 12:21:06 -04:00
// Add this proxy job to map
Processor - > AddProxyJob ( InGuid , Data ) ;
// We are only using LOD level 0 (ProxyMeshTargetLODLevel)
TArray < FMeshMergeData > MergeDataEntries ;
for ( int32 Index = 0 ; Index < RawMeshData . Num ( ) ; + + Index )
{
FMeshMergeData MergeData ;
MergeData . SourceStaticMesh = ComponentsToMerge [ Index ] - > GetStaticMesh ( ) ;
MergeData . RawMesh = RawMeshData [ Index ] ;
2018-02-14 14:13:42 -05:00
MergeData . bIsClippingMesh = false ;
2017-06-30 12:21:06 -04:00
FMeshMergeHelpers : : CalculateTextureCoordinateBoundsForRawMesh ( * MergeData . RawMesh , MergeData . TexCoordBounds ) ;
2018-02-14 14:13:42 -05:00
2017-06-30 12:21:06 -04:00
FMeshData * MeshData = GlobalMeshSettings . FindByPredicate ( [ & ] ( const FMeshData & Entry )
{
2019-01-17 18:54:05 -05:00
return Entry . RawMeshDescription = = MergeData . RawMesh & & ( Entry . CustomTextureCoordinates . Num ( ) | | Entry . TextureCoordinateIndex ! = 0 ) ;
2017-06-30 12:21:06 -04:00
} ) ;
if ( MeshData )
{
if ( MeshData - > CustomTextureCoordinates . Num ( ) )
{
MergeData . NewUVs = MeshData - > CustomTextureCoordinates ;
}
else
{
2019-01-17 18:54:05 -05:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = MeshData - > RawMeshDescription - > VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
MergeData . NewUVs . Reset ( MeshData - > RawMeshDescription - > VertexInstances ( ) . Num ( ) ) ;
for ( const FVertexInstanceID VertexInstanceID : MeshData - > RawMeshDescription - > VertexInstances ( ) . GetElementIDs ( ) )
{
MergeData . NewUVs . Add ( VertexInstanceUVs . Get ( VertexInstanceID , MeshData - > TextureCoordinateIndex ) ) ;
}
2017-06-30 12:21:06 -04:00
}
MergeData . TexCoordBounds [ 0 ] = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
}
MergeDataEntries . Add ( MergeData ) ;
}
// Populate landscape clipping geometry
2019-01-17 18:54:05 -05:00
for ( FMeshDescription * RawMesh : CullingRawMeshes )
2017-06-30 12:21:06 -04:00
{
FMeshMergeData ClipData ;
ClipData . bIsClippingMesh = true ;
ClipData . RawMesh = RawMesh ;
MergeDataEntries . Add ( ClipData ) ;
}
SlowTask . EnterProgressFrame ( 50.0f , LOCTEXT ( " CreateProxyMesh_GenerateProxy " , " Generating Proxy Mesh " ) ) ;
2018-02-14 14:13:42 -05:00
2017-06-30 12:21:06 -04:00
// Choose Simplygon Swarm (if available) or local proxy lod method
if ( ReductionModule . GetDistributedMeshMergingInterface ( ) ! = nullptr & & GetDefault < UEditorPerProjectUserSettings > ( ) - > bUseSimplygonSwarm & & bAllowAsync )
{
2018-02-14 14:13:42 -05:00
MaterialFlattenLambda ( FlattenedMaterials ) ;
2017-06-30 12:21:06 -04:00
ReductionModule . GetDistributedMeshMergingInterface ( ) - > ProxyLOD ( MergeDataEntries , Data - > InProxySettings , FlattenedMaterials , InGuid ) ;
}
else
{
2018-02-14 14:13:42 -05:00
IMeshMerging * MeshMerging = ReductionModule . GetMeshMergingInterface ( ) ;
// Register the Material Flattening code if parallel execution is supported, otherwise directly run it.
if ( MeshMerging - > bSupportsParallelMaterialBake ( ) )
{
MeshMerging - > BakeMaterialsDelegate . BindLambda ( MaterialFlattenLambda ) ;
}
else
{
MaterialFlattenLambda ( FlattenedMaterials ) ;
}
MeshMerging - > ProxyLOD ( MergeDataEntries , Data - > InProxySettings , FlattenedMaterials , InGuid ) ;
2017-06-30 12:21:06 -04:00
Processor - > Tick ( 0 ) ; // make sure caller gets merging results
}
2019-01-24 15:35:51 -05:00
// Clean up the CullingRawMeshes
for ( FMeshDescription * RawMesh : CullingRawMeshes )
{
delete RawMesh ;
}
2017-06-30 12:21:06 -04:00
}
2019-01-24 15:35:51 -05:00
2018-03-06 13:26:20 -05:00
bool FMeshMergeUtilities : : IsValidBaseMaterial ( const UMaterialInterface * InBaseMaterial , bool bShowToaster ) const
{
2018-05-23 21:04:31 -04:00
if ( InBaseMaterial ! = nullptr )
2018-03-06 13:26:20 -05:00
{
TArray < FGuid > ParameterIds ;
TArray < FString > MissingParameters ;
auto NameCheckLambda = [ & MissingParameters ] ( const TArray < FMaterialParameterInfo > & InCheck , const TArray < FName > & InRequired )
{
for ( const FName & Name : InRequired )
{
if ( ! InCheck . ContainsByPredicate ( [ Name ] ( const FMaterialParameterInfo & ParamInfo ) { return ( ParamInfo . Name = = Name ) ; } ) )
{
MissingParameters . Add ( Name . ToString ( ) ) ;
}
}
} ;
TArray < FMaterialParameterInfo > TextureParameterInfos ;
TArray < FName > RequiredTextureNames = { TEXT ( " DiffuseTexture " ) , TEXT ( " NormalTexture " ) , TEXT ( " PackedTexture " ) , TEXT ( " MetallicTexture " ) , TEXT ( " SpecularTexture " ) , TEXT ( " RoughnessTexture " ) , TEXT ( " EmissiveTexture " ) , TEXT ( " OpacityTexture " ) , TEXT ( " OpacityMaskTexture " ) , TEXT ( " AmbientOcclusionTexture " ) } ;
2018-05-23 21:04:31 -04:00
InBaseMaterial - > GetAllTextureParameterInfo ( TextureParameterInfos , ParameterIds ) ;
2018-03-06 13:26:20 -05:00
NameCheckLambda ( TextureParameterInfos , RequiredTextureNames ) ;
TArray < FMaterialParameterInfo > ScalarParameterInfos ;
TArray < FName > RequiredScalarNames = { TEXT ( " MetallicConst " ) , TEXT ( " SpecularConst " ) , TEXT ( " RoughnessConst " ) , TEXT ( " OpacityConst " ) , TEXT ( " OpacityMaskConst " ) , TEXT ( " AmbientOcclusionConst " ) , TEXT ( " EmissiveScale " ) } ;
2018-05-23 21:04:31 -04:00
InBaseMaterial - > GetAllScalarParameterInfo ( ScalarParameterInfos , ParameterIds ) ;
2018-03-06 13:26:20 -05:00
NameCheckLambda ( ScalarParameterInfos , RequiredScalarNames ) ;
TArray < FMaterialParameterInfo > VectorParameterInfos ;
TArray < FName > RequiredVectorNames = { TEXT ( " DiffuseConst " ) , TEXT ( " EmissiveConst " ) } ;
2018-05-23 21:04:31 -04:00
InBaseMaterial - > GetAllVectorParameterInfo ( VectorParameterInfos , ParameterIds ) ;
2018-03-06 13:26:20 -05:00
NameCheckLambda ( VectorParameterInfos , RequiredVectorNames ) ;
TArray < FMaterialParameterInfo > StaticSwitchParameterInfos ;
TArray < FName > RequiredSwitchNames = { TEXT ( " UseDiffuse " ) , TEXT ( " PackMetallic " ) , TEXT ( " PackSpecular " ) , TEXT ( " PackRoughness " ) , TEXT ( " UseMetallic " ) , TEXT ( " UseSpecular " ) , TEXT ( " UseRoughness " ) , TEXT ( " UseEmissive " ) , TEXT ( " UseOpacity " ) , TEXT ( " UseOpacityMask " ) , TEXT ( " UseAmbientOcclusion " ) } ;
2018-05-23 21:04:31 -04:00
InBaseMaterial - > GetAllStaticSwitchParameterInfo ( StaticSwitchParameterInfos , ParameterIds ) ;
2018-03-06 13:26:20 -05:00
NameCheckLambda ( StaticSwitchParameterInfos , RequiredSwitchNames ) ;
if ( MissingParameters . Num ( ) > 0 )
{
FString MissingNamesString ;
for ( const FString & Name : MissingParameters )
{
if ( ! MissingNamesString . IsEmpty ( ) )
{
MissingNamesString + = " , " ;
MissingNamesString + = Name ;
}
else
{
MissingNamesString + = Name ;
}
}
# if WITH_EDITOR
if ( bShowToaster )
{
FFormatNamedArguments Arguments ;
Arguments . Add ( TEXT ( " MaterialName " ) , FText : : FromString ( InBaseMaterial - > GetName ( ) ) ) ;
FText ErrorMessage = FText : : Format ( LOCTEXT ( " UHierarchicalLODSettings_PostEditChangeProperty " , " Material {MaterialName} is missing required Material Parameters (check log for details) " ) , Arguments ) ;
FNotificationInfo Info ( ErrorMessage ) ;
Info . ExpireDuration = 5.0f ;
FSlateNotificationManager : : Get ( ) . AddNotification ( Info ) ;
}
UE_LOG ( LogMeshMerging , Error , TEXT ( " Material %s is missing required Material Parameters %s, resetting to default. " ) , * InBaseMaterial - > GetName ( ) , * MissingNamesString ) ;
# endif // WITH_EDITOR
return false ;
}
else
{
return true ;
}
}
return false ;
}
void FMeshMergeUtilities : : RegisterExtension ( IMeshMergeExtension * InExtension )
{
MeshMergeExtensions . Add ( InExtension ) ;
}
void FMeshMergeUtilities : : UnregisterExtension ( IMeshMergeExtension * InExtension )
{
MeshMergeExtensions . Remove ( InExtension ) ;
}
2019-01-17 18:54:05 -05:00
bool RetrieveRawMeshData ( FMeshMergeDataTracker & DataTracker
, const int32 ComponentIndex
, const int32 LODIndex
, UStaticMeshComponent * Component
, const bool bPropagateMeshData
, TArray < FSectionInfo > & Sections
, FStaticMeshComponentAdapter & Adapter
, const bool bMergeMaterialData
, const FMeshMergingSettings & InSettings )
{
// Retrieve raw mesh data
FMeshDescription & RawMesh = DataTracker . AddAndRetrieveRawMesh ( ComponentIndex , LODIndex , Component - > GetStaticMesh ( ) ) ;
Adapter . RetrieveRawMeshData ( LODIndex , RawMesh , bPropagateMeshData ) ;
// Reset section for reuse
Sections . SetNum ( 0 , false ) ;
// Extract sections for given LOD index from the mesh
Adapter . RetrieveMeshSections ( LODIndex , Sections ) ;
TMap < FPolygonGroupID , FPolygonGroupID > RemapPolygonGroup ;
for ( int32 SectionIndex = 0 ; SectionIndex < Sections . Num ( ) ; + + SectionIndex )
{
const FSectionInfo & Section = Sections [ SectionIndex ] ;
// Unique section index for remapping
const int32 UniqueIndex = DataTracker . AddSection ( Section ) ;
// Store of original to unique section index entry for this component + LOD index
DataTracker . AddSectionRemapping ( ComponentIndex , LODIndex , Section . MaterialIndex , UniqueIndex ) ;
DataTracker . AddMaterialSlotName ( Section . Material , Section . MaterialSlotName ) ;
if ( ! bMergeMaterialData )
{
FMeshDescriptionOperations : : SwapPolygonPolygonGroup ( RawMesh , UniqueIndex , Section . StartIndex , Section . EndIndex , false ) ;
}
else
{
RemapPolygonGroup . Add ( FPolygonGroupID ( SectionIndex ) , FPolygonGroupID ( Section . MaterialIndex ) ) ;
}
}
if ( bMergeMaterialData )
{
FMeshDescriptionOperations : : RemapPolygonGroups ( RawMesh , RemapPolygonGroup ) ;
}
//Compact the PolygonGroupID to make sure it follow the section index
FElementIDRemappings RemapInformation ;
RawMesh . Compact ( RemapInformation ) ;
// If the component is an ISMC then we need to duplicate the vertex data
if ( Component - > GetClass ( ) = = UInstancedStaticMeshComponent : : StaticClass ( ) )
{
const UInstancedStaticMeshComponent * InstancedStaticMeshComponent = Cast < UInstancedStaticMeshComponent > ( Component ) ;
FMeshMergeHelpers : : ExpandInstances ( InstancedStaticMeshComponent , RawMesh , Sections ) ;
}
if ( InSettings . bUseLandscapeCulling )
{
FMeshMergeHelpers : : CullTrianglesFromVolumesAndUnderLandscapes ( Component - > GetWorld ( ) , Adapter . GetBounds ( ) , RawMesh ) ;
}
// If the valid became invalid during retrieval remove it again
const bool bValidMesh = RawMesh . VertexInstances ( ) . Num ( ) > 0 ;
if ( ! bValidMesh )
{
DataTracker . RemoveRawMesh ( ComponentIndex , LODIndex ) ;
}
else if ( Component - > GetStaticMesh ( ) ! = nullptr )
{
// If the mesh is valid at this point, record the lightmap UV so we have a record for use later
DataTracker . AddLightmapChannelRecord ( ComponentIndex , LODIndex , Component - > GetStaticMesh ( ) - > LightMapCoordinateIndex ) ;
}
return bValidMesh ;
}
2018-03-06 13:26:20 -05:00
void FMeshMergeUtilities : : MergeComponentsToStaticMesh ( const TArray < UPrimitiveComponent * > & ComponentsToMerge , UWorld * World , const FMeshMergingSettings & InSettings , UMaterialInterface * InBaseMaterial , UPackage * InOuter , const FString & InBasePackageName , TArray < UObject * > & OutAssetsToSync , FVector & OutMergedActorLocation , const float ScreenSize , bool bSilent /*= false*/ ) const
2017-06-30 12:21:06 -04:00
{
// Use first mesh for naming and pivot
bool bFirstMesh = true ;
FString MergedAssetPackageName ;
FVector MergedAssetPivot ;
2018-03-06 13:26:20 -05:00
2017-06-30 12:21:06 -04:00
TArray < UStaticMeshComponent * > StaticMeshComponentsToMerge ;
2018-03-06 13:26:20 -05:00
TArray < const UStaticMeshComponent * > ImposterComponents ;
2017-06-30 12:21:06 -04:00
for ( int32 MeshId = 0 ; MeshId < ComponentsToMerge . Num ( ) ; + + MeshId )
{
UStaticMeshComponent * MeshComponent = Cast < UStaticMeshComponent > ( ComponentsToMerge [ MeshId ] ) ;
if ( MeshComponent )
{
2018-03-06 13:26:20 -05:00
if ( MeshComponent - > bUseMaxLODAsImposter & & InSettings . bIncludeImposters )
{
ImposterComponents . Add ( MeshComponent ) ;
}
else
{
StaticMeshComponentsToMerge . Add ( MeshComponent ) ;
2017-06-30 12:21:06 -04:00
2018-03-06 13:26:20 -05:00
// Save the pivot and asset package name of the first mesh, will later be used for creating merged mesh asset
if ( bFirstMesh )
{
// Mesh component pivot point
MergedAssetPivot = InSettings . bPivotPointAtZero ? FVector : : ZeroVector : MeshComponent - > GetComponentTransform ( ) . GetLocation ( ) ;
// Source mesh asset package name
MergedAssetPackageName = MeshComponent - > GetStaticMesh ( ) - > GetOutermost ( ) - > GetName ( ) ;
2017-06-30 12:21:06 -04:00
2018-03-06 13:26:20 -05:00
bFirstMesh = false ;
}
2017-06-30 12:21:06 -04:00
}
}
}
// Nothing to do if no StaticMeshComponents
2018-03-06 13:26:20 -05:00
if ( StaticMeshComponentsToMerge . Num ( ) = = 0 & & ImposterComponents . Num ( ) = = 0 )
2017-06-30 12:21:06 -04:00
{
return ;
}
FMeshMergeDataTracker DataTracker ;
const bool bMergeAllLODs = InSettings . LODSelectionType = = EMeshLODSelectionType : : AllLODs ;
const bool bMergeMaterialData = InSettings . bMergeMaterials & & InSettings . LODSelectionType ! = EMeshLODSelectionType : : AllLODs ;
const bool bPropagateMeshData = InSettings . bBakeVertexDataToMesh | | ( bMergeMaterialData & & InSettings . bUseVertexDataForBakingMaterial ) ;
TArray < FStaticMeshComponentAdapter > Adapters ;
TArray < FSectionInfo > Sections ;
if ( bMergeAllLODs )
{
for ( int32 ComponentIndex = 0 ; ComponentIndex < StaticMeshComponentsToMerge . Num ( ) ; + + ComponentIndex )
{
UStaticMeshComponent * Component = StaticMeshComponentsToMerge [ ComponentIndex ] ;
Adapters . Add ( FStaticMeshComponentAdapter ( Component ) ) ;
FStaticMeshComponentAdapter & Adapter = Adapters . Last ( ) ;
2017-09-11 10:43:35 -04:00
if ( InSettings . bComputedLightMapResolution )
{
int32 LightMapHeight , LightMapWidth ;
if ( Component - > GetLightMapResolution ( LightMapWidth , LightMapHeight ) )
{
DataTracker . AddLightMapPixels ( LightMapWidth * LightMapHeight ) ;
}
}
2018-03-06 13:26:20 -05:00
const int32 NumLODs = [ & ] ( )
{
const int32 NumberOfLODsAvailable = Adapter . GetNumberOfLODs ( ) ;
if ( Component - > bUseMaxLODAsImposter )
{
return InSettings . bIncludeImposters ? NumberOfLODsAvailable : NumberOfLODsAvailable - 1 ;
}
return NumberOfLODsAvailable ;
} ( ) ;
2017-06-30 12:21:06 -04:00
for ( int32 LODIndex = 0 ; LODIndex < NumLODs ; + + LODIndex )
{
2019-01-17 18:54:05 -05:00
if ( ! RetrieveRawMeshData ( DataTracker
, ComponentIndex
, LODIndex
, Component
, bPropagateMeshData
, Sections
, Adapter
, false
, InSettings ) )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
//If the rawmesh was not retrieve properly break the loop
2017-06-30 12:21:06 -04:00
break ;
}
DataTracker . AddLODIndex ( LODIndex ) ;
}
}
}
else
{
// Retrieve HLOD module for calculating LOD index from screen size
FHierarchicalLODUtilitiesModule & Module = FModuleManager : : LoadModuleChecked < FHierarchicalLODUtilitiesModule > ( " HierarchicalLODUtilities " ) ;
IHierarchicalLODUtilities * Utilities = Module . GetUtilities ( ) ;
// Adding LOD 0 for merged mesh output
DataTracker . AddLODIndex ( 0 ) ;
// Retrieve mesh and section data for each component
2018-03-06 13:26:20 -05:00
for ( int32 ComponentIndex = 0 ; ComponentIndex < StaticMeshComponentsToMerge . Num ( ) ; + + ComponentIndex )
2017-06-30 12:21:06 -04:00
{
// Create material merge adapter for this component
UStaticMeshComponent * Component = StaticMeshComponentsToMerge [ ComponentIndex ] ;
Adapters . Add ( FStaticMeshComponentAdapter ( Component ) ) ;
FStaticMeshComponentAdapter & Adapter = Adapters . Last ( ) ;
// Determine LOD to use for merging, either user specified or calculated index and ensure we clamp to the maximum LOD index for this adapter
const int32 LODIndex = [ & ] ( )
{
2018-03-06 13:26:20 -05:00
switch ( InSettings . LODSelectionType )
2017-06-30 12:21:06 -04:00
{
2018-03-06 13:26:20 -05:00
case EMeshLODSelectionType : : SpecificLOD :
2017-06-30 12:21:06 -04:00
return FMath : : Min ( Adapter . GetNumberOfLODs ( ) - 1 , InSettings . SpecificLOD ) ;
2018-03-06 13:26:20 -05:00
case EMeshLODSelectionType : : CalculateLOD :
{
int32 Min = Adapter . GetNumberOfLODs ( ) - 1 ;
if ( Component - > bUseMaxLODAsImposter & & ! InSettings . bIncludeImposters )
{
Min = FMath : : Max ( 0 , Min - 1 ) ;
}
return FMath : : Min ( Min , Utilities - > GetLODLevelForScreenSize ( Component , FMath : : Clamp ( ScreenSize , 0.0f , 1.0f ) ) ) ;
}
default :
case EMeshLODSelectionType : : LowestDetailLOD :
{
if ( Component - > bUseMaxLODAsImposter & & ( ! InSettings . bIncludeImposters ) )
{
return FMath : : Max ( 0 , Adapter . GetNumberOfLODs ( ) - 2 ) ;
}
return Adapter . GetNumberOfLODs ( ) - 1 ;
}
2017-06-30 12:21:06 -04:00
}
} ( ) ;
2019-01-17 18:54:05 -05:00
RetrieveRawMeshData ( DataTracker
, ComponentIndex
, LODIndex
, Component
, bPropagateMeshData
, Sections
, Adapter
, bMergeMaterialData
, InSettings ) ;
2017-06-30 12:21:06 -04:00
}
}
DataTracker . ProcessRawMeshes ( ) ;
// Retrieve physics data
UBodySetup * BodySetupSource = nullptr ;
TArray < FKAggregateGeom > PhysicsGeometry ;
if ( InSettings . bMergePhysicsData )
{
ExtractPhysicsDataFromComponents ( ComponentsToMerge , PhysicsGeometry , BodySetupSource ) ;
}
// Find all unique materials and remap section to unique materials
TArray < UMaterialInterface * > UniqueMaterials ;
TMap < UMaterialInterface * , int32 > MaterialIndices ;
TMultiMap < uint32 , uint32 > SectionToMaterialMap ;
2018-03-06 13:26:20 -05:00
TMap < UMaterialInterface * , UMaterialInterface * > CollapsedMaterialMap ;
2017-06-30 12:21:06 -04:00
for ( int32 SectionIndex = 0 ; SectionIndex < DataTracker . NumberOfUniqueSections ( ) ; + + SectionIndex )
{
// Unique index for material
2018-03-06 13:26:20 -05:00
UMaterialInterface * MaterialInterface = DataTracker . GetMaterialForSectionIndex ( SectionIndex ) ;
int32 UniqueIndex = UniqueMaterials . IndexOfByPredicate ( [ & InSettings , MaterialInterface ] ( const UMaterialInterface * InMaterialInterface )
{
// Perform an optional custom comparison if we are trying to collapse material instances
if ( InSettings . bMergeEquivalentMaterials )
{
return FMaterialKey ( MaterialInterface ) = = FMaterialKey ( InMaterialInterface ) ;
}
return MaterialInterface = = InMaterialInterface ;
} ) ;
if ( UniqueIndex = = INDEX_NONE )
{
UniqueIndex = UniqueMaterials . Add ( MaterialInterface ) ;
}
// Update map to 'collapsed' materials
CollapsedMaterialMap . Add ( MaterialInterface , UniqueMaterials [ UniqueIndex ] ) ;
2017-06-30 12:21:06 -04:00
// Store off usage of unique material by unique sections
SectionToMaterialMap . Add ( UniqueIndex , SectionIndex ) ;
}
// For each unique material calculate how 'important' they are
TArray < float > MaterialImportanceValues ;
FMaterialUtilities : : DetermineMaterialImportance ( UniqueMaterials , MaterialImportanceValues ) ;
// If the user wants to merge materials into a single one
if ( bMergeMaterialData )
{
UMaterialOptions * MaterialOptions = PopulateMaterialOptions ( InSettings . MaterialSettings ) ;
// Check each material to see if the shader actually uses vertex data and collect flags
TArray < bool > bMaterialUsesVertexData ;
DetermineMaterialVertexDataUsage ( bMaterialUsesVertexData , UniqueMaterials , MaterialOptions ) ;
TArray < FMeshData > GlobalMeshSettings ;
TArray < FMaterialData > GlobalMaterialSettings ;
TArray < float > SectionMaterialImportanceValues ;
TMultiMap < FMeshLODKey , MaterialRemapPair > OutputMaterialsMap ;
TMap < EMaterialProperty , FIntPoint > PropertySizes ;
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( ! Entry . bUseConstantValue & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
PropertySizes . Add ( Entry . Property , Entry . bUseCustomSize ? Entry . CustomSize : MaterialOptions - > TextureSize ) ;
}
}
TMap < UMaterialInterface * , int32 > MaterialToDefaultMeshData ;
for ( TConstRawMeshIterator RawMeshIterator = DataTracker . GetConstRawMeshIterator ( ) ; RawMeshIterator ; + + RawMeshIterator )
{
const FMeshLODKey & Key = RawMeshIterator . Key ( ) ;
2019-01-17 18:54:05 -05:00
const FMeshDescription & RawMesh = RawMeshIterator . Value ( ) ;
2017-06-30 12:21:06 -04:00
const bool bRequiresUniqueUVs = DataTracker . DoesMeshLODRequireUniqueUVs ( Key ) ;
2018-03-06 13:26:20 -05:00
UStaticMeshComponent * Component = StaticMeshComponentsToMerge [ Key . GetMeshIndex ( ) ] ;
2017-06-30 12:21:06 -04:00
// Retrieve all sections and materials for key
TArray < SectionRemapPair > SectionRemapPairs ;
DataTracker . GetMappingsForMeshLOD ( Key , SectionRemapPairs ) ;
// Contains unique materials used for this key, and the accompanying section index which point to the material
TMap < UMaterialInterface * , TArray < int32 > > MaterialAndSectionIndices ;
for ( const SectionRemapPair & RemapPair : SectionRemapPairs )
{
const int32 UniqueIndex = RemapPair . Value ;
const int32 SectionIndex = RemapPair . Key ;
2018-03-06 13:26:20 -05:00
TArray < int32 > & SectionIndices = MaterialAndSectionIndices . FindOrAdd ( CollapsedMaterialMap . FindChecked ( DataTracker . GetMaterialForSectionIndex ( UniqueIndex ) ) ) ;
2017-06-30 12:21:06 -04:00
SectionIndices . Add ( SectionIndex ) ;
}
// Cache unique texture coordinates
TArray < FVector2D > UniqueTextureCoordinates ;
for ( TPair < UMaterialInterface * , TArray < int32 > > & MaterialSectionIndexPair : MaterialAndSectionIndices )
{
UMaterialInterface * Material = MaterialSectionIndexPair . Key ;
const int32 MaterialIndex = UniqueMaterials . IndexOfByKey ( Material ) ;
const TArray < int32 > & SectionIndices = MaterialSectionIndexPair . Value ;
const bool bDoesMaterialUseVertexData = bMaterialUsesVertexData [ MaterialIndex ] ;
FMaterialData MaterialData ;
2018-03-06 13:26:20 -05:00
MaterialData . Material = CollapsedMaterialMap . FindChecked ( Material ) ;
2017-06-30 12:21:06 -04:00
MaterialData . PropertySizes = PropertySizes ;
FMeshData MeshData ;
2018-03-06 13:26:20 -05:00
MeshData . Mesh = Key . GetMesh ( ) ;
MeshData . VertexColorHash = Key . GetVertexColorHash ( ) ;
MeshData . bMirrored = Component - > GetComponentTransform ( ) . GetDeterminant ( ) < 0.0f ;
2017-06-30 12:21:06 -04:00
int32 MeshDataIndex = 0 ;
if ( InSettings . bUseVertexDataForBakingMaterial & & ( bDoesMaterialUseVertexData | | bRequiresUniqueUVs ) )
{
2019-01-17 18:54:05 -05:00
MeshData . RawMeshDescription = DataTracker . GetRawMeshPtr ( Key ) ;
2017-09-11 10:43:35 -04:00
// if it has vertex color/*WedgetColors.Num()*/, it should also use light map UV index
// we can't do this for all meshes, but only for the mesh that has vertex color.
2019-01-17 18:54:05 -05:00
if ( bRequiresUniqueUVs | | MeshData . RawMeshDescription - > VertexInstances ( ) . Num ( ) > 0 )
2017-06-30 12:21:06 -04:00
{
// Check if there are lightmap uvs available?
const int32 LightMapUVIndex = StaticMeshComponentsToMerge [ Key . GetMeshIndex ( ) ] - > GetStaticMesh ( ) - > LightMapCoordinateIndex ;
2019-01-17 18:54:05 -05:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = MeshData . RawMeshDescription - > VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
if ( InSettings . bReuseMeshLightmapUVs & & VertexInstanceUVs . GetNumElements ( ) > 0 & & VertexInstanceUVs . GetNumIndices ( ) > LightMapUVIndex )
2017-06-30 12:21:06 -04:00
{
MeshData . TextureCoordinateIndex = LightMapUVIndex ;
}
else
{
if ( ! UniqueTextureCoordinates . Num ( ) )
{
2019-01-17 18:54:05 -05:00
FMeshDescriptionOperations : : GenerateUniqueUVsForStaticMesh ( * MeshData . RawMeshDescription , MaterialOptions - > TextureSize . GetMax ( ) , false , UniqueTextureCoordinates ) ;
2017-06-30 12:21:06 -04:00
ScaleTextureCoordinatesToBox ( FBox2D ( FVector2D : : ZeroVector , FVector2D ( 1 , 1 ) ) , UniqueTextureCoordinates ) ;
}
MeshData . CustomTextureCoordinates = UniqueTextureCoordinates ;
}
}
MeshData . TextureCoordinateBox = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
MeshData . MaterialIndices = SectionIndices ;
MeshDataIndex = GlobalMeshSettings . Num ( ) ;
Adapters [ Key . GetMeshIndex ( ) ] . ApplySettings ( Key . GetLODIndex ( ) , MeshData ) ;
2018-03-06 13:26:20 -05:00
int32 ExistingMeshDataIndex = INDEX_NONE ;
auto MaterialsAreEquivalent = [ & InSettings ] ( const UMaterialInterface * Material0 , const UMaterialInterface * Material1 )
{
if ( InSettings . bMergeEquivalentMaterials )
{
return FMaterialKey ( Material0 ) = = FMaterialKey ( Material1 ) ;
}
else
{
return Material0 = = Material1 ;
}
} ;
// Find any existing materials
for ( int32 GlobalMaterialSettingsIndex = 0 ; GlobalMaterialSettingsIndex < GlobalMaterialSettings . Num ( ) ; + + GlobalMaterialSettingsIndex )
{
const FMaterialData & ExistingMaterialData = GlobalMaterialSettings [ GlobalMaterialSettingsIndex ] ;
// Compare materials (note this assumes property sizes match!)
if ( MaterialsAreEquivalent ( ExistingMaterialData . Material , MaterialData . Material ) )
{
// materials match, so check the corresponding mesh data
const FMeshData & ExistingMeshData = GlobalMeshSettings [ GlobalMaterialSettingsIndex ] ;
bool bMatchesMesh = ( ExistingMeshData . Mesh = = MeshData . Mesh & &
ExistingMeshData . MaterialIndices = = MeshData . MaterialIndices & &
ExistingMeshData . bMirrored = = MeshData . bMirrored & &
ExistingMeshData . VertexColorHash = = MeshData . VertexColorHash ) ;
if ( bMatchesMesh )
{
MeshDataIndex = ExistingMeshDataIndex = GlobalMaterialSettingsIndex ;
break ;
}
}
}
if ( ExistingMeshDataIndex = = INDEX_NONE )
{
GlobalMeshSettings . Add ( MeshData ) ;
GlobalMaterialSettings . Add ( MaterialData ) ;
SectionMaterialImportanceValues . Add ( MaterialImportanceValues [ MaterialIndex ] ) ;
}
2017-06-30 12:21:06 -04:00
}
else
{
2019-01-17 18:54:05 -05:00
MeshData . RawMeshDescription = nullptr ;
2017-06-30 12:21:06 -04:00
MeshData . TextureCoordinateBox = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
// This prevents baking out the same material multiple times, which would be wasteful when it does not use vertex data anyway
const bool bPreviouslyAdded = MaterialToDefaultMeshData . Contains ( Material ) ;
int32 & DefaultMeshDataIndex = MaterialToDefaultMeshData . FindOrAdd ( Material ) ;
if ( ! bPreviouslyAdded )
{
DefaultMeshDataIndex = GlobalMeshSettings . Num ( ) ;
GlobalMeshSettings . Add ( MeshData ) ;
GlobalMaterialSettings . Add ( MaterialData ) ;
2018-02-14 14:13:42 -05:00
SectionMaterialImportanceValues . Add ( MaterialImportanceValues [ MaterialIndex ] ) ;
2017-06-30 12:21:06 -04:00
}
MeshDataIndex = DefaultMeshDataIndex ;
}
for ( const uint32 & OriginalSectionIndex : SectionIndices )
{
OutputMaterialsMap . Add ( Key , MaterialRemapPair ( OriginalSectionIndex , MeshDataIndex ) ) ;
}
}
}
TArray < FMeshData * > MeshSettingPtrs ;
for ( int32 SettingsIndex = 0 ; SettingsIndex < GlobalMeshSettings . Num ( ) ; + + SettingsIndex )
{
MeshSettingPtrs . Add ( & GlobalMeshSettings [ SettingsIndex ] ) ;
}
TArray < FMaterialData * > MaterialSettingPtrs ;
for ( int32 SettingsIndex = 0 ; SettingsIndex < GlobalMaterialSettings . Num ( ) ; + + SettingsIndex )
{
MaterialSettingPtrs . Add ( & GlobalMaterialSettings [ SettingsIndex ] ) ;
}
2018-05-23 21:04:31 -04:00
// If we are generating a single LOD and want to merge materials we can utilize texture space better by generating unique UVs
// for the merged mesh and baking out materials using those UVs
const bool bGloballyRemapUVs = ! bMergeAllLODs & & ! InSettings . bReuseMeshLightmapUVs ;
if ( bGloballyRemapUVs )
{
2019-01-17 18:54:05 -05:00
TArray < FMeshDescription > MergedRawMeshes ;
2018-05-23 21:04:31 -04:00
CreateMergedRawMeshes ( DataTracker , InSettings , StaticMeshComponentsToMerge , UniqueMaterials , CollapsedMaterialMap , OutputMaterialsMap , false , false , MergedAssetPivot , MergedRawMeshes ) ;
// Create texture coords for the merged mesh
TArray < FVector2D > GlobalTextureCoordinates ;
2019-01-17 18:54:05 -05:00
FMeshDescriptionOperations : : GenerateUniqueUVsForStaticMesh ( MergedRawMeshes [ 0 ] , MaterialOptions - > TextureSize . GetMax ( ) , true , GlobalTextureCoordinates ) ;
2018-05-23 21:04:31 -04:00
ScaleTextureCoordinatesToBox ( FBox2D ( FVector2D : : ZeroVector , FVector2D ( 1 , 1 ) ) , GlobalTextureCoordinates ) ;
// copy UVs back to the un-merged mesh's custom texture coords
// iterate the raw meshes in the same way as when we combined the mesh above in CreateMergedRawMeshes()
int32 GlobalUVIndex = 0 ;
for ( TConstRawMeshIterator RawMeshIterator = DataTracker . GetConstRawMeshIterator ( ) ; RawMeshIterator ; + + RawMeshIterator )
{
const FMeshLODKey & Key = RawMeshIterator . Key ( ) ;
2019-01-17 18:54:05 -05:00
const FMeshDescription & RawMesh = RawMeshIterator . Value ( ) ;
2018-05-23 21:04:31 -04:00
// Build a local array for this raw mesh
TArray < FVector2D > UniqueTextureCoordinates ;
2019-01-17 18:54:05 -05:00
UniqueTextureCoordinates . SetNumUninitialized ( RawMesh . VertexInstances ( ) . Num ( ) ) ;
2018-05-23 21:04:31 -04:00
for ( FVector2D & UniqueTextureCoordinate : UniqueTextureCoordinates )
{
UniqueTextureCoordinate = GlobalTextureCoordinates [ GlobalUVIndex + + ] ;
}
// copy to mesh data
for ( FMeshData & MeshData : GlobalMeshSettings )
{
2019-01-17 18:54:05 -05:00
if ( MeshData . RawMeshDescription = = & RawMesh )
2018-05-23 21:04:31 -04:00
{
MeshData . CustomTextureCoordinates = UniqueTextureCoordinates ;
}
}
}
// Dont smear borders as we will copy back non-pink pixels
for ( FMaterialData & MaterialData : GlobalMaterialSettings )
{
MaterialData . bPerformBorderSmear = false ;
}
}
2017-06-30 12:21:06 -04:00
TArray < FBakeOutput > BakeOutputs ;
IMaterialBakingModule & Module = FModuleManager : : Get ( ) . LoadModuleChecked < IMaterialBakingModule > ( " MaterialBaking " ) ;
Module . BakeMaterials ( MaterialSettingPtrs , MeshSettingPtrs , BakeOutputs ) ;
// Append constant properties ?
TArray < FColor > ConstantData ;
FIntPoint ConstantSize ( 1 , 1 ) ;
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( Entry . bUseConstantValue & & Entry . Property ! = MP_MAX )
2017-06-30 12:21:06 -04:00
{
ConstantData . SetNum ( 1 , false ) ;
ConstantData [ 0 ] = FLinearColor ( Entry . ConstantValue , Entry . ConstantValue , Entry . ConstantValue ) . ToFColor ( true ) ;
for ( FBakeOutput & Ouput : BakeOutputs )
{
Ouput . PropertyData . Add ( Entry . Property , ConstantData ) ;
Ouput . PropertySizes . Add ( Entry . Property , ConstantSize ) ;
}
}
}
TArray < FFlattenMaterial > FlattenedMaterials ;
ConvertOutputToFlatMaterials ( BakeOutputs , GlobalMaterialSettings , FlattenedMaterials ) ;
2018-05-23 21:04:31 -04:00
if ( ! bGloballyRemapUVs )
2017-06-30 12:21:06 -04:00
{
2018-05-23 21:04:31 -04:00
// Try to optimize materials where possible
for ( FFlattenMaterial & InMaterial : FlattenedMaterials )
{
FMaterialUtilities : : OptimizeFlattenMaterial ( InMaterial ) ;
}
2017-06-30 12:21:06 -04:00
}
FFlattenMaterial OutMaterial ;
for ( const FPropertyEntry & Entry : MaterialOptions - > Properties )
{
2017-09-11 10:43:35 -04:00
if ( Entry . Property ! = MP_MAX )
{
EFlattenMaterialProperties OldProperty = NewToOldProperty ( Entry . Property ) ;
OutMaterial . SetPropertySize ( OldProperty , Entry . bUseCustomSize ? Entry . CustomSize : MaterialOptions - > TextureSize ) ;
}
2017-06-30 12:21:06 -04:00
}
TArray < FUVOffsetScalePair > UVTransforms ;
2018-05-23 21:04:31 -04:00
if ( bGloballyRemapUVs )
2017-06-30 12:21:06 -04:00
{
2018-05-23 21:04:31 -04:00
// If we have globally remapped UVs we copy non-pink pixels over the dest texture rather than
// copying sub-charts
2017-06-30 12:21:06 -04:00
TArray < FBox2D > MaterialBoxes ;
2018-05-23 21:04:31 -04:00
MaterialBoxes . SetNumUninitialized ( GlobalMaterialSettings . Num ( ) ) ;
for ( FBox2D & Box2D : MaterialBoxes )
{
Box2D = FBox2D ( FVector2D ( 0.0f , 0.0f ) , FVector2D ( 1.0f , 1.0f ) ) ;
}
FlattenBinnedMaterials ( FlattenedMaterials , MaterialBoxes , 0 , true , OutMaterial , UVTransforms ) ;
2017-06-30 12:21:06 -04:00
}
else
{
2018-05-23 21:04:31 -04:00
/** Reweighting */
float TotalValue = 0.0f ;
for ( const float & Value : SectionMaterialImportanceValues )
{
TotalValue + = Value ;
}
float Multiplier = 1.0f / TotalValue ;
for ( float & Value : SectionMaterialImportanceValues )
{
Value * = Multiplier ;
}
/** End reweighting */
if ( InSettings . bUseTextureBinning )
{
TArray < FBox2D > MaterialBoxes ;
FMaterialUtilities : : GeneratedBinnedTextureSquares ( FVector2D ( 1.0f , 1.0f ) , SectionMaterialImportanceValues , MaterialBoxes ) ;
FlattenBinnedMaterials ( FlattenedMaterials , MaterialBoxes , InSettings . GutterSize , false , OutMaterial , UVTransforms ) ;
}
else
{
MergeFlattenedMaterials ( FlattenedMaterials , InSettings . GutterSize , OutMaterial , UVTransforms ) ;
}
2017-06-30 12:21:06 -04:00
}
2018-05-23 21:04:31 -04:00
2017-06-30 12:21:06 -04:00
// Adjust UVs
2018-03-06 13:26:20 -05:00
for ( int32 ComponentIndex = 0 ; ComponentIndex < StaticMeshComponentsToMerge . Num ( ) ; + + ComponentIndex )
2017-06-30 12:21:06 -04:00
{
2017-09-11 10:43:35 -04:00
TArray < uint32 > ProcessedMaterials ;
2017-06-30 12:21:06 -04:00
for ( TPair < FMeshLODKey , MaterialRemapPair > & MappingPair : OutputMaterialsMap )
{
2017-09-11 10:43:35 -04:00
if ( MappingPair . Key . GetMeshIndex ( ) = = ComponentIndex & & ! ProcessedMaterials . Contains ( MappingPair . Value . Key ) )
2017-06-30 12:21:06 -04:00
{
const int32 LODIndex = MappingPair . Key . GetLODIndex ( ) ;
// Found component entry
// Retrieve raw mesh data for this component and lod pair
2019-01-17 18:54:05 -05:00
FMeshDescription * RawMesh = DataTracker . GetRawMeshPtr ( MappingPair . Key ) ;
2017-06-30 12:21:06 -04:00
2018-03-06 13:26:20 -05:00
FMeshData & MeshData = GlobalMeshSettings [ MappingPair . Value . Value ] ;
2017-06-30 12:21:06 -04:00
const FUVOffsetScalePair & UVTransform = UVTransforms [ MappingPair . Value . Value ] ;
const uint32 MaterialIndex = MappingPair . Value . Key ;
2017-09-11 10:43:35 -04:00
ProcessedMaterials . Add ( MaterialIndex ) ;
2019-01-17 18:54:05 -05:00
if ( RawMesh - > Vertices ( ) . Num ( ) )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
TVertexInstanceAttributesRef < FVector2D > VertexInstanceUVs = RawMesh - > VertexInstanceAttributes ( ) . GetAttributesRef < FVector2D > ( MeshAttribute : : VertexInstance : : TextureCoordinate ) ;
int32 NumUVChannel = FMath : : Min ( VertexInstanceUVs . GetNumIndices ( ) , ( int32 ) MAX_MESH_TEXTURE_COORDS ) ;
for ( int32 UVChannelIdx = 0 ; UVChannelIdx < NumUVChannel ; + + UVChannelIdx )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
int32 VertexIndex = 0 ;
for ( FVertexInstanceID VertexInstanceID : RawMesh - > VertexInstances ( ) . GetElementIDs ( ) )
2018-03-06 13:26:20 -05:00
{
2019-01-17 18:54:05 -05:00
FVector2D UV = VertexInstanceUVs . Get ( VertexInstanceID , UVChannelIdx ) ;
if ( UVChannelIdx = = 0 )
2018-03-06 13:26:20 -05:00
{
2019-01-17 18:54:05 -05:00
if ( MeshData . CustomTextureCoordinates . Num ( ) )
{
UV = MeshData . CustomTextureCoordinates [ VertexIndex ] ;
}
else if ( MeshData . TextureCoordinateIndex ! = 0 )
{
check ( MeshData . TextureCoordinateIndex < NumUVChannel ) ;
UV = VertexInstanceUVs . Get ( VertexInstanceID , MeshData . TextureCoordinateIndex ) ;
}
2018-03-06 13:26:20 -05:00
}
2019-01-17 18:54:05 -05:00
const TArray < FPolygonID > & Polygons = RawMesh - > GetVertexInstanceConnectedPolygons ( VertexInstanceID ) ;
for ( FPolygonID PolygonID : Polygons )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
FPolygonGroupID PolygonGroupID = RawMesh - > GetPolygonPolygonGroup ( PolygonID ) ;
if ( PolygonGroupID . GetValue ( ) = = MaterialIndex )
2017-06-30 12:21:06 -04:00
{
if ( UVTransform . Value ! = FVector2D : : ZeroVector )
{
2019-01-17 18:54:05 -05:00
VertexInstanceUVs . Set ( VertexInstanceID , UVChannelIdx , UV * UVTransform . Value + UVTransform . Key ) ;
break ;
2017-06-30 12:21:06 -04:00
}
}
}
2019-01-17 18:54:05 -05:00
VertexIndex + + ;
2017-06-30 12:21:06 -04:00
}
}
}
}
}
}
for ( TRawMeshIterator Iterator = DataTracker . GetRawMeshIterator ( ) ; Iterator ; + + Iterator )
{
2019-01-17 18:54:05 -05:00
FMeshDescription & RawMesh = Iterator . Value ( ) ;
2017-06-30 12:21:06 -04:00
// Reset material indexes
2019-01-17 18:54:05 -05:00
TMap < FPolygonGroupID , FPolygonGroupID > RemapPolygonGroups ;
for ( FPolygonGroupID PolygonGroupID : RawMesh . PolygonGroups ( ) . GetElementIDs ( ) )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
RemapPolygonGroups . Add ( PolygonGroupID , FPolygonGroupID ( 0 ) ) ;
2017-06-30 12:21:06 -04:00
}
2019-01-17 18:54:05 -05:00
FMeshDescriptionOperations : : RemapPolygonGroups ( RawMesh , RemapPolygonGroups ) ;
2017-06-30 12:21:06 -04:00
}
2018-03-06 13:26:20 -05:00
UMaterialInterface * MergedMaterial = CreateProxyMaterial ( InBasePackageName , MergedAssetPackageName , InBaseMaterial , InOuter , InSettings , OutMaterial , OutAssetsToSync ) ;
2017-06-30 12:21:06 -04:00
UniqueMaterials . Empty ( 1 ) ;
UniqueMaterials . Add ( MergedMaterial ) ;
FSectionInfo NewSection ;
NewSection . Material = MergedMaterial ;
NewSection . EnabledProperties . Add ( GET_MEMBER_NAME_CHECKED ( FStaticMeshSection , bCastShadow ) ) ;
DataTracker . AddBakedMaterialSection ( NewSection ) ;
2018-03-06 13:26:20 -05:00
for ( IMeshMergeExtension * Extension : MeshMergeExtensions )
{
Extension - > OnCreatedProxyMaterial ( StaticMeshComponentsToMerge , MergedMaterial ) ;
}
2017-06-30 12:21:06 -04:00
}
2019-01-17 18:54:05 -05:00
TArray < FMeshDescription > MergedRawMeshes ;
2018-05-23 21:04:31 -04:00
TMultiMap < FMeshLODKey , MaterialRemapPair > OutputMaterialsMap ;
CreateMergedRawMeshes ( DataTracker , InSettings , StaticMeshComponentsToMerge , UniqueMaterials , CollapsedMaterialMap , OutputMaterialsMap , bMergeAllLODs , bMergeMaterialData , MergedAssetPivot , MergedRawMeshes ) ;
2018-03-06 13:26:20 -05:00
2017-06-30 12:21:06 -04:00
// Populate mesh section map
FMeshSectionInfoMap SectionInfoMap ;
for ( TConstLODIndexIterator Iterator = DataTracker . GetLODIndexIterator ( ) ; Iterator ; + + Iterator )
{
const int32 LODIndex = * Iterator ;
TArray < uint32 > UniqueMaterialIndices ;
2019-01-17 18:54:05 -05:00
const FMeshDescription & TargetRawMesh = MergedRawMeshes [ LODIndex ] ;
uint32 MaterialIndex = 0 ;
for ( FPolygonGroupID PolygonGroupID : TargetRawMesh . PolygonGroups ( ) . GetElementIDs ( ) )
2017-06-30 12:21:06 -04:00
{
2019-01-17 18:54:05 -05:00
//Skip empty group
if ( TargetRawMesh . GetPolygonGroupPolygons ( PolygonGroupID ) . Num ( ) > 0 )
{
if ( PolygonGroupID . GetValue ( ) < DataTracker . NumberOfUniqueSections ( ) )
{
UniqueMaterialIndices . AddUnique ( PolygonGroupID . GetValue ( ) ) ;
}
else
{
UniqueMaterialIndices . AddUnique ( MaterialIndex ) ;
}
MaterialIndex + + ;
}
2017-06-30 12:21:06 -04:00
}
2018-04-05 09:26:01 -04:00
UniqueMaterialIndices . Sort ( ) ;
2017-06-30 12:21:06 -04:00
for ( int32 Index = 0 ; Index < UniqueMaterialIndices . Num ( ) ; + + Index )
{
const int32 SectionIndex = UniqueMaterialIndices [ Index ] ;
const FSectionInfo & StoredSectionInfo = DataTracker . GetSection ( SectionIndex ) ;
FMeshSectionInfo SectionInfo ;
SectionInfo . bCastShadow = StoredSectionInfo . EnabledProperties . Contains ( GET_MEMBER_NAME_CHECKED ( FMeshSectionInfo , bCastShadow ) ) ;
SectionInfo . bEnableCollision = StoredSectionInfo . EnabledProperties . Contains ( GET_MEMBER_NAME_CHECKED ( FMeshSectionInfo , bEnableCollision ) ) ;
SectionInfo . MaterialIndex = UniqueMaterials . IndexOfByKey ( StoredSectionInfo . Material ) ;
SectionInfoMap . Set ( LODIndex , Index , SectionInfo ) ;
}
}
// Transform physics primitives to merged mesh pivot
if ( InSettings . bMergePhysicsData & & ! MergedAssetPivot . IsZero ( ) )
{
FTransform PivotTM ( - MergedAssetPivot ) ;
for ( FKAggregateGeom & Geometry : PhysicsGeometry )
{
2018-07-31 17:06:48 -04:00
FMeshMergeHelpers : : TransformPhysicsGeometry ( PivotTM , false , Geometry ) ;
2017-06-30 12:21:06 -04:00
}
}
// Compute target lightmap channel for each LOD, by looking at the first empty UV channel
const int32 LightMapUVChannel = [ & ] ( )
{
if ( InSettings . bGenerateLightMapUV )
{
const int32 TempChannel = DataTracker . GetAvailableLightMapUVChannel ( ) ;
if ( TempChannel ! = INDEX_NONE )
{
return TempChannel ;
}
else
{
// Output warning message
UE_LOG ( LogMeshMerging , Log , TEXT ( " Failed to find available lightmap uv channel " ) ) ;
}
}
return 0 ;
} ( ) ;
//
//Create merged mesh asset
//
{
FString AssetName ;
FString PackageName ;
if ( InBasePackageName . IsEmpty ( ) )
{
AssetName = TEXT ( " SM_MERGED_ " ) + FPackageName : : GetShortName ( MergedAssetPackageName ) ;
PackageName = FPackageName : : GetLongPackagePath ( MergedAssetPackageName ) + TEXT ( " / " ) + AssetName ;
}
else
{
AssetName = FPackageName : : GetShortName ( InBasePackageName ) ;
PackageName = InBasePackageName ;
}
UPackage * Package = InOuter ;
if ( Package = = nullptr )
{
Package = CreatePackage ( NULL , * PackageName ) ;
check ( Package ) ;
Package - > FullyLoad ( ) ;
Package - > Modify ( ) ;
}
2018-05-23 21:04:31 -04:00
FStaticMeshComponentRecreateRenderStateContext RecreateRenderStateContext ( FindObject < UStaticMesh > ( Package , * AssetName ) ) ;
2017-06-30 12:21:06 -04:00
UStaticMesh * StaticMesh = NewObject < UStaticMesh > ( Package , * AssetName , RF_Public | RF_Standalone ) ;
StaticMesh - > InitResources ( ) ;
FString OutputPath = StaticMesh - > GetPathName ( ) ;
// make sure it has a new lighting guid
StaticMesh - > LightingGuid = FGuid : : NewGuid ( ) ;
if ( InSettings . bGenerateLightMapUV )
{
StaticMesh - > LightMapResolution = InSettings . TargetLightMapResolution ;
StaticMesh - > LightMapCoordinateIndex = LightMapUVChannel ;
}
2018-03-06 13:26:20 -05:00
TArray < UMaterialInterface * > ImposterMaterials ;
2018-05-23 21:04:31 -04:00
FBox ImposterBounds ( EForceInit : : ForceInit ) ;
2017-06-30 12:21:06 -04:00
for ( int32 LODIndex = 0 ; LODIndex < MergedRawMeshes . Num ( ) ; + + LODIndex )
2019-01-17 18:54:05 -05:00
{
FMeshDescription & MergedMeshLOD = MergedRawMeshes [ LODIndex ] ;
if ( MergedMeshLOD . Vertices ( ) . Num ( ) > 0 )
2017-06-30 12:21:06 -04:00
{
2018-03-05 10:06:09 -05:00
FStaticMeshSourceModel & SrcModel = StaticMesh - > AddSourceModel ( ) ;
2017-06-30 12:21:06 -04:00
// Don't allow the engine to recalculate normals
2018-03-05 10:06:09 -05:00
SrcModel . BuildSettings . bRecomputeNormals = false ;
SrcModel . BuildSettings . bRecomputeTangents = false ;
SrcModel . BuildSettings . bRemoveDegenerates = false ;
SrcModel . BuildSettings . bUseHighPrecisionTangentBasis = false ;
SrcModel . BuildSettings . bUseFullPrecisionUVs = false ;
SrcModel . BuildSettings . bGenerateLightmapUVs = InSettings . bGenerateLightMapUV ;
SrcModel . BuildSettings . MinLightmapResolution = InSettings . bComputedLightMapResolution ? DataTracker . GetLightMapDimension ( ) : InSettings . TargetLightMapResolution ;
SrcModel . BuildSettings . SrcLightmapIndex = 0 ;
SrcModel . BuildSettings . DstLightmapIndex = LightMapUVChannel ;
2018-05-23 21:04:31 -04:00
if ( ! InSettings . bAllowDistanceField )
{
SrcModel . BuildSettings . DistanceFieldResolutionScale = 0.0f ;
}
2017-06-30 12:21:06 -04:00
2018-03-06 13:26:20 -05:00
const bool bContainsImposters = ImposterComponents . Num ( ) > 0 ;
if ( bContainsImposters )
{
// Merge imposter meshes to rawmesh
2018-05-23 21:04:31 -04:00
FMeshMergeHelpers : : MergeImpostersToRawMesh ( ImposterComponents , MergedMeshLOD , MergedAssetPivot , UniqueMaterials . Num ( ) , ImposterMaterials ) ;
const FTransform PivotTransform = FTransform ( MergedAssetPivot ) ;
for ( const UStaticMeshComponent * Component : ImposterComponents )
{
if ( Component - > GetStaticMesh ( ) )
{
ImposterBounds + = Component - > GetStaticMesh ( ) - > GetBoundingBox ( ) . TransformBy ( Component - > GetComponentToWorld ( ) . GetRelativeTransform ( PivotTransform ) ) ;
}
}
2018-03-06 13:26:20 -05:00
}
2019-01-17 18:54:05 -05:00
FMeshDescription * MeshDescription = StaticMesh - > CreateMeshDescription ( LODIndex ) ;
* MeshDescription = MergedMeshLOD ;
StaticMesh - > CommitMeshDescription ( LODIndex ) ;
2017-06-30 12:21:06 -04:00
}
}
2018-04-03 18:35:54 -04:00
auto IsMaterialImportedNameUnique = [ & StaticMesh ] ( FName ImportedMaterialSlotName )
{
for ( const FStaticMaterial & StaticMaterial : StaticMesh - > StaticMaterials )
{
# if WITH_EDITOR
if ( StaticMaterial . ImportedMaterialSlotName = = ImportedMaterialSlotName )
# else
if ( StaticMaterial . MaterialSlotName = = ImportedMaterialSlotName )
# endif
{
return false ;
}
}
return true ;
} ;
2017-06-30 12:21:06 -04:00
for ( UMaterialInterface * Material : UniqueMaterials )
{
if ( Material & & ( ! Material - > IsAsset ( ) & & InOuter ! = GetTransientPackage ( ) ) )
{
Material = nullptr ; // do not save non-asset materials
}
2018-04-03 18:35:54 -04:00
//Make sure we have unique slot name here
FName MaterialSlotName = DataTracker . GetMaterialSlotName ( Material ) ;
int32 Counter = 1 ;
while ( ! IsMaterialImportedNameUnique ( MaterialSlotName ) )
{
MaterialSlotName = * ( DataTracker . GetMaterialSlotName ( Material ) . ToString ( ) + TEXT ( " _ " ) + FString : : FromInt ( Counter + + ) ) ;
}
StaticMesh - > StaticMaterials . Add ( FStaticMaterial ( Material , MaterialSlotName ) ) ;
2017-06-30 12:21:06 -04:00
}
2018-03-06 13:26:20 -05:00
for ( UMaterialInterface * ImposterMaterial : ImposterMaterials )
{
2018-04-03 18:35:54 -04:00
//Make sure we have unique slot name here
FName MaterialSlotName = ImposterMaterial - > GetFName ( ) ;
int32 Counter = 1 ;
while ( ! IsMaterialImportedNameUnique ( MaterialSlotName ) )
{
MaterialSlotName = * ( ImposterMaterial - > GetName ( ) + TEXT ( " _ " ) + FString : : FromInt ( Counter + + ) ) ;
}
StaticMesh - > StaticMaterials . Add ( FStaticMaterial ( ImposterMaterial , MaterialSlotName ) ) ;
2018-03-06 13:26:20 -05:00
}
2017-06-30 12:21:06 -04:00
if ( InSettings . bMergePhysicsData )
{
StaticMesh - > CreateBodySetup ( ) ;
if ( BodySetupSource )
{
StaticMesh - > BodySetup - > CopyBodyPropertiesFrom ( BodySetupSource ) ;
}
StaticMesh - > BodySetup - > AggGeom = FKAggregateGeom ( ) ;
// Copy collision from the source meshes
for ( const FKAggregateGeom & Geom : PhysicsGeometry )
{
StaticMesh - > BodySetup - > AddCollisionFrom ( Geom ) ;
}
// Bake rotation into verts of convex hulls, so they scale correctly after rotation
for ( FKConvexElem & ConvexElem : StaticMesh - > BodySetup - > AggGeom . ConvexElems )
{
ConvexElem . BakeTransformToVerts ( ) ;
}
}
StaticMesh - > SectionInfoMap . CopyFrom ( SectionInfoMap ) ;
2018-02-14 14:13:42 -05:00
StaticMesh - > OriginalSectionInfoMap . CopyFrom ( SectionInfoMap ) ;
2017-06-30 12:21:06 -04:00
//Set the Imported version before calling the build
StaticMesh - > ImportVersion = EImportStaticMeshVersion : : LastVersion ;
2017-09-11 10:43:35 -04:00
StaticMesh - > LightMapResolution = InSettings . bComputedLightMapResolution ? DataTracker . GetLightMapDimension ( ) : InSettings . TargetLightMapResolution ;
2017-06-30 12:21:06 -04:00
2019-05-15 16:43:48 -04:00
# if WITH_EDITOR
//If we are running the automation test
if ( GIsAutomationTesting )
{
StaticMesh - > BuildCacheAutomationTestGuid = FGuid : : NewGuid ( ) ;
}
# endif
2017-06-30 12:21:06 -04:00
StaticMesh - > Build ( bSilent ) ;
2018-05-23 21:04:31 -04:00
if ( ImposterBounds . IsValid )
{
const FBox StaticMeshBox = StaticMesh - > GetBoundingBox ( ) ;
const FBox CombinedBox = StaticMeshBox + ImposterBounds ;
StaticMesh - > PositiveBoundsExtension = ( CombinedBox . Max - StaticMeshBox . Max ) ;
StaticMesh - > NegativeBoundsExtension = ( StaticMeshBox . Min - CombinedBox . Min ) ;
StaticMesh - > CalculateExtendedBounds ( ) ;
}
2017-06-30 12:21:06 -04:00
StaticMesh - > PostEditChange ( ) ;
OutAssetsToSync . Add ( StaticMesh ) ;
OutMergedActorLocation = MergedAssetPivot ;
}
2018-05-23 21:04:31 -04:00
}
2017-06-30 12:21:06 -04:00
2019-01-17 18:54:05 -05:00
void FMeshMergeUtilities : : CreateMergedRawMeshes ( FMeshMergeDataTracker & InDataTracker , const FMeshMergingSettings & InSettings , const TArray < UStaticMeshComponent * > & InStaticMeshComponentsToMerge , const TArray < UMaterialInterface * > & InUniqueMaterials , const TMap < UMaterialInterface * , UMaterialInterface * > & InCollapsedMaterialMap , const TMultiMap < FMeshLODKey , MaterialRemapPair > & InOutputMaterialsMap , bool bInMergeAllLODs , bool bInMergeMaterialData , const FVector & InMergedAssetPivot , TArray < FMeshDescription > & OutMergedRawMeshes ) const
2018-05-23 21:04:31 -04:00
{
if ( bInMergeAllLODs )
{
OutMergedRawMeshes . AddDefaulted ( InDataTracker . GetNumLODsForMergedMesh ( ) ) ;
for ( TConstLODIndexIterator Iterator = InDataTracker . GetLODIndexIterator ( ) ; Iterator ; + + Iterator )
{
// Find meshes for each lod
const int32 LODIndex = * Iterator ;
2019-01-17 18:54:05 -05:00
FMeshDescription & MergedMesh = OutMergedRawMeshes [ LODIndex ] ;
UStaticMesh : : RegisterMeshAttributes ( MergedMesh ) ;
2018-05-23 21:04:31 -04:00
for ( int32 ComponentIndex = 0 ; ComponentIndex < InStaticMeshComponentsToMerge . Num ( ) ; + + ComponentIndex )
{
int32 RetrievedLODIndex = LODIndex ;
2019-01-17 18:54:05 -05:00
FMeshDescription * RawMeshPtr = InDataTracker . TryFindRawMeshForLOD ( ComponentIndex , RetrievedLODIndex ) ;
2018-05-23 21:04:31 -04:00
if ( RawMeshPtr ! = nullptr )
{
2019-01-17 18:54:05 -05:00
InDataTracker . AddComponentToWedgeMapping ( ComponentIndex , LODIndex , MergedMesh . VertexInstances ( ) . Num ( ) ) ;
2018-05-23 21:04:31 -04:00
2019-01-17 18:54:05 -05:00
FMeshDescriptionOperations : : FAppendSettings AppendSettings ;
AppendSettings . PolygonGroupsDelegate = FAppendPolygonGroupsDelegate : : CreateLambda ( [ & bInMergeMaterialData , & InDataTracker , & InOutputMaterialsMap , & ComponentIndex , & LODIndex ] ( const FMeshDescription & SourceMesh , FMeshDescription & TargetMesh , PolygonGroupMap & RemapPolygonGroups )
{
TPolygonGroupAttributesConstRef < FName > SourceImportedMaterialSlotNames = SourceMesh . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
TPolygonGroupAttributesRef < FName > TargetImportedMaterialSlotNames = TargetMesh . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
//Copy the polygon group
if ( bInMergeMaterialData )
{
FPolygonGroupID PolygonGroupID ( 0 ) ;
if ( ! TargetMesh . PolygonGroups ( ) . IsValid ( PolygonGroupID ) )
{
TargetMesh . CreatePolygonGroupWithID ( PolygonGroupID ) ;
TargetImportedMaterialSlotNames [ PolygonGroupID ] = SourceMesh . PolygonGroups ( ) . IsValid ( PolygonGroupID ) ? SourceImportedMaterialSlotNames [ PolygonGroupID ] : FName ( TEXT ( " DefaultMaterialName " ) ) ;
}
for ( FPolygonGroupID SourcePolygonGroupID : SourceMesh . PolygonGroups ( ) . GetElementIDs ( ) )
{
RemapPolygonGroups . Add ( SourcePolygonGroupID , PolygonGroupID ) ;
}
}
else
{
TArray < SectionRemapPair > SectionMappings ;
InDataTracker . GetMappingsForMeshLOD ( FMeshLODKey ( ComponentIndex , LODIndex ) , SectionMappings ) ;
for ( FPolygonGroupID SourcePolygonGroupID : SourceMesh . PolygonGroups ( ) . GetElementIDs ( ) )
{
// First map from original section index to unique material index
int32 UniqueIndex = INDEX_NONE ;
// then map to the output material map, if any
if ( InOutputMaterialsMap . Num ( ) > 0 )
{
TArray < MaterialRemapPair > MaterialMappings ;
InOutputMaterialsMap . MultiFind ( FMeshLODKey ( ComponentIndex , LODIndex ) , MaterialMappings ) ;
for ( MaterialRemapPair & Pair : MaterialMappings )
{
if ( Pair . Key = = SourcePolygonGroupID . GetValue ( ) )
{
UniqueIndex = Pair . Value ;
break ;
}
}
// Note that at this point UniqueIndex is NOT a material index, but a unique section index!
}
2019-03-25 17:25:56 -04:00
if ( UniqueIndex = = INDEX_NONE )
2019-01-17 18:54:05 -05:00
{
UniqueIndex = SourcePolygonGroupID . GetValue ( ) ;
}
FPolygonGroupID TargetPolygonGroupID ( UniqueIndex ) ;
if ( ! TargetMesh . PolygonGroups ( ) . IsValid ( TargetPolygonGroupID ) )
{
while ( TargetMesh . PolygonGroups ( ) . Num ( ) < = UniqueIndex )
{
TargetPolygonGroupID = TargetMesh . CreatePolygonGroup ( ) ;
}
check ( TargetPolygonGroupID . GetValue ( ) = = UniqueIndex ) ;
TargetImportedMaterialSlotNames [ TargetPolygonGroupID ] = SourceImportedMaterialSlotNames [ SourcePolygonGroupID ] ;
}
RemapPolygonGroups . Add ( SourcePolygonGroupID , TargetPolygonGroupID ) ;
}
}
} ) ;
AppendSettings . bMergeVertexColor = InSettings . bBakeVertexDataToMesh ;
AppendSettings . MergedAssetPivot = InMergedAssetPivot ;
FMeshDescriptionOperations : : AppendMeshDescription ( * RawMeshPtr , MergedMesh , AppendSettings ) ;
}
}
2019-03-13 15:19:08 -04:00
//Cleanup the empty material to avoid empty section later
TArray < FPolygonGroupID > PolygonGroupToRemove ;
for ( FPolygonGroupID PolygonGroupID : MergedMesh . PolygonGroups ( ) . GetElementIDs ( ) )
{
if ( MergedMesh . GetPolygonGroupPolygons ( PolygonGroupID ) . Num ( ) < 1 )
{
PolygonGroupToRemove . Add ( PolygonGroupID ) ;
}
}
for ( FPolygonGroupID PolygonGroupID : PolygonGroupToRemove )
{
MergedMesh . DeletePolygonGroup ( PolygonGroupID ) ;
}
2019-01-17 18:54:05 -05:00
}
}
else
{
OutMergedRawMeshes . AddZeroed ( 1 ) ;
FMeshDescription & MergedMesh = OutMergedRawMeshes . Last ( ) ;
UStaticMesh : : RegisterMeshAttributes ( MergedMesh ) ;
for ( int32 ComponentIndex = 0 ; ComponentIndex < InStaticMeshComponentsToMerge . Num ( ) ; + + ComponentIndex )
{
int32 LODIndex = 0 ;
FMeshDescription * RawMeshPtr = InDataTracker . FindRawMeshAndLODIndex ( ComponentIndex , LODIndex ) ;
if ( RawMeshPtr ! = nullptr )
{
FMeshDescription & RawMesh = * RawMeshPtr ;
const int32 TargetLODIndex = 0 ;
InDataTracker . AddComponentToWedgeMapping ( ComponentIndex , TargetLODIndex , MergedMesh . VertexInstances ( ) . Num ( ) ) ;
FMeshDescriptionOperations : : FAppendSettings AppendSettings ;
AppendSettings . PolygonGroupsDelegate = FAppendPolygonGroupsDelegate : : CreateLambda ( [ & bInMergeMaterialData , & InDataTracker , & InOutputMaterialsMap , & ComponentIndex , & LODIndex ] ( const FMeshDescription & SourceMesh , FMeshDescription & TargetMesh , PolygonGroupMap & RemapPolygonGroups )
{
TPolygonGroupAttributesConstRef < FName > SourceImportedMaterialSlotNames = SourceMesh . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
TPolygonGroupAttributesRef < FName > TargetImportedMaterialSlotNames = TargetMesh . PolygonGroupAttributes ( ) . GetAttributesRef < FName > ( MeshAttribute : : PolygonGroup : : ImportedMaterialSlotName ) ;
//Copy the polygon group
2018-05-23 21:04:31 -04:00
if ( bInMergeMaterialData )
{
2019-01-17 18:54:05 -05:00
FPolygonGroupID PolygonGroupID ( 0 ) ;
if ( ! TargetMesh . PolygonGroups ( ) . IsValid ( PolygonGroupID ) )
{
TargetMesh . CreatePolygonGroupWithID ( PolygonGroupID ) ;
TargetImportedMaterialSlotNames [ PolygonGroupID ] = SourceMesh . PolygonGroups ( ) . IsValid ( PolygonGroupID ) ? SourceImportedMaterialSlotNames [ PolygonGroupID ] : FName ( TEXT ( " DefaultMaterialName " ) ) ;
}
for ( FPolygonGroupID SourcePolygonGroupID : SourceMesh . PolygonGroups ( ) . GetElementIDs ( ) )
{
RemapPolygonGroups . Add ( SourcePolygonGroupID , PolygonGroupID ) ;
}
2018-05-23 21:04:31 -04:00
}
else
{
TArray < SectionRemapPair > SectionMappings ;
InDataTracker . GetMappingsForMeshLOD ( FMeshLODKey ( ComponentIndex , LODIndex ) , SectionMappings ) ;
2019-01-17 18:54:05 -05:00
for ( FPolygonGroupID SourcePolygonGroupID : SourceMesh . PolygonGroups ( ) . GetElementIDs ( ) )
2018-05-23 21:04:31 -04:00
{
// First map from original section index to unique material index
int32 UniqueIndex = INDEX_NONE ;
// then map to the output material map, if any
2019-01-17 18:54:05 -05:00
if ( InOutputMaterialsMap . Num ( ) > 0 )
2018-05-23 21:04:31 -04:00
{
TArray < MaterialRemapPair > MaterialMappings ;
InOutputMaterialsMap . MultiFind ( FMeshLODKey ( ComponentIndex , LODIndex ) , MaterialMappings ) ;
2019-01-17 18:54:05 -05:00
for ( MaterialRemapPair & Pair : MaterialMappings )
2018-05-23 21:04:31 -04:00
{
2019-01-17 18:54:05 -05:00
if ( Pair . Key = = SourcePolygonGroupID . GetValue ( ) )
2018-05-23 21:04:31 -04:00
{
UniqueIndex = Pair . Value ;
break ;
}
}
// Note that at this point UniqueIndex is NOT a material index, but a unique section index!
}
2019-03-25 17:25:56 -04:00
2019-05-07 17:33:25 -04:00
//Fallback
2019-03-25 17:25:56 -04:00
if ( UniqueIndex = = INDEX_NONE )
2018-05-23 21:04:31 -04:00
{
2019-01-17 18:54:05 -05:00
UniqueIndex = SourcePolygonGroupID . GetValue ( ) ;
2018-05-23 21:04:31 -04:00
}
2019-05-07 17:33:25 -04:00
2019-01-17 18:54:05 -05:00
FPolygonGroupID TargetPolygonGroupID ( UniqueIndex ) ;
if ( ! TargetMesh . PolygonGroups ( ) . IsValid ( TargetPolygonGroupID ) )
2018-05-23 21:04:31 -04:00
{
2019-01-17 18:54:05 -05:00
while ( TargetMesh . PolygonGroups ( ) . Num ( ) < = UniqueIndex )
2018-05-23 21:04:31 -04:00
{
2019-01-17 18:54:05 -05:00
TargetPolygonGroupID = TargetMesh . CreatePolygonGroup ( ) ;
2018-05-23 21:04:31 -04:00
}
2019-01-17 18:54:05 -05:00
check ( TargetPolygonGroupID . GetValue ( ) = = UniqueIndex ) ;
TargetImportedMaterialSlotNames [ TargetPolygonGroupID ] = SourceImportedMaterialSlotNames [ SourcePolygonGroupID ] ;
2018-05-23 21:04:31 -04:00
}
2019-01-17 18:54:05 -05:00
RemapPolygonGroups . Add ( SourcePolygonGroupID , TargetPolygonGroupID ) ;
2018-05-23 21:04:31 -04:00
}
}
2019-01-17 18:54:05 -05:00
} ) ;
AppendSettings . bMergeVertexColor = InSettings . bBakeVertexDataToMesh ;
AppendSettings . MergedAssetPivot = InMergedAssetPivot ;
FMeshDescriptionOperations : : AppendMeshDescription ( * RawMeshPtr , MergedMesh , AppendSettings ) ;
2018-05-23 21:04:31 -04:00
}
}
}
for ( IMeshMergeExtension * Extension : MeshMergeExtensions )
{
Extension - > OnCreatedMergedRawMeshes ( InStaticMeshComponentsToMerge , InDataTracker , OutMergedRawMeshes ) ;
}
2017-06-30 12:21:06 -04:00
}
2018-03-06 13:26:20 -05:00
void FMeshMergeUtilities : : MergeComponentsToInstances ( const TArray < UPrimitiveComponent * > & ComponentsToMerge , UWorld * World , ULevel * Level , const FMeshInstancingSettings & InSettings , bool bActuallyMerge /*= true*/ , FText * OutResultsText /*= nullptr*/ ) const
{
auto HasInstanceVertexColors = [ ] ( UStaticMeshComponent * StaticMeshComponent )
{
for ( const FStaticMeshComponentLODInfo & CurrentLODInfo : StaticMeshComponent - > LODData )
{
if ( CurrentLODInfo . OverrideVertexColors ! = nullptr | | CurrentLODInfo . PaintedVertices . Num ( ) > 0 )
{
return true ;
}
}
2017-06-30 12:21:06 -04:00
2018-03-06 13:26:20 -05:00
return false ;
} ;
// Gather valid components
TArray < UStaticMeshComponent * > ValidComponents ;
for ( UPrimitiveComponent * ComponentToMerge : ComponentsToMerge )
{
if ( UStaticMeshComponent * StaticMeshComponent = Cast < UStaticMeshComponent > ( ComponentToMerge ) )
{
// Dont harvest from 'destination' actors
if ( StaticMeshComponent - > GetOwner ( ) - > GetClass ( ) ! = InSettings . ActorClassToUse . Get ( ) )
{
if ( ! InSettings . bSkipMeshesWithVertexColors | | ! HasInstanceVertexColors ( StaticMeshComponent ) )
{
ValidComponents . Add ( StaticMeshComponent ) ;
}
}
}
}
if ( OutResultsText ! = nullptr )
{
* OutResultsText = LOCTEXT ( " InstanceMergePredictedResultsNone " , " The current settings will not result in any instanced meshes being created " ) ;
}
if ( ValidComponents . Num ( ) > 0 )
{
/** Helper struct representing a spawned ISMC */
struct FComponentEntry
{
FComponentEntry ( UStaticMeshComponent * InComponent )
{
StaticMesh = InComponent - > GetStaticMesh ( ) ;
InComponent - > GetUsedMaterials ( Materials ) ;
bReverseCulling = InComponent - > GetComponentTransform ( ) . ToMatrixWithScale ( ) . Determinant ( ) < 0.0f ;
CollisionProfileName = InComponent - > GetCollisionProfileName ( ) ;
CollisionEnabled = InComponent - > GetCollisionEnabled ( ) ;
OriginalComponents . Add ( InComponent ) ;
}
bool operator = = ( const FComponentEntry & InOther ) const
{
return
StaticMesh = = InOther . StaticMesh & &
Materials = = InOther . Materials & &
bReverseCulling = = InOther . bReverseCulling & &
CollisionProfileName = = InOther . CollisionProfileName & &
CollisionEnabled = = InOther . CollisionEnabled ;
}
UStaticMesh * StaticMesh ;
TArray < UMaterialInterface * > Materials ;
TArray < UStaticMeshComponent * > OriginalComponents ;
FName CollisionProfileName ;
bool bReverseCulling ;
ECollisionEnabled : : Type CollisionEnabled ;
} ;
/** Helper struct representing a spawned ISMC-containing actor */
struct FActorEntry
{
FActorEntry ( UStaticMeshComponent * InComponent , ULevel * InLevel )
: MergedActor ( nullptr )
{
// intersect with HLOD volumes if we have a level
if ( InLevel )
{
for ( AActor * Actor : InLevel - > Actors )
{
if ( AHierarchicalLODVolume * HierarchicalLODVolume = Cast < AHierarchicalLODVolume > ( Actor ) )
{
FBox BoundingBox = InComponent - > Bounds . GetBox ( ) ;
FBox VolumeBox = HierarchicalLODVolume - > GetComponentsBoundingBox ( true ) ;
if ( VolumeBox . IsInside ( BoundingBox ) | | ( HierarchicalLODVolume - > bIncludeOverlappingActors & & VolumeBox . Intersect ( BoundingBox ) ) )
{
HLODVolume = HierarchicalLODVolume ;
break ;
}
}
}
}
}
bool operator = = ( const FActorEntry & InOther ) const
{
return HLODVolume = = InOther . HLODVolume ;
}
AActor * MergedActor ;
AHierarchicalLODVolume * HLODVolume ;
TArray < FComponentEntry > ComponentEntries ;
} ;
// Gather a list of components to merge
TArray < FActorEntry > ActorEntries ;
for ( UStaticMeshComponent * StaticMeshComponent : ValidComponents )
{
int32 ActorEntryIndex = ActorEntries . AddUnique ( FActorEntry ( StaticMeshComponent , InSettings . bUseHLODVolumes ? Level : nullptr ) ) ;
FActorEntry & ActorEntry = ActorEntries [ ActorEntryIndex ] ;
FComponentEntry ComponentEntry ( StaticMeshComponent ) ;
if ( FComponentEntry * ExistingComponentEntry = ActorEntry . ComponentEntries . FindByKey ( ComponentEntry ) )
{
ExistingComponentEntry - > OriginalComponents . Add ( StaticMeshComponent ) ;
}
else
{
ActorEntry . ComponentEntries . Add ( ComponentEntry ) ;
}
}
// Filter by component count
for ( FActorEntry & ActorEntry : ActorEntries )
{
ActorEntry . ComponentEntries = ActorEntry . ComponentEntries . FilterByPredicate ( [ & InSettings ] ( const FComponentEntry & InEntry )
{
return InEntry . OriginalComponents . Num ( ) > = InSettings . InstanceReplacementThreshold ;
} ) ;
}
// Remove any empty actor entries
ActorEntries . RemoveAll ( [ ] ( const FActorEntry & ActorEntry ) { return ActorEntry . ComponentEntries . Num ( ) = = 0 ; } ) ;
int32 TotalComponentCount = 0 ;
TArray < AActor * > ActorsToCleanUp ;
for ( FActorEntry & ActorEntry : ActorEntries )
{
for ( const FComponentEntry & ComponentEntry : ActorEntry . ComponentEntries )
{
TotalComponentCount + + ;
for ( UStaticMeshComponent * OriginalComponent : ComponentEntry . OriginalComponents )
{
if ( AActor * OriginalActor = OriginalComponent - > GetOwner ( ) )
{
ActorsToCleanUp . AddUnique ( OriginalActor ) ;
}
}
}
}
if ( ActorEntries . Num ( ) > 0 )
{
if ( OutResultsText ! = nullptr )
{
* OutResultsText = FText : : Format ( LOCTEXT ( " InstanceMergePredictedResults " , " The current settings will result in {0} instanced static mesh components ({1} actors will be replaced) " ) , FText : : AsNumber ( TotalComponentCount ) , FText : : AsNumber ( ActorsToCleanUp . Num ( ) ) ) ;
}
if ( bActuallyMerge )
{
// Create our actors
const FScopedTransaction Transaction ( LOCTEXT ( " PlaceInstancedActors " , " Place Instanced Actor(s) " )) ;
Level - > Modify ( ) ;
FActorSpawnParameters Params ;
Params . OverrideLevel = Level ;
// We now have the set of component data we want to apply
for ( FActorEntry & ActorEntry : ActorEntries )
{
ActorEntry . MergedActor = World - > SpawnActor < AActor > ( InSettings . ActorClassToUse . Get ( ) , Params ) ;
for ( const FComponentEntry & ComponentEntry : ActorEntry . ComponentEntries )
{
auto AddInstancedStaticMeshComponent = [ ] ( AActor * InActor )
{
// Check if we have a usable (empty) ISMC first
if ( UInstancedStaticMeshComponent * ExistingComponent = InActor - > FindComponentByClass < UInstancedStaticMeshComponent > ( ) )
{
if ( ExistingComponent - > PerInstanceSMData . Num ( ) = = 0 )
{
return ExistingComponent ;
}
}
UInstancedStaticMeshComponent * NewComponent = NewObject < UInstancedStaticMeshComponent > ( InActor ) ;
if ( InActor - > GetRootComponent ( ) )
{
// Attach to root if we already have one
NewComponent - > AttachToComponent ( InActor - > GetRootComponent ( ) , FAttachmentTransformRules : : KeepRelativeTransform ) ;
}
else
{
// Make a new root if we dont have a root already
InActor - > SetRootComponent ( NewComponent ) ;
}
// Take 'instanced' ownership so it persists with this actor
InActor - > RemoveOwnedComponent ( NewComponent ) ;
NewComponent - > CreationMethod = EComponentCreationMethod : : Instance ;
InActor - > AddOwnedComponent ( NewComponent ) ;
return NewComponent ;
} ;
UInstancedStaticMeshComponent * NewComponent = AddInstancedStaticMeshComponent ( ActorEntry . MergedActor ) ;
NewComponent - > SetStaticMesh ( ComponentEntry . StaticMesh ) ;
for ( int32 MaterialIndex = 0 ; MaterialIndex < ComponentEntry . Materials . Num ( ) ; + + MaterialIndex )
{
NewComponent - > SetMaterial ( MaterialIndex , ComponentEntry . Materials [ MaterialIndex ] ) ;
}
NewComponent - > SetReverseCulling ( ComponentEntry . bReverseCulling ) ;
NewComponent - > SetCollisionProfileName ( ComponentEntry . CollisionProfileName ) ;
NewComponent - > SetCollisionEnabled ( ComponentEntry . CollisionEnabled ) ;
NewComponent - > SetMobility ( EComponentMobility : : Static ) ;
for ( UStaticMeshComponent * OriginalComponent : ComponentEntry . OriginalComponents )
{
NewComponent - > AddInstance ( OriginalComponent - > GetComponentTransform ( ) ) ;
}
NewComponent - > RegisterComponent ( ) ;
}
World - > UpdateCullDistanceVolumes ( ActorEntry . MergedActor ) ;
}
// Now clean up our original actors
for ( AActor * ActorToCleanUp : ActorsToCleanUp )
{
if ( InSettings . MeshReplacementMethod = = EMeshInstancingReplacementMethod : : RemoveOriginalActors )
{
ActorToCleanUp - > Destroy ( ) ;
}
else if ( InSettings . MeshReplacementMethod = = EMeshInstancingReplacementMethod : : KeepOriginalActorsAsEditorOnly )
{
ActorToCleanUp - > Modify ( ) ;
ActorToCleanUp - > bIsEditorOnlyActor = true ;
ActorToCleanUp - > bHidden = true ;
ActorToCleanUp - > bHiddenEd = true ;
ActorToCleanUp - > SetIsTemporarilyHiddenInEditor ( true ) ;
}
}
// pop a toast allowing selection
auto SelectActorsLambda = [ ActorEntries ] ( )
{
GEditor - > GetSelectedActors ( ) - > Modify ( ) ;
GEditor - > GetSelectedActors ( ) - > BeginBatchSelectOperation ( ) ;
GEditor - > SelectNone ( false , true , false ) ;
for ( const FActorEntry & ActorEntry : ActorEntries )
{
GEditor - > SelectActor ( ActorEntry . MergedActor , true , false , true ) ;
}
GEditor - > GetSelectedActors ( ) - > EndBatchSelectOperation ( ) ;
} ;
FNotificationInfo NotificationInfo ( FText : : Format ( LOCTEXT ( " CreatedInstancedActorsMessage " , " Created {0} Instanced Actor(s) " ), FText::AsNumber(ActorEntries.Num()))) ;
NotificationInfo . Hyperlink = FSimpleDelegate : : CreateLambda ( SelectActorsLambda ) ;
NotificationInfo . HyperlinkText = LOCTEXT ( " SelectActorsHyperlink " , " Select Actors " ) ;
NotificationInfo . ExpireDuration = 5.0f ;
FSlateNotificationManager : : Get ( ) . AddNotification ( NotificationInfo ) ;
}
}
}
}
UMaterialInterface * FMeshMergeUtilities : : CreateProxyMaterial ( const FString & InBasePackageName , FString MergedAssetPackageName , UMaterialInterface * InBaseMaterial , UPackage * InOuter , const FMeshMergingSettings & InSettings , FFlattenMaterial OutMaterial , TArray < UObject * > & OutAssetsToSync ) const
2017-06-30 12:21:06 -04:00
{
// Create merged material asset
FString MaterialAssetName ;
FString MaterialPackageName ;
if ( InBasePackageName . IsEmpty ( ) )
{
2019-01-24 15:35:51 -05:00
MaterialAssetName = FPackageName : : GetShortName ( MergedAssetPackageName ) ;
2019-03-13 15:19:08 -04:00
MaterialPackageName = FPackageName : : GetLongPackagePath ( MergedAssetPackageName ) + TEXT ( " / " ) ;
2017-06-30 12:21:06 -04:00
}
else
{
2019-01-24 15:35:51 -05:00
MaterialAssetName = FPackageName : : GetShortName ( InBasePackageName ) ;
2019-03-13 15:19:08 -04:00
MaterialPackageName = FPackageName : : GetLongPackagePath ( InBasePackageName ) + TEXT ( " / " ) ;
2017-06-30 12:21:06 -04:00
}
UPackage * MaterialPackage = InOuter ;
if ( MaterialPackage = = nullptr )
{
2019-03-13 15:19:08 -04:00
MaterialPackage = CreatePackage ( nullptr , * ( MaterialPackageName + MaterialAssetName ) ) ;
2017-06-30 12:21:06 -04:00
check ( MaterialPackage ) ;
MaterialPackage - > FullyLoad ( ) ;
MaterialPackage - > Modify ( ) ;
}
2019-01-24 15:35:51 -05:00
UMaterialInstanceConstant * MergedMaterial = ProxyMaterialUtilities : : CreateProxyMaterialInstance ( MaterialPackage , InSettings . MaterialSettings , InBaseMaterial , OutMaterial , MaterialPackageName , MaterialAssetName , OutAssetsToSync ) ;
2017-06-30 12:21:06 -04:00
// Set material static lighting usage flag if project has static lighting enabled
static const auto AllowStaticLightingVar = IConsoleManager : : Get ( ) . FindTConsoleVariableDataInt ( TEXT ( " r.AllowStaticLighting " ) ) ;
const bool bAllowStaticLighting = ( ! AllowStaticLightingVar | | AllowStaticLightingVar - > GetValueOnGameThread ( ) ! = 0 ) ;
if ( bAllowStaticLighting )
{
MergedMaterial - > CheckMaterialUsage ( MATUSAGE_StaticLighting ) ;
}
return MergedMaterial ;
}
void FMeshMergeUtilities : : ExtractPhysicsDataFromComponents ( const TArray < UPrimitiveComponent * > & ComponentsToMerge , TArray < FKAggregateGeom > & InOutPhysicsGeometry , UBodySetup * & OutBodySetupSource ) const
{
InOutPhysicsGeometry . AddDefaulted ( ComponentsToMerge . Num ( ) ) ;
for ( int32 ComponentIndex = 0 ; ComponentIndex < ComponentsToMerge . Num ( ) ; + + ComponentIndex )
{
UPrimitiveComponent * PrimComp = ComponentsToMerge [ ComponentIndex ] ;
UBodySetup * BodySetup = nullptr ;
FTransform ComponentToWorld = FTransform : : Identity ;
if ( UStaticMeshComponent * StaticMeshComp = Cast < UStaticMeshComponent > ( PrimComp ) )
{
UStaticMesh * SrcMesh = StaticMeshComp - > GetStaticMesh ( ) ;
if ( SrcMesh )
{
BodySetup = SrcMesh - > BodySetup ;
}
ComponentToWorld = StaticMeshComp - > GetComponentToWorld ( ) ;
}
else if ( UShapeComponent * ShapeComp = Cast < UShapeComponent > ( PrimComp ) )
{
BodySetup = ShapeComp - > GetBodySetup ( ) ;
ComponentToWorld = ShapeComp - > GetComponentToWorld ( ) ;
}
2018-07-31 17:06:48 -04:00
USplineMeshComponent * SplineMeshComponent = Cast < USplineMeshComponent > ( PrimComp ) ;
FMeshMergeHelpers : : ExtractPhysicsGeometry ( BodySetup , ComponentToWorld , SplineMeshComponent ! = nullptr , InOutPhysicsGeometry [ ComponentIndex ] ) ;
if ( SplineMeshComponent )
2017-06-30 12:21:06 -04:00
{
FMeshMergeHelpers : : PropagateSplineDeformationToPhysicsGeometry ( SplineMeshComponent , InOutPhysicsGeometry [ ComponentIndex ] ) ;
}
// We will use first valid BodySetup as a source of physics settings
if ( OutBodySetupSource = = nullptr )
{
OutBodySetupSource = BodySetup ;
}
}
}
void FMeshMergeUtilities : : ScaleTextureCoordinatesToBox ( const FBox2D & Box , TArray < FVector2D > & InOutTextureCoordinates ) const
{
const FBox2D CoordinateBox ( InOutTextureCoordinates ) ;
const FVector2D CoordinateRange = CoordinateBox . GetSize ( ) ;
const FVector2D Offset = CoordinateBox . Min + Box . Min ;
const FVector2D Scale = Box . GetSize ( ) / CoordinateRange ;
for ( FVector2D & Coordinate : InOutTextureCoordinates )
{
Coordinate = ( Coordinate - Offset ) * Scale ;
}
}
2018-07-31 17:06:48 -04:00
# undef LOCTEXT_NAMESPACE // "MeshMergeUtils"