< Summary

Class:AvatarSystem.NoLODs
Assembly:AvatarSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/LOD.cs
Covered lines:1
Uncovered lines:5
Coverable lines:6
Total lines:265
Line coverage:16.6% (1 of 6)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Bind(...)0%2100%
SetLodIndex(...)0%2100%
SetImpostorTexture(...)0%2100%
SetImpostorTint(...)0%2100%
Dispose()0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/LOD.cs

#LineLine coverage
 1using System;
 2using System.Threading;
 3using Cysharp.Threading.Tasks;
 4using DCL;
 5using DCL.Helpers;
 6using UnityEngine;
 7using Object = UnityEngine.Object;
 8
 9namespace AvatarSystem
 10{
 11    public class LOD : ILOD
 12    {
 13        public const float IMPOSTOR_MOVEMENT_INTERPOLATION = 1.79f;
 14        private const float TRANSITION_DURATION = 0.75f;
 15
 16        /// <summary>
 17        /// 0 = 3D Avatar, SSAO, Facial Features
 18        /// 1 = 3D Avatar, NO SSAO, NO Facial Features
 19        /// 2 = Impostor, NO 3D Avatar
 20        /// </summary>
 21        public int lodIndex { get; private set; } = -1;
 22
 23        private readonly GameObject impostorContainer;
 24        private readonly IVisibility visibility;
 25        private readonly IAvatarMovementController avatarMovementController;
 26
 27        internal Renderer combinedAvatar;
 28        internal Renderer impostorRenderer;
 29        internal MeshFilter impostorMeshFilter;
 30        private float avatarAlpha;
 31
 32        private CancellationTokenSource transitionCTS;
 33        private CancellationTokenSource billboardLookAtCameraCTS;
 34        private string VISIBILITY_CONSTRAIN_IN_IMPOSTOR = "in_impostor";
 35        private string VISIBILITY_CONSTRAIN_IN_LOD1 = "in_LOD1";
 36
 37        public LOD(GameObject impostorContainer, IVisibility visibility, IAvatarMovementController avatarMovementControl
 38        {
 39            this.impostorContainer = impostorContainer;
 40            this.visibility = visibility;
 41            // TODO Once the AvatarMovementController is completly ported into the AvatarSystem we can decouple it from 
 42            this.avatarMovementController = avatarMovementController;
 43        }
 44
 45        internal void EnsureImpostor()
 46        {
 47            if (impostorRenderer != null && impostorMeshFilter != null)
 48                return;
 49            impostorRenderer = CreateImpostor();
 50            impostorMeshFilter = impostorRenderer.GetComponent<MeshFilter>();
 51            SetImpostorTexture(null);
 52        }
 53
 54        /// <summary>
 55        /// Set the impostor texture (null will take a randomized one)
 56        /// </summary>
 57        /// <param name="texture"></param>
 58        public void SetImpostorTexture(Texture2D texture)
 59        {
 60            EnsureImpostor();
 61
 62            if (texture == null)
 63                AvatarRendererHelpers.RandomizeAndApplyGenericImpostor(impostorMeshFilter.mesh, impostorRenderer.materia
 64            else
 65                AvatarRendererHelpers.SetImpostorTexture(texture, impostorMeshFilter.mesh, impostorRenderer.material);
 66        }
 67
 68        public void SetImpostorTint(Color color)
 69        {
 70            EnsureImpostor();
 71
 72            AvatarRendererHelpers.SetImpostorTintColor(impostorRenderer.material, color);
 73        }
 74
 75        public void Bind(Renderer combinedAvatar)
 76        {
 77            this.combinedAvatar = combinedAvatar;
 78            SetLodIndex(lodIndex, true);
 79        }
 80
 81        public void SetLodIndex(int lodIndex, bool inmediate = false)
 82        {
 83            if (inmediate)
 84            {
 85                avatarAlpha = lodIndex <= 1 ? 1f : 0f;
 86                UpdateSSAO(combinedAvatar, lodIndex);
 87                UpdateAlpha(avatarAlpha);
 88                UpdateMovementLerping(lodIndex);
 89
 90                if (avatarAlpha > 0)
 91                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 92                else
 93                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 94
 95                if (lodIndex == 0)
 96                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 97                else
 98                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 99
 100                SetImpostorEnabled(avatarAlpha == 0);
 101                return;
 102            }
 103
 104            if (lodIndex == this.lodIndex)
 105                return;
 106
 107            this.lodIndex = lodIndex;
 108
 109            transitionCTS?.Cancel();
 110            transitionCTS?.Dispose();
 111            transitionCTS = new CancellationTokenSource();
 112            Transition(transitionCTS.Token);
 113        }
 114
 115        private Renderer CreateImpostor()
 116        {
 117            GameObject quadImpostorContainer = Object.Instantiate(Resources.Load<GameObject>("QuadImpostor"), impostorCo
 118            return quadImpostorContainer.GetComponent<Renderer>();
 119        }
 120
 121        private async UniTaskVoid Transition(CancellationToken ct)
 122        {
 123            ct.ThrowIfCancellationRequested();
 124            EnsureImpostor();
 125
 126            try
 127            {
 128                visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 129                impostorRenderer.enabled = true;
 130                float targetAvatarAlpha = lodIndex <= 1 ? 1f : 0f;
 131                while (!Mathf.Approximately(targetAvatarAlpha, avatarAlpha))
 132                {
 133                    avatarAlpha = Mathf.MoveTowards(avatarAlpha, targetAvatarAlpha, (1f / TRANSITION_DURATION) * Time.de
 134                    UpdateAlpha(avatarAlpha);
 135                    await UniTask.NextFrame(ct);
 136                }
 137
 138                UpdateSSAO(combinedAvatar, lodIndex);
 139                UpdateMovementLerping(lodIndex);
 140
 141                if (avatarAlpha > 0)
 142                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 143                else
 144                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 145
 146                if (lodIndex == 0)
 147                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 148                else
 149                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 150
 151                SetImpostorEnabled(avatarAlpha == 0);
 152            }
 153            catch (OperationCanceledException)
 154            {
 155                //No disposing required
 156                throw;
 157            }
 158        }
 159
 160        private void SetImpostorEnabled(bool enabled)
 161        {
 162            EnsureImpostor();
 163
 164            impostorRenderer.enabled = enabled;
 165            billboardLookAtCameraCTS?.Cancel();
 166            billboardLookAtCameraCTS?.Dispose();
 167            if (enabled)
 168            {
 169                billboardLookAtCameraCTS = new CancellationTokenSource();
 170                BillboardLookAtCamera(billboardLookAtCameraCTS.Token);
 171            }
 172            else
 173            {
 174                billboardLookAtCameraCTS = null;
 175            }
 176        }
 177
 178        internal void UpdateAlpha(float avatarAlpha)
 179        {
 180            if (combinedAvatar == null)
 181                return;
 182
 183            EnsureImpostor();
 184
 185            Material[] mats = combinedAvatar.sharedMaterials;
 186            for (int j = 0; j < mats.Length; j++)
 187            {
 188                mats[j].SetFloat(ShaderUtils.DitherFade, avatarAlpha);
 189            }
 190
 191            Material impostorMaterial = impostorRenderer.material;
 192            //TODO implement dither in Unlit shader
 193            Color current = impostorMaterial.GetColor(ShaderUtils.BaseColor);
 194            current.a = 1f - avatarAlpha;
 195            impostorMaterial.SetColor(ShaderUtils.BaseColor, current);
 196        }
 197
 198        internal static void UpdateSSAO(Renderer renderer, int lodIndex)
 199        {
 200            if (renderer == null)
 201                return;
 202
 203            Material[] mats = renderer.sharedMaterials;
 204            for (int j = 0; j < mats.Length; j++)
 205            {
 206                if (lodIndex == 0)
 207                    mats[j].DisableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 208                else
 209                    mats[j].EnableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 210            }
 211        }
 212
 213        internal void UpdateMovementLerping(int lodIndex) { avatarMovementController.SetMovementLerpWait(lodIndex >= 2 ?
 214
 215        private async UniTaskVoid BillboardLookAtCamera(CancellationToken ct)
 216        {
 217            Camera camera = Camera.main;
 218            while (camera == null)
 219            {
 220                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 221                camera = Camera.main;
 222            }
 223
 224            while (true)
 225            {
 226                SetBillboardRotation(camera.transform);
 227                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 228            }
 229        }
 230
 231        internal void SetBillboardRotation(Transform lookAt)
 232        {
 233            EnsureImpostor();
 234
 235            impostorRenderer.transform.LookAt(lookAt);
 236            impostorRenderer.transform.eulerAngles = Vector3.Scale(impostorContainer.transform.eulerAngles, Vector3.up);
 237        }
 238
 239        public void Dispose()
 240        {
 241            transitionCTS?.Cancel();
 242            transitionCTS?.Dispose();
 243            transitionCTS = null;
 244
 245            billboardLookAtCameraCTS?.Cancel();
 246            billboardLookAtCameraCTS?.Dispose();
 247            billboardLookAtCameraCTS = null;
 248
 249            if (impostorRenderer != null)
 250                Object.Destroy(impostorRenderer.gameObject);
 251        }
 252    }
 253
 254    public class NoLODs : ILOD
 255    {
 0256        public int lodIndex { get; }
 257
 0258        public void Bind(Renderer combinedAvatar) { }
 0259        public void SetLodIndex(int lodIndex, bool inmediate = false) { }
 0260        public void SetImpostorTexture(Texture2D texture) { }
 0261        public void SetImpostorTint(Color color) { }
 262
 715263        public void Dispose() { }
 264    }
 265}