< Summary

Class:DCL.AvatarsLODController
Assembly:AvatarShape
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarLOD/AvatarsLODController.cs
Covered lines:90
Uncovered lines:3
Coverable lines:93
Total lines:222
Line coverage:96.7% (90 of 93)
Covered branches:0
Total branches:0
Covered methods:23
Total methods:24
Method coverage:95.8% (23 of 24)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarsLODController()0%110100%
Initialize()0%4.474069.23%
SetCamera(...)0%2100%
RegisterAvatar(...)0%220100%
CreateLodController(...)0%110100%
UnregisterAvatar(...)0%220100%
Update()0%110100%
UpdateAllLODs(...)0%770100%
CalculateAnimationTrhottle(...)0%2.032080%
GetMainCamera()0%330100%
IsInInvisibleDistance(...)0%220100%
ComposeLODControllersSortedByDistance(...)0%220100%
DistanceToOwnPlayer(...)0%330100%
IsInFrontOfCamera(...)0%110100%
Dispose()0%220100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Avatar/AvatarLOD/AvatarsLODController.cs

#LineLine coverage
 1using DCL.CameraTool;
 2using DCL.SettingsCommon;
 3using System;
 4using System.Collections.Generic;
 5using UnityEngine;
 6using QualitySettings = DCL.SettingsCommon.QualitySettings;
 7
 8namespace DCL
 9{
 10    public class AvatarsLODController : IAvatarsLODController
 11    {
 12        private const float RENDERED_DOT_PRODUCT_ANGLE = 0.25f;
 13        private const float AVATARS_INVISIBILITY_DISTANCE = 1.75f;
 14        private const float MIN_DISTANCE_BETWEEN_NAMES_PIXELS = 70f;
 15        private const float MIN_ANIMATION_FRAME_SKIP_DISTANCE_MULTIPLIER = 0.3f;
 16        private const float MAX_ANIMATION_FRAME_SKIP_DISTANCE_MULTIPLIER = 2f;
 17
 220518        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 1189519        private BaseVariable<float> simpleAvatarDistance => DataStore.i.avatarsLOD.simpleAvatarDistance;
 1189420        private BaseVariable<int> maxAvatars => DataStore.i.avatarsLOD.maxAvatars;
 1189421        private BaseVariable<int> maxImpostors => DataStore.i.avatarsLOD.maxImpostors;
 7622        private QualitySettings qualitySettings => Settings.i.qualitySettings.Data;
 23
 24        private readonly GPUSkinningThrottlingCurveSO gpuSkinningThrottlingCurve;
 44125        private readonly SimpleOverlappingTracker overlappingTracker = new (MIN_DISTANCE_BETWEEN_NAMES_PIXELS);
 26
 27        private UnityEngine.Camera camera;
 28        internal Transform cameraTransformValue;
 29
 44130        private readonly List<(IAvatarLODController lodController, float distance)> lodControllersWithDistance = new ();
 1377431        public Dictionary<string, IAvatarLODController> LodControllers { get; } = new ();
 32
 33        private Transform cameraTransform
 34        {
 35            get
 36            {
 19237                GetMainCamera();
 19238                return cameraTransformValue;
 39            }
 40        }
 41
 4842        private Vector3 cameraTransformPosition => cameraTransform != null ? cameraTransform.position : Vector3.zero;
 4843        private Vector3 cameraTransformForward => cameraTransform != null ? cameraTransform.forward : Vector3.forward;
 44
 44145        public AvatarsLODController()
 46        {
 44147            gpuSkinningThrottlingCurve = Resources.Load<GPUSkinningThrottlingCurveSO>("GPUSkinningThrottlingCurve");
 44148        }
 49
 50        public void Initialize()
 51        {
 44152            Environment.i.platform.updateEventHandler.AddListener(IUpdateEventHandler.EventType.Update, Update);
 53
 88254            foreach (IAvatarLODController lodController in LodControllers.Values) { lodController.Dispose(); }
 55
 44156            LodControllers.Clear();
 57
 88258            foreach (var keyValuePair in otherPlayers.Get()) { RegisterAvatar(keyValuePair.Key, keyValuePair.Value); }
 59
 44160            otherPlayers.OnAdded += RegisterAvatar;
 44161            otherPlayers.OnRemoved += UnregisterAvatar;
 44162        }
 63
 64        public void SetCamera(UnityEngine.Camera newCamera)
 65        {
 066            camera = newCamera;
 067            cameraTransformValue = camera.transform;
 068        }
 69
 70        public void RegisterAvatar(string id, Player player)
 71        {
 2272            if (LodControllers.ContainsKey(id))
 173                return;
 74
 2175            LodControllers.Add(id, CreateLodController(player));
 2176        }
 77
 78        protected internal virtual IAvatarLODController CreateLodController(Player player) =>
 1779            new AvatarLODController(player);
 80
 81        public void UnregisterAvatar(string id, Player player)
 82        {
 683            if (!LodControllers.ContainsKey(id))
 184                return;
 85
 586            LodControllers[id].SetLOD0();
 587            LodControllers[id].Dispose();
 588            LodControllers.Remove(id);
 589        }
 90
 91        public void Update()
 92        {
 1189493            UpdateAllLODs(maxAvatars.Get(), maxImpostors.Get());
 1189494        }
 95
 96        internal void UpdateAllLODs(int maxAvatars = DataStore_AvatarsLOD.DEFAULT_MAX_AVATAR, int maxImpostors = DataSto
 97        {
 1189598            GetMainCamera();
 99
 11895100            var avatarsCount = 0; //Full Avatar + Simple Avatar
 11895101            var impostorCount = 0; //Impostor
 102
 11895103            float simpleAvatarDistance = this.simpleAvatarDistance.Get();
 11895104            Vector3 ownPlayerPosition = CommonScriptableObjects.playerUnityPosition.Get();
 105
 11895106            overlappingTracker.Reset();
 107
 11991108            foreach (var controllerDistancePair in
 11895109                     ComposeLODControllersSortedByDistance(LodControllers.Values, ownPlayerPosition))
 110            {
 48111                (IAvatarLODController lodController, float distance) = controllerDistancePair;
 112
 48113                if (IsInInvisibleDistance(distance))
 114                {
 3115                    lodController.SetNameVisible(false);
 3116                    lodController.SetInvisible();
 3117                    continue;
 118                }
 119
 45120                if (avatarsCount < maxAvatars)
 121                {
 38122                    lodController.SetAnimationThrottling(CalculateAnimationTrhottle(distance));
 123
 38124                    if (distance < simpleAvatarDistance)
 34125                        lodController.SetLOD0();
 126                    else
 4127                        lodController.SetLOD1();
 128
 38129                    avatarsCount++;
 130
 38131                    lodController.SetNameVisible(camera == null || overlappingTracker.RegisterPosition(lodController.pla
 132
 38133                    continue;
 134                }
 135
 7136                lodController.SetNameVisible(false);
 137
 7138                if (impostorCount < maxImpostors)
 139                {
 6140                    lodController.SetLOD2();
 6141                    lodController.UpdateImpostorTint(distance);
 6142                    impostorCount++;
 6143                    continue;
 144                }
 145
 1146                lodController.SetInvisible();
 147            }
 11895148        }
 149
 150        private int CalculateAnimationTrhottle(float distance)
 151        {
 38152            if (!qualitySettings.enableDetailObjectCulling) return 1;
 153
 38154            float culling = qualitySettings.detailObjectCullingLimit;
 155
 156            // The higher the culling, the higher distance multiplier we give to the calculation
 38157            float distanceNerf = Mathf.Lerp(
 158                MIN_ANIMATION_FRAME_SKIP_DISTANCE_MULTIPLIER,
 159                MAX_ANIMATION_FRAME_SKIP_DISTANCE_MULTIPLIER,
 160                culling / 100f);
 161
 162            // The curve starts adding frame skips at 15 distance (one parcel)
 38163            return (int)gpuSkinningThrottlingCurve.curve.Evaluate(distance * distanceNerf);
 164        }
 165
 166        private void GetMainCamera()
 167        {
 12087168            if (camera == null)
 169            {
 6241170                camera = UnityEngine.Camera.main;
 171
 6241172                if (camera != null)
 301173                    cameraTransformValue = camera.transform;
 174            }
 12087175        }
 176
 177        private static bool IsInInvisibleDistance(float distance)
 178        {
 48179            bool firstPersonCamera = CommonScriptableObjects.cameraMode.Get() == CameraMode.ModeId.FirstPerson;
 180
 48181            return firstPersonCamera ? distance < AVATARS_INVISIBILITY_DISTANCE : distance < 0f; // < 0 is behind camera
 182        }
 183
 184        private List<(IAvatarLODController lodController, float distance)> ComposeLODControllersSortedByDistance(Diction
 185        {
 11895186            lodControllersWithDistance.Clear();
 187
 23886188            foreach (IAvatarLODController x in lodControllers)
 48189                lodControllersWithDistance.Add((x, DistanceToOwnPlayer(x.player, ownPlayerPosition)));
 190
 11895191            lodControllersWithDistance.Sort(CompareByDistance());
 192
 11895193            return lodControllersWithDistance;
 194
 195            Comparison<(IAvatarLODController lodController, float distance)> CompareByDistance() =>
 11934196                (x, y) => x.distance.CompareTo(y.distance);
 197        }
 198
 199        /// <summary>
 200        /// Returns -1 if player is not in front of camera or not found
 201        /// </summary>
 202        private float DistanceToOwnPlayer(Player player, Vector3 ownPlayerPosition)
 203        {
 48204            if (player == null || !IsInFrontOfCamera(player.worldPosition))
 2205                return -1;
 206
 46207            return Vector3.Distance(ownPlayerPosition, player.worldPosition);
 208        }
 209
 210        private bool IsInFrontOfCamera(Vector3 position) =>
 48211            Vector3.Dot(cameraTransformForward, (position - cameraTransformPosition).normalized) >= RENDERED_DOT_PRODUCT
 212
 213        public void Dispose()
 214        {
 963215            foreach (IAvatarLODController lodController in LodControllers.Values) { lodController.Dispose(); }
 216
 441217            otherPlayers.OnAdded -= RegisterAvatar;
 441218            otherPlayers.OnRemoved -= UnregisterAvatar;
 441219            Environment.i.platform.updateEventHandler.RemoveListener(IUpdateEventHandler.EventType.Update, Update);
 441220        }
 221    }
 222}