Files

3817 lines
143 KiB
C++
Raw Permalink Normal View History

// Copyright Epic Games, Inc. All Rights Reserved.
#include "MeshMergeUtilities.h"
#include "Engine/MapBuildDataRegistry.h"
#include "MeshMerge/MeshInstancingSettings.h"
#include "MeshMerge/MeshMergingSettings.h"
#include "MeshMerge/MeshProxySettings.h"
#include "Engine/StaticMeshSocket.h"
#include "MaterialOptions.h"
#include "IMaterialBakingModule.h"
#include "Misc/PackageName.h"
#include "MaterialUtilities.h"
#include "Materials/MaterialInstanceConstant.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 "Materials/MaterialInstanceDynamic.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 "Engine/StaticMesh.h"
#include "PhysicsEngine/ConvexElem.h"
#include "PhysicsEngine/BodySetup.h"
#include "MeshUtilities.h"
#include "ImageUtils.h"
#include "LandscapeHeightfieldCollisionComponent.h"
#include "IMeshReductionManagerModule.h"
#include "IMeshReductionInterfaces.h"
#include "ProxyGenerationProcessor.h"
#include "IMaterialBakingAdapter.h"
#include "StaticMeshComponentLODInfo.h"
#include "SkeletalMeshAdapter.h"
#include "StaticMeshAdapter.h"
#include "MeshMergeDataTracker.h"
#include "Misc/FileHelper.h"
#include "MeshMergeHelpers.h"
#include "Settings/EditorExperimentalSettings.h"
#include "MaterialBakingStructures.h"
#include "Async/ParallelFor.h"
#include "ScopedTransaction.h"
#include "Components/InstancedStaticMeshComponent.h"
#include "Engine/LODActor.h"
#include "HierarchicalLODVolume.h"
#include "Engine/Selection.h"
#include "MaterialBakingHelpers.h"
#include "IMeshMergeExtension.h"
#include "RawMesh.h"
#include "StaticMeshAttributes.h"
#include "StaticMeshOperations.h"
#include "TriangleTypes.h"
#include "MaterialUtilities.h"
Edigrating 3 CLs to improve HLOD generation time (faster material baking & mesh merging) CL 10373564 by danny.couture Optimize Material Baking (Phase 1) - Introduce a mecanism to override the vertex/index buffer allocator used for dynamic meshes - Avoid GDynamicMesh non-ticked pools build-up by using our own vertex/index buffer pool during baking - Reduce reallocation and incurred soft page faults by reusing a single set of vertex/index buffers big enough for the biggest mesh - Preemptively detect if smearing would result in monochrome texture to avoid useless work - Shrink smeared monochrome textures during the baking process for huge memory savings - Move UV smearing in worker threads to avoid blocking the game thread - Required shaders are now built asynchronously - Add progress bar for material baking - 28m23 [at] 150 GB RAM -> 2m14s [at] 45 GB RAM for 6 channels [at] 512x512 when baking materials on ProxyLOD for DATASET-0008a with DDC empty #rb Jurre.deBaare, Sebastien.Lussier CL 10516258 by danny.couture Optimize Material Baking (Phase 2) - Implement pipelining with staging buffers to avoid GPU stalls when reading from render targets - Reuse the same prepared FMeshBatch instead of rebuilding it for each draw pass - Prepare the RenderItem in advance on other threads to reduce work on the game thread - Move the staging surface copy out of the render thread - Small vertex and index buffers are not reused to avoid dependency locks when mapping them - Fix bug in Canvas Flush_RenderThread found while running HLOD rebuild commandlet on Fortnite - Delete old and unused MaterialBakingModule.h from public files - 4m44s -> 59s for baking 6 channel [at] 1024x1024 when baking materials on ProxyLOD for DATASET-0008a with shaders already compiled - Time spent in Material Baking when rebuilding all HLOD on Apollo_POI_Large_HLOD (Phase 1 + 2 combined) - 10m18s -> 2m36s for a first rebuild all in editor with no shaders in DDC (cold) - 1m23s -> 20s for a second rebuild all in editor (warm) #rb Jeremy.Moore, Sebastien.Lussier CL 11135986 by sebastien.lussier Optimized mesh merging * Added DeletePolygons() & DeleteTriangles methods to FMeshDescription which rely on TSets<> instead of performing costly TArray::AddUnique() calls() * Parallelized UV generation and avoided duplicate processing of the same mesh+lod pairs * Optimized FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh() * Goes from 100s to 10s in my test case #rb danny.couture, jeanfrancois.dube, richard.talbotwatkin #ROBOMERGE-OWNER: sebastien.lussier #ROBOMERGE-AUTHOR: sebastien.lussier #ROBOMERGE-SOURCE: CL 11206337 via CL 11206341 via CL 11206346 #ROBOMERGE-BOT: (v643-11205221) [CL 11206493 by sebastien lussier in Main branch]
2020-02-03 11:08:35 -05:00
#include "Async/Future.h"
#include "Async/Async.h"
Async Texture Compilation - Feature can be activated in the Experimental section of the Editor Settings - Replace Texture2D/TextureCube resources by placeholders until their PlatformData is ready - Add a utility class allowing to encapsulate raw field pointers without breaking compatibility - Protect PlatformData from unsafe access through encapsulation. - Protect texture's resource from race conditions between game and render threads through encapsulation. - This allows to get rid of FlushRenderingCommands and long game-thread stutters when Updating a texture's resource. - UpdateResource was never safe to call without a FlushRenderingCommands and multiple call-site are doing exactly that, this will fix those cases. - Those were probably undetected due to their low occurence rate under normal conditions but can easily be reproed during async texture compilation on 32 cores. - Force wait on required texture compilations for MaterialBaking, ProxyMesh, Thumbnail generation for disk usage - Wait on all textures compilation whenever a wait for all shaders compilation is requested for safety (i.e. screenshot) - Compile UI and heightmap textures with higher priority to reduce visual artefacts - Increase priority of texture that have been rendered to improve time-to-usefulness of the editor under low core count - Async compilation is disabled for -game / non-editor mode as there is currently no support for async bulk data loading from external files - Properly cancel async tasks when UTexture is garbage collected before the compilation is finished - Show progress when explicitly waiting on compilation - Changing the mip settings in the texture editor (or any settings requiring the running platform data to be recomputed) will now be processed asynchronously. DEBUGGING - Can be forcibly enabled/disabled through command-line via -asynctexturecompilation=[off, on, paused] - Can pause texture compilation using Editor.AsyncTextureCompilation = 2 or -asynctexturecompilation=paused - Can manually resume a specified amount of paused compilation using Editor.AsyncTextureCompilationResume [Num] - Can forcibly wait on all compilation using Editor.AsyncTextureCompilationFlushAll BENCHMARKS - 3m15s to 1m20s when loading Apollo_Terrain with no textures in DDC (AMD TR 3970X) - 6m45s to 1m11s when loading Apollo_Terrain with no textures in DDC (-corelimit=8) - 3m10s to 1m54s when lauching PIE on Apollo_Terrain with no textures in DDC (AMD TR 3970X) - 7m43s to 1m36s when lauching PIE on Apollo_Terrain with no textures in DDC (-corelimit=8) - 0m57s to 0m42s when importing Attic_NVIDIA.usd with no textures in DDC (AMD TR 3970X) - 2m14s to 0m35s when importing Attic_NVIDIA.usd with no textures in DDC (-corelimit=4) TESTS - Success on all material baking tests from EngineTests with -asynctexturecompilation=paused - Runned with -corelimit=1 all the way to unlimited - Cooking worked - Opening the texture editor/material editor will force the compilation to finish like expected. - Changing a setting in the texture editor will recompile async, even allowing to close the editor and continue doing other changes. - Unpausing the compilation will update the texture thumbnails properly. - Started with -asynctexturecompilation=paused, and then unpaused after a map loading, and then into a PIE session to stresstest UpdateResources. - Tested both dx11/dx12 - Vulkan fails on Fortnite even with -asynctexturecompilation=off because of Landscape weigthmap, not this CL. - Compiled and tested FortniteGame / UE4 / ShooterGame projects #rb Uriel.Doyon, Francis.Hurteau [CL 13694814 by danny couture in ue5-main branch]
2020-06-16 22:16:25 -04:00
#include "TextureCompiler.h"
Edigrating 3 CLs to improve HLOD generation time (faster material baking & mesh merging) CL 10373564 by danny.couture Optimize Material Baking (Phase 1) - Introduce a mecanism to override the vertex/index buffer allocator used for dynamic meshes - Avoid GDynamicMesh non-ticked pools build-up by using our own vertex/index buffer pool during baking - Reduce reallocation and incurred soft page faults by reusing a single set of vertex/index buffers big enough for the biggest mesh - Preemptively detect if smearing would result in monochrome texture to avoid useless work - Shrink smeared monochrome textures during the baking process for huge memory savings - Move UV smearing in worker threads to avoid blocking the game thread - Required shaders are now built asynchronously - Add progress bar for material baking - 28m23 [at] 150 GB RAM -> 2m14s [at] 45 GB RAM for 6 channels [at] 512x512 when baking materials on ProxyLOD for DATASET-0008a with DDC empty #rb Jurre.deBaare, Sebastien.Lussier CL 10516258 by danny.couture Optimize Material Baking (Phase 2) - Implement pipelining with staging buffers to avoid GPU stalls when reading from render targets - Reuse the same prepared FMeshBatch instead of rebuilding it for each draw pass - Prepare the RenderItem in advance on other threads to reduce work on the game thread - Move the staging surface copy out of the render thread - Small vertex and index buffers are not reused to avoid dependency locks when mapping them - Fix bug in Canvas Flush_RenderThread found while running HLOD rebuild commandlet on Fortnite - Delete old and unused MaterialBakingModule.h from public files - 4m44s -> 59s for baking 6 channel [at] 1024x1024 when baking materials on ProxyLOD for DATASET-0008a with shaders already compiled - Time spent in Material Baking when rebuilding all HLOD on Apollo_POI_Large_HLOD (Phase 1 + 2 combined) - 10m18s -> 2m36s for a first rebuild all in editor with no shaders in DDC (cold) - 1m23s -> 20s for a second rebuild all in editor (warm) #rb Jeremy.Moore, Sebastien.Lussier CL 11135986 by sebastien.lussier Optimized mesh merging * Added DeletePolygons() & DeleteTriangles methods to FMeshDescription which rely on TSets<> instead of performing costly TArray::AddUnique() calls() * Parallelized UV generation and avoided duplicate processing of the same mesh+lod pairs * Optimized FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh() * Goes from 100s to 10s in my test case #rb danny.couture, jeanfrancois.dube, richard.talbotwatkin #ROBOMERGE-OWNER: sebastien.lussier #ROBOMERGE-AUTHOR: sebastien.lussier #ROBOMERGE-SOURCE: CL 11206337 via CL 11206341 via CL 11206346 #ROBOMERGE-BOT: (v643-11205221) [CL 11206493 by sebastien lussier in Main branch]
2020-02-03 11:08:35 -05:00
#include "Widgets/Notifications/SNotificationList.h"
#include "Framework/Notifications/NotificationManager.h"
#include "ISMPartition/ISMComponentBatcher.h"
#include "ISMPartition/ISMComponentDescriptor.h"
#include "UObject/GCObjectScopeGuard.h"
#include "Algo/RemoveIf.h"
#define LOCTEXT_NAMESPACE "MeshMergeUtils"
DEFINE_LOG_CATEGORY(LogMeshMerging);
static TAutoConsoleVariable<int32> CVarMeshMergeUtilitiesUVGenerationMethod(
TEXT("MeshMergeUtilities.UVGenerationMethod"),
0,
TEXT("UV generation method when creating merged or proxy meshes\n"
"0 - Engine default - (currently Patch Builder)\n"
"1 - Legacy\n"
"2 - UVAtlas\n"
"3 - XAtlas\n"
"4 - Patch Builder\n"));
static FStaticMeshOperations::EGenerateUVMethod GetUVGenerationMethodToUse()
{
switch (CVarMeshMergeUtilitiesUVGenerationMethod.GetValueOnAnyThread())
{
case 1: return FStaticMeshOperations::EGenerateUVMethod::Legacy;
case 2: return FStaticMeshOperations::EGenerateUVMethod::UVAtlas;
case 3: return FStaticMeshOperations::EGenerateUVMethod::XAtlas;
case 4: return FStaticMeshOperations::EGenerateUVMethod::PatchBuilder;
default: return FStaticMeshOperations::EGenerateUVMethod::Default;
}
}
FMeshMergeUtilities::FMeshMergeUtilities()
{
Processor = new FProxyGenerationProcessor(this);
}
FMeshMergeUtilities::~FMeshMergeUtilities()
{
FModuleManager::Get().OnModulesChanged().Remove(ModuleLoadedDelegateHandle);
}
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
TMap<uint32, FMeshDescription> RawMeshLODs;
// Unique set of sections in mesh
TArray<FSectionInfo> UniqueSections;
TArray<FSectionInfo> Sections;
int32 NumLODs = Adapter->GetNumberOfLODs();
// LOD index, <original section index, unique section index>
TArray<TMap<int32, int32>> UniqueSectionIndexPerLOD;
UniqueSectionIndexPerLOD.AddDefaulted(NumLODs);
// Retrieve raw mesh data and unique sections
for (int32 LODIndex = 0; LODIndex < NumLODs; ++LODIndex)
{
// Reset section for reuse
Sections.SetNum(0, EAllowShrinking::No);
// Extract raw mesh data
const bool bProcessedLOD = MaterialOptions->LODIndices.Contains(LODIndex);
if (bProcessedLOD)
{
FMeshDescription& RawMesh = RawMeshLODs.Add(LODIndex);
FStaticMeshAttributes(RawMesh).Register();
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[LODIndex].Emplace(SectionIndex, UniqueIndex);
}
}
TArray<UMaterialInterface*> UniqueMaterials;
TMultiMap<uint32, uint32> UniqueMaterialToUniqueSectionMap;
// 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);
UniqueMaterialToUniqueSectionMap.Add(UniqueIndex, SectionIndex);
}
TArray<FMeshData> GlobalMeshSettings;
TArray<FMaterialData> GlobalMaterialSettings;
TArray<TMap<uint32, uint32>> OutputMaterialsMap;
OutputMaterialsMap.AddDefaulted(NumLODs);
for (int32 MaterialIndex = 0; MaterialIndex < UniqueMaterials.Num(); ++MaterialIndex)
{
UMaterialInterface* Material = UniqueMaterials[MaterialIndex];
// Retrieve all sections using this material
TArray<uint32> SectionIndices;
UniqueMaterialToUniqueSectionMap.MultiFind(MaterialIndex, SectionIndices);
if (MaterialOptions->bUseMeshData)
{
for (const int32 LODIndex : MaterialOptions->LODIndices)
{
FMeshData MeshSettings;
MeshSettings.MeshDescription = nullptr;
// Add material indices used for rendering out material
for (const auto& Pair : UniqueSectionIndexPerLOD[LODIndex])
{
if (SectionIndices.Contains(Pair.Value))
{
MeshSettings.MaterialIndices.Add(Pair.Key);
}
}
if (MeshSettings.MaterialIndices.Num())
{
// Retrieve raw mesh
MeshSettings.MeshDescription = RawMeshLODs.Find(LODIndex);
//Should not be using mesh data if there is no mesh
check(MeshSettings.MeshDescription);
MeshSettings.TextureCoordinateBox = FBox2D(FVector2D(0.0f, 0.0f), FVector2D(1.0f, 1.0f));
const bool bUseVertexColor = FStaticMeshOperations::HasVertexColor(*(MeshSettings.MeshDescription));
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;
}
Adapter->ApplySettings(LODIndex, MeshSettings);
// In case part of the UVs is not within the 0-1 range try to use the lightmap UVs
const bool bNeedsUniqueUVs = FMeshMergeHelpers::CheckWrappingUVs(*(MeshSettings.MeshDescription), MeshSettings.TextureCoordinateIndex);
const int32 LightMapUVIndex = Adapter->LightmapUVIndex();
TVertexInstanceAttributesConstRef<FVector2f> VertexInstanceUVs = FStaticMeshConstAttributes(*MeshSettings.MeshDescription).GetVertexInstanceUVs();
First pass of MeshDescription API and format refactor. - Removed hardcoded element type arrays (Vertices, Edges, Triangles etc.). Mesh element types can now be arbitrarily added, with any number of channels. - Mesh element containers have a much leaner format; instead of sparse arrays, they are now represented by a simple bitarray, determining whether an index is used or not. Consequently, mesh topology is now entirely described with the attribute system, e.g. edge start and end vertices, triangle vertices, etc. - Support added for attributes of arbitrary dimensions, e.g. float[4] or int[2]. - Support added for attributes which index into another mesh element container. - Added FMeshElementIndexer: this is an efficient container for maintaining backward references from one element type to another; for example, edges have an attribute specifying which vertices are at each end (an attribute of type FVertexID[2]). With an indexer, it is possible to look up which edges contain a given vertex, even though this is not explicitly stored. Indexers are designed to do minimal allocations and update lazily and in batch when necessary. - Added support for preserving UV topology in static meshes. UVs are now a first-class element type which may be indexed directly from triangles. - Added the facility to access the underlying array in an attribute array directly. - Triangles now directly reference their vertex, edge and UV IDs. Vertex instances are to be deprecated. - Changed various systems to be triangle-centric rather than polygon-centric, as this is faster. Triangles are presumed to be the elementary face type in a MeshDescription, even if polygons are still supported. The concept of polygons will be somewhat shifted to mean a group of triangles which should be treated collectively for editing purposes. - Optimised normal/tangent generation and FBX import. - Deprecated EditableMesh, MeshEditor and StaticMeshEditorExtension plugins - these are to be removed, but they still have certain hooks in place which need removing. #rb [CL 13568702 by Richard TalbotWatkin in ue5-main branch]
2020-05-28 10:56:57 -04:00
if (bNeedsUniqueUVs && MeshSettings.TextureCoordinateIndex != LightMapUVIndex && VertexInstanceUVs.GetNumElements() > 0 && VertexInstanceUVs.GetNumChannels() > LightMapUVIndex)
{
MeshSettings.TextureCoordinateIndex = LightMapUVIndex;
}
FMaterialData MaterialSettings;
MaterialSettings.Material = Material;
// Add all user defined properties for baking out
for (const FPropertyEntry& Entry : MaterialOptions->Properties)
{
if (!Entry.bUseConstantValue && Entry.Property != MP_MAX)
{
int32 NumTextureCoordinates;
bool bUsesVertexData;
Material->AnalyzeMaterialProperty(Entry.Property, NumTextureCoordinates, bUsesVertexData);
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[LODIndex].Emplace(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)
{
for (const auto& Pair : UniqueSectionIndexPerLOD[LODIndex])
{
if (SectionIndices.Contains(Pair.Value))
{
MeshSettings.MaterialIndices.Add(Pair.Key);
}
}
}
if (MeshSettings.MaterialIndices.Num())
{
MeshSettings.MeshDescription = nullptr;
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)
{
if (!Entry.bUseConstantValue && Material->IsPropertyActive(Entry.Property) && Entry.Property != MP_MAX)
{
MaterialSettings.PropertySizes.Add(Entry.Property, Entry.bUseCustomSize ? Entry.CustomSize : MaterialOptions->TextureSize);
}
}
for (int32 LODIndex : MaterialOptions->LODIndices)
{
for (const auto& Pair : UniqueSectionIndexPerLOD[LODIndex])
{
if (SectionIndices.Contains(Pair.Value))
{
/// For each original material index add an entry to the corresponding LOD and bake output index
OutputMaterialsMap[LODIndex].Emplace(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)
{
if (Entry.bUseConstantValue && Entry.Property != MP_MAX)
{
ConstantData.SetNum(1, EAllowShrinking::No);
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 LODIndex = 0; LODIndex < NumLODs; ++LODIndex)
{
const bool bProcessedLOD = MaterialOptions->LODIndices.Contains(LODIndex);
if (!bProcessedLOD)
{
for (const auto& Pair : UniqueSectionIndexPerLOD[LODIndex])
{
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)
{
// Key == original section index, Value == unique material index
for (const auto& Pair : OutputMaterialsMap[LODIndex])
{
int32 SetIndex = Adapter->GetMaterialIndex(LODIndex, Pair.Key);
if (!NonReplaceMaterialIndices.Contains(SetIndex))
{
Adapter->SetMaterial(SetIndex, NewMaterials[Pair.Value]);
}
else
{
// 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
int32 NewMaterialIndex = INDEX_NONE;
if (Adapter->GetMaterialSlotName(Pair.Key).IsNone() || Adapter->GetImportedMaterialSlotName(Pair.Key).IsNone())
{
NewMaterialIndex = Adapter->AddMaterial(NewMaterials[Pair.Value]);
}
else
{
NewMaterialIndex = Adapter->AddMaterial(NewMaterials[Pair.Value], Adapter->GetMaterialSlotName(Pair.Key), Adapter->GetImportedMaterialSlotName(Pair.Key));
}
NewMaterialRemap.Add(Pair.Value, NewMaterialIndex);
Adapter->RemapMaterialIndex(LODIndex, Pair.Key, NewMaterialIndex);
}
}
}
}
Adapter->UpdateUVChannelData();
GlobalMeshSettings.Empty();
}
void FMeshMergeUtilities::BakeMaterialsForComponent(USkeletalMeshComponent* SkeletalMeshComponent) const
{
if (!SkeletalMeshComponent || !SkeletalMeshComponent->GetSkeletalMeshAsset())
{
return;
}
// 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 = SkeletalMeshComponent->GetSkeletalMeshAsset()->GetLODNum();
IMaterialBakingModule& Module = FModuleManager::Get().LoadModuleChecked<IMaterialBakingModule>("MaterialBaking");
if (!Module.SetupMaterialBakeSettings(Objects, NumLODs))
{
return;
}
// Bake out materials for skeletal mesh
SkeletalMeshComponent->GetSkeletalMeshAsset()->Modify();
FSkeletalMeshComponentAdapter Adapter(SkeletalMeshComponent);
BakeMaterialsForComponent(Objects, &Adapter);
SkeletalMeshComponent->MarkRenderStateDirty();
}
void FMeshMergeUtilities::BakeMaterialsForComponent(UStaticMeshComponent* StaticMeshComponent) const
{
if (!StaticMeshComponent || !StaticMeshComponent->GetStaticMesh())
{
return;
}
// 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
{
if (!StaticMesh)
{
return;
}
// 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
StaticMesh->Modify();
FStaticMeshAdapter Adapter(StaticMesh);
BakeMaterialsForComponent(Objects, &Adapter);
}
static bool DetermineMaterialVertexDataUsage(UMaterialInterface* Material, const UMaterialOptions* MaterialOptions)
{
Edigrating 3 CLs to improve HLOD generation time (faster material baking & mesh merging) CL 10373564 by danny.couture Optimize Material Baking (Phase 1) - Introduce a mecanism to override the vertex/index buffer allocator used for dynamic meshes - Avoid GDynamicMesh non-ticked pools build-up by using our own vertex/index buffer pool during baking - Reduce reallocation and incurred soft page faults by reusing a single set of vertex/index buffers big enough for the biggest mesh - Preemptively detect if smearing would result in monochrome texture to avoid useless work - Shrink smeared monochrome textures during the baking process for huge memory savings - Move UV smearing in worker threads to avoid blocking the game thread - Required shaders are now built asynchronously - Add progress bar for material baking - 28m23 [at] 150 GB RAM -> 2m14s [at] 45 GB RAM for 6 channels [at] 512x512 when baking materials on ProxyLOD for DATASET-0008a with DDC empty #rb Jurre.deBaare, Sebastien.Lussier CL 10516258 by danny.couture Optimize Material Baking (Phase 2) - Implement pipelining with staging buffers to avoid GPU stalls when reading from render targets - Reuse the same prepared FMeshBatch instead of rebuilding it for each draw pass - Prepare the RenderItem in advance on other threads to reduce work on the game thread - Move the staging surface copy out of the render thread - Small vertex and index buffers are not reused to avoid dependency locks when mapping them - Fix bug in Canvas Flush_RenderThread found while running HLOD rebuild commandlet on Fortnite - Delete old and unused MaterialBakingModule.h from public files - 4m44s -> 59s for baking 6 channel [at] 1024x1024 when baking materials on ProxyLOD for DATASET-0008a with shaders already compiled - Time spent in Material Baking when rebuilding all HLOD on Apollo_POI_Large_HLOD (Phase 1 + 2 combined) - 10m18s -> 2m36s for a first rebuild all in editor with no shaders in DDC (cold) - 1m23s -> 20s for a second rebuild all in editor (warm) #rb Jeremy.Moore, Sebastien.Lussier CL 11135986 by sebastien.lussier Optimized mesh merging * Added DeletePolygons() & DeleteTriangles methods to FMeshDescription which rely on TSets<> instead of performing costly TArray::AddUnique() calls() * Parallelized UV generation and avoided duplicate processing of the same mesh+lod pairs * Optimized FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh() * Goes from 100s to 10s in my test case #rb danny.couture, jeanfrancois.dube, richard.talbotwatkin #ROBOMERGE-OWNER: sebastien.lussier #ROBOMERGE-AUTHOR: sebastien.lussier #ROBOMERGE-SOURCE: CL 11206337 via CL 11206341 via CL 11206346 #ROBOMERGE-BOT: (v643-11205221) [CL 11206493 by sebastien lussier in Main branch]
2020-02-03 11:08:35 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE(DetermineMaterialVertexDataUsage);
for (const FPropertyEntry& Entry : MaterialOptions->Properties)
{
// Don't have to check a property if the result is going to be constant anyway
if (!Entry.bUseConstantValue && Entry.Property != MP_MAX)
{
int32 NumTextureCoordinates;
bool bUsesVertexData;
Material->AnalyzeMaterialProperty(Entry.Property, NumTextureCoordinates, bUsesVertexData);
if (bUsesVertexData || NumTextureCoordinates > 1)
{
return true;
}
}
}
return false;
}
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 = ToFlattenProperty(SizePair.Key);
if (ensure(OldProperty != EFlattenMaterialProperties::NumFlattenMaterialProperties))
{
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);
}
}
void FMeshMergeUtilities::TransferOutputToFlatMaterials(const TArray<FMaterialData>& InMaterialData, TArray<FBakeOutput>& InOutBakeOutputs, TArray<FFlattenMaterial> &OutFlattenedMaterials) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshMergeUtilities::TransferOutputToFlatMaterials)
OutFlattenedMaterials.SetNum(InOutBakeOutputs.Num());
for (int32 OutputIndex = 0; OutputIndex < InOutBakeOutputs.Num(); ++OutputIndex)
{
FBakeOutput& Output = InOutBakeOutputs[OutputIndex];
const FMaterialData& MaterialInfo = InMaterialData[OutputIndex];
FFlattenMaterial& Material = OutFlattenedMaterials[OutputIndex];
for (TPair<EMaterialProperty, FIntPoint> SizePair : Output.PropertySizes)
{
EFlattenMaterialProperties OldProperty = ToFlattenProperty(SizePair.Key);
if (ensure(OldProperty != EFlattenMaterialProperties::NumFlattenMaterialProperties))
{
Material.SetPropertySize(OldProperty, SizePair.Value);
Material.GetPropertySamples(OldProperty) = MoveTemp(Output.PropertyData[SizePair.Key]);
}
}
Material.bDitheredLODTransition = MaterialInfo.Material->IsDitheredLODTransition();
Material.BlendMode = BLEND_Opaque;
Material.bTwoSided = MaterialInfo.Material->IsTwoSided();
Material.EmissiveScale = Output.EmissiveScale;
}
}
EFlattenMaterialProperties FMeshMergeUtilities::ToFlattenProperty(EMaterialProperty MaterialProperty) const
{
switch (MaterialProperty)
{
case EMaterialProperty::MP_BaseColor: return EFlattenMaterialProperties::Diffuse;
case EMaterialProperty::MP_Metallic: return EFlattenMaterialProperties::Metallic;
case EMaterialProperty::MP_Specular: return EFlattenMaterialProperties::Specular;
case EMaterialProperty::MP_Roughness: return EFlattenMaterialProperties::Roughness;
case EMaterialProperty::MP_Anisotropy: return EFlattenMaterialProperties::Anisotropy;
case EMaterialProperty::MP_Normal: return EFlattenMaterialProperties::Normal;
case EMaterialProperty::MP_Tangent: return EFlattenMaterialProperties::Tangent;
case EMaterialProperty::MP_Opacity: return EFlattenMaterialProperties::Opacity;
case EMaterialProperty::MP_EmissiveColor: return EFlattenMaterialProperties::Emissive;
case EMaterialProperty::MP_OpacityMask: return EFlattenMaterialProperties::OpacityMask;
case EMaterialProperty::MP_AmbientOcclusion: return EFlattenMaterialProperties::AmbientOcclusion;
default: return EFlattenMaterialProperties::NumFlattenMaterialProperties;
}
}
UMaterialOptions* FMeshMergeUtilities::PopulateMaterialOptions(const FMaterialProxySettings& MaterialSettings) const
{
UMaterialOptions* MaterialOptions = DuplicateObject(GetMutableDefault<UMaterialOptions>(), GetTransientPackage());
MaterialOptions->Properties.Empty();
MaterialOptions->TextureSize = MaterialSettings.TextureSize;
FPropertyEntry Property;
PopulatePropertyEntry(MaterialSettings, MP_BaseColor, Property);
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_Specular, Property);
if (MaterialSettings.bSpecularMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_Roughness, Property);
if (MaterialSettings.bRoughnessMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_Anisotropy, Property);
if (MaterialSettings.bAnisotropyMap)
{
MaterialOptions->Properties.Add(Property);
}
PopulatePropertyEntry(MaterialSettings, MP_Metallic, Property);
if (MaterialSettings.bMetallicMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_Normal, Property);
if (MaterialSettings.bNormalMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_Tangent, Property);
if (MaterialSettings.bTangentMap)
{
MaterialOptions->Properties.Add(Property);
}
PopulatePropertyEntry(MaterialSettings, MP_Opacity, Property);
if (MaterialSettings.bOpacityMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_OpacityMask, Property);
if (MaterialSettings.bOpacityMaskMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_EmissiveColor, Property);
if (MaterialSettings.bEmissiveMap)
MaterialOptions->Properties.Add(Property);
PopulatePropertyEntry(MaterialSettings, MP_AmbientOcclusion, Property);
if (MaterialSettings.bAmbientOcclusionMap)
MaterialOptions->Properties.Add(Property);
return MaterialOptions;
}
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_Anisotropy: return MaterialSettings.AnisotropyTextureSize;
case MP_Metallic: return MaterialSettings.MetallicTextureSize;
case MP_Normal: return MaterialSettings.NormalTextureSize;
case MP_Tangent: return MaterialSettings.TangentTextureSize;
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_Tangent: return HalfRes;
case MP_BaseColor: return HalfRes;
case MP_Specular: return QuarterRes;
case MP_Roughness: return QuarterRes;
case MP_Anisotropy: 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;
}
default:
UE_LOG(LogMeshMerging, Error, TEXT("Unsupported TextureSizingType value. You should resolve the material texture size first with ResolveTextureSize()"));
}
/** 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_Tangent: return !MaterialSettings.bTangentMap;
case MP_Specular: return !MaterialSettings.bSpecularMap;
case MP_Roughness: return !MaterialSettings.bRoughnessMap;
case MP_Anisotropy: return !MaterialSettings.bAnisotropyMap;
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_Tangent: return 1.0f;
case MP_Specular: return MaterialSettings.SpecularConstant;
case MP_Roughness: return MaterialSettings.RoughnessConstant;
case MP_Anisotropy: return MaterialSettings.AnisotropyConstant;
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;
}
}
}();
}
void FMeshMergeUtilities::CopyTextureRect(const FColor* Src, const FIntPoint& SrcSize, FColor* Dst, const FIntPoint& DstSize, const FIntPoint& DstPos, bool bCopyOnlyMaskedPixels) const
{
const int32 RowLength = SrcSize.X * sizeof(FColor);
FColor* RowDst = Dst + DstSize.X*DstPos.Y;
const FColor* RowSrc = Src;
if(bCopyOnlyMaskedPixels)
{
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;
}
}
}
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;
}
void FMeshMergeUtilities::MergeFlattenedMaterials(TArray<struct FFlattenMaterial>& InMaterialList, int32 InGutter, FFlattenMaterial& OutMergedMaterial, TArray<FUVOffsetScalePair>& OutUVTransforms) const
{
// Fill output UV transforms with invalid values
OutUVTransforms.SetNumZeroed(InMaterialList.Num());
const int32 AtlasGridSize = FMath::CeilToInt(FMath::Sqrt(static_cast<float>(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);
Samples.SetNumUninitialized(AtlasNumSamples);
// Fill with magenta (as we will be box blurring this later)
for(FColor& SampleColor : Samples)
{
SampleColor = FColor(255, 0, 255);
}
}
}
int32 AtlasRowIdx = 0;
int32 AtlasColIdx = 0;
FIntPoint Gutter(InGutter, InGutter);
FIntPoint DoubleGutter(InGutter * 2, InGutter * 2);
FIntPoint GlobalAtlasTargetPos = Gutter;
bool bSamplesWritten[(uint32)EFlattenMaterialProperties::NumFlattenMaterialProperties];
FMemory::Memset(bSamplesWritten, 0);
// Used to calculate UV transforms
const FIntPoint GlobalAtlasTextureSize = OutMergedMaterial.GetPropertySize(EFlattenMaterialProperties::Diffuse);
const FIntPoint GlobalExportTextureSize = (GlobalAtlasTextureSize / AtlasGridSize) - DoubleGutter;
const FIntPoint GlobalExportEntrySize = (GlobalAtlasTextureSize / AtlasGridSize);
// 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;
for (int32 PropertyIndex = 0; PropertyIndex < (int32)EFlattenMaterialProperties::NumFlattenMaterialProperties; ++PropertyIndex)
{
const EFlattenMaterialProperties Property = (EFlattenMaterialProperties)PropertyIndex;
const FIntPoint PropertyTextureSize = OutMergedMaterial.GetPropertySize(Property);
const int32 NumPropertySamples = PropertyTextureSize.X*PropertyTextureSize.Y;
const FIntPoint PropertyAtlasTextureSize = (PropertyTextureSize / AtlasGridSize) - DoubleGutter;
const FIntPoint PropertyAtlasEntrySize = (PropertyTextureSize / AtlasGridSize);
const FIntPoint AtlasTargetPos((AtlasColIdx * PropertyAtlasEntrySize.X) + InGutter, (AtlasRowIdx * PropertyAtlasEntrySize.Y) + InGutter);
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));
// Offset
OutUVTransforms[MatIdx].Key = FVector2D(
(float)GlobalAtlasTargetPos.X / GlobalAtlasTextureSize.X,
(float)GlobalAtlasTargetPos.Y / GlobalAtlasTextureSize.Y);
// Scale
OutUVTransforms[MatIdx].Value = FVector2D(
(float)GlobalExportTextureSize.X / GlobalAtlasTextureSize.X,
(float)GlobalExportTextureSize.Y / GlobalAtlasTextureSize.Y);
AtlasColIdx++;
if (AtlasColIdx >= AtlasGridSize)
{
AtlasColIdx = 0;
AtlasRowIdx++;
}
GlobalAtlasTargetPos = FIntPoint((AtlasColIdx * GlobalExportEntrySize.X) + InGutter, (AtlasRowIdx * GlobalExportEntrySize.Y) + InGutter);
}
// 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)
{
EFlattenMaterialProperties Property = (EFlattenMaterialProperties)PropertyIndex;
if (!bSamplesWritten[PropertyIndex])
{
OutMergedMaterial.GetPropertySamples(Property).Empty();
OutMergedMaterial.SetPropertySize(Property, FIntPoint(0, 0));
}
else
{
// Smear borders
const FIntPoint PropertySize = OutMergedMaterial.GetPropertySize(Property);
FMaterialBakingHelpers::PerformUVBorderSmear(OutMergedMaterial.GetPropertySamples(Property), PropertySize.X, PropertySize.Y);
}
}
}
void FMeshMergeUtilities::FlattenBinnedMaterials(TArray<struct FFlattenMaterial>& InMaterialList, const TArray<FBox2D>& InMaterialBoxes, int32 InGutter, bool bCopyOnlyMaskedPixels, FFlattenMaterial& OutMergedMaterial, TArray<FUVOffsetScalePair>& OutUVTransforms) const
{
// Fill output UV transforms with invalid values
OutUVTransforms.SetNumZeroed(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);
OutSamples.SetNumUninitialized(OutTextureSize.X * OutTextureSize.Y);
// 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);
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
FIntPoint OutputSize = FIntPoint((OutTextureSize.X * MaterialBox.GetSize().X) - (InGutter * 2), (OutTextureSize.Y * MaterialBox.GetSize().Y) - (InGutter * 2));
ConditionalImageResize(InputSize, OutputSize, InputSamples, false);
// Copy material data to the merged 'atlas' texture
FIntPoint OutputPosition = FIntPoint((OutTextureSize.X * MaterialBox.Min.X) + InGutter, (OutTextureSize.Y * MaterialBox.Min.Y) + InGutter);
CopyTextureRect(InputSamples.GetData(), OutputSize, OutSamples.GetData(), OutTextureSize, OutputPosition, bCopyOnlyMaskedPixels);
// Set the UV tranforms only once
if (Index == 0)
{
FUVOffsetScalePair& UVTransform = OutUVTransforms[MaterialIndex];
UVTransform.Key = MaterialBox.Min + (Gutter2D / FVector2D(OutTextureSize));
UVTransform.Value = MaterialBox.GetSize() - ((Gutter2D * 2.0f) / FVector2D(OutTextureSize));
}
bMaterialsWritten = true;
}
}
if (!bMaterialsWritten)
{
OutSamples.Empty();
OutMergedMaterial.SetPropertySize(Property, FIntPoint(0, 0));
}
else
{
// Smear borders
const FIntPoint PropertySize = OutMergedMaterial.GetPropertySize(Property);
FMaterialBakingHelpers::PerformUVBorderSmear(OutSamples, PropertySize.X, PropertySize.Y);
}
}
}
}
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((float)Index * (float)MaterialsPerThread);
const int32 EndIndex = FMath::Min(FMath::CeilToInt((float)(Index + 1) * (float)MaterialsPerThread), InMaterialList.Num());
for (; StartIndex < EndIndex; ++StartIndex)
{
FFlattenMaterial& Material = InMaterialList[StartIndex];
if (Material.EmissiveScale != MaxScale)
{
for (FColor& Sample : Material.GetPropertySamples(EFlattenMaterialProperties::Emissive))
{
if (Sample != FColor::Magenta)
{
Sample.R = Sample.R * Multiplier;
Sample.G = Sample.G * Multiplier;
Sample.B = Sample.B * Multiplier;
Sample.A = Sample.A * Multiplier;
}
}
}
}
}, NumThreads == 1);
return MaxScale;
}
static TArray<FVector2D> GetCustomTextureCoordinates(const FMeshDescription& InMeshDescription, const UStaticMesh* InStaticMesh, const FMeshProxySettings& InMeshProxySettings)
{
TArray<FVector2D> CustomTextureCoordinates;
TVertexInstanceAttributesConstRef<FVector2f> VertexInstanceUVs = FStaticMeshConstAttributes(InMeshDescription).GetVertexInstanceUVs();
// If we already have lightmap uvs generated and they are valid, we can reuse those instead of having to generate new ones
const int32 LightMapCoordinateIndex = InStaticMesh->GetLightMapCoordinateIndex();
if (InMeshProxySettings.bReuseMeshLightmapUVs &&
LightMapCoordinateIndex > 0 &&
VertexInstanceUVs.GetNumElements() > 0 &&
VertexInstanceUVs.GetNumChannels() > LightMapCoordinateIndex)
{
for (const FVertexInstanceID VertexInstanceID : InMeshDescription.VertexInstances().GetElementIDs())
{
CustomTextureCoordinates.Add(FVector2D(VertexInstanceUVs.Get(VertexInstanceID, LightMapCoordinateIndex)));
}
}
else
{
FStaticMeshOperations::FGenerateUVOptions GenerateUVOptions;
GenerateUVOptions.TextureResolution = InMeshProxySettings.MaterialSettings.TextureSize.GetMax();
GenerateUVOptions.bMergeTrianglesWithIdenticalAttributes = false;
GenerateUVOptions.UVMethod = GetUVGenerationMethodToUse();
bool bSuccess = FStaticMeshOperations::GenerateUV(InMeshDescription, GenerateUVOptions, CustomTextureCoordinates);
if (!bSuccess)
{
UE_LOG(LogMeshMerging, Warning, TEXT("GenerateUV: Failed to pack UVs for static mesh \"%s\" (num triangles = %d, texture resolution = %d)."), *InStaticMesh->GetName(), InMeshDescription.Triangles().Num(), InMeshProxySettings.MaterialSettings.TextureSize.GetMax());
CustomTextureCoordinates.Empty();
}
}
return CustomTextureCoordinates;
}
class FProxyMeshDescriptor
{
public:
FProxyMeshDescriptor(const UStaticMeshComponent* StaticMeshComponent, int32 LODIndex)
: LODIndex(LODIndex)
, LightMapIndex(INDEX_NONE)
{
ISMDescriptor.InitFrom(StaticMeshComponent, false);
// Retrieve lightmap for usage of lightmap data
if (StaticMeshComponent->LODData.IsValidIndex(0))
{
const FStaticMeshComponentLODInfo& ComponentLODInfo = StaticMeshComponent->LODData[0];
const FMeshMapBuildData* MeshMapBuildData = StaticMeshComponent->GetMeshMapBuildData(ComponentLODInfo);
if (MeshMapBuildData)
{
LightMap = MeshMapBuildData->LightMap;
LightMapIndex = StaticMeshComponent->GetStaticMesh()->GetLightMapCoordinateIndex();
}
}
Hash = ISMDescriptor.ComputeHash();
Hash = FCrc::TypeCrc32(LODIndex, Hash);
if (LightMapIndex != INDEX_NONE)
{
Hash = FCrc::TypeCrc32(LightMap.GetReference(), Hash);
Hash = FCrc::TypeCrc32(LightMapIndex, Hash);
}
}
bool operator==(const FProxyMeshDescriptor& InOther) const
{
return Hash == InOther.Hash &&
LODIndex == InOther.LODIndex &&
LightMap == InOther.LightMap &&
LightMapIndex == InOther.LightMapIndex &&
ISMDescriptor == InOther.ISMDescriptor;
}
int32 GetLODIndex() const { return LODIndex; }
FLightMapRef GetLightMap() const { return LightMap; }
int32 GetLightMapIndex() const { return LightMapIndex; }
UStaticMesh* GetStaticMesh() const { return ISMDescriptor.StaticMesh; }
bool IsValid() const { return !MeshDescription.IsEmpty(); }
const FMeshDescription& GetMeshDescription() const
{
return MeshDescription;
}
const TArray<FVector2D>& GetCustomTextureCoordinates() const
{
return CustomTextureCoordinates;
}
void PrepareMeshDescription(const FMeshProxySettings& InMeshProxySettings)
{
TRACE_CPUPROFILER_EVENT_SCOPE(PrepareMeshDescription);
// Retrieve mesh data in FMeshDescription form
FStaticMeshAttributes(MeshDescription).Register();
FMeshMergeHelpers::RetrieveMesh(ISMDescriptor.StaticMesh, LODIndex, MeshDescription);
CustomTextureCoordinates = ::GetCustomTextureCoordinates(MeshDescription, ISMDescriptor.StaticMesh, InMeshProxySettings);
if (CustomTextureCoordinates.IsEmpty())
{
// Failure, clear mesh description
MeshDescription.Empty();
}
}
private:
int32 Hash;
int32 LODIndex;
FLightMapRef LightMap;
int32 LightMapIndex;
FISMComponentDescriptor ISMDescriptor;
FMeshDescription MeshDescription;
TArray<FVector2D> CustomTextureCoordinates;
};
static void ScaleTextureCoordinatesToBox(const FBox2D& Box, TArray<FVector2D>& InOutTextureCoordinates)
{
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;
}
}
typedef TFunctionRef<int32(const UStaticMeshComponent*)> FGetMeshLODFunc;
struct FInstancedMeshDescriptionData
{
FMeshDescription* MeshDescription;
TArray<FTransform> InstancesTransforms;
};
static TArray<FInstancedMeshDescriptionData> GatherGeometry(const TArray<UStaticMeshComponent*>& InStaticMeshComponents, const FMeshProxySettings& InMeshProxySettings, TArray<FProxyMeshDescriptor>& InDescriptors, const TArray<TArray<int32>>& InMeshesToMergePerDescriptor, const TArray<int32>& InMeshesToMeshDescriptor, FGetMeshLODFunc InGetMeshLODFunc, int32& OutSummedLightmapPixels)
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshMergeUtilities::GatherGeometry);
TArray<FMeshDescription> TempDescriptionData;
TempDescriptionData.SetNum(InStaticMeshComponents.Num());
TAtomic<int32> SummedLightmapPixels(0);
TArray<FInstancedMeshDescriptionData> MeshesDescriptions;
MeshesDescriptions.SetNum(InDescriptors.Num());
// If grouping by identical meshes, prepare each mesh description along with their flattened UVs
// These meshes descriptions will serve for material baking, but also as the basis for creating a
// single merged mesh out of all instances.
if (InMeshProxySettings.bGroupIdenticalMeshesForBaking)
{
TRACE_CPUPROFILER_EVENT_SCOPE(UniqueUVs);
ParallelFor(InDescriptors.Num(), [&InDescriptors, &InMeshProxySettings](uint32 Index)
{
InDescriptors[Index].PrepareMeshDescription(InMeshProxySettings);
});
}
// Gather geometry from each component, expand ISMC geometry
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherGeometryFromComponents);
ParallelFor(InStaticMeshComponents.Num(), [InStaticMeshComponents, &InGetMeshLODFunc, InDescriptors, InMeshesToMeshDescriptor, InMeshProxySettings, &TempDescriptionData, &SummedLightmapPixels](uint32 Index)
{
TRACE_CPUPROFILER_EVENT_SCOPE(GatherGeometryFromComponent);
const UStaticMeshComponent* StaticMeshComponent = InStaticMeshComponents[Index];
FMeshDescription& MeshDescription = TempDescriptionData[Index];
// Retrieve meshes
if (!InMeshProxySettings.bGroupIdenticalMeshesForBaking || !InDescriptors[InMeshesToMeshDescriptor[Index]].IsValid())
{
const int32 LODIndex = InGetMeshLODFunc(StaticMeshComponent);
static const bool bPropagateVertexColours = true;
// Retrieve mesh data in FMeshDescription form
FStaticMeshAttributes(MeshDescription).Register();
FMeshMergeHelpers::RetrieveMesh(StaticMeshComponent, LODIndex, MeshDescription, bPropagateVertexColours);
}
else
{
MeshDescription = InDescriptors[InMeshesToMeshDescriptor[Index]].GetMeshDescription();
if (!InMeshProxySettings.bGroupIdenticalMeshesForBaking)
{
FStaticMeshOperations::ApplyTransform(MeshDescription, StaticMeshComponent->GetComponentTransform());
}
}
// If the component is an ISMC then we need to duplicate the vertex data
int32 NumInstances = 1;
if (const UInstancedStaticMeshComponent* InstancedStaticMeshComponent = Cast<UInstancedStaticMeshComponent>(StaticMeshComponent))
{
if (!InMeshProxySettings.bGroupIdenticalMeshesForBaking)
{
FMeshMergeHelpers::ExpandInstances(InstancedStaticMeshComponent, MeshDescription);
}
NumInstances = InstancedStaticMeshComponent->PerInstanceSMData.Num();
}
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
SummedLightmapPixels += FMath::Max(16, LightMapHeight * LightMapWidth * NumInstances);
}, EParallelForFlags::Unbalanced);
}
// For each mesh, append each component geometry
{
TRACE_CPUPROFILER_EVENT_SCOPE(AppendMeshes);
FStaticMeshOperations::FAppendSettings AppendSettings;
for (int32 ChannelIdx = 0; ChannelIdx < FStaticMeshOperations::FAppendSettings::MAX_NUM_UV_CHANNELS; ++ChannelIdx)
{
AppendSettings.bMergeUVChannels[ChannelIdx] = true;
}
ParallelFor(InDescriptors.Num(), [&MeshesDescriptions, &InMeshesToMergePerDescriptor, &InStaticMeshComponents, &TempDescriptionData, &AppendSettings, &InMeshProxySettings](uint32 Index)
{
TRACE_CPUPROFILER_EVENT_SCOPE(AppendMeshes);
FMeshDescription* TargetMeshDescription = new FMeshDescription();
FStaticMeshAttributes(*TargetMeshDescription).Register();
// When using this option, do not expand the instances, but rather send their transforms to the ProxyLOD tool
if (InMeshProxySettings.bGroupIdenticalMeshesForBaking)
{
TArray<FTransform> InstancesTransforms;
for (int32 Idx : InMeshesToMergePerDescriptor[Index])
{
UStaticMeshComponent* StaticMeshComponent = InStaticMeshComponents[Idx];
if (InMeshProxySettings.bGroupIdenticalMeshesForBaking)
{
FTransform ComponentTransform = StaticMeshComponent->GetComponentTransform();
if (const UInstancedStaticMeshComponent* InstancedStaticMeshComponent = Cast<UInstancedStaticMeshComponent>(StaticMeshComponent))
{
for (const FInstancedStaticMeshInstanceData& InstanceData : InstancedStaticMeshComponent->PerInstanceSMData)
{
InstancesTransforms.Add(FTransform(InstanceData.Transform) * ComponentTransform);
}
}
else
{
InstancesTransforms.Add(ComponentTransform);
}
}
}
if (!InMeshesToMergePerDescriptor[Index].IsEmpty())
{
*TargetMeshDescription = TempDescriptionData[InMeshesToMergePerDescriptor[Index][0]];
}
MeshesDescriptions[Index].InstancesTransforms = MoveTemp(InstancesTransforms);
}
else
{
TArray<const FMeshDescription*> SourceMeshDescriptions;
SourceMeshDescriptions.Reserve(InMeshesToMergePerDescriptor[Index].Num());
for (int32 TempIdx : InMeshesToMergePerDescriptor[Index])
{
SourceMeshDescriptions.Add(&TempDescriptionData[TempIdx]);
}
FStaticMeshOperations::AppendMeshDescriptions(SourceMeshDescriptions, *TargetMeshDescription, AppendSettings);
}
MeshesDescriptions[Index].MeshDescription = TargetMeshDescription;
}, EParallelForFlags::Unbalanced);
}
OutSummedLightmapPixels = SummedLightmapPixels;
return MeshesDescriptions;
}
TArray<FMeshData> PrepareBakingMeshes(const struct FMeshProxySettings& InMeshProxySettings, const TArray<FProxyMeshDescriptor>& InDescriptors, TArray<FInstancedMeshDescriptionData> InMeshDescriptionData)
{
TRACE_CPUPROFILER_EVENT_SCOPE(PrepareBakingMeshes)
check(InDescriptors.Num() == InMeshDescriptionData.Num());
TArray<FMeshData> MeshData;
MeshData.SetNum(InDescriptors.Num());
// GetLightMap() must be called from the game thread
check(IsInGameThread());
for (int32 MeshIndex = 0; MeshIndex < InDescriptors.Num(); ++MeshIndex)
{
if (InDescriptors[MeshIndex].GetLightMapIndex() != INDEX_NONE)
{
MeshData[MeshIndex].LightMap = InDescriptors[MeshIndex].GetLightMap();
MeshData[MeshIndex].LightMapIndex = InDescriptors[MeshIndex].GetLightMapIndex();
}
}
// Parallel step - fetching custom (unwrapped) texture coordinates is the slowest part here
ParallelFor(InDescriptors.Num(), [&MeshData, &InDescriptors, &InMeshDescriptionData, &InMeshProxySettings](uint32 MeshIndex)
{
const FProxyMeshDescriptor& MeshDescriptor = InDescriptors[MeshIndex];
FMeshData& MeshSettings = MeshData[MeshIndex];
MeshSettings.TextureCoordinateBox = FBox2D(FVector2D(0.0f, 0.0f), FVector2D(1.0f, 1.0f));
if (InMeshProxySettings.bGroupIdenticalMeshesForBaking)
{
// Grouping by identical meshes, the UVs should have already been setup
MeshSettings.MeshDescription = &MeshDescriptor.GetMeshDescription();
MeshSettings.CustomTextureCoordinates = MeshDescriptor.GetCustomTextureCoordinates();
}
else
{
FMeshDescription& MeshDescription = *InMeshDescriptionData[MeshIndex].MeshDescription;
MeshSettings.MeshDescription = &MeshDescription;
MeshSettings.CustomTextureCoordinates = GetCustomTextureCoordinates(MeshDescription, MeshDescriptor.GetStaticMesh(), InMeshProxySettings);
}
if (MeshSettings.CustomTextureCoordinates.IsEmpty())
{
MeshSettings.MeshDescription = nullptr;
MeshSettings.TextureCoordinateIndex = 0;
}
});
return MeshData;
}
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
{
CreateProxyMesh(InActors, InMeshProxySettings, GEngine->DefaultFlattenMaterial, 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
{
CreateProxyMesh(InStaticMeshComps, InMeshProxySettings, GEngine->DefaultFlattenMaterial, 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
{
// 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 @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
// Collect components to merge
TArray<UStaticMeshComponent*> ComponentsToMerge;
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
for (AActor* Actor : InActors)
{
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
TInlineComponentArray<UStaticMeshComponent*> Components;
Actor->GetComponents(Components);
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
ComponentsToMerge.Append(Components);
}
CreateProxyMesh(ComponentsToMerge, InMeshProxySettings, InBaseMaterial, InOuter, InProxyBasePackageName, InGuid, InProxyCreatedDelegate, bAllowAsync, ScreenSize);
}
void FMeshMergeUtilities::CreateProxyMesh(const TArray<UStaticMeshComponent*>& InComponentsToMerge, const struct FMeshProxySettings& InMeshProxySettings, UMaterialInterface* InBaseMaterial,
UPackage* InOuter, const FString& InProxyBasePackageName, const FGuid InGuid, const FCreateProxyDelegate& InProxyCreatedDelegate, const bool bAllowAsync, const float ScreenSize) const
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshMergeUtilities::CreateProxyMesh)
// 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 @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
// Error/warning checking for input
if (ReductionModule.GetMeshMergingInterface() == nullptr)
{
UE_LOG(LogMeshMerging, Error, TEXT("No mesh reduction module available. You must enable a plugin that provides that functionality (ex: ProxyLODPlugin)"));
return;
}
// Check that the delegate has a func-ptr bound to it
if (!InProxyCreatedDelegate.IsBound())
{
UE_LOG(LogMeshMerging, Warning, TEXT("Invalid (unbound) delegate for returning generated proxy mesh"));
return;
}
TArray<UStaticMeshComponent*> ComponentsToMerge = InComponentsToMerge;
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
// Remove invalid components
ComponentsToMerge.RemoveAll([](UStaticMeshComponent* Val) { return Val->GetStaticMesh() == nullptr; });
// No actors given as input
if (ComponentsToMerge.Num() == 0)
{
UE_LOG(LogMeshMerging, Log, TEXT("No static mesh specified to generate a proxy mesh for"));
TArray<UObject*> OutAssetsToSync;
InProxyCreatedDelegate.ExecuteIfBound(InGuid, OutAssetsToSync);
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();
TArray<FRawMeshExt> SourceMeshes;
TMap<FMeshIdAndLOD, TArray<int32>> GlobalMaterialMap;
FBoxSphereBounds::Builder EstimatedBoundsBuilder;
for (const UStaticMeshComponent* StaticMeshComponent : ComponentsToMerge)
{
EstimatedBoundsBuilder += StaticMeshComponent->Bounds;
}
FBoxSphereBounds EstimatedBounds = EstimatedBoundsBuilder;
Copying //UE4/Dev-Enterprise to //UE4/Dev-Main (Source: //UE4/Dev-Enterprise @ 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 @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@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 [CL 4342443 by JeanMichel Dignard in Main branch]
2018-09-04 16:35:02 -04:00
static const float FOVRad = FMath::DegreesToRadians(45.0f);
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);
auto SelectLODFunc = [&InMeshProxySettings, &Utilities, EstimatedDistance] (const UStaticMeshComponent* Component)
{
int32 LODIndex = 0;
if (InMeshProxySettings.bCalculateCorrectLODModel)
{
LODIndex = Utilities->GetLODLevelForScreenSize(Component, Utilities->CalculateScreenSizeFromDrawDistance(Component->Bounds.SphereRadius, ProjectionMatrix, EstimatedDistance));
}
return LODIndex;
};
SlowTask.EnterProgressFrame(5.0f, LOCTEXT("CreateProxyMesh_CollectingMeshes", "Collecting Input Static Meshes"));
// Mesh / LOD index
TMap<uint32, FMeshDescription*> RawMeshLODs;
// Mesh index, <original section index, unique section index>
TMultiMap<uint32, TPair<uint32, uint32>> MeshSectionToUniqueSection;
// Unique set of sections in mesh
TArray<FSectionInfo> UniqueSections;
TMultiMap<uint32, uint32> SectionToMesh;
TArray<const UStaticMeshComponent*> ImposterMeshComponents;
TArray<UStaticMeshComponent*> StaticMeshComponents;
for (UStaticMeshComponent* StaticMeshComponent : ComponentsToMerge)
{
if (StaticMeshComponent->HLODBatchingPolicy != EHLODBatchingPolicy::None)
{
ImposterMeshComponents.Add(StaticMeshComponent);
}
else
{
StaticMeshComponents.Add(StaticMeshComponent);
}
}
TArray<FProxyMeshDescriptor> MeshDescriptors;
TArray<TArray<int32>> MeshesToMergePerDescriptor;
TArray<int32> MeshToMeshDescriptor;
TArray<TArray<FSectionInfo>> GlobalSections;
MeshToMeshDescriptor.Reserve(StaticMeshComponents.Num());
for (int32 ComponentIndex = 0; ComponentIndex < StaticMeshComponents.Num(); ++ComponentIndex)
{
const UStaticMeshComponent* StaticMeshComponent = StaticMeshComponents[ComponentIndex];
FProxyMeshDescriptor MeshDescriptor(StaticMeshComponent, SelectLODFunc(StaticMeshComponent));
int32 Index;
if (!InMeshProxySettings.bGroupIdenticalMeshesForBaking || !MeshDescriptors.Find(MeshDescriptor, Index))
{
Index = MeshDescriptors.Num();
MeshDescriptors.Add(MeshDescriptor);
MeshesToMergePerDescriptor.AddDefaulted();
TArray<FSectionInfo>& Sections = GlobalSections.AddDefaulted_GetRef();
// Extract sections for given LOD index from the mesh
FMeshMergeHelpers::ExtractSections(StaticMeshComponent, MeshDescriptor.GetLODIndex(), Sections);
}
MeshesToMergePerDescriptor[Index].Add(ComponentIndex);
MeshToMeshDescriptor.Add(Index);
}
for (int32 MeshIndex = 0; MeshIndex < GlobalSections.Num(); ++MeshIndex)
{
TArray<FSectionInfo>& Sections = GlobalSections[MeshIndex];
for (int32 SectionIndex = 0; SectionIndex < Sections.Num(); ++SectionIndex)
{
FSectionInfo& Section = Sections[SectionIndex];
const int32 UniqueIndex = UniqueSections.AddUnique(Section);
MeshSectionToUniqueSection.Add(MeshIndex, TPair<uint32, uint32>(SectionIndex, UniqueIndex));
SectionToMesh.Add(UniqueIndex, MeshIndex);
}
}
int32 SummedLightmapPixels;
TArray<FInstancedMeshDescriptionData> MeshDescriptionData = GatherGeometry(StaticMeshComponents, InMeshProxySettings, MeshDescriptors, MeshesToMergePerDescriptor, MeshToMeshDescriptor, SelectLODFunc, SummedLightmapPixels);
TArray<FMeshData> MeshBakingData = PrepareBakingMeshes(InMeshProxySettings, MeshDescriptors, MeshDescriptionData);
TArray<UMaterialInterface*> UniqueMaterials;
//Unique material index to unique section index
TMultiMap<uint32, uint32> MaterialToSectionMap;
for (int32 SectionIndex = 0; SectionIndex < UniqueSections.Num(); ++SectionIndex)
{
FSectionInfo& Section = UniqueSections[SectionIndex];
const int32 UniqueIndex = UniqueMaterials.AddUnique(Section.Material);
MaterialToSectionMap.Add(UniqueIndex, SectionIndex);
}
TArray<FMeshData> GlobalMeshSettings;
TArray<FMaterialData> GlobalMaterialSettings;
FMaterialProxySettings MaterialProxySettings = InMeshProxySettings.MaterialSettings;
TArray<UPrimitiveComponent*> PrimitiveComponents;
Algo::Transform(StaticMeshComponents, PrimitiveComponents, [](UStaticMeshComponent* SMComponent) { return SMComponent; });
if (MaterialProxySettings.ResolveTexelDensity(PrimitiveComponents))
{
double Total3DArea = 0;
for (const FInstancedMeshDescriptionData& InstancedMeshDescriptionData : MeshDescriptionData)
{
double Mesh3DArea = 0;
const FMeshDescription& MeshDescription = *InstancedMeshDescriptionData.MeshDescription;
FStaticMeshConstAttributes Attributes(MeshDescription);
TVertexAttributesConstRef<FVector3f> Positions = Attributes.GetVertexPositions();
for (const FTriangleID TriangleID : MeshDescription.Triangles().GetElementIDs())
{
// World space area
TArrayView<const FVertexID> TriVertices = MeshDescription.GetTriangleVertices(TriangleID);
Mesh3DArea += UE::Geometry::VectorUtil::Area(Positions[TriVertices[0]], Positions[TriVertices[1]], Positions[TriVertices[2]]);
}
// Account for multiple instances (no transforms means a single instance)
uint32 NumInstances = FMath::Max(1, InstancedMeshDescriptionData.InstancesTransforms.Num());
Total3DArea += Mesh3DArea * NumInstances;
}
MaterialProxySettings.TextureSize = FMaterialUtilities::GetTextureSizeFromTargetTexelDensity(Total3DArea, 1.0f, MaterialProxySettings.TargetTexelDensityPerMeter);
MaterialProxySettings.TextureSizingType = ETextureSizingType::TextureSizingType_UseSingleTextureSize;
}
UMaterialOptions* Options = PopulateMaterialOptions(MaterialProxySettings);
TGCObjectScopeGuard<UMaterialOptions> MaterialOptionsGCScopeGuard(Options);
TArray<EMaterialProperty> MaterialProperties;
for (const FPropertyEntry& Entry : Options->Properties)
{
if (Entry.Property != MP_MAX)
{
MaterialProperties.Add(Entry.Property);
}
}
// Mesh index / ( Mesh relative section index / output index )
TMultiMap<uint32, TPair<uint32, uint32>> OutputMaterialsMap;
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshMergeUtilities::MaterialAnalysisAndUVGathering);
for (int32 MaterialIndex = 0; MaterialIndex < UniqueMaterials.Num(); ++MaterialIndex)
{
UMaterialInterface* Material = UniqueMaterials[MaterialIndex];
//Unique section indices
TArray<uint32> SectionIndices;
MaterialToSectionMap.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)
{
if (!Entry.bUseConstantValue && Material->IsPropertyActive(Entry.Property) && Entry.Property != MP_MAX)
{
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 (const uint32 MeshIndex : MeshIndices)
{
FMeshData MeshSettings = MeshBakingData[MeshIndex];
// Section index is a unique one so we need to map it to the mesh's equivalent(s)
TArray<TPair<uint32, uint32>> SectionToUniqueSectionIndices;
MeshSectionToUniqueSection.MultiFind(MeshIndex, SectionToUniqueSectionIndices);
for (const TPair<uint32, uint32>& IndexPair : SectionToUniqueSectionIndices)
{
if (IndexPair.Value == SectionIndex)
{
MeshSettings.MaterialIndices.Add(IndexPair.Key);
OutputMaterialsMap.Add(MeshIndex, TPair<uint32, uint32>(IndexPair.Key, GlobalMeshSettings.Num()));
}
}
GlobalMeshSettings.Add(MoveTemp(MeshSettings));
GlobalMaterialSettings.Add(MaterialSettings);
}
}
}
else
{
// Add simple bake entry
FMeshData MeshSettings;
MeshSettings.MeshDescription = nullptr;
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>> SectionToUniqueSectionIndices;
MeshSectionToUniqueSection.MultiFind(MeshIndex, SectionToUniqueSectionIndices);
for (const TPair<uint32, uint32>& IndexPair : SectionToUniqueSectionIndices)
{
if (IndexPair.Value == SectionIndex)
{
OutputMaterialsMap.Add(MeshIndex, TPair<uint32, uint32>(IndexPair.Key, GlobalMeshSettings.Num()));
}
}
}
}
GlobalMeshSettings.Add(MeshSettings);
GlobalMaterialSettings.Add(MaterialSettings);
}
}
}
TArray<FFlattenMaterial> FlattenedMaterials;
IMaterialBakingModule& MaterialBakingModule = FModuleManager::Get().LoadModuleChecked<IMaterialBakingModule>("MaterialBaking");
auto MaterialFlattenLambda =
[this, &Options, &GlobalMeshSettings, &GlobalMaterialSettings, &MeshDescriptionData, &OutputMaterialsMap, &MaterialBakingModule](TArray<FFlattenMaterial>& FlattenedMaterialArray)
{
TRACE_CPUPROFILER_EVENT_SCOPE(MaterialFlatten)
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]);
}
// This scope ensures BakeOutputs is never used after TransferOutputToFlatMaterials
{
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)
{
ConstantData.SetNum(1, EAllowShrinking::No);
ConstantData[0] = FColor(Entry.ConstantValue * 255.0f, Entry.ConstantValue * 255.0f, Entry.ConstantValue * 255.0f);
for (FBakeOutput& Output : BakeOutputs)
{
Output.PropertyData.Add(Entry.Property, ConstantData);
Output.PropertySizes.Add(Entry.Property, ConstantSize);
}
}
}
TransferOutputToFlatMaterials(GlobalMaterialSettings, BakeOutputs, FlattenedMaterialArray);
}
{
TRACE_CPUPROFILER_EVENT_SCOPE(RemapBakedMaterials)
// 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 < MeshDescriptionData.Num(); ++MeshIndex)
{
FMeshDescription& MeshDescription = *MeshDescriptionData[MeshIndex].MeshDescription;
TArray<TPair<uint32, uint32>> SectionAndOutputIndices;
OutputMaterialsMap.MultiFind(MeshIndex, SectionAndOutputIndices);
TArray<int32> Remap;
// Reorder loops
for (const TPair<uint32, uint32>& IndexPair : SectionAndOutputIndices)
{
const int32 SectionIndex = IndexPair.Key;
const int32 NewIndex = IndexPair.Value;
if (Remap.Num() < (SectionIndex + 1))
{
Remap.SetNum(SectionIndex + 1);
}
Remap[SectionIndex] = NewIndex;
}
TMap<FPolygonGroupID, FPolygonGroupID> RemapPolygonGroup;
for (const FPolygonGroupID PolygonGroupID : MeshDescription.PolygonGroups().GetElementIDs())
{
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));
}
MeshDescription.RemapPolygonGroups(RemapPolygonGroup);
}
}
};
// Landscape culling. NB these are temporary copies of the culling data and should be deleted after use.
TArray<FMeshDescription*> CullingRawMeshes;
if (InMeshProxySettings.bUseLandscapeCulling)
{
SlowTask.EnterProgressFrame(5.0f, LOCTEXT("CreateProxyMesh_LandscapeCulling", "Applying Landscape Culling"));
UWorld* InWorld = ComponentsToMerge[0]->GetWorld();
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;
Data->ImposterComponents = ImposterMeshComponents;
Data->StaticMeshComponents = StaticMeshComponents;
Data->BaseMaterial = InBaseMaterial;
// Lightmap resolution
if (InMeshProxySettings.bComputeLightMapResolution)
{
Data->InProxySettings.LightMapResolution = FMath::CeilToInt(FMath::Sqrt(static_cast<float>(SummedLightmapPixels)));
}
// Add this proxy job to map
Processor->AddProxyJob(InGuid, Data);
TArray<FInstancedMeshMergeData> MergeDataEntries;
{
TRACE_CPUPROFILER_EVENT_SCOPE(MergeDataPreparation)
for (int32 Index = 0; Index < MeshDescriptionData.Num(); ++Index)
{
FInstancedMeshMergeData MergeData;
MergeData.SourceStaticMesh = MeshDescriptors[Index].GetStaticMesh();
MergeData.RawMesh = MeshDescriptionData[Index].MeshDescription;
MergeData.NewUVs = MeshDescriptors[Index].GetCustomTextureCoordinates();
MergeData.bIsClippingMesh = false;
MergeData.InstanceTransforms = MeshDescriptionData[Index].InstancesTransforms;
FMeshMergeHelpers::CalculateTextureCoordinateBoundsForMesh(*MergeData.RawMesh, MergeData.TexCoordBounds);
if (MergeData.NewUVs.IsEmpty())
{
FMeshData* MeshData = GlobalMeshSettings.FindByPredicate([&](const FMeshData& Entry)
{
return Entry.MeshDescription == MergeData.RawMesh && (Entry.CustomTextureCoordinates.Num() || Entry.TextureCoordinateIndex != 0);
});
if (MeshData)
{
if (MeshData->CustomTextureCoordinates.Num())
{
MergeData.NewUVs = MeshData->CustomTextureCoordinates;
}
else
{
TVertexInstanceAttributesConstRef<FVector2f> VertexInstanceUVs = FStaticMeshConstAttributes(*MeshData->MeshDescription).GetVertexInstanceUVs();
MergeData.NewUVs.Reset(MeshData->MeshDescription->VertexInstances().Num());
for (const FVertexInstanceID VertexInstanceID : MeshData->MeshDescription->VertexInstances().GetElementIDs())
{
MergeData.NewUVs.Add(FVector2D(VertexInstanceUVs.Get(VertexInstanceID, MeshData->TextureCoordinateIndex)));
}
}
MergeData.TexCoordBounds[0] = FBox2D(FVector2D(0.0f, 0.0f), FVector2D(1.0f, 1.0f));
}
}
MergeDataEntries.Add(MergeData);
}
}
if (MergeDataEntries.Num() != 0)
{
// Populate landscape clipping geometry
for (FMeshDescription* RawMesh : CullingRawMeshes)
{
FInstancedMeshMergeData ClipData;
ClipData.bIsClippingMesh = true;
ClipData.RawMesh = RawMesh;
MergeDataEntries.Add(ClipData);
}
SlowTask.EnterProgressFrame(50.0f, LOCTEXT("CreateProxyMesh_GenerateProxy", "Generating Proxy Mesh"));
{
TRACE_CPUPROFILER_EVENT_SCOPE(ProxyGeneration)
// Choose Simplygon Swarm (if available) or local proxy lod method
if (ReductionModule.GetDistributedMeshMergingInterface() != nullptr && GetDefault<UEditorPerProjectUserSettings>()->bUseSimplygonSwarm && bAllowAsync)
{
MaterialFlattenLambda(FlattenedMaterials);
ReductionModule.GetDistributedMeshMergingInterface()->ProxyLOD(MergeDataEntries, Data->InProxySettings, FlattenedMaterials, InGuid);
}
else
{
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);
Processor->Tick(0); // make sure caller gets merging results
}
}
}
else
{
FMeshDescription MeshDescription;
FStaticMeshAttributes(MeshDescription).Register();
FFlattenMaterial FlattenMaterial;
Processor->ProxyGenerationComplete(MeshDescription, FlattenMaterial, InGuid);
}
TRACE_CPUPROFILER_EVENT_SCOPE(Cleanup)
// Clean up the CullingRawMeshes
ParallelFor(CullingRawMeshes.Num(),
[&CullingRawMeshes](int32 Index)
{
delete CullingRawMeshes[Index];
}
);
// Clean up the MeshDescriptionData
ParallelFor(
MeshDescriptionData.Num(),
[&MeshDescriptionData](int32 Index)
{
delete MeshDescriptionData[Index].MeshDescription;
}
);
}
void FMeshMergeUtilities::RetrieveMeshDescription(const UStaticMeshComponent* InStaticMeshComponent, int32 LODIndex, FMeshDescription& InOutMeshDescription, bool bPropagateMeshData) const
{
FMeshMergeHelpers::RetrieveMesh(InStaticMeshComponent, LODIndex, InOutMeshDescription, bPropagateMeshData);
}
void FMeshMergeUtilities::RetrieveMeshDescription(const USkeletalMeshComponent* InSkeletalMeshComponent, int32 LODIndex, FMeshDescription& InOutMeshDescription, bool bPropagateMeshData) const
{
FMeshMergeHelpers::RetrieveMesh(InSkeletalMeshComponent, LODIndex, InOutMeshDescription, bPropagateMeshData);
}
void FMeshMergeUtilities::RetrieveMeshDescription(const UStaticMesh* InStaticMesh, int32 LODIndex, FMeshDescription& InOutMeshDescription) const
{
FMeshMergeHelpers::RetrieveMesh(InStaticMesh, LODIndex, InOutMeshDescription);
}
void FMeshMergeUtilities::RegisterExtension(IMeshMergeExtension* InExtension)
{
MeshMergeExtensions.Add(InExtension);
}
void FMeshMergeUtilities::UnregisterExtension(IMeshMergeExtension* InExtension)
{
MeshMergeExtensions.Remove(InExtension);
}
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, EAllowShrinking::No);
// Extract sections for given LOD index from the mesh
Adapter.RetrieveMeshSections(LODIndex, Sections);
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, SectionIndex, UniqueIndex);
DataTracker.AddMaterialSlotName(Section.Material, Section.MaterialSlotName);
if (!bMergeMaterialData)
{
FStaticMeshOperations::SwapPolygonPolygonGroup(RawMesh, UniqueIndex, Section.StartIndex, Section.EndIndex, false);
}
}
//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->IsA<UInstancedStaticMeshComponent>())
{
const UInstancedStaticMeshComponent* InstancedStaticMeshComponent = Cast<UInstancedStaticMeshComponent>(Component);
FMeshMergeHelpers::ExpandInstances(InstancedStaticMeshComponent, RawMesh);
}
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()->GetLightMapCoordinateIndex());
}
return bValidMesh;
}
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
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshMergeUtilities::MergeComponentsToStaticMesh);
// Use first mesh for naming and pivot
bool bFirstMesh = true;
FString MergedAssetPackageName;
FVector MergedAssetPivot;
TArray<UStaticMeshComponent*> StaticMeshComponentsToMerge;
TArray<const UStaticMeshComponent*> ImposterComponents;
for (int32 MeshId = 0; MeshId < ComponentsToMerge.Num(); ++MeshId)
{
UStaticMeshComponent* MeshComponent = Cast<UStaticMeshComponent>(ComponentsToMerge[MeshId]);
if (MeshComponent)
{
Async Texture Compilation - Feature can be activated in the Experimental section of the Editor Settings - Replace Texture2D/TextureCube resources by placeholders until their PlatformData is ready - Add a utility class allowing to encapsulate raw field pointers without breaking compatibility - Protect PlatformData from unsafe access through encapsulation. - Protect texture's resource from race conditions between game and render threads through encapsulation. - This allows to get rid of FlushRenderingCommands and long game-thread stutters when Updating a texture's resource. - UpdateResource was never safe to call without a FlushRenderingCommands and multiple call-site are doing exactly that, this will fix those cases. - Those were probably undetected due to their low occurence rate under normal conditions but can easily be reproed during async texture compilation on 32 cores. - Force wait on required texture compilations for MaterialBaking, ProxyMesh, Thumbnail generation for disk usage - Wait on all textures compilation whenever a wait for all shaders compilation is requested for safety (i.e. screenshot) - Compile UI and heightmap textures with higher priority to reduce visual artefacts - Increase priority of texture that have been rendered to improve time-to-usefulness of the editor under low core count - Async compilation is disabled for -game / non-editor mode as there is currently no support for async bulk data loading from external files - Properly cancel async tasks when UTexture is garbage collected before the compilation is finished - Show progress when explicitly waiting on compilation - Changing the mip settings in the texture editor (or any settings requiring the running platform data to be recomputed) will now be processed asynchronously. DEBUGGING - Can be forcibly enabled/disabled through command-line via -asynctexturecompilation=[off, on, paused] - Can pause texture compilation using Editor.AsyncTextureCompilation = 2 or -asynctexturecompilation=paused - Can manually resume a specified amount of paused compilation using Editor.AsyncTextureCompilationResume [Num] - Can forcibly wait on all compilation using Editor.AsyncTextureCompilationFlushAll BENCHMARKS - 3m15s to 1m20s when loading Apollo_Terrain with no textures in DDC (AMD TR 3970X) - 6m45s to 1m11s when loading Apollo_Terrain with no textures in DDC (-corelimit=8) - 3m10s to 1m54s when lauching PIE on Apollo_Terrain with no textures in DDC (AMD TR 3970X) - 7m43s to 1m36s when lauching PIE on Apollo_Terrain with no textures in DDC (-corelimit=8) - 0m57s to 0m42s when importing Attic_NVIDIA.usd with no textures in DDC (AMD TR 3970X) - 2m14s to 0m35s when importing Attic_NVIDIA.usd with no textures in DDC (-corelimit=4) TESTS - Success on all material baking tests from EngineTests with -asynctexturecompilation=paused - Runned with -corelimit=1 all the way to unlimited - Cooking worked - Opening the texture editor/material editor will force the compilation to finish like expected. - Changing a setting in the texture editor will recompile async, even allowing to close the editor and continue doing other changes. - Unpausing the compilation will update the texture thumbnails properly. - Started with -asynctexturecompilation=paused, and then unpaused after a map loading, and then into a PIE session to stresstest UpdateResources. - Tested both dx11/dx12 - Vulkan fails on Fortnite even with -asynctexturecompilation=off because of Landscape weigthmap, not this CL. - Compiled and tested FortniteGame / UE4 / ShooterGame projects #rb Uriel.Doyon, Francis.Hurteau [CL 13694814 by danny couture in ue5-main branch]
2020-06-16 22:16:25 -04:00
// Make sure referenced lightmaps and shadowmaps are compiled
if (MeshComponent->LODData.IsValidIndex(0))
{
const FStaticMeshComponentLODInfo& ComponentLODInfo = MeshComponent->LODData[0];
const FMeshMapBuildData* MeshMapBuildData = MeshComponent->GetMeshMapBuildData(ComponentLODInfo);
if (MeshMapBuildData)
{
TArray<UTexture2D*> ReferencedTextures;
FLightMap2D* Lightmap = MeshMapBuildData && MeshMapBuildData->LightMap ? MeshMapBuildData->LightMap->GetLightMap2D() : nullptr;
if (Lightmap)
{
Lightmap->GetReferencedTextures(ReferencedTextures);
}
FShadowMap2D* Shadowmap = MeshMapBuildData && MeshMapBuildData->ShadowMap ? MeshMapBuildData->ShadowMap->GetShadowMap2D() : nullptr;
if (Shadowmap && Shadowmap->IsValid())
{
ReferencedTextures.Add(Shadowmap->GetTexture());
}
FTextureCompilingManager::Get().FinishCompilation(TArray<UTexture*>(MoveTemp(ReferencedTextures)));
}
}
if((MeshComponent->HLODBatchingPolicy != EHLODBatchingPolicy::None) && InSettings.bIncludeImposters)
{
ImposterComponents.Add(MeshComponent);
}
else
{
StaticMeshComponentsToMerge.Add(MeshComponent);
}
// 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();
bFirstMesh = false;
}
}
}
// Nothing to do if no StaticMeshComponents
if (StaticMeshComponentsToMerge.Num() == 0 && ImposterComponents.Num() == 0)
{
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)
{
Edigrating 3 CLs to improve HLOD generation time (faster material baking & mesh merging) CL 10373564 by danny.couture Optimize Material Baking (Phase 1) - Introduce a mecanism to override the vertex/index buffer allocator used for dynamic meshes - Avoid GDynamicMesh non-ticked pools build-up by using our own vertex/index buffer pool during baking - Reduce reallocation and incurred soft page faults by reusing a single set of vertex/index buffers big enough for the biggest mesh - Preemptively detect if smearing would result in monochrome texture to avoid useless work - Shrink smeared monochrome textures during the baking process for huge memory savings - Move UV smearing in worker threads to avoid blocking the game thread - Required shaders are now built asynchronously - Add progress bar for material baking - 28m23 [at] 150 GB RAM -> 2m14s [at] 45 GB RAM for 6 channels [at] 512x512 when baking materials on ProxyLOD for DATASET-0008a with DDC empty #rb Jurre.deBaare, Sebastien.Lussier CL 10516258 by danny.couture Optimize Material Baking (Phase 2) - Implement pipelining with staging buffers to avoid GPU stalls when reading from render targets - Reuse the same prepared FMeshBatch instead of rebuilding it for each draw pass - Prepare the RenderItem in advance on other threads to reduce work on the game thread - Move the staging surface copy out of the render thread - Small vertex and index buffers are not reused to avoid dependency locks when mapping them - Fix bug in Canvas Flush_RenderThread found while running HLOD rebuild commandlet on Fortnite - Delete old and unused MaterialBakingModule.h from public files - 4m44s -> 59s for baking 6 channel [at] 1024x1024 when baking materials on ProxyLOD for DATASET-0008a with shaders already compiled - Time spent in Material Baking when rebuilding all HLOD on Apollo_POI_Large_HLOD (Phase 1 + 2 combined) - 10m18s -> 2m36s for a first rebuild all in editor with no shaders in DDC (cold) - 1m23s -> 20s for a second rebuild all in editor (warm) #rb Jeremy.Moore, Sebastien.Lussier CL 11135986 by sebastien.lussier Optimized mesh merging * Added DeletePolygons() & DeleteTriangles methods to FMeshDescription which rely on TSets<> instead of performing costly TArray::AddUnique() calls() * Parallelized UV generation and avoided duplicate processing of the same mesh+lod pairs * Optimized FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh() * Goes from 100s to 10s in my test case #rb danny.couture, jeanfrancois.dube, richard.talbotwatkin #ROBOMERGE-OWNER: sebastien.lussier #ROBOMERGE-AUTHOR: sebastien.lussier #ROBOMERGE-SOURCE: CL 11206337 via CL 11206341 via CL 11206346 #ROBOMERGE-BOT: (v643-11205221) [CL 11206493 by sebastien lussier in Main branch]
2020-02-03 11:08:35 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE(RetrieveRawMeshData);
for (int32 ComponentIndex = 0; ComponentIndex < StaticMeshComponentsToMerge.Num(); ++ComponentIndex)
{
UStaticMeshComponent* Component = StaticMeshComponentsToMerge[ComponentIndex];
Adapters.Add(FStaticMeshComponentAdapter(Component));
FStaticMeshComponentAdapter& Adapter = Adapters.Last();
if (InSettings.bComputedLightMapResolution)
{
int32 LightMapHeight, LightMapWidth;
if (Component->GetLightMapResolution(LightMapWidth, LightMapHeight))
{
DataTracker.AddLightMapPixels(LightMapWidth * LightMapHeight);
}
}
const int32 NumLODs = [&]()
{
const int32 NumberOfLODsAvailable = Adapter.GetNumberOfLODs();
if (Component->HLODBatchingPolicy != EHLODBatchingPolicy::None)
{
return InSettings.bIncludeImposters ? NumberOfLODsAvailable : NumberOfLODsAvailable - 1;
}
return NumberOfLODsAvailable;
}();
for (int32 LODIndex = 0; LODIndex < NumLODs; ++LODIndex)
{
if (!RetrieveRawMeshData(DataTracker
, ComponentIndex
, LODIndex
, Component
, bPropagateMeshData
, Sections
, Adapter
, false
, InSettings))
{
//If the rawmesh was not retrieve properly break the loop
break;
}
DataTracker.AddLODIndex(LODIndex);
}
}
}
else
{
Edigrating 3 CLs to improve HLOD generation time (faster material baking & mesh merging) CL 10373564 by danny.couture Optimize Material Baking (Phase 1) - Introduce a mecanism to override the vertex/index buffer allocator used for dynamic meshes - Avoid GDynamicMesh non-ticked pools build-up by using our own vertex/index buffer pool during baking - Reduce reallocation and incurred soft page faults by reusing a single set of vertex/index buffers big enough for the biggest mesh - Preemptively detect if smearing would result in monochrome texture to avoid useless work - Shrink smeared monochrome textures during the baking process for huge memory savings - Move UV smearing in worker threads to avoid blocking the game thread - Required shaders are now built asynchronously - Add progress bar for material baking - 28m23 [at] 150 GB RAM -> 2m14s [at] 45 GB RAM for 6 channels [at] 512x512 when baking materials on ProxyLOD for DATASET-0008a with DDC empty #rb Jurre.deBaare, Sebastien.Lussier CL 10516258 by danny.couture Optimize Material Baking (Phase 2) - Implement pipelining with staging buffers to avoid GPU stalls when reading from render targets - Reuse the same prepared FMeshBatch instead of rebuilding it for each draw pass - Prepare the RenderItem in advance on other threads to reduce work on the game thread - Move the staging surface copy out of the render thread - Small vertex and index buffers are not reused to avoid dependency locks when mapping them - Fix bug in Canvas Flush_RenderThread found while running HLOD rebuild commandlet on Fortnite - Delete old and unused MaterialBakingModule.h from public files - 4m44s -> 59s for baking 6 channel [at] 1024x1024 when baking materials on ProxyLOD for DATASET-0008a with shaders already compiled - Time spent in Material Baking when rebuilding all HLOD on Apollo_POI_Large_HLOD (Phase 1 + 2 combined) - 10m18s -> 2m36s for a first rebuild all in editor with no shaders in DDC (cold) - 1m23s -> 20s for a second rebuild all in editor (warm) #rb Jeremy.Moore, Sebastien.Lussier CL 11135986 by sebastien.lussier Optimized mesh merging * Added DeletePolygons() & DeleteTriangles methods to FMeshDescription which rely on TSets<> instead of performing costly TArray::AddUnique() calls() * Parallelized UV generation and avoided duplicate processing of the same mesh+lod pairs * Optimized FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh() * Goes from 100s to 10s in my test case #rb danny.couture, jeanfrancois.dube, richard.talbotwatkin #ROBOMERGE-OWNER: sebastien.lussier #ROBOMERGE-AUTHOR: sebastien.lussier #ROBOMERGE-SOURCE: CL 11206337 via CL 11206341 via CL 11206346 #ROBOMERGE-BOT: (v643-11205221) [CL 11206493 by sebastien lussier in Main branch]
2020-02-03 11:08:35 -05:00
TRACE_CPUPROFILER_EVENT_SCOPE(RetrieveRawMeshData);
// 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
for (int32 ComponentIndex = 0; ComponentIndex < StaticMeshComponentsToMerge.Num(); ++ComponentIndex)
{
// 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 = [&]()
{
int32 LowestDetailLOD = Adapter.GetNumberOfLODs() - 1;
if (Component->HLODBatchingPolicy != EHLODBatchingPolicy::None && !InSettings.bIncludeImposters)
{
LowestDetailLOD = FMath::Max(0, LowestDetailLOD - 1);
}
switch (InSettings.LODSelectionType)
{
case EMeshLODSelectionType::SpecificLOD:
return FMath::Min(LowestDetailLOD, InSettings.SpecificLOD);
case EMeshLODSelectionType::CalculateLOD:
return FMath::Min(LowestDetailLOD, Utilities->GetLODLevelForScreenSize(Component, FMath::Clamp(ScreenSize, 0.0f, 1.0f)));
case EMeshLODSelectionType::LowestDetailLOD:
default:
return LowestDetailLOD;
}
}();
RetrieveRawMeshData(DataTracker
, ComponentIndex
, LODIndex
, Component
, bPropagateMeshData
, Sections
, Adapter
, bMergeMaterialData
, InSettings);
}
}
Edigrating 3 CLs to improve HLOD generation time (faster material baking & mesh merging) CL 10373564 by danny.couture Optimize Material Baking (Phase 1) - Introduce a mecanism to override the vertex/index buffer allocator used for dynamic meshes - Avoid GDynamicMesh non-ticked pools build-up by using our own vertex/index buffer pool during baking - Reduce reallocation and incurred soft page faults by reusing a single set of vertex/index buffers big enough for the biggest mesh - Preemptively detect if smearing would result in monochrome texture to avoid useless work - Shrink smeared monochrome textures during the baking process for huge memory savings - Move UV smearing in worker threads to avoid blocking the game thread - Required shaders are now built asynchronously - Add progress bar for material baking - 28m23 [at] 150 GB RAM -> 2m14s [at] 45 GB RAM for 6 channels [at] 512x512 when baking materials on ProxyLOD for DATASET-0008a with DDC empty #rb Jurre.deBaare, Sebastien.Lussier CL 10516258 by danny.couture Optimize Material Baking (Phase 2) - Implement pipelining with staging buffers to avoid GPU stalls when reading from render targets - Reuse the same prepared FMeshBatch instead of rebuilding it for each draw pass - Prepare the RenderItem in advance on other threads to reduce work on the game thread - Move the staging surface copy out of the render thread - Small vertex and index buffers are not reused to avoid dependency locks when mapping them - Fix bug in Canvas Flush_RenderThread found while running HLOD rebuild commandlet on Fortnite - Delete old and unused MaterialBakingModule.h from public files - 4m44s -> 59s for baking 6 channel [at] 1024x1024 when baking materials on ProxyLOD for DATASET-0008a with shaders already compiled - Time spent in Material Baking when rebuilding all HLOD on Apollo_POI_Large_HLOD (Phase 1 + 2 combined) - 10m18s -> 2m36s for a first rebuild all in editor with no shaders in DDC (cold) - 1m23s -> 20s for a second rebuild all in editor (warm) #rb Jeremy.Moore, Sebastien.Lussier CL 11135986 by sebastien.lussier Optimized mesh merging * Added DeletePolygons() & DeleteTriangles methods to FMeshDescription which rely on TSets<> instead of performing costly TArray::AddUnique() calls() * Parallelized UV generation and avoided duplicate processing of the same mesh+lod pairs * Optimized FMeshDescriptionOperations::GenerateUniqueUVsForStaticMesh() * Goes from 100s to 10s in my test case #rb danny.couture, jeanfrancois.dube, richard.talbotwatkin #ROBOMERGE-OWNER: sebastien.lussier #ROBOMERGE-AUTHOR: sebastien.lussier #ROBOMERGE-SOURCE: CL 11206337 via CL 11206341 via CL 11206346 #ROBOMERGE-BOT: (v643-11205221) [CL 11206493 by sebastien lussier in Main branch]
2020-02-03 11:08:35 -05:00
{
TRACE_CPUPROFILER_EVENT_SCOPE(ProcessRawMeshes);
DataTracker.ProcessRawMeshes();
}
// Merge sockets
TMap<FName, UStaticMeshSocket*> MergedSockets;
if (InSettings.bMergeMeshSockets)
{
const FTransform PivotTransform = FTransform(MergedAssetPivot);
for (UPrimitiveComponent* PrimitiveComponent : ComponentsToMerge)
{
if (UStaticMeshComponent* StaticMeshComponent = Cast<UStaticMeshComponent>(PrimitiveComponent))
{
if (UStaticMesh* StaticMesh = StaticMeshComponent->GetStaticMesh())
{
for (UStaticMeshSocket* Socket : StaticMesh->Sockets)
{
if (Socket)
{
UStaticMeshSocket* SocketCopy = DuplicateObject<UStaticMeshSocket>(Socket, nullptr);
// Fix name - rename if duplicates are found
FString PlainName = SocketCopy->SocketName.GetPlainNameString();
int32 CurrentNumber = SocketCopy->SocketName.GetNumber();
while (MergedSockets.Contains(SocketCopy->SocketName))
{
SocketCopy->SocketName = FName(PlainName, CurrentNumber++);
}
// Fix transform - make relative to pivot
FTransform SocketTransformWorldSpace = StaticMeshComponent->GetSocketTransform(Socket->SocketName, RTS_World);
FTransform SocketTransformPivotSpace = SocketTransformWorldSpace.GetRelativeTransform(PivotTransform);
SocketCopy->RelativeLocation = SocketTransformPivotSpace.GetLocation();
SocketCopy->RelativeRotation = FRotator(SocketTransformPivotSpace.GetRotation());
SocketCopy->RelativeScale = SocketTransformPivotSpace.GetScale3D();
MergedSockets.Add(SocketCopy->SocketName, SocketCopy);
}
}
}
}
}
}
// Find all unique materials and remap section to unique materials
TArray<UMaterialInterface*> UniqueMaterials;
TMap<UMaterialInterface*, UMaterialInterface*> CollapsedMaterialMap;
for (int32 SectionIndex = 0; SectionIndex < DataTracker.NumberOfUniqueSections(); ++SectionIndex)
{
// Unique index for material
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]);
}
// Retrieve physics data
UBodySetup* BodySetupSource = nullptr;
TArray<FKAggregateGeom> PhysicsGeometry;
if (InSettings.bMergePhysicsData)
{
RetrievePhysicsData(ComponentsToMerge, PhysicsGeometry, BodySetupSource);
}
TMultiMap< FMeshLODKey, MaterialRemapPair > OutputMaterialsMap;
// If the user wants to merge materials into a single one
if (bMergeMaterialData && UniqueMaterials.Num() != 0)
{
// Create the merged material
FFlattenMaterial FlattenMaterial;
CreateMergedMaterial(DataTracker, InSettings, StaticMeshComponentsToMerge, Adapters, UniqueMaterials, CollapsedMaterialMap, OutputMaterialsMap, bMergeAllLODs, bMergeMaterialData, MergedAssetPivot, FlattenMaterial);
if (FlattenMaterial.HasData())
{
// Don't recreate render states with the material update context as we will manually do it through
// the FStaticMeshComponentRecreateRenderStateContext used below at the creation of the static mesh.
FMaterialUpdateContext MaterialUpdateContext(FMaterialUpdateContext::EOptions::Default & ~FMaterialUpdateContext::EOptions::RecreateRenderStates);
UMaterialInterface* MergedMaterial = CreateProxyMaterial(InBasePackageName, MergedAssetPackageName, InBaseMaterial, InOuter, InSettings, FlattenMaterial, OutAssetsToSync, &MaterialUpdateContext);
UniqueMaterials.Empty(1);
UniqueMaterials.Add(MergedMaterial);
FSectionInfo NewSection;
NewSection.Material = MergedMaterial;
NewSection.EnabledProperties.Add(GET_MEMBER_NAME_CHECKED(FStaticMeshSection, bCastShadow));
DataTracker.AddBakedMaterialSection(NewSection);
for (IMeshMergeExtension* Extension : MeshMergeExtensions)
{
Extension->OnCreatedProxyMaterial(StaticMeshComponentsToMerge, MergedMaterial);
}
}
}
// Create the merged mesh
TArray<FMeshDescription> MergedRawMeshes;
CreateMergedRawMeshes(DataTracker, InSettings, StaticMeshComponentsToMerge, UniqueMaterials, CollapsedMaterialMap, OutputMaterialsMap, bMergeAllLODs, bMergeMaterialData, MergedAssetPivot, MergedRawMeshes);
// Notify listeners that our merged mesh was created
for (IMeshMergeExtension* Extension : MeshMergeExtensions)
{
Extension->OnCreatedMergedRawMeshes(StaticMeshComponentsToMerge, DataTracker, MergedRawMeshes);
}
// Populate mesh section map
FMeshSectionInfoMap SectionInfoMap;
for (TConstLODIndexIterator Iterator = DataTracker.GetLODIndexIterator(); Iterator; ++Iterator)
{
const int32 LODIndex = *Iterator;
TArray<uint32> UniqueMaterialIndices;
const FMeshDescription& TargetRawMesh = MergedRawMeshes[LODIndex];
uint32 MaterialIndex = 0;
for (FPolygonGroupID PolygonGroupID : TargetRawMesh.PolygonGroups().GetElementIDs())
{
//Skip empty group
if (TargetRawMesh.GetPolygonGroupPolygonIDs(PolygonGroupID).Num() > 0)
{
if (PolygonGroupID.GetValue() < DataTracker.NumberOfUniqueSections())
{
UniqueMaterialIndices.AddUnique(PolygonGroupID.GetValue());
}
else
{
UniqueMaterialIndices.AddUnique(MaterialIndex);
}
MaterialIndex++;
}
}
UniqueMaterialIndices.Sort();
for (int32 Index = 0; Index < UniqueMaterialIndices.Num(); ++Index)
{
const int32 SectionIndex = UniqueMaterialIndices[Index];
// unclear when this would not be the case, but it seems to be able to occur
if (SectionIndex < DataTracker.NumberOfUniqueSections())
{
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.Num() == 1 ? 0 : UniqueMaterials.IndexOfByKey(CollapsedMaterialMap[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)
{
Copying //UE4/Dev-Physics to //UE4/Dev-Main (Source: //UE4/Dev-Physics @ 4242698) #rb none #lockdown Nick.Penwarden ============================ MAJOR FEATURES & CHANGES ============================ Change 4023283 by Michael.Lentine Fix memory leak. Change 4024243 by Michael.Lentine Add debugging output code from github #4533. #jira ue-55764 Change 4026362 by Michael.Lentine Merged github #3704. #jira ue-463394 Change 4026545 by Michael.Lentine Fix ordering of collision settings changed callback #jira ue-50475 Change 4026609 by Michael.Lentine Fix crash in destruction for when world is not valid #jira ue-53989 Change 4026786 by Michael.Lentine Merging github #4632 to fix memory leak. #jira ue-57255 Change 4027293 by Michael.Lentine Integrate github #4338. #jira ue-53497 Change 4033517 by Michael.Lentine Fix collision body creation for spline merging. #jira ue-53956 Change 4039750 by Michael.Lentine Add basic error message if cooking fails. Change 4040210 by Michael.Lentine Check for nullptr Change 4098887 by Michael.Lentine Fix warnings. Change 4103511 by Michael.Lentine Prevent crash when BodyInstance is invalid. Change 4117826 by Michael.Lentine Fix check for body being fixed. Change 4122307 by Benn.Gallagher PS4/clang build fixes Change 4124479 by Benn.Gallagher Fix non-portable filename used as an include (Linux editor build CIS error) Change 4125450 by Benn.Gallagher Fixup Ocean Change 4127210 by Michael.Lentine Update the PreviousBoneTransforms array when setting transforms in DestructibleComponent #jira ue-58813 Change 4127309 by Benn.Gallagher Fix Win32 shipping builds Change 4134570 by Michael.Lentine Missed fixes for WITH_UEPHYSICS. Change 4134585 by Michael.Lentine Missed a few more files. Change 4134670 by Michael.Lentine Update formatting. Change 4134671 by Michael.Lentine More formatting. Change 4150615 by Benn.Gallagher Moved immediate mode into engine, as it is now depended on by the physics engine. Change 4150680 by Benn.Gallagher Missed file Change 4150980 by Benn.Gallagher Rename kinematic target for immediate mode to avoid ambiguous symbols in engine Change 4151400 by Brice.Criswell Apeiron Levelset initilization issue. ---- Change 4157880 by Benn.Gallagher More fixing unresolved template specialisations for FN editor. Change 4159128 by Michael.Lentine Compile fixes Change 4159786 by Brice.Criswell Apeiron Levelset curvature initialization fix, clamps out of bounds phi values to phi[i]. ------ Change 4160382 by Michael.Lentine Fix node initialization Change 4160463 by Brice.Criswell Apeiron Levelset index fix. ---- Change 4161425 by Benn.Gallagher Added package, class and struct redirects for moving immediate physics into engine. Change 4164195 by Brice.Criswell GeometryCollection : Code review updates - Removed typedef for GeometryCollection::ManagedArray<T> - Renamed Enumerations to begin with E prefix, retyped to be uint8. - Removed EArrayScoipe::FScopeNone, now defautls to FScopeShared - Formatted type modifiers to follow UE4 coding standard. - Derived the ManagedArrayBase from FNonCopyable - Disabled TManagedArrays copy constructor and assignment operator. - Converted most accessors on GeometryCollection to TSharedRef. - Added .inl style definitions to simplify the management of the ManagedArrayTypes ----- Change 4164235 by Brice.Criswell GeometryCollection : Added New Files - Added the ManagedArrayTypes files. ---- Change 4164309 by Brice.Criswell GeometryCollection : Moved the initialization of the RigidBodyIdArray and CenterOfMassArray into the WITH_APEIRON definition. ----- Change 4166133 by Brice.Criswell GeometryCollection Added GeometryCollectionEdit class to protect access to the rest and dynamic collections. ----- Change 4171540 by Michael.Lentine Fix reset #robomerge destruction Change 4171912 by Michael.Lentine Rename BVHParticles #robomerge destruction Change 4172445 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ---- Change 4172623 by Brice.Criswell GeometryCollection Debugging ToString to inspect the GeometryCollection ---- Change 4172711 by Michael.Lentine Add Immediate Path to Geometry Collection Change 4172778 by Michael.Lentine Update LL Interface to use Simulation type. #robomerge destruction Change 4172780 by Michael.Lentine Missed files #robomerge destruction Change 4173238 by Benn.Gallagher Missed file from last checkin Change 4173554 by Benn.Gallagher Few extra changes for const correctness and actor counts Change 4174153 by Benn.Gallagher Fixed non-unity build issue from Geom Collection. Change 4175355 by Brice.Criswell GeometryCollection Separated the GeometryCollection from USE_APEIRON flag. ----- Change 4175533 by Brice.Criswell GeometryCollection Defaulting Aperion to off. ----- Change 4175761 by Michael.Lentine Fix collisions. Change 4177105 by Benn.Gallagher Another geom collection CIS fix when running without PCHs Change 4177796 by Brice.Criswell GeometryCollection - Added parenting function to manage the BoneHierarchy Array - Split collection along yz-plane. ----- Change 4177989 by Brice.Criswell GeometryCollection - Moved Hierarchy and Transform array elements into base class TransformCollection - Renamed ParticleGroup to TransformGroup. ----- Change 4178826 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ---- Change 4178840 by Brice.Criswell Geometry Collection Removed FORCEINLINE from GeometryCollectionEdit.GetRestCollection ---- Change 4179493 by Brice.Criswell GeometryCollection New icons. ----- Change 4182428 by Brice.Criswell Build Configuration Apeiron configuration. - Modified bCompileApeiron to enable the compilation of the Apeiron plugin. - Added bUseApeiron to enable Apeiron in the physics interfaces. Changed PhysScene_Apeiron to enable when bCompileApeiron is enabled. Disabled the GeometryCollection* Plugins in the build. ------- Change 4185886 by Brice.Criswell GeometryCollection Renaming TransformGroup. --- Change 4186389 by michael.lentine Don't create in parallel for immediate mode. Change 4186457 by michael.lentine Hack to prevent crashing when Visible is nullptr. Change 4198872 by Brice.Criswell Apeiron Clustering changes - Clustering based on hierarchy's defined within the Geometry Collection ----- Change 4199861 by Brice.Criswell GeometryCollection Disable Apeiron in the Collection. ------ Change 4200089 by Brice.Criswell GeometryCollection Updated to enable Apeiron in the GeometryCollection when the bCompileAperion flag is enabled in the UnrealBuildTool. --- Change 4200333 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ----- Change 4202231 by Michael.Lentine Disable collisions between adjacent bodies connected by a joint. This typically would be specified by an artist but classic PhysX always does this uncondintionally so our clients are used to this. Change 4202748 by Michael.Lentine Fix 2015 compile. Change 4204528 by Michael.Lentine Disable Apeiron. Change 4206396 by Michael.Lentine Fix 2015 build. Static cast apparently is not an accetible conversion from uint32 to bool. #robomerge destruction Change 4206604 by Michael.Lentine Fix for using ccd and kinematic. #jira UE-61694 #robomerge destruction Change 4206711 by mason.seay Refreshed Set Angular Drive nodes to clear out orphan pins Change 4207286 by Brice.Criswell GeometryCollection Transform hierarchy evaluation within BoneHierarchy of the Collection. Parenting operations are implemented on an updated morphology using : ParentTransforms(UGeometryCollection* GeometryCollection, const int32 InsertAtIndex, const TArray<int32>& SelectedBones); To parent a new transform: int32 RootIndex << within len( TransformGroup ) or -1 for a non-parented node. int32 BoneIndex = Collection->AddElements(1, UGeometryCollection::TransformGroup); GeometryCollectionAlgo::ParentTransform(Collection, RootIndex, BoneIndex); Transform[BoneIndex] = <some transform within local space of the RootIndex> Default collections have all geometry not parented. The function EnsureSingleRoot was added to guarantee that the collection has at least one parent node. FGeometryCollectionCommands::EnsureSinglRoot(UGeometryCollection* RestCollection) Then matrices relative to the collections root are calculated using: GlobalMatrices(UGeometryCollection* GeometryCollection, TArray<FTransform> & Transforms); Added Damage Threshold to GeometryCollectionActor ------ Change 4208039 by Brice.Criswell GeometryCollection Fix for static include failure. --- Change 4208170 by Brice.Criswell GeometryCache SplitAlongYZ to support multiple levels and orientations. --- Change 4208174 by Michael.Lentine Avoid shadow warnings and switch logs to verbose instead of warnings. #robomerge destruction Change 4210255 by Benn.Gallagher Static analysis fixes Change 4210394 by Michael.Lentine Use correct particle type for updateconstraints. Change 4211153 by Brice.Criswell Apeiron Exposing friction and coefficient of restitution to the actor. ----- Change 4213034 by michael.lentine Rename bounding volume Change 4216783 by Michael.Lentine Committing cooking fix to Dev-Physics in order to get smoke tests running. Change 4218078 by Benn.Gallagher Fixed memory and TLS slot leak caused by previous change to physics scene cleanup while cooking #jira UE-61633 Change 4219206 by Michael.Lentine Use the adaptor to get the rotation. #jira ue-61748 Change 4220469 by Benn.Gallagher Fixed overlaps re-triggering on movement due to bad transform chaining from component to phys actor to shape #jira UE-61703 Change 4220538 by Benn.Gallagher Fixed PhysX errors when setting global transforms of kinematic and static objects. #jira none Change 4222138 by Michael.Lentine Update use of Vulkan on android. Change 4222139 by Michael.Lentine Update OculusHMD plugin to use correct vulkan search path. Change 4225740 by Michael.Lentine Integrate changes to update rotation and mass. Change 4225928 by michael.lentine Use more accurate collision point. Change 4226560 by michael.lentine Enable contact graph Change 4227397 by Michael.Lentine If we don't have a global scene we need to not detect collisions. #robomerge destruction Change 4227410 by Michael.Lentine Missing include #robomerge destruction Change 4228107 by Michael.Lentine Integrate static contact changes. Change 4228612 by michael.lentine Use more correct thresholding. Change 4228734 by Benn.Gallagher Getting LLImmediate high level stood up and simulating Implementation is incomplete, only what is required to get simple scenes simulating under immediate mode Change 4228748 by Benn.Gallagher Missed file from checkin Change 4228885 by Ori.Cohen Added base physics interface class to help provide default behavior and easily chain functionality together Change 4228992 by Ori.Cohen Fix cis Change 4229921 by Benn.Gallagher Fixed contact pre-filter performance regression Change 4230825 by Benn.Gallagher Moved WIP physics interfaces to Experimental/ folders Change 4230853 by Benn.Gallagher Fixup includes after moving WIP physics interfaces Change 4231414 by Michael.Lentine Use global namespace to avoid mac compile errors. #jira ue-62137 [CL 4242847 by Michael Lentine in Main branch]
2018-07-31 02:23:26 -04:00
FMeshMergeHelpers::TransformPhysicsGeometry(PivotTM, false, Geometry);
}
}
// 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)
{
// Output warning message
UE_LOG(LogMeshMerging, Warning, TEXT("Failed to find an available channel for Lightmap UVs. Lightmap UVs will not be generated."));
}
return TempChannel;
}
return (int32)INDEX_NONE;
}();
//
//Create merged mesh asset
//
MergedRawMeshes.SetNum(Algo::RemoveIf(MergedRawMeshes, [](const FMeshDescription& MeshDescription) { return MeshDescription.IsEmpty(); }));
const bool bContainsImposters = !ImposterComponents.IsEmpty();
const bool bContainsMergedMeshes = !MergedRawMeshes.IsEmpty();
if (bContainsMergedMeshes || bContainsImposters)
{
FString AssetName;
FString PackageName;
if (InBasePackageName.IsEmpty())
{
AssetName = TEXT("SM_MERGED_") + FPackageName::GetShortName(MergedAssetPackageName);
PackageName = FPackageName::GetLongPackagePath(MergedAssetPackageName) / AssetName;
}
else
{
AssetName = TEXT("SM_") + FPackageName::GetShortName(InBasePackageName);
PackageName = FPackageName::GetLongPackagePath(InBasePackageName) / AssetName;
}
UPackage* Package = InOuter;
if (Package == nullptr)
{
Package = CreatePackage( *PackageName);
check(Package);
Package->FullyLoad();
Package->Modify();
}
// Check that an asset of a different class does not already exist
{
UObject* ExistingObject = StaticFindObject( nullptr, Package, *AssetName);
if(ExistingObject && !ExistingObject->GetClass()->IsChildOf(UStaticMesh::StaticClass()))
{
// Change name of merged static mesh to avoid name collision
UPackage* ParentPackage = CreatePackage( *FPaths::GetPath(Package->GetPathName()));
ParentPackage->FullyLoad();
AssetName = MakeUniqueObjectName( ParentPackage, UStaticMesh::StaticClass(), *AssetName).ToString();
Package = CreatePackage( *(ParentPackage->GetPathName() / AssetName ));
check(Package);
Package->FullyLoad();
Package->Modify();
// Let user know name of merged static mesh has changed
UE_LOG(LogMeshMerging, Warning,
TEXT("Cannot create %s %s.%s\n")
TEXT("An object with the same fully qualified name but a different class already exists.\n")
TEXT("\tExisting Object: %s\n")
TEXT("The merged mesh will be named %s.%s"),
*UStaticMesh::StaticClass()->GetName(), *ExistingObject->GetOutermost()->GetPathName(), *ExistingObject->GetName(),
*ExistingObject->GetFullName(), *Package->GetPathName(), *AssetName);
}
}
FStaticMeshComponentRecreateRenderStateContext RecreateRenderStateContext(FindObject<UStaticMesh>(Package, *AssetName));
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->SetLightingGuid();
if (LightMapUVChannel != INDEX_NONE)
{
StaticMesh->SetLightMapResolution(InSettings.TargetLightMapResolution);
StaticMesh->SetLightMapCoordinateIndex(LightMapUVChannel);
}
// Ray tracing support
StaticMesh->bSupportRayTracing = InSettings.bSupportRayTracing;
TArray<UMaterialInterface*> ImposterMaterials;
FBox ImposterBounds(EForceInit::ForceInit);
for (int32 LODIndex = 0; LODIndex < MergedRawMeshes.Num(); ++LODIndex)
{
FMeshDescription& MergedMeshLOD = MergedRawMeshes[LODIndex];
if (MergedMeshLOD.Vertices().Num() > 0 || bContainsImposters)
{
FStaticMeshSourceModel& SrcModel = StaticMesh->AddSourceModel();
// Don't allow the engine to recalculate normals
SrcModel.BuildSettings.bRecomputeNormals = false;
SrcModel.BuildSettings.bRecomputeTangents = false;
SrcModel.BuildSettings.bRemoveDegenerates = false;
SrcModel.BuildSettings.bUseHighPrecisionTangentBasis = false;
SrcModel.BuildSettings.bUseFullPrecisionUVs = false;
SrcModel.BuildSettings.bBuildReversedIndexBuffer = false;
SrcModel.BuildSettings.bGenerateLightmapUVs = LightMapUVChannel != INDEX_NONE;
SrcModel.BuildSettings.MinLightmapResolution = InSettings.bComputedLightMapResolution ? DataTracker.GetLightMapDimension() : InSettings.TargetLightMapResolution;
SrcModel.BuildSettings.SrcLightmapIndex = 0;
SrcModel.BuildSettings.DstLightmapIndex = LightMapUVChannel != INDEX_NONE ? LightMapUVChannel : 0;
if(!InSettings.bAllowDistanceField)
{
SrcModel.BuildSettings.DistanceFieldResolutionScale = 0.0f;
}
if (bContainsImposters)
{
// Merge imposter meshes to rawmesh
FMeshMergeHelpers::MergeImpostersToMesh(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));
}
}
}
FMeshDescription* MeshDescription = StaticMesh->CreateMeshDescription(LODIndex, MergedMeshLOD);
UStaticMesh::FCommitMeshDescriptionParams CommitParams;
CommitParams.bUseHashAsGuid = true;
StaticMesh->CommitMeshDescription(LODIndex, CommitParams);
}
}
auto IsMaterialImportedNameUnique = [&StaticMesh](FName ImportedMaterialSlotName)
{
for (const FStaticMaterial& StaticMaterial : StaticMesh->GetStaticMaterials())
{
#if WITH_EDITOR
if (StaticMaterial.ImportedMaterialSlotName == ImportedMaterialSlotName)
#else
if (StaticMaterial.MaterialSlotName == ImportedMaterialSlotName)
#endif
{
return false;
}
}
return true;
};
for (UMaterialInterface* Material : UniqueMaterials)
{
if (Material && (!Material->IsAsset() && InOuter != GetTransientPackage()))
{
// MIDs are not assets, duplicate them and outer them to the static mesh.
if (UMaterialInstanceDynamic* MID = Cast<UMaterialInstanceDynamic>(Material))
{
Material = DuplicateObject<UMaterialInstanceDynamic>(MID, StaticMesh);
}
else
{
Material = nullptr; // do not save non-asset materials
}
}
//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->GetStaticMaterials().Add(FStaticMaterial(Material, MaterialSlotName));
}
for(UMaterialInterface* ImposterMaterial : ImposterMaterials)
{
//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->GetStaticMaterials().Add(FStaticMaterial(ImposterMaterial, MaterialSlotName));
}
if (InSettings.bMergePhysicsData)
{
StaticMesh->CreateBodySetup();
if (BodySetupSource)
{
StaticMesh->GetBodySetup()->CopyBodyPropertiesFrom(BodySetupSource);
}
StaticMesh->GetBodySetup()->AggGeom = FKAggregateGeom();
// Copy collision from the source meshes
for (const FKAggregateGeom& Geom : PhysicsGeometry)
{
StaticMesh->GetBodySetup()->AddCollisionFrom(Geom);
}
// Bake rotation into verts of convex hulls, so they scale correctly after rotation
for (FKConvexElem& ConvexElem : StaticMesh->GetBodySetup()->AggGeom.ConvexElems)
{
ConvexElem.BakeTransformToVerts();
}
}
// Add merged sockets
if (InSettings.bMergeMeshSockets)
{
for (auto& [SocketName, Socket] : MergedSockets)
{
Socket->Rename(nullptr, StaticMesh);
StaticMesh->AddSocket(Socket);
}
}
StaticMesh->GetSectionInfoMap().CopyFrom(SectionInfoMap);
StaticMesh->GetOriginalSectionInfoMap().CopyFrom(SectionInfoMap);
//Set the Imported version before calling the build
StaticMesh->ImportVersion = EImportStaticMeshVersion::LastVersion;
StaticMesh->SetLightMapResolution(InSettings.bComputedLightMapResolution ? DataTracker.GetLightMapDimension() : InSettings.TargetLightMapResolution);
// Nanite settings
StaticMesh->NaniteSettings = InSettings.NaniteSettings;
#if WITH_EDITOR
//If we are running the automation test
if (GIsAutomationTesting)
{
StaticMesh->BuildCacheAutomationTestGuid = FGuid::NewGuid();
}
#endif
// Ensure the new mesh is not referencing non standalone materials
FMeshMergeHelpers::FixupNonStandaloneMaterialReferences(StaticMesh);
if (ImposterBounds.IsValid)
{
const FBox StaticMeshBox = StaticMesh->GetBoundingBox();
const FBox CombinedBox = StaticMeshBox + ImposterBounds;
StaticMesh->SetPositiveBoundsExtension((CombinedBox.Max - StaticMeshBox.Max));
StaticMesh->SetNegativeBoundsExtension((StaticMeshBox.Min - CombinedBox.Min));
StaticMesh->CalculateExtendedBounds();
}
StaticMesh->PostEditChange();
OutAssetsToSync.Add(StaticMesh);
OutMergedActorLocation = MergedAssetPivot;
}
else
{
UE_LOG(LogMeshMerging, Display, TEXT("MergeComponentsToStaticMesh: Skipped creation of a static mesh asset as no input meshes were provided"));
}
}
void FMeshMergeUtilities::CreateMergedMaterial(FMeshMergeDataTracker& InDataTracker, const FMeshMergingSettings& InSettings, const TArray<UStaticMeshComponent*>& InStaticMeshComponentsToMerge, TArray<FStaticMeshComponentAdapter>& InAdapters, const TArray<UMaterialInterface*>& InUniqueMaterials, const TMap<UMaterialInterface*, UMaterialInterface*>& InCollapsedMaterialMap, TMultiMap<FMeshLODKey, MaterialRemapPair>& InOutputMaterialsMap, bool bInMergeAllLODs, bool bInMergeMaterialData, const FVector& InMergedAssetPivot, FFlattenMaterial& OutFlattenMaterial) const
{
OutFlattenMaterial.ReleaseData();
TArray<UPrimitiveComponent*> PrimitiveComponents;
Algo::Transform(InStaticMeshComponentsToMerge, PrimitiveComponents, [](UStaticMeshComponent* SMComponent) { return SMComponent; });
FMaterialProxySettings MaterialProxySettings = InSettings.MaterialSettings;
if (MaterialProxySettings.ResolveTexelDensity(PrimitiveComponents))
{
MaterialProxySettings.TextureSize = InDataTracker.GetTextureSizeFromTargetTexelDensity(MaterialProxySettings.TargetTexelDensityPerMeter);
MaterialProxySettings.TextureSizingType = ETextureSizingType::TextureSizingType_UseSingleTextureSize;
}
UMaterialOptions* MaterialOptions = PopulateMaterialOptions(MaterialProxySettings);
TGCObjectScopeGuard<UMaterialOptions> MaterialOptionsGCScopeGuard(MaterialOptions);
// Check each material to see if the shader actually uses vertex data and collect flags
TArray<TOptional<bool>> bMaterialUsesVertexData;
bMaterialUsesVertexData.SetNum(InUniqueMaterials.Num());
// Deferred call, as this may not be required by all code paths and is pretty costly to compute
auto DoesMaterialUsesVertexData = [&](const int32 InMaterialIndex)
{
if (!bMaterialUsesVertexData[InMaterialIndex].IsSet())
{
bMaterialUsesVertexData[InMaterialIndex] = DetermineMaterialVertexDataUsage(InUniqueMaterials[InMaterialIndex], MaterialOptions);
}
return bMaterialUsesVertexData[InMaterialIndex].GetValue();
};
// For each unique material calculate how 'important' they are
TArray<float> MaterialImportanceValues;
FMaterialUtilities::DetermineMaterialImportance(InUniqueMaterials, MaterialImportanceValues);
TArray<FMeshData> GlobalMeshSettings;
TArray<FMaterialData> GlobalMaterialSettings;
TArray<float> SectionMaterialImportanceValues;
TMap<EMaterialProperty, FIntPoint> PropertySizes;
for (const FPropertyEntry& Entry : MaterialOptions->Properties)
{
if (!Entry.bUseConstantValue && Entry.Property != MP_MAX)
{
PropertySizes.Add(Entry.Property, Entry.bUseCustomSize ? Entry.CustomSize : MaterialOptions->TextureSize);
}
}
// 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 = !bInMergeAllLODs && !InSettings.bReuseMeshLightmapUVs;
typedef TTuple<UStaticMesh*, int32> FMeshLODTuple;
typedef TFuture<TArray<FVector2D>> FUVComputeFuture;
TMap<FMeshLODTuple, FUVComputeFuture> MeshLODsTextureCoordinates;
TMap<int32, FMeshLODTuple> MeshDataAwaitingResults;
for (TConstRawMeshIterator RawMeshIterator = InDataTracker.GetConstRawMeshIterator(); RawMeshIterator; ++RawMeshIterator)
{
const FMeshLODKey& Key = RawMeshIterator.Key();
const FMeshDescription& RawMesh = RawMeshIterator.Value();
const bool bRequiresUniqueUVs = InDataTracker.DoesMeshLODRequireUniqueUVs(Key);
const FMeshDescription* MeshDescription = InDataTracker.GetRawMeshPtr(Key);
UStaticMeshComponent* Component = InStaticMeshComponentsToMerge[Key.GetMeshIndex()];
UStaticMesh* StaticMesh = Component->GetStaticMesh();
// Retrieve all sections and materials for key
TArray<SectionRemapPair> SectionRemapPairs;
InDataTracker.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;
TArray<int32>& SectionIndices = MaterialAndSectionIndices.FindOrAdd(InCollapsedMaterialMap.FindChecked(InDataTracker.GetMaterialForSectionIndex(UniqueIndex)));
SectionIndices.Add(SectionIndex);
}
for (TPair<UMaterialInterface*, TArray<int32>>& MaterialSectionIndexPair : MaterialAndSectionIndices)
{
UMaterialInterface* Material = MaterialSectionIndexPair.Key;
const int32 MaterialIndex = InUniqueMaterials.IndexOfByKey(Material);
const TArray<int32>& SectionIndices = MaterialSectionIndexPair.Value;
FMaterialData MaterialData;
MaterialData.Material = InCollapsedMaterialMap.FindChecked(Material);
MaterialData.PropertySizes = PropertySizes;
FMeshData NewMeshData;
const bool bUseMeshData = bGloballyRemapUVs || (InSettings.bUseVertexDataForBakingMaterial && (bRequiresUniqueUVs || DoesMaterialUsesVertexData(MaterialIndex)));
if (bUseMeshData)
{
NewMeshData.Mesh = Key.GetMesh();
NewMeshData.MeshDescription = MeshDescription;
NewMeshData.VertexColorHash = Key.GetVertexColorHash();
NewMeshData.bMirrored = Component->GetComponentTransform().GetDeterminant() < 0.0f;
NewMeshData.MaterialIndices = SectionIndices;
if (!Component->GetCustomPrimitiveData().Data.IsEmpty())
{
NewMeshData.PrimitiveData = FPrimitiveData();
NewMeshData.PrimitiveData->CustomPrimitiveData = &Component->GetCustomPrimitiveData();
}
}
auto CompareMaterialData = [&InSettings](const FMaterialData& LHS, const FMaterialData& RHS)
{
return InSettings.bMergeEquivalentMaterials ? FMaterialKey(LHS.Material) == FMaterialKey(RHS.Material) : LHS.Material == RHS.Material;
};
auto CompareCustomPrimitiveData = [](const FCustomPrimitiveData* LHS, const FCustomPrimitiveData* RHS)
{
// Return true if both are null, false if one of them is null - otherwise, compare content
return (!LHS && !RHS) ? true : (!LHS || !RHS) ? false : (*LHS == *RHS);
};
auto ComparePrimitiveData = [&CompareCustomPrimitiveData](const TOptional<FPrimitiveData>& LHS, const TOptional<FPrimitiveData>& RHS)
{
// Return true if both are null, false if one of them is null - otherwise, compare content
return (!LHS && !RHS) ? true : (!LHS || !RHS) ? false : CompareCustomPrimitiveData(LHS->CustomPrimitiveData, RHS->CustomPrimitiveData);
};
auto CompareMeshData = [&ComparePrimitiveData](const FMeshData& LHS, const FMeshData& RHS)
{
return (LHS.Mesh == RHS.Mesh) && (LHS.MaterialIndices == RHS.MaterialIndices) && (LHS.bMirrored == RHS.bMirrored) && (LHS.VertexColorHash == RHS.VertexColorHash) && ComparePrimitiveData(LHS.PrimitiveData, RHS.PrimitiveData);
};
// Find material & mesh pair
int32 MeshDataIndex = INDEX_NONE;
for (int32 GlobalMaterialSettingsIndex = 0; GlobalMaterialSettingsIndex < GlobalMaterialSettings.Num(); ++GlobalMaterialSettingsIndex)
{
if (CompareMaterialData(GlobalMaterialSettings[GlobalMaterialSettingsIndex], MaterialData) &&
CompareMeshData(GlobalMeshSettings[GlobalMaterialSettingsIndex], NewMeshData))
{
MeshDataIndex = GlobalMaterialSettingsIndex;
break;
}
}
// We've found a match, no need to process this mesh/material pair
if (MeshDataIndex == INDEX_NONE)
{
// We're processing a new pair
MeshDataIndex = GlobalMeshSettings.Num();
FMeshData& MeshData = GlobalMeshSettings.Emplace_GetRef(NewMeshData);
GlobalMaterialSettings.Add(MaterialData);
SectionMaterialImportanceValues.Add(MaterialImportanceValues[MaterialIndex]);
if (bUseMeshData)
{
// 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.
if (bRequiresUniqueUVs || MeshData.MeshDescription->VertexInstances().Num() > 0)
{
// Check if there are lightmap uvs available?
const int32 LightMapUVIndex = StaticMesh->GetLightMapCoordinateIndex();
TVertexInstanceAttributesConstRef<FVector2f> VertexInstanceUVs = FStaticMeshConstAttributes(*MeshData.MeshDescription).GetVertexInstanceUVs();
if (InSettings.bReuseMeshLightmapUVs && VertexInstanceUVs.GetNumElements() > 0 && VertexInstanceUVs.GetNumChannels() > LightMapUVIndex)
{
MeshData.TextureCoordinateIndex = LightMapUVIndex;
}
else
{
// Verify if we started an async task to generate UVs for this static mesh & LOD
FMeshLODTuple Tuple(Key.GetMesh(), Key.GetLODIndex());
if (!MeshLODsTextureCoordinates.Find(Tuple))
{
// No job found yet, fire an async task
MeshLODsTextureCoordinates.Add(Tuple, Async(EAsyncExecution::Thread, [MeshDescription, MaterialOptions, this]()
{
FStaticMeshOperations::FGenerateUVOptions GenerateUVOptions;
GenerateUVOptions.TextureResolution = MaterialOptions->TextureSize.GetMax();
GenerateUVOptions.bMergeTrianglesWithIdenticalAttributes = false;
GenerateUVOptions.UVMethod = GetUVGenerationMethodToUse();
TArray<FVector2D> UniqueTextureCoordinates;
FStaticMeshOperations::GenerateUV(*MeshDescription, GenerateUVOptions, UniqueTextureCoordinates);
if (GenerateUVOptions.UVMethod == FStaticMeshOperations::EGenerateUVMethod::Legacy)
{
ScaleTextureCoordinatesToBox(FBox2D(FVector2D::ZeroVector, FVector2D(1, 1)), UniqueTextureCoordinates);
}
return UniqueTextureCoordinates;
}));
}
// Keep track of the fact that this mesh is waiting for the UV computation to finish
MeshDataAwaitingResults.Add(MeshDataIndex, Tuple);
}
}
InAdapters[Key.GetMeshIndex()].ApplySettings(Key.GetLODIndex(), MeshData);
}
}
for (const auto& OriginalSectionIndex : SectionIndices)
{
InOutputMaterialsMap.Add(Key, MaterialRemapPair(OriginalSectionIndex, MeshDataIndex));
}
}
}
// Fetch results from the async UV computation tasks
for (auto MeshData : MeshDataAwaitingResults)
{
GlobalMeshSettings[MeshData.Key].CustomTextureCoordinates = MeshLODsTextureCoordinates[MeshData.Value].Get();
}
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]);
}
if (bGloballyRemapUVs)
{
// Adjust merge settings when merging for the unique UV/material baking pass
// The final merged mesh will use the original settings
FMeshMergingSettings RemapUVMergeSettings = InSettings;
// Keep vertex data in order to properly generate unique UVs
RemapUVMergeSettings.bBakeVertexDataToMesh = true;
// Keep all UVs as some channels might be needed to properly bake the material
for (EUVOutput& OutputUV : RemapUVMergeSettings.OutputUVs)
{
OutputUV = EUVOutput::OutputChannel;
}
TArray<FMeshDescription> MergedRawMeshes;
CreateMergedRawMeshes(InDataTracker, RemapUVMergeSettings, InStaticMeshComponentsToMerge, InUniqueMaterials, InCollapsedMaterialMap, InOutputMaterialsMap, false, false, InMergedAssetPivot, MergedRawMeshes);
// Create texture coords for the merged mesh
FStaticMeshOperations::FGenerateUVOptions GenerateUVOptions;
GenerateUVOptions.TextureResolution = MaterialOptions->TextureSize.GetMax();
GenerateUVOptions.bMergeTrianglesWithIdenticalAttributes = true;
GenerateUVOptions.UVMethod = GetUVGenerationMethodToUse();
TArray<FVector2D> GlobalTextureCoordinates;
bool bSuccess = FStaticMeshOperations::GenerateUV(MergedRawMeshes[0], GenerateUVOptions, GlobalTextureCoordinates);
if (bSuccess)
{
if (GenerateUVOptions.UVMethod == FStaticMeshOperations::EGenerateUVMethod::Legacy)
{
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 = InDataTracker.GetConstRawMeshIterator(); RawMeshIterator; ++RawMeshIterator)
{
const FMeshLODKey& Key = RawMeshIterator.Key();
const FMeshDescription& RawMesh = RawMeshIterator.Value();
// Build a local array for this raw mesh
TArray<FVector2D> UniqueTextureCoordinates;
UniqueTextureCoordinates.SetNumUninitialized(RawMesh.VertexInstances().Num());
for (FVector2D& UniqueTextureCoordinate : UniqueTextureCoordinates)
{
UniqueTextureCoordinate = GlobalTextureCoordinates[GlobalUVIndex++];
}
// copy to mesh data
for (FMeshData& MeshData : GlobalMeshSettings)
{
if (MeshData.MeshDescription == &RawMesh)
{
MeshData.CustomTextureCoordinates = UniqueTextureCoordinates;
}
}
}
// Dont smear borders as we will copy back non-pink pixels
for (FMaterialData& MaterialData : GlobalMaterialSettings)
{
MaterialData.bPerformBorderSmear = false;
}
}
else
{
UE_LOG(LogMeshMerging, Warning, TEXT("GenerateUV: Failed to pack UVs for static mesh"));
}
}
TArray<FFlattenMaterial> FlattenedMaterials;
// This scope ensures BakeOutputs is never used after TransferOutputToFlatMaterials
{
TArray<FBakeOutput> BakeOutputs;
IMaterialBakingModule& Module = FModuleManager::Get().LoadModuleChecked<IMaterialBakingModule>("MaterialBaking");
// If we're working with a new set of UVs, we can bake all materials directly to the same bake output
// as our remapped UVs for each mesh don't overlap.
if (bGloballyRemapUVs)
{
FBakeOutput& BakeOutput = BakeOutputs.Emplace_GetRef();
Module.BakeMaterials(MaterialSettingPtrs, MeshSettingPtrs, BakeOutput);
}
else
{
Module.BakeMaterials(MaterialSettingPtrs, MeshSettingPtrs, BakeOutputs);
}
// Append constant properties ?
TArray<FColor> ConstantData;
FIntPoint ConstantSize(1, 1);
for (const FPropertyEntry& Entry : MaterialOptions->Properties)
{
if (Entry.bUseConstantValue && Entry.Property != MP_MAX)
{
ConstantData.SetNum(1, EAllowShrinking::No);
ConstantData[0] = FLinearColor(Entry.ConstantValue, Entry.ConstantValue, Entry.ConstantValue).ToFColor(true);
for (FBakeOutput& Output : BakeOutputs)
{
Output.PropertyData.Add(Entry.Property, ConstantData);
Output.PropertySizes.Add(Entry.Property, ConstantSize);
}
}
}
TransferOutputToFlatMaterials(GlobalMaterialSettings, BakeOutputs, FlattenedMaterials);
}
if (!bGloballyRemapUVs)
{
// Try to optimize materials where possible
for (FFlattenMaterial& InMaterial : FlattenedMaterials)
{
FMaterialUtilities::OptimizeFlattenMaterial(InMaterial);
}
}
for (const FPropertyEntry& Entry : MaterialOptions->Properties)
{
if (Entry.Property != MP_MAX)
{
EFlattenMaterialProperties OldProperty = ToFlattenProperty(Entry.Property);
if (ensure(OldProperty != EFlattenMaterialProperties::NumFlattenMaterialProperties))
{
OutFlattenMaterial.SetPropertySize(OldProperty, Entry.bUseCustomSize ? Entry.CustomSize : MaterialOptions->TextureSize);
}
}
}
TArray<FUVOffsetScalePair> UVTransforms;
if (bGloballyRemapUVs)
{
// If we have globally remapped UVs we copy non-pink pixels over the dest texture rather than
// copying sub-charts
TArray<FBox2D> MaterialBoxes;
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, OutFlattenMaterial, UVTransforms);
static const FUVOffsetScalePair NoUVTransform = { FVector2D::Zero(), FVector2D::One() };
UVTransforms.Init(NoUVTransform, GlobalMaterialSettings.Num());
}
else
{
/** 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, OutFlattenMaterial, UVTransforms);
}
else
{
MergeFlattenedMaterials(FlattenedMaterials, InSettings.GutterSize, OutFlattenMaterial, UVTransforms);
}
}
// Adjust UVs
for (int32 ComponentIndex = 0; ComponentIndex < InStaticMeshComponentsToMerge.Num(); ++ComponentIndex)
{
TArray<uint32> ProcessedMaterials;
for (TPair<FMeshLODKey, MaterialRemapPair>& MappingPair : InOutputMaterialsMap)
{
if (MappingPair.Key.GetMeshIndex() == ComponentIndex && !ProcessedMaterials.Contains(MappingPair.Value.Key))
{
// Retrieve raw mesh data for this component and lod pair
FMeshDescription* RawMesh = InDataTracker.GetRawMeshPtr(MappingPair.Key);
FMeshData& MeshData = GlobalMeshSettings[MappingPair.Value.Value];
const FUVOffsetScalePair& UVTransform = UVTransforms[MappingPair.Value.Value];
const uint32 MaterialIndex = MappingPair.Value.Key;
ProcessedMaterials.Add(MaterialIndex);
if (RawMesh->Vertices().Num())
{
TVertexInstanceAttributesRef<FVector2f> VertexInstanceUVs = FStaticMeshAttributes(*RawMesh).GetVertexInstanceUVs();
int32 NumUVChannel = FMath::Min(VertexInstanceUVs.GetNumChannels(), (int32)MAX_MESH_TEXTURE_COORDS);
for (int32 UVChannelIdx = 0; UVChannelIdx < NumUVChannel; ++UVChannelIdx)
{
int32 VertexIndex = 0;
for (FVertexInstanceID VertexInstanceID : RawMesh->VertexInstances().GetElementIDs())
{
FVector2D UV = FVector2D(VertexInstanceUVs.Get(VertexInstanceID, UVChannelIdx));
if (UVChannelIdx == 0)
{
if (MeshData.CustomTextureCoordinates.Num())
{
UV = MeshData.CustomTextureCoordinates[VertexIndex];
}
else if (MeshData.TextureCoordinateIndex != 0)
{
check(MeshData.TextureCoordinateIndex < NumUVChannel);
UV = FVector2D(VertexInstanceUVs.Get(VertexInstanceID, MeshData.TextureCoordinateIndex));
}
}
const TArray<FPolygonID>& Polygons = RawMesh->GetVertexInstanceConnectedPolygons(VertexInstanceID);
for (FPolygonID PolygonID : Polygons)
{
FPolygonGroupID PolygonGroupID = RawMesh->GetPolygonPolygonGroup(PolygonID);
if (PolygonGroupID.GetValue() == MaterialIndex)
{
if (UVTransform.Value != FVector2D::ZeroVector)
{
VertexInstanceUVs.Set(VertexInstanceID, UVChannelIdx, FVector2f(UV * UVTransform.Value + UVTransform.Key)); // LWC_TODO: Precision loss
break;
}
}
}
VertexIndex++;
}
}
}
}
}
}
for (TRawMeshIterator Iterator = InDataTracker.GetRawMeshIterator(); Iterator; ++Iterator)
{
FMeshDescription& RawMesh = Iterator.Value();
// Reset material indexes
TMap<FPolygonGroupID, FPolygonGroupID> RemapPolygonGroups;
for (FPolygonGroupID PolygonGroupID : RawMesh.PolygonGroups().GetElementIDs())
{
RemapPolygonGroups.Add(PolygonGroupID, FPolygonGroupID(0));
}
RawMesh.RemapPolygonGroups(RemapPolygonGroups);
}
OutFlattenMaterial.UVChannel = INDEX_NONE;
}
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
{
TRACE_CPUPROFILER_EVENT_SCOPE(FMeshMergeUtilities::CreateMergedRawMeshes)
if (bInMergeAllLODs)
{
OutMergedRawMeshes.AddDefaulted(InDataTracker.GetNumLODsForMergedMesh());
for (TConstLODIndexIterator Iterator = InDataTracker.GetLODIndexIterator(); Iterator; ++Iterator)
{
// Find meshes for each lod
const int32 LODIndex = *Iterator;
FMeshDescription& MergedMesh = OutMergedRawMeshes[LODIndex];
FStaticMeshAttributes(MergedMesh).Register();
for (int32 ComponentIndex = 0; ComponentIndex < InStaticMeshComponentsToMerge.Num(); ++ComponentIndex)
{
int32 RetrievedLODIndex = LODIndex;
FMeshDescription* RawMeshPtr = InDataTracker.TryFindRawMeshForLOD(ComponentIndex, RetrievedLODIndex);
if (RawMeshPtr != nullptr)
{
InDataTracker.AddComponentToWedgeMapping(ComponentIndex, LODIndex, MergedMesh.VertexInstances().Num());
FStaticMeshOperations::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!
}
if(UniqueIndex == INDEX_NONE)
{
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;
for (int32 ChannelIdx = 0; ChannelIdx < FStaticMeshOperations::FAppendSettings::MAX_NUM_UV_CHANNELS; ++ChannelIdx)
{
AppendSettings.bMergeUVChannels[ChannelIdx] = InDataTracker.DoesUVChannelContainData(ChannelIdx, LODIndex) && InSettings.OutputUVs[ChannelIdx] == EUVOutput::OutputChannel;
}
FStaticMeshOperations::AppendMeshDescription(*RawMeshPtr, MergedMesh, AppendSettings);
}
}
//Cleanup the empty material to avoid empty section later
TArray<FPolygonGroupID> PolygonGroupToRemove;
for (FPolygonGroupID PolygonGroupID : MergedMesh.PolygonGroups().GetElementIDs())
{
if (MergedMesh.GetPolygonGroupPolygonIDs(PolygonGroupID).Num() < 1)
{
PolygonGroupToRemove.Add(PolygonGroupID);
}
}
for (FPolygonGroupID PolygonGroupID : PolygonGroupToRemove)
{
MergedMesh.DeletePolygonGroup(PolygonGroupID);
}
}
}
else
{
FMeshDescription& MergedMesh = OutMergedRawMeshes.AddDefaulted_GetRef();
FStaticMeshAttributes(MergedMesh).Register();
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());
FStaticMeshOperations::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!
}
//Fallback
if(UniqueIndex == INDEX_NONE)
{
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;
for (int32 ChannelIdx = 0; ChannelIdx < FStaticMeshOperations::FAppendSettings::MAX_NUM_UV_CHANNELS; ++ChannelIdx)
{
AppendSettings.bMergeUVChannels[ChannelIdx] = InDataTracker.DoesUVChannelContainData(ChannelIdx, LODIndex) && InSettings.OutputUVs[ChannelIdx] == EUVOutput::OutputChannel;
}
FStaticMeshOperations::AppendMeshDescription(*RawMeshPtr, MergedMesh, AppendSettings);
}
}
}
}
void FMeshMergeUtilities::MergeComponentsToInstances(const TArray<UPrimitiveComponent*>& ComponentsToMerge, UWorld* World, ULevel* Level, const FMeshInstancingSettings& InSettings, bool bActuallyMerge /*= true*/, bool bReplaceSourceActors /* = false */, FText* OutResultsText /*= nullptr*/) const
{
auto HasInstanceVertexColors = [](UStaticMeshComponent* StaticMeshComponent)
{
for (const FStaticMeshComponentLODInfo& CurrentLODInfo : StaticMeshComponent->LODData)
{
if(CurrentLODInfo.OverrideVertexColors != nullptr || CurrentLODInfo.PaintedVertices.Num() > 0)
{
return true;
}
}
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(false);
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)
{
UInstancedStaticMeshComponent* NewComponent = nullptr;
NewComponent = (UInstancedStaticMeshComponent*)ActorEntry.MergedActor->FindComponentByClass(InSettings.ISMComponentToUse.Get());
if (NewComponent && NewComponent->PerInstanceSMData.Num() > 0)
{
NewComponent = nullptr;
}
if (NewComponent == nullptr)
{
NewComponent = NewObject<UInstancedStaticMeshComponent>(ActorEntry.MergedActor, InSettings.ISMComponentToUse.Get(), NAME_None, RF_Transactional);
NewComponent->bHasPerInstanceHitProxies = true;
if (ActorEntry.MergedActor->GetRootComponent())
{
// Attach to root if we already have one
NewComponent->AttachToComponent(ActorEntry.MergedActor->GetRootComponent(), FAttachmentTransformRules::KeepRelativeTransform);
}
else
{
// Make a new root if we dont have a root already
ActorEntry.MergedActor->SetRootComponent(NewComponent);
}
// Take 'instanced' ownership so it persists with this actor
ActorEntry.MergedActor->RemoveOwnedComponent(NewComponent);
NewComponent->CreationMethod = EComponentCreationMethod::Instance;
ActorEntry.MergedActor->AddOwnedComponent(NewComponent);
}
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);
FISMComponentBatcher ISMComponentBatcher;
ISMComponentBatcher.Append(ComponentEntry.OriginalComponents);
ISMComponentBatcher.InitComponent(NewComponent);
NewComponent->RegisterComponent();
}
World->UpdateCullDistanceVolumes(ActorEntry.MergedActor);
}
// Now clean up our original actors
for(AActor* ActorToCleanUp : ActorsToCleanUp)
{
if (bReplaceSourceActors)
{
ActorToCleanUp->Destroy();
}
else
{
ActorToCleanUp->Modify();
ActorToCleanUp->bIsEditorOnlyActor = true;
ActorToCleanUp->SetHidden(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();
};
// Always change selection if we removed the source actors,
// Otherwise, allow selection change through notification
if (bReplaceSourceActors)
{
SelectActorsLambda();
}
else
{
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, const FFlattenMaterial& OutMaterial, TArray<UObject *>& OutAssetsToSync, FMaterialUpdateContext* InMaterialUpdateContext) const
{
// Create merged material asset
FString MaterialAssetName;
FString MaterialPackageName;
if (InBasePackageName.IsEmpty())
{
MaterialAssetName = FPackageName::GetShortName(MergedAssetPackageName);
MaterialPackageName = FPackageName::GetLongPackagePath(MergedAssetPackageName) + TEXT("/");
}
else
{
MaterialAssetName = FPackageName::GetShortName(InBasePackageName);
MaterialPackageName = FPackageName::GetLongPackagePath(InBasePackageName) + TEXT("/");
}
UPackage* MaterialPackage = InOuter;
if (MaterialPackage == nullptr)
{
MaterialPackage = CreatePackage( *(MaterialPackageName + MaterialAssetName));
check(MaterialPackage);
MaterialPackage->FullyLoad();
MaterialPackage->Modify();
}
UMaterialInstanceConstant* MergedMaterial = FMaterialUtilities::CreateFlattenMaterialInstance(MaterialPackage, InSettings.MaterialSettings, InBaseMaterial, OutMaterial, MaterialPackageName, MaterialAssetName, OutAssetsToSync, InMaterialUpdateContext);
// Set material static lighting usage flag if project has static lighting enabled
if (IsStaticLightingAllowed())
{
MergedMaterial->CheckMaterialUsage(MATUSAGE_StaticLighting);
}
return MergedMaterial;
}
void FMeshMergeUtilities::RetrievePhysicsData(const TArray<UPrimitiveComponent*>& ComponentsToMerge, TArray<FKAggregateGeom>& InOutPhysicsGeometry, UBodySetup*& OutBodySetupSource) const
{
InOutPhysicsGeometry.AddDefaulted(ComponentsToMerge.Num());
for (int32 ComponentIndex = 0, PhysicsGeometryIndex = 0; ComponentIndex < ComponentsToMerge.Num(); ++ComponentIndex)
{
UPrimitiveComponent* PrimComp = ComponentsToMerge[ComponentIndex];
UBodySetup* BodySetup = nullptr;
FTransform ComponentToWorld = FTransform::Identity;
auto ExtractPhysicGeometry = [&BodySetup, &ComponentToWorld, &OutBodySetupSource, &InOutPhysicsGeometry, PrimComp](int32 PhysicsIndex) {
USplineMeshComponent* SplineMeshComponent = Cast<USplineMeshComponent>(PrimComp);
FMeshMergeHelpers::ExtractPhysicsGeometry(BodySetup, ComponentToWorld, SplineMeshComponent != nullptr, InOutPhysicsGeometry[PhysicsIndex]);
if (SplineMeshComponent)
{
FMeshMergeHelpers::PropagateSplineDeformationToPhysicsGeometry(SplineMeshComponent, InOutPhysicsGeometry[PhysicsIndex]);
}
// We will use first valid BodySetup as a source of physics settings
if (OutBodySetupSource == nullptr)
{
OutBodySetupSource = BodySetup;
}
};
if (UInstancedStaticMeshComponent* ISMComp = Cast<UInstancedStaticMeshComponent>(PrimComp))
{
const int32 NumberOfInstances = ISMComp->PerInstanceSMData.Num();
const UStaticMesh* SrcMesh = ISMComp->GetStaticMesh();
if (NumberOfInstances > 1)
{
InOutPhysicsGeometry.AddDefaulted(NumberOfInstances - 1);
}
if (SrcMesh)
{
BodySetup = SrcMesh->GetBodySetup();
}
for (const FInstancedStaticMeshInstanceData& InstanceData : ISMComp->PerInstanceSMData)
{
ComponentToWorld = FTransform(InstanceData.Transform) * ISMComp->GetComponentToWorld();
ExtractPhysicGeometry(PhysicsGeometryIndex++);
}
}
else if (UStaticMeshComponent* StaticMeshComp = Cast<UStaticMeshComponent>(PrimComp))
{
UStaticMesh* SrcMesh = StaticMeshComp->GetStaticMesh();
if (SrcMesh)
{
BodySetup = SrcMesh->GetBodySetup();
}
ComponentToWorld = StaticMeshComp->GetComponentToWorld();
ExtractPhysicGeometry(PhysicsGeometryIndex++);
}
else if (UShapeComponent* ShapeComp = Cast<UShapeComponent>(PrimComp))
{
BodySetup = ShapeComp->GetBodySetup();
ComponentToWorld = ShapeComp->GetComponentToWorld();
ExtractPhysicGeometry(PhysicsGeometryIndex++);
}
Copying //UE4/Dev-Physics to //UE4/Dev-Main (Source: //UE4/Dev-Physics @ 4242698) #rb none #lockdown Nick.Penwarden ============================ MAJOR FEATURES & CHANGES ============================ Change 4023283 by Michael.Lentine Fix memory leak. Change 4024243 by Michael.Lentine Add debugging output code from github #4533. #jira ue-55764 Change 4026362 by Michael.Lentine Merged github #3704. #jira ue-463394 Change 4026545 by Michael.Lentine Fix ordering of collision settings changed callback #jira ue-50475 Change 4026609 by Michael.Lentine Fix crash in destruction for when world is not valid #jira ue-53989 Change 4026786 by Michael.Lentine Merging github #4632 to fix memory leak. #jira ue-57255 Change 4027293 by Michael.Lentine Integrate github #4338. #jira ue-53497 Change 4033517 by Michael.Lentine Fix collision body creation for spline merging. #jira ue-53956 Change 4039750 by Michael.Lentine Add basic error message if cooking fails. Change 4040210 by Michael.Lentine Check for nullptr Change 4098887 by Michael.Lentine Fix warnings. Change 4103511 by Michael.Lentine Prevent crash when BodyInstance is invalid. Change 4117826 by Michael.Lentine Fix check for body being fixed. Change 4122307 by Benn.Gallagher PS4/clang build fixes Change 4124479 by Benn.Gallagher Fix non-portable filename used as an include (Linux editor build CIS error) Change 4125450 by Benn.Gallagher Fixup Ocean Change 4127210 by Michael.Lentine Update the PreviousBoneTransforms array when setting transforms in DestructibleComponent #jira ue-58813 Change 4127309 by Benn.Gallagher Fix Win32 shipping builds Change 4134570 by Michael.Lentine Missed fixes for WITH_UEPHYSICS. Change 4134585 by Michael.Lentine Missed a few more files. Change 4134670 by Michael.Lentine Update formatting. Change 4134671 by Michael.Lentine More formatting. Change 4150615 by Benn.Gallagher Moved immediate mode into engine, as it is now depended on by the physics engine. Change 4150680 by Benn.Gallagher Missed file Change 4150980 by Benn.Gallagher Rename kinematic target for immediate mode to avoid ambiguous symbols in engine Change 4151400 by Brice.Criswell Apeiron Levelset initilization issue. ---- Change 4157880 by Benn.Gallagher More fixing unresolved template specialisations for FN editor. Change 4159128 by Michael.Lentine Compile fixes Change 4159786 by Brice.Criswell Apeiron Levelset curvature initialization fix, clamps out of bounds phi values to phi[i]. ------ Change 4160382 by Michael.Lentine Fix node initialization Change 4160463 by Brice.Criswell Apeiron Levelset index fix. ---- Change 4161425 by Benn.Gallagher Added package, class and struct redirects for moving immediate physics into engine. Change 4164195 by Brice.Criswell GeometryCollection : Code review updates - Removed typedef for GeometryCollection::ManagedArray<T> - Renamed Enumerations to begin with E prefix, retyped to be uint8. - Removed EArrayScoipe::FScopeNone, now defautls to FScopeShared - Formatted type modifiers to follow UE4 coding standard. - Derived the ManagedArrayBase from FNonCopyable - Disabled TManagedArrays copy constructor and assignment operator. - Converted most accessors on GeometryCollection to TSharedRef. - Added .inl style definitions to simplify the management of the ManagedArrayTypes ----- Change 4164235 by Brice.Criswell GeometryCollection : Added New Files - Added the ManagedArrayTypes files. ---- Change 4164309 by Brice.Criswell GeometryCollection : Moved the initialization of the RigidBodyIdArray and CenterOfMassArray into the WITH_APEIRON definition. ----- Change 4166133 by Brice.Criswell GeometryCollection Added GeometryCollectionEdit class to protect access to the rest and dynamic collections. ----- Change 4171540 by Michael.Lentine Fix reset #robomerge destruction Change 4171912 by Michael.Lentine Rename BVHParticles #robomerge destruction Change 4172445 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ---- Change 4172623 by Brice.Criswell GeometryCollection Debugging ToString to inspect the GeometryCollection ---- Change 4172711 by Michael.Lentine Add Immediate Path to Geometry Collection Change 4172778 by Michael.Lentine Update LL Interface to use Simulation type. #robomerge destruction Change 4172780 by Michael.Lentine Missed files #robomerge destruction Change 4173238 by Benn.Gallagher Missed file from last checkin Change 4173554 by Benn.Gallagher Few extra changes for const correctness and actor counts Change 4174153 by Benn.Gallagher Fixed non-unity build issue from Geom Collection. Change 4175355 by Brice.Criswell GeometryCollection Separated the GeometryCollection from USE_APEIRON flag. ----- Change 4175533 by Brice.Criswell GeometryCollection Defaulting Aperion to off. ----- Change 4175761 by Michael.Lentine Fix collisions. Change 4177105 by Benn.Gallagher Another geom collection CIS fix when running without PCHs Change 4177796 by Brice.Criswell GeometryCollection - Added parenting function to manage the BoneHierarchy Array - Split collection along yz-plane. ----- Change 4177989 by Brice.Criswell GeometryCollection - Moved Hierarchy and Transform array elements into base class TransformCollection - Renamed ParticleGroup to TransformGroup. ----- Change 4178826 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ---- Change 4178840 by Brice.Criswell Geometry Collection Removed FORCEINLINE from GeometryCollectionEdit.GetRestCollection ---- Change 4179493 by Brice.Criswell GeometryCollection New icons. ----- Change 4182428 by Brice.Criswell Build Configuration Apeiron configuration. - Modified bCompileApeiron to enable the compilation of the Apeiron plugin. - Added bUseApeiron to enable Apeiron in the physics interfaces. Changed PhysScene_Apeiron to enable when bCompileApeiron is enabled. Disabled the GeometryCollection* Plugins in the build. ------- Change 4185886 by Brice.Criswell GeometryCollection Renaming TransformGroup. --- Change 4186389 by michael.lentine Don't create in parallel for immediate mode. Change 4186457 by michael.lentine Hack to prevent crashing when Visible is nullptr. Change 4198872 by Brice.Criswell Apeiron Clustering changes - Clustering based on hierarchy's defined within the Geometry Collection ----- Change 4199861 by Brice.Criswell GeometryCollection Disable Apeiron in the Collection. ------ Change 4200089 by Brice.Criswell GeometryCollection Updated to enable Apeiron in the GeometryCollection when the bCompileAperion flag is enabled in the UnrealBuildTool. --- Change 4200333 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ----- Change 4202231 by Michael.Lentine Disable collisions between adjacent bodies connected by a joint. This typically would be specified by an artist but classic PhysX always does this uncondintionally so our clients are used to this. Change 4202748 by Michael.Lentine Fix 2015 compile. Change 4204528 by Michael.Lentine Disable Apeiron. Change 4206396 by Michael.Lentine Fix 2015 build. Static cast apparently is not an accetible conversion from uint32 to bool. #robomerge destruction Change 4206604 by Michael.Lentine Fix for using ccd and kinematic. #jira UE-61694 #robomerge destruction Change 4206711 by mason.seay Refreshed Set Angular Drive nodes to clear out orphan pins Change 4207286 by Brice.Criswell GeometryCollection Transform hierarchy evaluation within BoneHierarchy of the Collection. Parenting operations are implemented on an updated morphology using : ParentTransforms(UGeometryCollection* GeometryCollection, const int32 InsertAtIndex, const TArray<int32>& SelectedBones); To parent a new transform: int32 RootIndex << within len( TransformGroup ) or -1 for a non-parented node. int32 BoneIndex = Collection->AddElements(1, UGeometryCollection::TransformGroup); GeometryCollectionAlgo::ParentTransform(Collection, RootIndex, BoneIndex); Transform[BoneIndex] = <some transform within local space of the RootIndex> Default collections have all geometry not parented. The function EnsureSingleRoot was added to guarantee that the collection has at least one parent node. FGeometryCollectionCommands::EnsureSinglRoot(UGeometryCollection* RestCollection) Then matrices relative to the collections root are calculated using: GlobalMatrices(UGeometryCollection* GeometryCollection, TArray<FTransform> & Transforms); Added Damage Threshold to GeometryCollectionActor ------ Change 4208039 by Brice.Criswell GeometryCollection Fix for static include failure. --- Change 4208170 by Brice.Criswell GeometryCache SplitAlongYZ to support multiple levels and orientations. --- Change 4208174 by Michael.Lentine Avoid shadow warnings and switch logs to verbose instead of warnings. #robomerge destruction Change 4210255 by Benn.Gallagher Static analysis fixes Change 4210394 by Michael.Lentine Use correct particle type for updateconstraints. Change 4211153 by Brice.Criswell Apeiron Exposing friction and coefficient of restitution to the actor. ----- Change 4213034 by michael.lentine Rename bounding volume Change 4216783 by Michael.Lentine Committing cooking fix to Dev-Physics in order to get smoke tests running. Change 4218078 by Benn.Gallagher Fixed memory and TLS slot leak caused by previous change to physics scene cleanup while cooking #jira UE-61633 Change 4219206 by Michael.Lentine Use the adaptor to get the rotation. #jira ue-61748 Change 4220469 by Benn.Gallagher Fixed overlaps re-triggering on movement due to bad transform chaining from component to phys actor to shape #jira UE-61703 Change 4220538 by Benn.Gallagher Fixed PhysX errors when setting global transforms of kinematic and static objects. #jira none Change 4222138 by Michael.Lentine Update use of Vulkan on android. Change 4222139 by Michael.Lentine Update OculusHMD plugin to use correct vulkan search path. Change 4225740 by Michael.Lentine Integrate changes to update rotation and mass. Change 4225928 by michael.lentine Use more accurate collision point. Change 4226560 by michael.lentine Enable contact graph Change 4227397 by Michael.Lentine If we don't have a global scene we need to not detect collisions. #robomerge destruction Change 4227410 by Michael.Lentine Missing include #robomerge destruction Change 4228107 by Michael.Lentine Integrate static contact changes. Change 4228612 by michael.lentine Use more correct thresholding. Change 4228734 by Benn.Gallagher Getting LLImmediate high level stood up and simulating Implementation is incomplete, only what is required to get simple scenes simulating under immediate mode Change 4228748 by Benn.Gallagher Missed file from checkin Change 4228885 by Ori.Cohen Added base physics interface class to help provide default behavior and easily chain functionality together Change 4228992 by Ori.Cohen Fix cis Change 4229921 by Benn.Gallagher Fixed contact pre-filter performance regression Change 4230825 by Benn.Gallagher Moved WIP physics interfaces to Experimental/ folders Change 4230853 by Benn.Gallagher Fixup includes after moving WIP physics interfaces Change 4231414 by Michael.Lentine Use global namespace to avoid mac compile errors. #jira ue-62137 [CL 4242847 by Michael Lentine in Main branch]
2018-07-31 02:23:26 -04:00
}
}
Copying //UE4/Dev-Physics to //UE4/Dev-Main (Source: //UE4/Dev-Physics @ 4242698) #rb none #lockdown Nick.Penwarden ============================ MAJOR FEATURES & CHANGES ============================ Change 4023283 by Michael.Lentine Fix memory leak. Change 4024243 by Michael.Lentine Add debugging output code from github #4533. #jira ue-55764 Change 4026362 by Michael.Lentine Merged github #3704. #jira ue-463394 Change 4026545 by Michael.Lentine Fix ordering of collision settings changed callback #jira ue-50475 Change 4026609 by Michael.Lentine Fix crash in destruction for when world is not valid #jira ue-53989 Change 4026786 by Michael.Lentine Merging github #4632 to fix memory leak. #jira ue-57255 Change 4027293 by Michael.Lentine Integrate github #4338. #jira ue-53497 Change 4033517 by Michael.Lentine Fix collision body creation for spline merging. #jira ue-53956 Change 4039750 by Michael.Lentine Add basic error message if cooking fails. Change 4040210 by Michael.Lentine Check for nullptr Change 4098887 by Michael.Lentine Fix warnings. Change 4103511 by Michael.Lentine Prevent crash when BodyInstance is invalid. Change 4117826 by Michael.Lentine Fix check for body being fixed. Change 4122307 by Benn.Gallagher PS4/clang build fixes Change 4124479 by Benn.Gallagher Fix non-portable filename used as an include (Linux editor build CIS error) Change 4125450 by Benn.Gallagher Fixup Ocean Change 4127210 by Michael.Lentine Update the PreviousBoneTransforms array when setting transforms in DestructibleComponent #jira ue-58813 Change 4127309 by Benn.Gallagher Fix Win32 shipping builds Change 4134570 by Michael.Lentine Missed fixes for WITH_UEPHYSICS. Change 4134585 by Michael.Lentine Missed a few more files. Change 4134670 by Michael.Lentine Update formatting. Change 4134671 by Michael.Lentine More formatting. Change 4150615 by Benn.Gallagher Moved immediate mode into engine, as it is now depended on by the physics engine. Change 4150680 by Benn.Gallagher Missed file Change 4150980 by Benn.Gallagher Rename kinematic target for immediate mode to avoid ambiguous symbols in engine Change 4151400 by Brice.Criswell Apeiron Levelset initilization issue. ---- Change 4157880 by Benn.Gallagher More fixing unresolved template specialisations for FN editor. Change 4159128 by Michael.Lentine Compile fixes Change 4159786 by Brice.Criswell Apeiron Levelset curvature initialization fix, clamps out of bounds phi values to phi[i]. ------ Change 4160382 by Michael.Lentine Fix node initialization Change 4160463 by Brice.Criswell Apeiron Levelset index fix. ---- Change 4161425 by Benn.Gallagher Added package, class and struct redirects for moving immediate physics into engine. Change 4164195 by Brice.Criswell GeometryCollection : Code review updates - Removed typedef for GeometryCollection::ManagedArray<T> - Renamed Enumerations to begin with E prefix, retyped to be uint8. - Removed EArrayScoipe::FScopeNone, now defautls to FScopeShared - Formatted type modifiers to follow UE4 coding standard. - Derived the ManagedArrayBase from FNonCopyable - Disabled TManagedArrays copy constructor and assignment operator. - Converted most accessors on GeometryCollection to TSharedRef. - Added .inl style definitions to simplify the management of the ManagedArrayTypes ----- Change 4164235 by Brice.Criswell GeometryCollection : Added New Files - Added the ManagedArrayTypes files. ---- Change 4164309 by Brice.Criswell GeometryCollection : Moved the initialization of the RigidBodyIdArray and CenterOfMassArray into the WITH_APEIRON definition. ----- Change 4166133 by Brice.Criswell GeometryCollection Added GeometryCollectionEdit class to protect access to the rest and dynamic collections. ----- Change 4171540 by Michael.Lentine Fix reset #robomerge destruction Change 4171912 by Michael.Lentine Rename BVHParticles #robomerge destruction Change 4172445 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ---- Change 4172623 by Brice.Criswell GeometryCollection Debugging ToString to inspect the GeometryCollection ---- Change 4172711 by Michael.Lentine Add Immediate Path to Geometry Collection Change 4172778 by Michael.Lentine Update LL Interface to use Simulation type. #robomerge destruction Change 4172780 by Michael.Lentine Missed files #robomerge destruction Change 4173238 by Benn.Gallagher Missed file from last checkin Change 4173554 by Benn.Gallagher Few extra changes for const correctness and actor counts Change 4174153 by Benn.Gallagher Fixed non-unity build issue from Geom Collection. Change 4175355 by Brice.Criswell GeometryCollection Separated the GeometryCollection from USE_APEIRON flag. ----- Change 4175533 by Brice.Criswell GeometryCollection Defaulting Aperion to off. ----- Change 4175761 by Michael.Lentine Fix collisions. Change 4177105 by Benn.Gallagher Another geom collection CIS fix when running without PCHs Change 4177796 by Brice.Criswell GeometryCollection - Added parenting function to manage the BoneHierarchy Array - Split collection along yz-plane. ----- Change 4177989 by Brice.Criswell GeometryCollection - Moved Hierarchy and Transform array elements into base class TransformCollection - Renamed ParticleGroup to TransformGroup. ----- Change 4178826 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ---- Change 4178840 by Brice.Criswell Geometry Collection Removed FORCEINLINE from GeometryCollectionEdit.GetRestCollection ---- Change 4179493 by Brice.Criswell GeometryCollection New icons. ----- Change 4182428 by Brice.Criswell Build Configuration Apeiron configuration. - Modified bCompileApeiron to enable the compilation of the Apeiron plugin. - Added bUseApeiron to enable Apeiron in the physics interfaces. Changed PhysScene_Apeiron to enable when bCompileApeiron is enabled. Disabled the GeometryCollection* Plugins in the build. ------- Change 4185886 by Brice.Criswell GeometryCollection Renaming TransformGroup. --- Change 4186389 by michael.lentine Don't create in parallel for immediate mode. Change 4186457 by michael.lentine Hack to prevent crashing when Visible is nullptr. Change 4198872 by Brice.Criswell Apeiron Clustering changes - Clustering based on hierarchy's defined within the Geometry Collection ----- Change 4199861 by Brice.Criswell GeometryCollection Disable Apeiron in the Collection. ------ Change 4200089 by Brice.Criswell GeometryCollection Updated to enable Apeiron in the GeometryCollection when the bCompileAperion flag is enabled in the UnrealBuildTool. --- Change 4200333 by Brice.Criswell Copying //UE4/Dev-Destruction to Dev-Physics (//UE4/Dev-Physics) ----- Change 4202231 by Michael.Lentine Disable collisions between adjacent bodies connected by a joint. This typically would be specified by an artist but classic PhysX always does this uncondintionally so our clients are used to this. Change 4202748 by Michael.Lentine Fix 2015 compile. Change 4204528 by Michael.Lentine Disable Apeiron. Change 4206396 by Michael.Lentine Fix 2015 build. Static cast apparently is not an accetible conversion from uint32 to bool. #robomerge destruction Change 4206604 by Michael.Lentine Fix for using ccd and kinematic. #jira UE-61694 #robomerge destruction Change 4206711 by mason.seay Refreshed Set Angular Drive nodes to clear out orphan pins Change 4207286 by Brice.Criswell GeometryCollection Transform hierarchy evaluation within BoneHierarchy of the Collection. Parenting operations are implemented on an updated morphology using : ParentTransforms(UGeometryCollection* GeometryCollection, const int32 InsertAtIndex, const TArray<int32>& SelectedBones); To parent a new transform: int32 RootIndex << within len( TransformGroup ) or -1 for a non-parented node. int32 BoneIndex = Collection->AddElements(1, UGeometryCollection::TransformGroup); GeometryCollectionAlgo::ParentTransform(Collection, RootIndex, BoneIndex); Transform[BoneIndex] = <some transform within local space of the RootIndex> Default collections have all geometry not parented. The function EnsureSingleRoot was added to guarantee that the collection has at least one parent node. FGeometryCollectionCommands::EnsureSinglRoot(UGeometryCollection* RestCollection) Then matrices relative to the collections root are calculated using: GlobalMatrices(UGeometryCollection* GeometryCollection, TArray<FTransform> & Transforms); Added Damage Threshold to GeometryCollectionActor ------ Change 4208039 by Brice.Criswell GeometryCollection Fix for static include failure. --- Change 4208170 by Brice.Criswell GeometryCache SplitAlongYZ to support multiple levels and orientations. --- Change 4208174 by Michael.Lentine Avoid shadow warnings and switch logs to verbose instead of warnings. #robomerge destruction Change 4210255 by Benn.Gallagher Static analysis fixes Change 4210394 by Michael.Lentine Use correct particle type for updateconstraints. Change 4211153 by Brice.Criswell Apeiron Exposing friction and coefficient of restitution to the actor. ----- Change 4213034 by michael.lentine Rename bounding volume Change 4216783 by Michael.Lentine Committing cooking fix to Dev-Physics in order to get smoke tests running. Change 4218078 by Benn.Gallagher Fixed memory and TLS slot leak caused by previous change to physics scene cleanup while cooking #jira UE-61633 Change 4219206 by Michael.Lentine Use the adaptor to get the rotation. #jira ue-61748 Change 4220469 by Benn.Gallagher Fixed overlaps re-triggering on movement due to bad transform chaining from component to phys actor to shape #jira UE-61703 Change 4220538 by Benn.Gallagher Fixed PhysX errors when setting global transforms of kinematic and static objects. #jira none Change 4222138 by Michael.Lentine Update use of Vulkan on android. Change 4222139 by Michael.Lentine Update OculusHMD plugin to use correct vulkan search path. Change 4225740 by Michael.Lentine Integrate changes to update rotation and mass. Change 4225928 by michael.lentine Use more accurate collision point. Change 4226560 by michael.lentine Enable contact graph Change 4227397 by Michael.Lentine If we don't have a global scene we need to not detect collisions. #robomerge destruction Change 4227410 by Michael.Lentine Missing include #robomerge destruction Change 4228107 by Michael.Lentine Integrate static contact changes. Change 4228612 by michael.lentine Use more correct thresholding. Change 4228734 by Benn.Gallagher Getting LLImmediate high level stood up and simulating Implementation is incomplete, only what is required to get simple scenes simulating under immediate mode Change 4228748 by Benn.Gallagher Missed file from checkin Change 4228885 by Ori.Cohen Added base physics interface class to help provide default behavior and easily chain functionality together Change 4228992 by Ori.Cohen Fix cis Change 4229921 by Benn.Gallagher Fixed contact pre-filter performance regression Change 4230825 by Benn.Gallagher Moved WIP physics interfaces to Experimental/ folders Change 4230853 by Benn.Gallagher Fixup includes after moving WIP physics interfaces Change 4231414 by Michael.Lentine Use global namespace to avoid mac compile errors. #jira ue-62137 [CL 4242847 by Michael Lentine in Main branch]
2018-07-31 02:23:26 -04:00
#undef LOCTEXT_NAMESPACE // "MeshMergeUtils"