< 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:87
Uncovered lines:11
Coverable lines:98
Total lines:205
Line coverage:88.7% (87 of 98)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
AvatarsLODController()0%110100%
OnFeatureFlagChanged(...)0%6200%
Initialize(...)0%5.25080%
RegisterAvatar(...)0%330100%
CreateLodController(...)0%2100%
UnregisterAvatar(...)0%220100%
Update()0%2.012085.71%
UpdateLODsBillboard()0%330100%
UpdateAllLODs(...)0%8.018094.12%
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 System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using UnityEngine;
 5
 6namespace DCL
 7{
 8    public class AvatarsLODController : IAvatarsLODController
 9    {
 10        internal const string AVATAR_LODS_FLAG_NAME = "avatar_lods";
 11        internal const float RENDERED_DOT_PRODUCT_ANGLE = 0.25f;
 12        internal const float AVATARS_INVISIBILITY_DISTANCE = 1.75f;
 13        private const float MIN_DISTANCE_BETWEEN_NAMES_PIXELS = 70f;
 14
 135815        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 1216        private BaseVariable<float> simpleAvatarDistance => DataStore.i.avatarsLOD.simpleAvatarDistance;
 017        private BaseVariable<float> LODDistance => DataStore.i.avatarsLOD.LODDistance;
 1018        private BaseVariable<int> maxAvatars => DataStore.i.avatarsLOD.maxAvatars;
 1019        private BaseVariable<int> maxImpostors => DataStore.i.avatarsLOD.maxImpostors;
 20        private Vector3 cameraPosition;
 21        private Vector3 cameraForward;
 22        private GPUSkinningThrottlingCurveSO gpuSkinningThrottlingCurve;
 66823        private SimpleOverlappingTracker overlappingTracker = new SimpleOverlappingTracker(MIN_DISTANCE_BETWEEN_NAMES_PI
 24
 66825        internal readonly Dictionary<string, IAvatarLODController> lodControllers = new Dictionary<string, IAvatarLODCon
 26        internal bool enabled;
 27        private UnityEngine.Camera mainCamera;
 28
 66829        public AvatarsLODController()
 30        {
 66831            gpuSkinningThrottlingCurve = Resources.Load<GPUSkinningThrottlingCurveSO>("GPUSkinningThrottlingCurve");
 66832            DataStore.i.featureFlags.flags.OnChange += OnFeatureFlagChanged;
 66833        }
 34
 35        private void OnFeatureFlagChanged(FeatureFlag current, FeatureFlag previous)
 36        {
 037            if (enabled == current.IsFeatureEnabled(AVATAR_LODS_FLAG_NAME))
 038                return;
 039            Initialize(current);
 040        }
 41
 42        internal void Initialize(FeatureFlag current)
 43        {
 444            enabled = current.IsFeatureEnabled(AVATAR_LODS_FLAG_NAME);
 445            if (!enabled)
 046                return;
 47
 848            foreach (IAvatarLODController lodController in lodControllers.Values)
 49            {
 050                lodController.Dispose();
 51            }
 52
 453            lodControllers.Clear();
 54
 1255            foreach (var keyValuePair in otherPlayers.Get())
 56            {
 257                RegisterAvatar(keyValuePair.Key, keyValuePair.Value);
 58            }
 59
 460            otherPlayers.OnAdded += RegisterAvatar;
 461            otherPlayers.OnRemoved += UnregisterAvatar;
 462        }
 63
 64        public void RegisterAvatar(string id, Player player)
 65        {
 566            if (!enabled || lodControllers.ContainsKey(id))
 167                return;
 68
 469            lodControllers.Add(id, CreateLodController(player));
 470        }
 71
 072        protected internal virtual IAvatarLODController CreateLodController(Player player) => new AvatarLODController(pl
 73
 74        public void UnregisterAvatar(string id, Player player)
 75        {
 376            if (!lodControllers.ContainsKey(id))
 177                return;
 78
 279            lodControllers[id].Dispose();
 280            lodControllers.Remove(id);
 281        }
 82
 83        public void Update()
 84        {
 1085            if (!enabled)
 086                return;
 87
 1088            cameraPosition = CommonScriptableObjects.cameraPosition.Get();
 1089            cameraForward = CommonScriptableObjects.cameraForward.Get();
 90
 1091            UpdateAllLODs(maxAvatars.Get(), maxImpostors.Get());
 1092            UpdateLODsBillboard();
 1093        }
 94
 95        internal void UpdateLODsBillboard()
 96        {
 7897            foreach (var kvp in lodControllers)
 98            {
 2999                Player player = kvp.Value.player;
 100
 29101                if (!IsInFrontOfCamera(player.worldPosition))
 102                    continue;
 103
 27104                Vector3 previousForward = player.forwardDirection;
 27105                Vector3 lookAtDir = (cameraPosition - player.worldPosition).normalized;
 106
 27107                lookAtDir.y = previousForward.y;
 27108                player.renderer.SetImpostorForward(lookAtDir);
 109            }
 10110        }
 111
 112        internal void UpdateAllLODs(int maxAvatars = DataStore_AvatarsLOD.DEFAULT_MAX_AVATAR, int maxImpostors = DataSto
 113        {
 12114            if (mainCamera == null)
 12115                mainCamera = UnityEngine.Camera.main;
 12116            int avatarsCount = 0; //Full Avatar + Simple Avatar
 12117            int impostorCount = 0; //Impostor
 118
 12119            float simpleAvatarDistance = this.simpleAvatarDistance.Get();
 12120            Vector3 ownPlayerPosition = CommonScriptableObjects.playerUnityPosition.Get();
 121
 12122            overlappingTracker.Reset();
 123
 12124            (IAvatarLODController lodController, float distance)[] lodControllersByDistance = ComposeLODControllersSorte
 90125            for (int index = 0; index < lodControllersByDistance.Length; index++)
 126            {
 33127                (IAvatarLODController lodController, float distance) = lodControllersByDistance[index];
 33128                if (IsInInvisibleDistance(distance))
 129                {
 7130                    lodController.SetNameVisible(false);
 7131                    lodController.SetInvisible();
 7132                    continue;
 133                }
 134
 26135                if (avatarsCount < maxAvatars)
 136                {
 18137                    lodController.SetThrottling((int)gpuSkinningThrottlingCurve.curve.Evaluate(distance));
 18138                    if (distance < simpleAvatarDistance)
 11139                        lodController.SetFullAvatar();
 140                    else
 7141                        lodController.SetSimpleAvatar();
 18142                    avatarsCount++;
 143
 18144                    if (mainCamera == null)
 18145                        lodController.SetNameVisible(true);
 146                    else
 0147                        lodController.SetNameVisible(overlappingTracker.RegisterPosition(lodController.player.playerName
 0148                    continue;
 149                }
 150
 8151                lodController.SetNameVisible(false);
 8152                if (impostorCount < maxImpostors)
 153                {
 7154                    lodController.SetImpostor();
 7155                    lodController.UpdateImpostorTint(distance);
 7156                    impostorCount++;
 7157                    continue;
 158                }
 159
 1160                lodController.SetInvisible();
 161            }
 12162        }
 163
 164        private bool IsInInvisibleDistance(float distance)
 165        {
 33166            bool firstPersonCamera = CommonScriptableObjects.cameraMode.Get() == CameraMode.ModeId.FirstPerson;
 167
 33168            return firstPersonCamera ? distance < AVATARS_INVISIBILITY_DISTANCE : distance < 0f; // < 0 is behind camera
 169        }
 170
 171        private (IAvatarLODController lodController, float distance)[] ComposeLODControllersSortedByDistance(IEnumerable
 172        {
 45173            (IAvatarLODController lodController, float distance)[] lodControllersWithDistance = lodControllers.Select(x 
 42174            Array.Sort(lodControllersWithDistance, (x, y) => x.distance.CompareTo(y.distance));
 12175            return lodControllersWithDistance;
 176        }
 177
 178        /// <summary>
 179        /// Returns -1 if player is not in front of camera or not found
 180        /// </summary>
 181        /// <param name="player"></param>
 182        /// <returns></returns>
 183        private float DistanceToOwnPlayer(Player player, Vector3 ownPlayerPosition)
 184        {
 33185            if (player == null || !IsInFrontOfCamera(player.worldPosition))
 6186                return -1;
 187
 27188            return Vector3.Distance(ownPlayerPosition, player.worldPosition);
 189        }
 190
 62191        private bool IsInFrontOfCamera(Vector3 position) { return Vector3.Dot(cameraForward, (position - cameraPosition)
 192
 193        public void Dispose()
 194        {
 673195            DataStore.i.featureFlags.flags.OnChange -= OnFeatureFlagChanged;
 1418196            foreach (IAvatarLODController lodController in lodControllers.Values)
 197            {
 36198                lodController.Dispose();
 199            }
 200
 673201            otherPlayers.OnAdded -= RegisterAvatar;
 673202            otherPlayers.OnRemoved -= UnregisterAvatar;
 673203        }
 204    }
 205}