< Summary

Class:AvatarSystem.LOD
Assembly:AvatarSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/LOD.cs
Covered lines:52
Uncovered lines:68
Coverable lines:120
Total lines:265
Line coverage:43.3% (52 of 120)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
LOD(...)0%110100%
EnsureImpostor()0%330100%
SetImpostorTexture(...)0%2.262060%
SetImpostorTint(...)0%110100%
Bind(...)0%2100%
SetLodIndex(...)0%90900%
CreateImpostor()0%110100%
Transition()0%90900%
SetImpostorEnabled(...)0%20400%
UpdateAlpha(...)0%33092.31%
UpdateSSAO(...)0%4.024090%
UpdateMovementLerping(...)0%330100%
BillboardLookAtCamera()0%42600%
SetBillboardRotation(...)0%110100%
Dispose()0%6.056088.89%

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>
 69921        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;
 69934        private string VISIBILITY_CONSTRAIN_IN_IMPOSTOR = "in_impostor";
 69935        private string VISIBILITY_CONSTRAIN_IN_LOD1 = "in_LOD1";
 36
 69937        public LOD(GameObject impostorContainer, IVisibility visibility, IAvatarMovementController avatarMovementControl
 38        {
 69939            this.impostorContainer = impostorContainer;
 69940            this.visibility = visibility;
 41            // TODO Once the AvatarMovementController is completly ported into the AvatarSystem we can decouple it from 
 69942            this.avatarMovementController = avatarMovementController;
 69943        }
 44
 45        internal void EnsureImpostor()
 46        {
 2647            if (impostorRenderer != null && impostorMeshFilter != null)
 1648                return;
 1049            impostorRenderer = CreateImpostor();
 1050            impostorMeshFilter = impostorRenderer.GetComponent<MeshFilter>();
 1051            SetImpostorTexture(null);
 1052        }
 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        {
 1060            EnsureImpostor();
 61
 1062            if (texture == null)
 1063                AvatarRendererHelpers.RandomizeAndApplyGenericImpostor(impostorMeshFilter.mesh, impostorRenderer.materia
 64            else
 065                AvatarRendererHelpers.SetImpostorTexture(texture, impostorMeshFilter.mesh, impostorRenderer.material);
 066        }
 67
 68        public void SetImpostorTint(Color color)
 69        {
 270            EnsureImpostor();
 71
 272            AvatarRendererHelpers.SetImpostorTintColor(impostorRenderer.material, color);
 273        }
 74
 75        public void Bind(Renderer combinedAvatar)
 76        {
 077            this.combinedAvatar = combinedAvatar;
 078            SetLodIndex(lodIndex, true);
 079        }
 80
 81        public void SetLodIndex(int lodIndex, bool inmediate = false)
 82        {
 083            if (inmediate)
 84            {
 085                avatarAlpha = lodIndex <= 1 ? 1f : 0f;
 086                UpdateSSAO(combinedAvatar, lodIndex);
 087                UpdateAlpha(avatarAlpha);
 088                UpdateMovementLerping(lodIndex);
 89
 090                if (avatarAlpha > 0)
 091                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 92                else
 093                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 94
 095                if (lodIndex == 0)
 096                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 97                else
 098                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 99
 0100                SetImpostorEnabled(avatarAlpha == 0);
 0101                return;
 102            }
 103
 0104            if (lodIndex == this.lodIndex)
 0105                return;
 106
 0107            this.lodIndex = lodIndex;
 108
 0109            transitionCTS?.Cancel();
 0110            transitionCTS?.Dispose();
 0111            transitionCTS = new CancellationTokenSource();
 0112            Transition(transitionCTS.Token);
 0113        }
 114
 115        private Renderer CreateImpostor()
 116        {
 10117            GameObject quadImpostorContainer = Object.Instantiate(Resources.Load<GameObject>("QuadImpostor"), impostorCo
 10118            return quadImpostorContainer.GetComponent<Renderer>();
 119        }
 120
 121        private async UniTaskVoid Transition(CancellationToken ct)
 122        {
 0123            ct.ThrowIfCancellationRequested();
 0124            EnsureImpostor();
 125
 126            try
 127            {
 0128                visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 0129                impostorRenderer.enabled = true;
 0130                float targetAvatarAlpha = lodIndex <= 1 ? 1f : 0f;
 0131                while (!Mathf.Approximately(targetAvatarAlpha, avatarAlpha))
 132                {
 0133                    avatarAlpha = Mathf.MoveTowards(avatarAlpha, targetAvatarAlpha, (1f / TRANSITION_DURATION) * Time.de
 0134                    UpdateAlpha(avatarAlpha);
 0135                    await UniTask.NextFrame(ct);
 136                }
 137
 0138                UpdateSSAO(combinedAvatar, lodIndex);
 0139                UpdateMovementLerping(lodIndex);
 140
 0141                if (avatarAlpha > 0)
 0142                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 143                else
 0144                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 145
 0146                if (lodIndex == 0)
 0147                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 148                else
 0149                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 150
 0151                SetImpostorEnabled(avatarAlpha == 0);
 0152            }
 0153            catch (OperationCanceledException)
 154            {
 155                //No disposing required
 0156                throw;
 157            }
 0158        }
 159
 160        private void SetImpostorEnabled(bool enabled)
 161        {
 0162            EnsureImpostor();
 163
 0164            impostorRenderer.enabled = enabled;
 0165            billboardLookAtCameraCTS?.Cancel();
 0166            billboardLookAtCameraCTS?.Dispose();
 0167            if (enabled)
 168            {
 0169                billboardLookAtCameraCTS = new CancellationTokenSource();
 0170                BillboardLookAtCamera(billboardLookAtCameraCTS.Token);
 0171            }
 172            else
 173            {
 0174                billboardLookAtCameraCTS = null;
 175            }
 0176        }
 177
 178        internal void UpdateAlpha(float avatarAlpha)
 179        {
 3180            if (combinedAvatar == null)
 0181                return;
 182
 3183            EnsureImpostor();
 184
 3185            Material[] mats = combinedAvatar.sharedMaterials;
 12186            for (int j = 0; j < mats.Length; j++)
 187            {
 3188                mats[j].SetFloat(ShaderUtils.DitherFade, avatarAlpha);
 189            }
 190
 3191            Material impostorMaterial = impostorRenderer.material;
 192            //TODO implement dither in Unlit shader
 3193            Color current = impostorMaterial.GetColor(ShaderUtils.BaseColor);
 3194            current.a = 1f - avatarAlpha;
 3195            impostorMaterial.SetColor(ShaderUtils.BaseColor, current);
 3196        }
 197
 198        internal static void UpdateSSAO(Renderer renderer, int lodIndex)
 199        {
 3200            if (renderer == null)
 0201                return;
 202
 3203            Material[] mats = renderer.sharedMaterials;
 12204            for (int j = 0; j < mats.Length; j++)
 205            {
 3206                if (lodIndex == 0)
 1207                    mats[j].DisableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 208                else
 2209                    mats[j].EnableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 210            }
 3211        }
 212
 6213        internal void UpdateMovementLerping(int lodIndex) { avatarMovementController.SetMovementLerpWait(lodIndex >= 2 ?
 214
 215        private async UniTaskVoid BillboardLookAtCamera(CancellationToken ct)
 216        {
 0217            Camera camera = Camera.main;
 0218            while (camera == null)
 219            {
 0220                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 0221                camera = Camera.main;
 222            }
 223
 0224            while (true)
 225            {
 0226                SetBillboardRotation(camera.transform);
 0227                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 228            }
 229        }
 230
 231        internal void SetBillboardRotation(Transform lookAt)
 232        {
 1233            EnsureImpostor();
 234
 1235            impostorRenderer.transform.LookAt(lookAt);
 1236            impostorRenderer.transform.eulerAngles = Vector3.Scale(impostorContainer.transform.eulerAngles, Vector3.up);
 1237        }
 238
 239        public void Dispose()
 240        {
 690241            transitionCTS?.Cancel();
 690242            transitionCTS?.Dispose();
 690243            transitionCTS = null;
 244
 690245            billboardLookAtCameraCTS?.Cancel();
 690246            billboardLookAtCameraCTS?.Dispose();
 690247            billboardLookAtCameraCTS = null;
 248
 690249            if (impostorRenderer != null)
 0250                Object.Destroy(impostorRenderer.gameObject);
 690251        }
 252    }
 253
 254    public class NoLODs : ILOD
 255    {
 256        public int lodIndex { get; }
 257
 258        public void Bind(Renderer combinedAvatar) { }
 259        public void SetLodIndex(int lodIndex, bool inmediate = false) { }
 260        public void SetImpostorTexture(Texture2D texture) { }
 261        public void SetImpostorTint(Color color) { }
 262
 263        public void Dispose() { }
 264    }
 265}