< Summary

Class:AvatarSystem.Avatar
Assembly:AvatarSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/Avatar.cs
Covered lines:23
Uncovered lines:67
Coverable lines:90
Total lines:218
Line coverage:25.5% (23 of 90)
Covered branches:0
Total branches:0
Covered methods:6
Total methods:26
Method coverage:23% (6 of 26)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Avatar(...)0%110100%
Load()0%30500%
LoadTry()0%56700%
GetDeepParentOf(...)0%20400%
LoadWearables()0%42600%
Prepare(...)0%2100%
Bind()0%2100%
Inform(...)0%6200%
AddVisibilityConstraint(...)0%2100%
RemoveVisibilityConstrain(...)0%2100%
GetEmotesController()0%110100%
SetLODLevel(...)0%2100%
SetAnimationThrottling(...)0%2100%
SetImpostorTexture(...)0%2100%
SetImpostorTint(...)0%2100%
GetBones()0%2100%
GetMainRenderer()0%2100%
Dispose()0%330100%
CancelAndRestoreOngoingProcessesExceptTheLoading()0%660100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/Avatar.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Runtime.ExceptionServices;
 4using System.Threading;
 5using Cysharp.Threading.Tasks;
 6using DCL;
 7using DCL.Emotes;
 8using DCL.Tasks;
 9using GPUSkinning;
 10using UnityEngine;
 11
 12namespace AvatarSystem
 13{
 14    // [ADR 65 - https://github.com/decentraland/adr]
 15    public class Avatar : IAvatar
 16    {
 17        private const string NEW_CDN_FF = "ab-new-cdn";
 18
 19        private const float RESCALING_BOUNDS_FACTOR = 100f;
 20        internal const string LOADING_VISIBILITY_CONSTRAIN = "Loading";
 21
 22        protected readonly ILoader loader;
 23        protected readonly IVisibility visibility;
 24
 25        private readonly IAvatarCurator avatarCurator;
 26        private readonly ILOD lod;
 27        private readonly IGPUSkinning gpuSkinning;
 28        private readonly IGPUSkinningThrottlerService gpuSkinningThrottlerService;
 29        protected readonly IAvatarEmotesController emotesController;
 30
 31        private CancellationTokenSource loadCancellationToken;
 52132        public IAvatar.Status status { get; private set; } = IAvatar.Status.Idle;
 033        public Vector3 extents { get; private set; }
 034        public int lodLevel => lod?.lodIndex ?? 0;
 35        public event Action<Renderer> OnCombinedRendererUpdate;
 036        private FeatureFlag featureFlags => DataStore.i.featureFlags.flags.Get();
 37
 51838        internal Avatar(IAvatarCurator avatarCurator, ILoader loader,
 39            IVisibility visibility, ILOD lod, IGPUSkinning gpuSkinning, IGPUSkinningThrottlerService gpuSkinningThrottle
 40            IAvatarEmotesController emotesController)
 41        {
 51842            this.avatarCurator = avatarCurator;
 51843            this.loader = loader;
 51844            this.visibility = visibility;
 51845            this.lod = lod;
 51846            this.gpuSkinning = gpuSkinning;
 51847            this.gpuSkinningThrottlerService = gpuSkinningThrottlerService;
 51848            this.emotesController = emotesController;
 51849        }
 50
 51        /// <summary>
 52        /// Starts the loading process for the Avatar.
 53        /// </summary>
 54        /// <param name="wearablesIds"></param>
 55        /// <param name="settings"></param>
 56        /// <param name="ct"></param>
 57        public async UniTask Load(List<string> wearablesIds, List<string> emotesIds, AvatarSettings settings, Cancellati
 58        {
 059            status = IAvatar.Status.Idle;
 60
 061            loadCancellationToken = loadCancellationToken.SafeRestart();
 062            CancellationToken linkedCt = CancellationTokenSource.CreateLinkedTokenSource(ct, loadCancellationToken.Token
 63
 064            try { await LoadTry(wearablesIds, emotesIds, settings, linkedCt); }
 065            catch (OperationCanceledException)
 66            {
 67                // Cancel any ongoing process except the current loadCancellationToken
 68                // since it was provoking a double cancellation thus inconsistencies in the flow
 69                // TODO: disposing collaborators is an anti-pattern in the current context. Disposed objects should not 
 070                CancelAndRestoreOngoingProcessesExceptTheLoading();
 71
 072                throw;
 73            }
 074            catch (Exception e)
 75            {
 76                // Cancel any ongoing process except the current loadCancellationToken
 77                // since it was provoking a double cancellation thus inconsistencies in the flow
 78                // TODO: disposing collaborators is an anti-pattern in the current context. Disposed objects should not 
 079                CancelAndRestoreOngoingProcessesExceptTheLoading();
 80
 081                Debug.Log($"Avatar.Load failed with wearables:[{string.Join(",", wearablesIds)}] " +
 82                          $"for bodyshape:{settings.bodyshapeId} and player {settings.playerName}");
 83
 084                if (e.InnerException != null)
 085                    ExceptionDispatchInfo.Capture(e.InnerException).Throw();
 86                else
 087                    throw;
 088            }
 089        }
 90
 91        protected virtual async UniTask LoadTry(List<string> wearablesIds, List<string> emotesIds, AvatarSettings settin
 92        {
 093            List<WearableItem> emotes = await LoadWearables(wearablesIds, emotesIds, settings, linkedCt: linkedCt);
 94
 095            GameObject container = loader.bodyshapeContainer;
 96
 097            if (featureFlags.IsFeatureEnabled(NEW_CDN_FF))
 98            {
 099                if (loader.bodyshapeContainer.transform.childCount > 0)
 100                {
 0101                    loader.bodyshapeContainer.transform.Find("Armature");
 0102                    Transform child = loader.bodyshapeContainer.transform.GetChild(0);
 103
 104                    // Asset bundles assets dont have the gltf-scene name as the root, they have the file hash, for this
 0105                    child.name = "Armature";
 106                }
 107            }
 108            else
 109            {
 0110                GameObject parent = GetDeepParentOf(loader.bodyshapeContainer, "Armature");
 0111                container = parent != null ? parent : container;
 112            }
 113
 0114            emotesController.Prepare(settings.bodyshapeId, container);
 0115            Prepare(settings, emotes);
 0116            Bind();
 0117            Inform(loader.combinedRenderer);
 0118        }
 119
 120        private GameObject GetDeepParentOf(GameObject container, string childName)
 121        {
 0122            foreach (Transform child in container.transform)
 0123                return child.name == childName ? child.parent.gameObject : GetDeepParentOf(child.gameObject, childName);
 124
 0125            return null;
 0126        }
 127
 128        protected async UniTask<List<WearableItem>> LoadWearables(List<string> wearablesIds, List<string> emotesIds, Ava
 129        {
 130            BodyWearables bodyWearables;
 131            List<WearableItem> wearables;
 132            List<WearableItem> emotes;
 133
 0134            (bodyWearables, wearables, emotes) = await avatarCurator.Curate(settings, wearablesIds, emotesIds, linkedCt)
 135
 0136            if (!loader.IsValidForBodyShape(bodyWearables))
 0137                visibility.AddGlobalConstrain(LOADING_VISIBILITY_CONSTRAIN);
 138
 0139            await loader.Load(bodyWearables, wearables, settings, bonesRenderers, linkedCt);
 0140            return emotes;
 0141        }
 142
 143        protected void Prepare(AvatarSettings settings, List<WearableItem> emotes)
 144        {
 145            //Scale the bounds due to the giant avatar not being skinned yet
 0146            extents = loader.combinedRenderer.localBounds.extents * 2f / RESCALING_BOUNDS_FACTOR;
 147
 0148            emotesController.LoadEmotes(settings.bodyshapeId, emotes);
 0149            gpuSkinning.Prepare(loader.combinedRenderer);
 0150            gpuSkinningThrottlerService.Register(gpuSkinning);
 0151        }
 152
 153        protected void Bind()
 154        {
 0155            visibility.Bind(gpuSkinning.renderer, loader.facialFeaturesRenderers);
 0156            visibility.RemoveGlobalConstrain(LOADING_VISIBILITY_CONSTRAIN);
 0157            lod.Bind(gpuSkinning.renderer);
 0158        }
 159
 160        protected void Inform(Renderer loaderCombinedRenderer)
 161        {
 0162            status = IAvatar.Status.Loaded;
 0163            OnCombinedRendererUpdate?.Invoke(loaderCombinedRenderer);
 0164        }
 165
 166        public virtual void AddVisibilityConstraint(string key)
 167        {
 0168            visibility.AddGlobalConstrain(key);
 0169            emotesController.AddVisibilityConstraint(key);
 0170        }
 171
 172        public void RemoveVisibilityConstrain(string key)
 173        {
 0174            visibility.RemoveGlobalConstrain(key);
 0175            emotesController.RemoveVisibilityConstraint(key);
 0176        }
 177
 218178        public IAvatarEmotesController GetEmotesController() => emotesController;
 179
 180        public void SetLODLevel(int lodIndex) =>
 0181            lod.SetLodIndex(lodIndex);
 182
 183        public void SetAnimationThrottling(int framesBetweenUpdate) =>
 0184            gpuSkinningThrottlerService.ModifyThrottling(gpuSkinning, framesBetweenUpdate);
 185
 186        public void SetImpostorTexture(Texture2D impostorTexture) =>
 0187            lod.SetImpostorTexture(impostorTexture);
 188
 189        public void SetImpostorTint(Color color) =>
 0190            lod.SetImpostorTint(color);
 191
 192        public Transform[] GetBones() =>
 0193            loader.GetBones();
 194
 195        public Renderer GetMainRenderer() =>
 0196            gpuSkinning.renderer;
 197
 0198        public IReadOnlyList<SkinnedMeshRenderer> originalVisibleRenderers => loader.originalVisibleRenderers;
 199
 200        public void Dispose()
 201        {
 521202            loadCancellationToken?.Cancel();
 521203            loadCancellationToken?.Dispose();
 521204            loadCancellationToken = null;
 521205            CancelAndRestoreOngoingProcessesExceptTheLoading();
 521206        }
 207
 208        private void CancelAndRestoreOngoingProcessesExceptTheLoading()
 209        {
 521210            status = IAvatar.Status.Idle;
 521211            avatarCurator?.Dispose();
 521212            loader?.Dispose();
 521213            visibility?.Dispose();
 521214            lod?.Dispose();
 521215            gpuSkinningThrottlerService?.Unregister(gpuSkinning);
 521216        }
 217    }
 218}