1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
|
FPrimitiveSceneProxy* FStaticMeshComponentHelper::CreateSceneProxy(T& Component, FStaticMeshComponentHelper::ESceneProxyCreationError* OutError)
{
UStaticMesh* StaticMesh = Component.GetStaticMesh();
auto SetError = [OutError](FStaticMeshComponentHelper::ESceneProxyCreationError InError)
{
if (OutError)
{
*OutError = InError;
}
};
if constexpr (!bAssumeRenderDataIsReady)
{
if (StaticMesh == nullptr)
{
UE_LOG(LogStaticMesh, Verbose, TEXT("Skipping CreateSceneProxy for StaticMeshComponent %s (StaticMesh is null)"), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::InvalidMesh);
return nullptr;
}
// Prevent accessing the RenderData during async compilation. The RenderState will be recreated when compilation finishes.
if (StaticMesh->IsCompiling())
{
UE_LOG(LogStaticMesh, Verbose, TEXT("Skipping CreateSceneProxy for StaticMeshComponent %s (StaticMesh is not ready)"), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::MeshCompiling);
return nullptr;
}
if (StaticMesh->GetRenderData() == nullptr)
{
UE_LOG(LogStaticMesh, Verbose, TEXT("Skipping CreateSceneProxy for StaticMeshComponent %s (RenderData is null)"), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::InvalidMesh);
return nullptr;
}
if (!StaticMesh->GetRenderData()->IsInitialized())
{
UE_LOG(LogStaticMesh, Verbose, TEXT("Skipping CreateSceneProxy for StaticMeshComponent %s (RenderData is not initialized)"), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::InvalidMesh);
return nullptr;
}
}
else
{
check(StaticMesh);
check(!StaticMesh->IsCompiling());
check(StaticMesh->GetRenderData());
check(StaticMesh->GetRenderData()->IsInitialized());
}
EPSOPrecachePriority PSOPrecachePriority = GetStaticMeshComponentBoostPSOPrecachePriority();
if (Component.CheckPSOPrecachingAndBoostPriority(PSOPrecachePriority) && GetPSOPrecacheProxyCreationStrategy() == EPSOPrecacheProxyCreationStrategy::DelayUntilPSOPrecached)
{
UE_LOG(LogStaticMesh, Verbose, TEXT("Skipping CreateSceneProxy for StaticMeshComponent %s (Static mesh component PSOs are still compiling)"), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::WaitingPSOs);
return nullptr;
}
const bool bIsMaskingAllowed = Nanite::IsMaskingAllowed(Component.GetWorld(), Component.GetForceNaniteForMasked());
Nanite::FMaterialAudit NaniteMaterials{};
// Is Nanite supported, and is there built Nanite data for this static mesh?
const bool bUseNanite = Component.ShouldCreateNaniteProxy(&NaniteMaterials);
if (bUseNanite)
{
// Nanite is fully supported
return Component.CreateStaticMeshSceneProxy(NaniteMaterials, true);
}
// If we didn't get a proxy, but Nanite was enabled on the asset when it was built, evaluate proxy creation
if (Component.HasValidNaniteData())
{
if (NaniteMaterials.IsValid(bIsMaskingAllowed))
{
const bool bAllowProxyRender = Nanite::GetProxyRenderMode() == Nanite::EProxyRenderMode::Allow
#if WITH_EDITORONLY_DATA
// Check for specific case of static mesh editor "proxy toggle"
|| (Component.IsDisplayNaniteFallbackMesh() && Nanite::GetProxyRenderMode() == Nanite::EProxyRenderMode::AllowForDebugging)
#endif
;
if (!bAllowProxyRender) // Never render proxies
{
// We don't want to fall back to Nanite proxy rendering, so just make the mesh invisible instead.
return nullptr;
}
}
// Fall back to rendering Nanite proxy meshes with traditional static mesh scene proxies
FSceneInterface* Scene = Component.GetScene();
const EShaderPlatform ShaderPlatform = Scene ? Scene->GetShaderPlatform() : GMaxRHIShaderPlatform;
// TODO: handle Nanite representation being overriden using OnGetNaniteResources
// for now need to check UStaticMesh::HasValidNaniteData() directly here
const bool bFallbackGenerated = !StaticMesh->HasValidNaniteData() || StaticMesh->HasNaniteFallbackMesh(ShaderPlatform);
if (!bFallbackGenerated)
{
// TODO: automatically enable fallback on the static mesh asset?
UE_LOG(LogStaticMesh, Warning, TEXT("Unable to create a proxy for StaticMeshComponent [%s] because it doesn't have a fallback mesh."), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::InvalidMesh);
return nullptr;
}
}
// Validate the LOD resources here
const FStaticMeshLODResourcesArray& LODResources = StaticMesh->GetRenderData()->LODResources;
const int32 SMCurrentMinLOD = StaticMesh->GetMinLODIdx();
const int32 EffectiveMinLOD = Component.GetOverrideMinLOD() ? FMath::Max(Component.GetMinLOD(), SMCurrentMinLOD) : SMCurrentMinLOD;
if (LODResources.Num() == 0 || LODResources[FMath::Clamp<int32>(EffectiveMinLOD, 0, LODResources.Num() - 1)].VertexBuffers.StaticMeshVertexBuffer.GetNumVertices() == 0)
{
UE_LOG(LogStaticMesh, Verbose, TEXT("Skipping CreateSceneProxy for StaticMeshComponent %s (LOD problems)"), *UObjectHelper::GetFullNameIfAvailable(Component));
SetError(ESceneProxyCreationError::InvalidMesh);
return nullptr;
}
return Component.CreateStaticMeshSceneProxy(NaniteMaterials, false);
}
|