-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathSceneLODInfo.cs
More file actions
188 lines (158 loc) · 8.93 KB
/
Copy pathSceneLODInfo.cs
File metadata and controls
188 lines (158 loc) · 8.93 KB
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
using System.Collections.Generic;
using Arch.Core;
using DCL.LOD.Systems;
using ECS.StreamableLoading.AssetBundles;
using ECS.StreamableLoading.Common;
using System;
using UnityEngine;
using Utility;
namespace DCL.LOD.Components
{
public struct SceneLODInfo
{
//This is a sync method, so we can use a shared list
private static readonly List<Renderer> TEMP_RENDERERS = new (3);
public string id;
public LODCacheInfo metadata;
public AssetPromise<AssetBundleData, GetAssetBundleIntention> CurrentLODPromise;
public byte CurrentLODLevelPromise;
//TODO (JUANI) : Ideally, this would be a LODAsset that gets cached, so we dont have to make the asset bundle request again and neither we have to
// recreate the RootGameobject
// Same problem related to UNloadSceneLODSystem.UnloadSceneLOFForISS. We need to clear the result because this gets re-initiated every time
public InitialSceneStateLOD InitialSceneStateLOD;
public void Dispose(World world)
{
InitialSceneStateLOD.Dispose(world);
InitialSceneStateLOD = null;
CurrentLODPromise.ForgetLoading(world);
}
public static SceneLODInfo Create()
{
return new SceneLODInfo
{
CurrentLODLevelPromise = byte.MaxValue,
InitialSceneStateLOD = new InitialSceneStateLOD(),
};
}
public void RecalculateLODDistances(float defaultFOV, float defaultLodBias, int loadingDistance, int sceneParcels)
{
var lods = metadata.LodGroup.GetLODs();
SetupLODRelativeHeights(lods, defaultFOV, defaultLodBias, loadingDistance, sceneParcels);
metadata.LodGroup.SetLODs(lods);
}
public void AddFailedLOD()
{
metadata.FailedLODs = SceneLODInfoUtils.SetLODResult(metadata.FailedLODs, CurrentLODLevelPromise);
CurrentLODLevelPromise = byte.MaxValue;
}
public void AddSuccessLOD(GameObject instantiatedLOD, LODAsset lodAsset, float defaultFOV, float defaultLodBias, int loadingDistance, int sceneParcels)
{
metadata.SuccessfullLODs = SceneLODInfoUtils.SetLODResult(metadata.SuccessfullLODs, CurrentLODLevelPromise);
metadata.LODAssets[CurrentLODLevelPromise] = lodAsset;
instantiatedLOD.transform.SetParent(metadata.LodGroup.transform);
instantiatedLOD.GetComponentsInChildren(true, TEMP_RENDERERS);
if (TEMP_RENDERERS.Count != 0)
{
var lods = metadata.LodGroup.GetLODs();
SetupRenderers(lods, TEMP_RENDERERS, CurrentLODLevelPromise);
SetupLODRelativeHeights(lods, defaultFOV, defaultLodBias, loadingDistance, sceneParcels);
metadata.LodGroup.SetLODs(lods);
}
CurrentLODLevelPromise = byte.MaxValue;
}
private void SetupLODRelativeHeights(UnityEngine.LOD[] lods, float defaultFOV, float defaultLodBias, int loadingDistance, int sceneParcels)
{
int loadedLODAmount = SceneLODInfoUtils.LODCount(metadata.SuccessfullLODs);
CalculateCullRelativeHeight(defaultFOV, defaultLodBias, loadingDistance);
if (loadedLODAmount == 1)
{
if (SceneLODInfoUtils.HasLODResult(metadata.SuccessfullLODs, 0))
{
//LOD0 is ready to be shown. Therefore, the relative percentage should be the cull percentage
lods[0].screenRelativeTransitionHeight = metadata.CullRelativeHeightPercentage;
lods[1].screenRelativeTransitionHeight = metadata.CullRelativeHeightPercentage - 0.001f;
}
else
{
//LOD1 is ready to be shown. Therefore, the relative percentage of LOD1 should be the cull percentage, while LOD0 percentage should remain at 100% of the screen
lods[0].screenRelativeTransitionHeight = 1;
lods[1].screenRelativeTransitionHeight = metadata.CullRelativeHeightPercentage;
}
}
else if (loadedLODAmount == 2)
{
// If both LODs are loaded, we set the cull percentage for LOD1 and then assign the mid point between cull and 100% for LOD0
// In the case of the "small" scenes, LOD1 will have even less presence, LOD0 will be visible at larger distances
const float SMALL_SCENE_PARCEL_COUNT = 10.0f; // This is an arbitrary value that will be tweaked depending on performance
float sceneSizeFactor = Mathf.Lerp(0.25f, 0.5f, (sceneParcels - 1) / SMALL_SCENE_PARCEL_COUNT);
lods[0].screenRelativeTransitionHeight = (1 - metadata.CullRelativeHeightPercentage) * sceneSizeFactor + metadata.CullRelativeHeightPercentage;
lods[1].screenRelativeTransitionHeight = metadata.CullRelativeHeightPercentage;
}
}
private void SetupRenderers(UnityEngine.LOD[] lods, List<Renderer> renderersToSetup, int lodToSetup)
{
var renderers = LODGroupPoolUtils.RENDERER_ARRAY_POOL.Rent(TEMP_RENDERERS.Count);
renderersToSetup.CopyTo(renderers);
lods[lodToSetup].renderers = renderers;
if (lodToSetup == 1 && SceneLODInfoUtils.LODCount(metadata.SuccessfullLODs) == 1)
lods[0].renderers = renderers;
CalculateLODBounds(lods[lodToSetup].renderers, renderersToSetup.Count);
}
private void CalculateLODBounds(Renderer[] lodRenderers, int renderersCount)
{
var mergedBounds = lodRenderers[0].bounds;
// Encapsulate the bounds of the remaining renderers
for (int i = 1; i < renderersCount; i++)
mergedBounds.Encapsulate(lodRenderers[i].bounds);
//Object size based on the Y axis, as screen-relative height is what Unity evaluates for LODGroup transitions
metadata.LodGroup.size = mergedBounds.size.y;
}
private void CalculateCullRelativeHeight(float defaultFOV, float defaultLodBias, int loadingDistance)
{
//The cull distance is at loading distance - 1 parcel for some space buffer
//(It should first load, and then cull in)
float cullDistance = (loadingDistance - 1) * ParcelMathHelper.PARCEL_SIZE;
float halfFov = defaultFOV / 2.0f * Mathf.Deg2Rad;
float tanValue = Mathf.Tan(halfFov);
float size = metadata.LodGroup.size;
metadata.CullRelativeHeightPercentage = Mathf.Clamp(SceneLODInfoUtils.CalculateScreenRelativeCullHeight(tanValue, cullDistance + size / 2, size / 2, defaultLodBias), 0.02f, 0.999f);
metadata.LODChangeRelativeDistance = SceneLODInfoUtils.CalculateLODChangeRelativeHeight(metadata.CullRelativeHeightPercentage, tanValue, metadata.LodGroup.size / 2, defaultLodBias);
}
public bool HasLOD(byte lodForAcquisition) =>
IsInitialized() &&
(SceneLODInfoUtils.HasLODResult(metadata.SuccessfullLODs, lodForAcquisition) ||
SceneLODInfoUtils.HasLODResult(metadata.FailedLODs, lodForAcquisition) ||
CurrentLODLevelPromise == lodForAcquisition);
public bool IsInitialized() =>
!string.IsNullOrEmpty(id);
public bool IsLODInstantiated(byte lodForAcquisition) =>
IsInitialized() &&
(SceneLODInfoUtils.HasLODResult(metadata.SuccessfullLODs, lodForAcquisition) ||
SceneLODInfoUtils.HasLODResult(metadata.FailedLODs, lodForAcquisition));
public bool HasActiveLODPromise() =>
CurrentLODLevelPromise != byte.MaxValue;
public void ForgetAllLoadings(World world)
{
CurrentLODPromise.ForgetLoading(world);
InitialSceneStateLOD.ForgetLoading(world);
}
public void ClearISS(float defaultFOV, float defaultLodBias, int loadingDistance, int sceneParcels)
{
if (InitialSceneStateLOD.CurrentState == InitialSceneStateLOD.State.RESOLVED)
{
metadata.SuccessfullLODs = SceneLODInfoUtils.ClearLODResult(metadata.SuccessfullLODs, 0);
UnityEngine.LOD[] currentLODs = metadata.LodGroup.GetLODs();
// Remove LOD_0 (index 0) from the array, keeping LOD_1
// LODs are always created with size 2, and SetupLODRelativeHeights expects size 2, so we maintain size 2
// Move LOD_1 (what was at index 1) to index 0, and add an empty LOD at index 1
UnityEngine.LOD[] lodsWithoutLOD0 = new UnityEngine.LOD[2]
{
new (1f, Array.Empty<Renderer>()),
new (0.99f, currentLODs[1].renderers),
};
SetupLODRelativeHeights(lodsWithoutLOD0, defaultFOV, defaultLodBias, loadingDistance, sceneParcels);
metadata.LodGroup.SetLODs(lodsWithoutLOD0);
}
}
}
}