< 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:85
Uncovered lines:13
Coverable lines:98
Total lines:203
Line coverage:86.7% (85 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%220100%
UpdateLODsBillboard()0%330100%
UpdateAllLODs(...)0%88097.06%
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
 015        private BaseDictionary<string, Player> otherPlayers => DataStore.i.player.otherPlayers;
 016        private BaseVariable<float> simpleAvatarDistance => DataStore.i.avatarsLOD.simpleAvatarDistance;
 017        private BaseVariable<float> LODDistance => DataStore.i.avatarsLOD.LODDistance;
 018        private BaseVariable<int> maxAvatars => DataStore.i.avatarsLOD.maxAvatars;
 019        private BaseVariable<int> maxImpostors => DataStore.i.avatarsLOD.maxImpostors;
 20        private Vector3 cameraPosition;
 21        private Vector3 cameraForward;
 22        private GPUSkinningThrottlingCurveSO gpuSkinningThrottlingCurve;
 68623        private SimpleOverlappingTracker overlappingTracker = new SimpleOverlappingTracker(MIN_DISTANCE_BETWEEN_NAMES_PI
 24
 68625        internal readonly Dictionary<string, IAvatarLODController> lodControllers = new Dictionary<string, IAvatarLODCon
 26        internal bool enabled;
 27        private UnityEngine.Camera mainCamera;
 28
 68629        public AvatarsLODController()
 30        {
 68631            gpuSkinningThrottlingCurve = Resources.Load<GPUSkinningThrottlingCurveSO>("GPUSkinningThrottlingCurve");
 68632            DataStore.i.featureFlags.flags.OnChange += OnFeatureFlagChanged;
 68633        }
 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            }
 452            lodControllers.Clear();
 53
 1254            foreach (var keyValuePair in otherPlayers.Get())
 55            {
 256                RegisterAvatar(keyValuePair.Key, keyValuePair.Value);
 57            }
 458            otherPlayers.OnAdded += RegisterAvatar;
 459            otherPlayers.OnRemoved += UnregisterAvatar;
 460        }
 61
 62        public void RegisterAvatar(string id, Player player)
 63        {
 564            if (!enabled || lodControllers.ContainsKey(id))
 165                return;
 66
 467            lodControllers.Add(id, CreateLodController(player));
 468        }
 69
 070        protected internal virtual IAvatarLODController CreateLodController(Player player) => new AvatarLODController(pl
 71
 72        public void UnregisterAvatar(string id, Player player)
 73        {
 374            if (!lodControllers.ContainsKey(id))
 175                return;
 76
 277            lodControllers[id].Dispose();
 278            lodControllers.Remove(id);
 279        }
 80
 81        public void Update()
 82        {
 1896483            if (!enabled)
 1895484                return;
 85
 1086            cameraPosition = CommonScriptableObjects.cameraPosition.Get();
 1087            cameraForward = CommonScriptableObjects.cameraForward.Get();
 88
 1089            UpdateAllLODs(maxAvatars.Get(), maxImpostors.Get());
 1090            UpdateLODsBillboard();
 1091        }
 92
 93        internal void UpdateLODsBillboard()
 94        {
 7895            foreach (var kvp in lodControllers)
 96            {
 2997                Player player = kvp.Value.player;
 98
 2999                if (!IsInFrontOfCamera(player.worldPosition))
 100                    continue;
 101
 27102                Vector3 previousForward = player.forwardDirection;
 27103                Vector3 lookAtDir = (cameraPosition - player.worldPosition).normalized;
 104
 27105                lookAtDir.y = previousForward.y;
 27106                player.renderer.SetImpostorForward(lookAtDir);
 107            }
 10108        }
 109
 110        internal void UpdateAllLODs(int maxAvatars = DataStore.DataStore_AvatarsLOD.DEFAULT_MAX_AVATAR, int maxImpostors
 111        {
 12112            if (mainCamera == null)
 11113                mainCamera = UnityEngine.Camera.main;
 12114            int avatarsCount = 0; //Full Avatar + Simple Avatar
 12115            int impostorCount = 0; //Impostor
 116
 12117            float simpleAvatarDistance = this.simpleAvatarDistance.Get();
 12118            Vector3 ownPlayerPosition = CommonScriptableObjects.playerUnityPosition.Get();
 119
 12120            overlappingTracker.Reset();
 121
 12122            (IAvatarLODController lodController, float distance)[] lodControllersByDistance = ComposeLODControllersSorte
 90123            for (int index = 0; index < lodControllersByDistance.Length; index++)
 124            {
 33125                (IAvatarLODController lodController, float distance) = lodControllersByDistance[index];
 33126                if (IsInInvisibleDistance(distance))
 127                {
 7128                    lodController.SetNameVisible(false);
 7129                    lodController.SetInvisible();
 7130                    continue;
 131                }
 132
 26133                if (avatarsCount < maxAvatars)
 134                {
 18135                    lodController.SetThrottling((int)gpuSkinningThrottlingCurve.curve.Evaluate(distance));
 18136                    if (distance < simpleAvatarDistance)
 11137                        lodController.SetFullAvatar();
 138                    else
 7139                        lodController.SetSimpleAvatar();
 18140                    avatarsCount++;
 141
 18142                    if (mainCamera == null)
 0143                        lodController.SetNameVisible(true);
 144                    else
 18145                        lodController.SetNameVisible(overlappingTracker.RegisterPosition(lodController.player.playerName
 18146                    continue;
 147                }
 148
 8149                lodController.SetNameVisible(false);
 8150                if (impostorCount < maxImpostors)
 151                {
 7152                    lodController.SetImpostor();
 7153                    lodController.UpdateImpostorTint(distance);
 7154                    impostorCount++;
 7155                    continue;
 156                }
 157
 1158                lodController.SetInvisible();
 159            }
 12160        }
 161
 162        private bool IsInInvisibleDistance(float distance)
 163        {
 33164            bool firstPersonCamera = CommonScriptableObjects.cameraMode.Get() == CameraMode.ModeId.FirstPerson;
 165
 33166            return firstPersonCamera ? distance < AVATARS_INVISIBILITY_DISTANCE : distance < 0f; // < 0 is behind camera
 167        }
 168
 169        private (IAvatarLODController lodController, float distance)[] ComposeLODControllersSortedByDistance(IEnumerable
 170        {
 45171            (IAvatarLODController lodController, float distance)[] lodControllersWithDistance = lodControllers.Select(x 
 42172            Array.Sort(lodControllersWithDistance, (x, y) => x.distance.CompareTo(y.distance));
 12173            return lodControllersWithDistance;
 174        }
 175
 176        /// <summary>
 177        /// Returns -1 if player is not in front of camera or not found
 178        /// </summary>
 179        /// <param name="player"></param>
 180        /// <returns></returns>
 181        private float DistanceToOwnPlayer(Player player, Vector3 ownPlayerPosition)
 182        {
 33183            if (player == null || !IsInFrontOfCamera(player.worldPosition))
 6184                return -1;
 185
 27186            return Vector3.Distance(ownPlayerPosition, player.worldPosition);
 187        }
 188
 62189        private bool IsInFrontOfCamera(Vector3 position) { return Vector3.Dot(cameraForward, (position - cameraPosition)
 190
 191        public void Dispose()
 192        {
 707193            DataStore.i.featureFlags.flags.OnChange -= OnFeatureFlagChanged;
 1486194            foreach (IAvatarLODController lodController in lodControllers.Values)
 195            {
 36196                lodController.Dispose();
 197            }
 198
 707199            otherPlayers.OnAdded -= RegisterAvatar;
 707200            otherPlayers.OnRemoved -= UnregisterAvatar;
 707201        }
 202    }
 203}