< 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:258
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        BaseVariable<Transform> cameraTransform = DataStore.i.camera.transform;
 37
 38        public LOD(GameObject impostorContainer, IVisibility visibility, IAvatarMovementController avatarMovementControl
 39        {
 40            this.impostorContainer = impostorContainer;
 41            this.visibility = visibility;
 42            // TODO Once the AvatarMovementController is completly ported into the AvatarSystem we can decouple it from 
 43            this.avatarMovementController = avatarMovementController;
 44        }
 45
 46        internal void EnsureImpostor()
 47        {
 48            if (impostorRenderer != null && impostorMeshFilter != null)
 49                return;
 50            impostorRenderer = CreateImpostor();
 51            impostorMeshFilter = impostorRenderer.GetComponent<MeshFilter>();
 52            SetImpostorTexture(null);
 53        }
 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        {
 61            EnsureImpostor();
 62
 63            if (texture == null)
 64                AvatarRendererHelpers.RandomizeAndApplyGenericImpostor(impostorMeshFilter.mesh, impostorRenderer.materia
 65            else
 66                AvatarRendererHelpers.SetImpostorTexture(texture, impostorMeshFilter.mesh, impostorRenderer.material);
 67        }
 68
 69        public void SetImpostorTint(Color color)
 70        {
 71            EnsureImpostor();
 72
 73            AvatarRendererHelpers.SetImpostorTintColor(impostorRenderer.material, color);
 74        }
 75
 76        public void Bind(Renderer combinedAvatar)
 77        {
 78            this.combinedAvatar = combinedAvatar;
 79            SetLodIndex(lodIndex, true);
 80        }
 81
 82        public void SetLodIndex(int lodIndex, bool inmediate = false)
 83        {
 84            if (inmediate)
 85            {
 86                avatarAlpha = lodIndex <= 1 ? 1f : 0f;
 87                UpdateSSAO(combinedAvatar, lodIndex);
 88                UpdateAlpha(avatarAlpha);
 89                UpdateMovementLerping(lodIndex);
 90
 91                if (avatarAlpha > 0)
 92                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 93                else
 94                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 95
 96                if (lodIndex <= 0)
 97                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 98                else
 99                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 100
 101                SetImpostorEnabled(avatarAlpha == 0);
 102                return;
 103            }
 104
 105            if (lodIndex == this.lodIndex)
 106                return;
 107
 108            this.lodIndex = lodIndex;
 109
 110            transitionCTS?.Cancel();
 111            transitionCTS?.Dispose();
 112            transitionCTS = new CancellationTokenSource();
 113            Transition(transitionCTS.Token);
 114        }
 115
 116        private Renderer CreateImpostor()
 117        {
 118            GameObject quadImpostorContainer = Object.Instantiate(Resources.Load<GameObject>("QuadImpostor"), impostorCo
 119            return quadImpostorContainer.GetComponent<Renderer>();
 120        }
 121
 122        private async UniTaskVoid Transition(CancellationToken ct)
 123        {
 124            ct.ThrowIfCancellationRequested();
 125            EnsureImpostor();
 126
 127            try
 128            {
 129                visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 130                impostorRenderer.enabled = true;
 131                float targetAvatarAlpha = lodIndex <= 1 ? 1f : 0f;
 132                while (!Mathf.Approximately(targetAvatarAlpha, avatarAlpha))
 133                {
 134                    avatarAlpha = Mathf.MoveTowards(avatarAlpha, targetAvatarAlpha, (1f / TRANSITION_DURATION) * Time.de
 135                    UpdateAlpha(avatarAlpha);
 136                    await UniTask.NextFrame(ct);
 137                }
 138
 139                UpdateSSAO(combinedAvatar, lodIndex);
 140                UpdateMovementLerping(lodIndex);
 141
 142                if (avatarAlpha > 0)
 143                    visibility.RemoveCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 144                else
 145                    visibility.AddCombinedRendererConstrain(VISIBILITY_CONSTRAIN_IN_IMPOSTOR);
 146
 147                if (lodIndex <= 0)
 148                    visibility.RemoveFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 149                else
 150                    visibility.AddFacialFeaturesConstrain(VISIBILITY_CONSTRAIN_IN_LOD1);
 151
 152                SetImpostorEnabled(avatarAlpha == 0);
 153            }
 154            catch (OperationCanceledException)
 155            {
 156                //No disposing required
 157                throw;
 158            }
 159        }
 160
 161        private void SetImpostorEnabled(bool enabled)
 162        {
 163            EnsureImpostor();
 164
 165            impostorRenderer.enabled = enabled;
 166            billboardLookAtCameraCTS?.Cancel();
 167            billboardLookAtCameraCTS?.Dispose();
 168            if (enabled)
 169            {
 170                billboardLookAtCameraCTS = new CancellationTokenSource();
 171                BillboardLookAtCamera(billboardLookAtCameraCTS.Token);
 172            }
 173            else
 174            {
 175                billboardLookAtCameraCTS = null;
 176            }
 177        }
 178
 179        internal void UpdateAlpha(float avatarAlpha)
 180        {
 181            if (combinedAvatar == null)
 182                return;
 183
 184            EnsureImpostor();
 185
 186            Material[] mats = combinedAvatar.sharedMaterials;
 187            for (int j = 0; j < mats.Length; j++)
 188            {
 189                mats[j].SetFloat(ShaderUtils.DitherFade, avatarAlpha);
 190            }
 191
 192            Material impostorMaterial = impostorRenderer.material;
 193            //TODO implement dither in Unlit shader
 194            Color current = impostorMaterial.GetColor(ShaderUtils.BaseColor);
 195            current.a = 1f - avatarAlpha;
 196            impostorMaterial.SetColor(ShaderUtils.BaseColor, current);
 197        }
 198
 199        internal static void UpdateSSAO(Renderer renderer, int lodIndex)
 200        {
 201            if (renderer == null)
 202                return;
 203
 204            Material[] mats = renderer.sharedMaterials;
 205            for (int j = 0; j < mats.Length; j++)
 206            {
 207                if (lodIndex <= 0)
 208                    mats[j].DisableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 209                else
 210                    mats[j].EnableKeyword(ShaderUtils.SSAO_OFF_KEYWORD);
 211            }
 212        }
 213
 214        internal void UpdateMovementLerping(int lodIndex) { avatarMovementController.SetMovementLerpWait(lodIndex >= 2 ?
 215
 216        private async UniTaskVoid BillboardLookAtCamera(CancellationToken ct)
 217        {
 218            while (true)
 219            {
 220                SetBillboardRotation(cameraTransform.Get());
 221                await UniTask.WaitForEndOfFrame(ct).AttachExternalCancellation(ct);
 222            }
 223        }
 224
 225        internal void SetBillboardRotation(Transform lookAt)
 226        {
 227            EnsureImpostor();
 228            impostorRenderer.transform.LookAt(lookAt);
 229            impostorRenderer.transform.eulerAngles = Vector3.Scale(impostorRenderer.transform.eulerAngles, Vector3.up);
 230        }
 231
 232        public void Dispose()
 233        {
 234            transitionCTS?.Cancel();
 235            transitionCTS?.Dispose();
 236            transitionCTS = null;
 237
 238            billboardLookAtCameraCTS?.Cancel();
 239            billboardLookAtCameraCTS?.Dispose();
 240            billboardLookAtCameraCTS = null;
 241
 242            if (impostorRenderer != null)
 243                Object.Destroy(impostorRenderer.gameObject);
 244        }
 245    }
 246
 247    public class NoLODs : ILOD
 248    {
 0249        public int lodIndex { get; }
 250
 0251        public void Bind(Renderer combinedAvatar) { }
 0252        public void SetLodIndex(int lodIndex, bool inmediate = false) { }
 0253        public void SetImpostorTexture(Texture2D texture) { }
 0254        public void SetImpostorTint(Color color) { }
 255
 623256        public void Dispose() { }
 257    }
 258}