< 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:64
Coverable lines:117
Total lines:258
Line coverage:45.2% (53 of 117)
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 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>
 43221        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;
 43234        private string VISIBILITY_CONSTRAIN_IN_IMPOSTOR = "in_impostor";
 43235        private string VISIBILITY_CONSTRAIN_IN_LOD1 = "in_LOD1";
 43236        BaseVariable<Transform> cameraTransform = DataStore.i.camera.transform;
 37
 43238        public LOD(GameObject impostorContainer, IVisibility visibility, IAvatarMovementController avatarMovementControl
 39        {
 43240            this.impostorContainer = impostorContainer;
 43241            this.visibility = visibility;
 42            // TODO Once the AvatarMovementController is completly ported into the AvatarSystem we can decouple it from 
 43243            this.avatarMovementController = avatarMovementController;
 43244        }
 45
 46        internal void EnsureImpostor()
 47        {
 2648            if (impostorRenderer != null && impostorMeshFilter != null)
 1649                return;
 1050            impostorRenderer = CreateImpostor();
 1051            impostorMeshFilter = impostorRenderer.GetComponent<MeshFilter>();
 1052            SetImpostorTexture(null);
 1053        }
 54
 55        /// <summary>
 56        /// Set the impostor texture (null will take a randomized one)
 57        /// </summary>
 58        /// <param name="texture"></param>
 59        public void SetImpostorTexture(Texture2D texture)
 60        {
 1061            EnsureImpostor();
 62
 1063            if (texture == null)
 1064                AvatarRendererHelpers.RandomizeAndApplyGenericImpostor(impostorMeshFilter.mesh, impostorRenderer.materia
 65            else
 066                AvatarRendererHelpers.SetImpostorTexture(texture, impostorMeshFilter.mesh, impostorRenderer.material);
 067        }
 68
 69        public void SetImpostorTint(Color color)
 70        {
 271            EnsureImpostor();
 72
 273            AvatarRendererHelpers.SetImpostorTintColor(impostorRenderer.material, color);
 274        }
 75
 76        public void Bind(Renderer combinedAvatar)
 77        {
 078            this.combinedAvatar = combinedAvatar;
 079            SetLodIndex(lodIndex, true);
 080        }
 81
 82        public void SetLodIndex(int lodIndex, bool inmediate = false)
 83        {
 084            if (inmediate)
 85            {
 086                avatarAlpha = lodIndex <= 1 ? 1f : 0f;
 087                UpdateSSAO(combinedAvatar, lodIndex);
 088                UpdateAlpha(avatarAlpha);
 089                UpdateMovementLerping(lodIndex);
 90
 091                if (avatarAlpha > 0)
 092                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 93                else
 094                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 95
 096                if (lodIndex <= 0)
 097                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 98                else
 099                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 100
 0101                SetImpostorEnabled(avatarAlpha == 0);
 0102                return;
 103            }
 104
 0105            if (lodIndex == this.lodIndex)
 0106                return;
 107
 0108            this.lodIndex = lodIndex;
 109
 0110            transitionCTS?.Cancel();
 0111            transitionCTS?.Dispose();
 0112            transitionCTS = new CancellationTokenSource();
 0113            Transition(transitionCTS.Token);
 0114        }
 115
 116        private Renderer CreateImpostor()
 117        {
 10118            GameObject quadImpostorContainer = Object.Instantiate(Resources.Load<GameObject>("QuadImpostor"), impostorCo
 10119            return quadImpostorContainer.GetComponent<Renderer>();
 120        }
 121
 122        private async UniTaskVoid Transition(CancellationToken ct)
 123        {
 0124            ct.ThrowIfCancellationRequested();
 0125            EnsureImpostor();
 126
 127            try
 128            {
 0129                visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 0130                impostorRenderer.enabled = true;
 0131                float targetAvatarAlpha = lodIndex <= 1 ? 1f : 0f;
 0132                while (!Mathf.Approximately(targetAvatarAlpha, avatarAlpha))
 133                {
 0134                    avatarAlpha = Mathf.MoveTowards(avatarAlpha, targetAvatarAlpha, (1f / TRANSITION_DURATION) * Time.de
 0135                    UpdateAlpha(avatarAlpha);
 0136                    await UniTask.NextFrame(ct);
 137                }
 138
 0139                UpdateSSAO(combinedAvatar, lodIndex);
 0140                UpdateMovementLerping(lodIndex);
 141
 0142                if (avatarAlpha > 0)
 0143                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 144                else
 0145                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 146
 0147                if (lodIndex <= 0)
 0148                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 149                else
 0150                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 151
 0152                SetImpostorEnabled(avatarAlpha == 0);
 0153            }
 0154            catch (OperationCanceledException)
 155            {
 156                //No disposing required
 0157                throw;
 158            }
 0159        }
 160
 161        private void SetImpostorEnabled(bool enabled)
 162        {
 0163            EnsureImpostor();
 164
 0165            impostorRenderer.enabled = enabled;
 0166            billboardLookAtCameraCTS?.Cancel();
 0167            billboardLookAtCameraCTS?.Dispose();
 0168            if (enabled)
 169            {
 0170                billboardLookAtCameraCTS = new CancellationTokenSource();
 0171                BillboardLookAtCamera(billboardLookAtCameraCTS.Token);
 0172            }
 173            else
 174            {
 0175                billboardLookAtCameraCTS = null;
 176            }
 0177        }
 178
 179        internal void UpdateAlpha(float avatarAlpha)
 180        {
 3181            if (combinedAvatar == null)
 0182                return;
 183
 3184            EnsureImpostor();
 185
 3186            Material[] mats = combinedAvatar.sharedMaterials;
 12187            for (int j = 0; j < mats.Length; j++)
 188            {
 3189                mats[j].SetFloat(ShaderUtils.DitherFade, avatarAlpha);
 190            }
 191
 3192            Material impostorMaterial = impostorRenderer.material;
 193            //TODO implement dither in Unlit shader
 3194            Color current = impostorMaterial.GetColor(ShaderUtils.BaseColor);
 3195            current.a = 1f - avatarAlpha;
 3196            impostorMaterial.SetColor(ShaderUtils.BaseColor, current);
 3197        }
 198
 199        internal static void UpdateSSAO(Renderer renderer, int lodIndex)
 200        {
 3201            if (renderer == null)
 0202                return;
 203
 3204            Material[] mats = renderer.sharedMaterials;
 12205            for (int j = 0; j < mats.Length; j++)
 206            {
 3207                if (lodIndex <= 0)
 1208                    mats[j].DisableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 209                else
 2210                    mats[j].EnableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 211            }
 3212        }
 213
 6214        internal void UpdateMovementLerping(int lodIndex) { avatarMovementController.SetMovementLerpWait(lodIndex >= 2 ?
 215
 216        private async UniTaskVoid BillboardLookAtCamera(CancellationToken ct)
 217        {
 0218            while (true)
 219            {
 0220                SetBillboardRotation(cameraTransform.Get());
 0221                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 222            }
 223        }
 224
 225        internal void SetBillboardRotation(Transform lookAt)
 226        {
 1227            EnsureImpostor();
 1228            impostorRenderer.transform.LookAt(lookAt);
 1229            impostorRenderer.transform.eulerAngles = Vector3.Scale(impostorRenderer.transform.eulerAngles, Vector3.up);
 1230        }
 231
 232        public void Dispose()
 233        {
 424234            transitionCTS?.Cancel();
 424235            transitionCTS?.Dispose();
 424236            transitionCTS = null;
 237
 424238            billboardLookAtCameraCTS?.Cancel();
 424239            billboardLookAtCameraCTS?.Dispose();
 424240            billboardLookAtCameraCTS = null;
 241
 424242            if (impostorRenderer != null)
 0243                Object.Destroy(impostorRenderer.gameObject);
 424244        }
 245    }
 246
 247    public class NoLODs : ILOD
 248    {
 249        public int lodIndex { get; }
 250
 251        public void Bind(Renderer combinedAvatar) { }
 252        public void SetLodIndex(int lodIndex, bool inmediate = false) { }
 253        public void SetImpostorTexture(Texture2D texture) { }
 254        public void SetImpostorTint(Color color) { }
 255
 256        public void Dispose() { }
 257    }
 258}