< Summary

Class:AvatarSystem.LOD
Assembly:AvatarSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/LOD.cs
Covered lines:53
Uncovered lines:63
Coverable lines:116
Total lines:247
Line coverage:45.6% (53 of 116)
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%12300%
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 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>
 34622        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;
 34635        private string VISIBILITY_CONSTRAIN_IN_IMPOSTOR = "in_impostor";
 34636        private string VISIBILITY_CONSTRAIN_IN_LOD1 = "in_LOD1";
 34637        BaseVariable<Transform> cameraTransform = DataStore.i.camera.transform;
 38
 34639        public LOD(GameObject impostorContainer, IVisibility visibility, IAvatarMovementController avatarMovementControl
 40        {
 34641            this.impostorContainer = impostorContainer;
 34642            this.visibility = visibility;
 43            // TODO Once the AvatarMovementController is completly ported into the AvatarSystem we can decouple it from 
 34644            this.avatarMovementController = avatarMovementController;
 34645        }
 46
 47        internal void EnsureImpostor()
 48        {
 2649            if (impostorRenderer != null && impostorMeshFilter != null)
 1650                return;
 1051            impostorRenderer = CreateImpostor();
 1052            impostorMeshFilter = impostorRenderer.GetComponent<MeshFilter>();
 1053            SetImpostorTexture(null);
 1054        }
 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        {
 1062            EnsureImpostor();
 63
 1064            if (texture == null)
 1065                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        {
 272            EnsureImpostor();
 73
 274            AvatarRendererHelpers.SetImpostorTintColor(impostorRenderer.material, color);
 275        }
 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        {
 10119            GameObject quadImpostorContainer = Object.Instantiate(Resources.Load<GameObject>("QuadImpostor"), impostorCo
 10120            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        {
 3182            if (combinedAvatar == null)
 0183                return;
 184
 3185            EnsureImpostor();
 186
 3187            Material[] mats = combinedAvatar.sharedMaterials;
 12188            for (int j = 0; j < mats.Length; j++)
 189            {
 3190                mats[j].SetFloat(ShaderUtils.DitherFade, avatarAlpha);
 191            }
 192
 3193            Material impostorMaterial = impostorRenderer.material;
 194            //TODO implement dither in Unlit shader
 3195            Color current = impostorMaterial.GetColor(ShaderUtils.BaseColor);
 3196            current.a = 1f - avatarAlpha;
 3197            impostorMaterial.SetColor(ShaderUtils.BaseColor, current);
 3198        }
 199
 200        internal static void UpdateSSAO(Renderer renderer, int lodIndex)
 201        {
 3202            if (renderer == null)
 0203                return;
 204
 3205            Material[] mats = renderer.sharedMaterials;
 12206            for (int j = 0; j < mats.Length; j++)
 207            {
 3208                if (lodIndex <= 0)
 1209                    mats[j].DisableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 210                else
 2211                    mats[j].EnableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 212            }
 3213        }
 214
 6215        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        {
 1228            EnsureImpostor();
 1229            impostorRenderer.transform.LookAt(lookAt);
 1230            impostorRenderer.transform.eulerAngles = Vector3.Scale(impostorRenderer.transform.eulerAngles, Vector3.up);
 1231        }
 232
 233        public void Dispose()
 234        {
 238235            transitionCTS?.Cancel();
 238236            transitionCTS?.Dispose();
 238237            transitionCTS = null;
 238
 238239            billboardLookAtCameraCTS?.Cancel();
 238240            billboardLookAtCameraCTS?.Dispose();
 238241            billboardLookAtCameraCTS = null;
 242
 238243            if (impostorRenderer != null)
 0244                Object.Destroy(impostorRenderer.gameObject);
 238245        }
 246    }
 247}