< Summary

Class:AvatarSystem.LOD
Assembly:AvatarSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/LOD.cs
Covered lines:17
Uncovered lines:99
Coverable lines:116
Total lines:247
Line coverage:14.6% (17 of 116)
Covered branches:0
Total branches:0
Covered methods:4
Total methods:17
Method coverage:23.5% (4 of 17)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
LOD(...)0%110100%
EnsureImpostor()0%12300%
SetImpostorTexture(...)0%6200%
SetImpostorTint(...)0%2100%
Bind(...)0%2100%
SetLodIndex(...)0%90900%
CreateImpostor()0%2100%
Transition()0%90900%
SetImpostorEnabled(...)0%20400%
UpdateAlpha(...)0%12300%
UpdateSSAO(...)0%20400%
UpdateMovementLerping(...)0%12300%
BillboardLookAtCamera()0%12300%
SetBillboardRotation(...)0%2100%
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 DCL.Shaders;
 7using UnityEngine;
 8using Object = UnityEngine.Object;
 9
 10namespace AvatarSystem
 11{
 12    public class LOD : ILOD
 13    {
 14        public const float IMPOSTOR_MOVEMENT_INTERPOLATION = 1.79f;
 15        private const float TRANSITION_DURATION = 0.75f;
 16
 17        /// <summary>
 18        /// 0 = 3D Avatar, SSAO, Facial Features
 19        /// 1 = 3D Avatar, NO SSAO, NO Facial Features
 20        /// 2 = Impostor, NO 3D Avatar
 21        /// </summary>
 31022        public int lodIndex { get; private set; } = -1;
 23
 24        private readonly GameObject impostorContainer;
 25        private readonly IVisibility visibility;
 26        private readonly IAvatarMovementController avatarMovementController;
 27
 28        internal Renderer combinedAvatar;
 29        internal Renderer impostorRenderer;
 30        internal MeshFilter impostorMeshFilter;
 31        private float avatarAlpha;
 32
 33        private CancellationTokenSource transitionCTS;
 34        private CancellationTokenSource billboardLookAtCameraCTS;
 31035        private string VISIBILITY_CONSTRAIN_IN_IMPOSTOR = "in_impostor";
 31036        private string VISIBILITY_CONSTRAIN_IN_LOD1 = "in_LOD1";
 31037        BaseVariable<Transform> cameraTransform = DataStore.i.camera.transform;
 38
 31039        public LOD(GameObject impostorContainer, IVisibility visibility, IAvatarMovementController avatarMovementControl
 40        {
 31041            this.impostorContainer = impostorContainer;
 31042            this.visibility = visibility;
 43            // TODO Once the AvatarMovementController is completly ported into the AvatarSystem we can decouple it from 
 31044            this.avatarMovementController = avatarMovementController;
 31045        }
 46
 47        internal void EnsureImpostor()
 48        {
 049            if (impostorRenderer != null && impostorMeshFilter != null)
 050                return;
 051            impostorRenderer = CreateImpostor();
 052            impostorMeshFilter = impostorRenderer.GetComponent<MeshFilter>();
 053            SetImpostorTexture(null);
 054        }
 55
 56        /// <summary>
 57        /// Set the impostor texture (null will take a randomized one)
 58        /// </summary>
 59        /// <param name="texture"></param>
 60        public void SetImpostorTexture(Texture2D texture)
 61        {
 062            EnsureImpostor();
 63
 064            if (texture == null)
 065                AvatarRendererHelpers.RandomizeAndApplyGenericImpostor(impostorMeshFilter.mesh, impostorRenderer.materia
 66            else
 067                AvatarRendererHelpers.SetImpostorTexture(texture, impostorMeshFilter.mesh, impostorRenderer.material);
 068        }
 69
 70        public void SetImpostorTint(Color color)
 71        {
 072            EnsureImpostor();
 73
 074            AvatarRendererHelpers.SetImpostorTintColor(impostorRenderer.material, color);
 075        }
 76
 77        public void Bind(Renderer combinedAvatar)
 78        {
 079            this.combinedAvatar = combinedAvatar;
 080            SetLodIndex(lodIndex, true);
 081        }
 82
 83        public void SetLodIndex(int lodIndex, bool inmediate = false)
 84        {
 085            if (inmediate)
 86            {
 087                avatarAlpha = lodIndex <= 1 ? 1f : 0f;
 088                UpdateSSAO(combinedAvatar, lodIndex);
 089                UpdateAlpha(avatarAlpha);
 090                UpdateMovementLerping(lodIndex);
 91
 092                if (avatarAlpha > 0)
 093                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 94                else
 095                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 96
 097                if (lodIndex <= 0)
 098                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 99                else
 0100                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 101
 0102                SetImpostorEnabled(avatarAlpha == 0);
 0103                return;
 104            }
 105
 0106            if (lodIndex == this.lodIndex)
 0107                return;
 108
 0109            this.lodIndex = lodIndex;
 110
 0111            transitionCTS?.Cancel();
 0112            transitionCTS?.Dispose();
 0113            transitionCTS = new CancellationTokenSource();
 0114            Transition(transitionCTS.Token);
 0115        }
 116
 117        private Renderer CreateImpostor()
 118        {
 0119            GameObject quadImpostorContainer = Object.Instantiate(Resources.Load<GameObject>("QuadImpostor"), impostorCo
 0120            return quadImpostorContainer.GetComponent<Renderer>();
 121        }
 122
 123        private async UniTaskVoid Transition(CancellationToken ct)
 124        {
 0125            ct.ThrowIfCancellationRequested();
 0126            EnsureImpostor();
 127
 128            try
 129            {
 0130                visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 0131                impostorRenderer.enabled = true;
 0132                float targetAvatarAlpha = lodIndex <= 1 ? 1f : 0f;
 0133                while (!Mathf.Approximately(targetAvatarAlpha, avatarAlpha))
 134                {
 0135                    avatarAlpha = Mathf.MoveTowards(avatarAlpha, targetAvatarAlpha, (1f / TRANSITION_DURATION) * Time.de
 0136                    UpdateAlpha(avatarAlpha);
 0137                    await UniTask.NextFrame(ct);
 138                }
 139
 0140                UpdateSSAO(combinedAvatar, lodIndex);
 0141                UpdateMovementLerping(lodIndex);
 142
 0143                if (avatarAlpha > 0)
 0144                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 145                else
 0146                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 147
 0148                if (lodIndex <= 0)
 0149                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 150                else
 0151                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 152
 0153                SetImpostorEnabled(avatarAlpha == 0);
 0154            }
 0155            catch (OperationCanceledException)
 156            {
 157                //No disposing required
 0158                throw;
 159            }
 0160        }
 161
 162        private void SetImpostorEnabled(bool enabled)
 163        {
 0164            EnsureImpostor();
 165
 0166            impostorRenderer.enabled = enabled;
 0167            billboardLookAtCameraCTS?.Cancel();
 0168            billboardLookAtCameraCTS?.Dispose();
 0169            if (enabled)
 170            {
 0171                billboardLookAtCameraCTS = new CancellationTokenSource();
 0172                BillboardLookAtCamera(billboardLookAtCameraCTS.Token);
 173            }
 174            else
 175            {
 0176                billboardLookAtCameraCTS = null;
 177            }
 0178        }
 179
 180        internal void UpdateAlpha(float avatarAlpha)
 181        {
 0182            if (combinedAvatar == null)
 0183                return;
 184
 0185            EnsureImpostor();
 186
 0187            Material[] mats = combinedAvatar.sharedMaterials;
 0188            for (int j = 0; j < mats.Length; j++)
 189            {
 0190                mats[j].SetFloat(ShaderUtils.DitherFade, avatarAlpha);
 191            }
 192
 0193            Material impostorMaterial = impostorRenderer.material;
 194            //TODO implement dither in Unlit shader
 0195            Color current = impostorMaterial.GetColor(ShaderUtils.BaseColor);
 0196            current.a = 1f - avatarAlpha;
 0197            impostorMaterial.SetColor(ShaderUtils.BaseColor, current);
 0198        }
 199
 200        internal static void UpdateSSAO(Renderer renderer, int lodIndex)
 201        {
 0202            if (renderer == null)
 0203                return;
 204
 0205            Material[] mats = renderer.sharedMaterials;
 0206            for (int j = 0; j < mats.Length; j++)
 207            {
 0208                if (lodIndex <= 0)
 0209                    mats[j].DisableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 210                else
 0211                    mats[j].EnableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 212            }
 0213        }
 214
 0215        internal void UpdateMovementLerping(int lodIndex) { avatarMovementController.SetMovementLerpWait(lodIndex >= 2 ?
 216
 217        private async UniTaskVoid BillboardLookAtCamera(CancellationToken ct)
 218        {
 0219            while (true)
 220            {
 0221                SetBillboardRotation(cameraTransform.Get());
 0222                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 223            }
 224        }
 225
 226        internal void SetBillboardRotation(Transform lookAt)
 227        {
 0228            EnsureImpostor();
 0229            impostorRenderer.transform.LookAt(lookAt);
 0230            impostorRenderer.transform.eulerAngles = Vector3.Scale(impostorRenderer.transform.eulerAngles, Vector3.up);
 0231        }
 232
 233        public void Dispose()
 234        {
 219235            transitionCTS?.Cancel();
 219236            transitionCTS?.Dispose();
 219237            transitionCTS = null;
 238
 219239            billboardLookAtCameraCTS?.Cancel();
 219240            billboardLookAtCameraCTS?.Dispose();
 219241            billboardLookAtCameraCTS = null;
 242
 219243            if (impostorRenderer != null)
 0244                Object.Destroy(impostorRenderer.gameObject);
 219245        }
 246    }
 247}