< Summary

Class:MainScripts.DCL.Controllers.HUD.CharacterPreview.CharacterPreviewController
Assembly:CharacterPreviewController
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/CharacterPreview/CharacterPreviewController.cs
Covered lines:41
Uncovered lines:55
Coverable lines:96
Total lines:254
Line coverage:42.7% (41 of 96)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
CharacterPreviewController()0%110100%
Awake()0%110100%
SetEnabled(...)0%110100%
Initialize(...)0%4.374071.43%
TryUpdateModelAsync()0%72800%
OnDestroy()0%440100%
CreateAvatar()0%110100%
CreateAvatarWithHologram()0%2100%
UpdateModelAsync()0%56700%
TakeSnapshots(...)0%12300%
TakeSnapshots_Routine()0%30500%
Snapshot(...)0%2100%
SetFocus(...)0%110100%
SetFocus(...)0%4.034087.5%
CameraTransition()0%4.134080%
Rotate(...)0%2100%
ResetRotation()0%2100%
PlayEmote(...)0%110100%
Dispose()0%110100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Controllers/HUD/CharacterPreview/CharacterPreviewController.cs

#LineLine coverage
 1using AvatarSystem;
 2using Cysharp.Threading.Tasks;
 3using DCL;
 4using System;
 5using System.Collections;
 6using System.Collections.Generic;
 7using System.Linq;
 8using System.Threading;
 9using UnityEngine;
 10
 11namespace MainScripts.DCL.Controllers.HUD.CharacterPreview
 12{
 13    public class CharacterPreviewController : MonoBehaviour, ICharacterPreviewController
 14    {
 15        private const int SNAPSHOT_BODY_WIDTH_RES = 256;
 16        private const int SNAPSHOT_BODY_HEIGHT_RES = 512;
 17
 18        private const int SNAPSHOT_FACE_256_WIDTH_RES = 256;
 19        private const int SNAPSHOT_FACE_256_HEIGHT_RES = 256;
 20
 21        private const int SUPERSAMPLING = 1;
 22        private const float CAMERA_TRANSITION_TIME = 0.5f;
 23
 24        public delegate void OnSnapshotsReady(Texture2D face256, Texture2D body);
 25
 26        public enum CameraFocus
 27        {
 28            DefaultEditing,
 29            FaceEditing,
 30            FaceSnapshot,
 31            BodySnapshot,
 32            Preview
 33        }
 34
 35        private Dictionary<CameraFocus, Transform> cameraFocusLookUp;
 36
 37        [SerializeField] private new Camera camera;
 38        [SerializeField] private Transform defaultEditingTemplate;
 39        [SerializeField] private Transform faceEditingTemplate;
 40
 41        [SerializeField] private Transform faceSnapshotTemplate;
 42        [SerializeField] private Transform bodySnapshotTemplate;
 43        [SerializeField] private Transform previewTemplate;
 44
 45        [SerializeField] private GameObject avatarContainer;
 46        [SerializeField] private Transform avatarRevealContainer;
 47
 48        private Service<IAvatarFactory> avatarFactory;
 49
 50        private IAvatar avatar;
 4951        private readonly AvatarModel currentAvatarModel = new () { wearables = new List<string>() };
 4952        private CancellationTokenSource loadingCts = new ();
 53
 54        private IAnimator animator;
 55        private Quaternion avatarContainerDefaultRotation;
 56
 57        private void Awake()
 58        {
 4859            cameraFocusLookUp = new Dictionary<CameraFocus, Transform>()
 60            {
 61                { CameraFocus.DefaultEditing, defaultEditingTemplate },
 62                { CameraFocus.FaceEditing, faceEditingTemplate },
 63                { CameraFocus.FaceSnapshot, faceSnapshotTemplate },
 64                { CameraFocus.BodySnapshot, bodySnapshotTemplate },
 65                { CameraFocus.Preview, previewTemplate }
 66            };
 67
 4868            this.animator = GetComponentInChildren<IAnimator>();
 4869            avatarContainerDefaultRotation = avatarContainer.transform.rotation;
 4870        }
 71
 72        public void SetEnabled(bool isEnabled)
 73        {
 14074            gameObject.SetActive(isEnabled);
 14075            camera.enabled = isEnabled;
 14076        }
 77
 78        public void Initialize(CharacterPreviewMode loadingMode, RenderTexture targetTexture)
 79        {
 4880            avatar?.Dispose();
 4881            avatar = loadingMode switch
 82                     {
 083                         CharacterPreviewMode.WithHologram => CreateAvatarWithHologram(),
 4884                         CharacterPreviewMode.WithoutHologram => CreateAvatar(),
 085                         _ => avatar,
 86                     };
 87
 4888            camera.targetTexture = targetTexture;
 4889        }
 90
 91        public async UniTask TryUpdateModelAsync(AvatarModel newModel, CancellationToken cancellationToken = default)
 92        {
 093            if (newModel.HaveSameWearablesAndColors(currentAvatarModel) && avatar.status == IAvatar.Status.Loaded)
 094                return;
 95
 096            loadingCts?.Cancel();
 097            loadingCts?.Dispose();
 098            loadingCts = new CancellationTokenSource();
 99
 0100            cancellationToken = cancellationToken == default
 101                ? loadingCts.Token
 102                : CancellationTokenSource.CreateLinkedTokenSource(loadingCts.Token, cancellationToken).Token;
 103
 104
 0105            await UpdateModelAsync(newModel, cancellationToken);
 0106        }
 107
 108        private void OnDestroy()
 109        {
 48110            loadingCts?.Cancel();
 48111            loadingCts?.Dispose();
 48112            loadingCts = null;
 48113            avatar?.Dispose();
 48114        }
 115
 116        private IAvatar CreateAvatar() =>
 48117            avatarFactory.Ref.CreateAvatar(avatarContainer, this.animator, NoLODs.i, new Visibility());
 118
 119        private IAvatar CreateAvatarWithHologram() =>
 0120            avatarFactory.Ref.CreateAvatarWithHologram(avatarContainer, avatarRevealContainer, avatarContainer, this.ani
 121
 122        private async UniTask UpdateModelAsync(AvatarModel newModel, CancellationToken ct)
 123        {
 0124            currentAvatarModel.CopyFrom(newModel);
 125
 126            try
 127            {
 0128                ct.ThrowIfCancellationRequested();
 0129                List<string> wearables = new List<string>(newModel.wearables);
 0130                wearables.Add(newModel.bodyShape);
 131
 0132                await avatar.Load(wearables, newModel.emotes.Select(x => x.urn).ToList(), new AvatarSettings
 133                {
 134                    bodyshapeId = newModel.bodyShape,
 135                    eyesColor = newModel.eyeColor,
 136                    hairColor = newModel.hairColor,
 137                    skinColor = newModel.skinColor
 138                }, ct);
 0139            }
 0140            catch (Exception e) when (e is not OperationCanceledException) { Debug.LogException(e); }
 0141        }
 142
 143        public void TakeSnapshots(OnSnapshotsReady onSuccess, Action onFailed)
 144        {
 0145            if (avatar.status != IAvatar.Status.Loaded)
 146            {
 0147                onFailed?.Invoke();
 0148                return;
 149            }
 150
 0151            StartCoroutine(TakeSnapshots_Routine(onSuccess));
 0152        }
 153
 154        private IEnumerator TakeSnapshots_Routine(OnSnapshotsReady callback)
 155        {
 0156            global::DCL.Environment.i.platform.cullingController.Stop();
 157
 0158            var current = camera.targetTexture;
 0159            camera.targetTexture = null;
 0160            var avatarAnimator = avatarContainer.gameObject.GetComponentInChildren<AvatarAnimatorLegacy>();
 161
 0162            SetFocus(CameraFocus.FaceSnapshot, false);
 0163            avatarAnimator.Reset();
 0164            yield return null;
 0165            Texture2D face256 = Snapshot(SNAPSHOT_FACE_256_WIDTH_RES, SNAPSHOT_FACE_256_HEIGHT_RES);
 166
 0167            SetFocus(CameraFocus.BodySnapshot, false);
 0168            avatarAnimator.Reset();
 0169            yield return null;
 0170            Texture2D body = Snapshot(SNAPSHOT_BODY_WIDTH_RES, SNAPSHOT_BODY_HEIGHT_RES);
 171
 0172            SetFocus(CameraFocus.DefaultEditing, false);
 173
 0174            camera.targetTexture = current;
 175
 0176            global::DCL.Environment.i.platform.cullingController.Start();
 0177            callback?.Invoke(face256, body);
 0178        }
 179
 180        private Texture2D Snapshot(int width, int height)
 181        {
 0182            RenderTexture rt = new RenderTexture(width * SUPERSAMPLING, height * SUPERSAMPLING, 32);
 0183            camera.targetTexture = rt;
 0184            Texture2D screenShot = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false);
 0185            camera.Render();
 0186            RenderTexture.active = rt;
 0187            screenShot.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
 0188            screenShot.Apply();
 189
 0190            return screenShot;
 191        }
 192
 193        private Coroutine cameraTransitionCoroutine;
 194
 195        public void SetFocus(CameraFocus focus, bool useTransition = true)
 196        {
 95197            SetFocus(cameraFocusLookUp[focus], useTransition);
 95198        }
 199
 200        private void SetFocus(Transform transform, bool useTransition = true)
 201        {
 95202            if (cameraTransitionCoroutine != null) { StopCoroutine(cameraTransitionCoroutine); }
 203
 95204            if (useTransition && gameObject.activeInHierarchy)
 205            {
 1206                cameraTransitionCoroutine = StartCoroutine(CameraTransition(camera.transform.position, transform.positio
 207            }
 208            else
 209            {
 94210                var cameraTransform = camera.transform;
 94211                cameraTransform.position = transform.position;
 94212                cameraTransform.rotation = transform.rotation;
 213            }
 94214        }
 215
 216        private IEnumerator CameraTransition(Vector3 initPos, Vector3 endPos, Quaternion initRotation, Quaternion endRot
 217        {
 1218            var cameraTransform = camera.transform;
 1219            float currentTime = 0;
 220
 1221            float inverseTime = 1 / time;
 222
 1223            while (currentTime < time)
 224            {
 1225                currentTime = Mathf.Clamp(currentTime + Time.deltaTime, 0, time);
 1226                cameraTransform.position = Vector3.Lerp(initPos, endPos, currentTime * inverseTime);
 1227                cameraTransform.rotation = Quaternion.Lerp(initRotation, endRotation, currentTime * inverseTime);
 1228                yield return null;
 229            }
 230
 0231            cameraTransitionCoroutine = null;
 0232        }
 233
 234        public void Rotate(float rotationVelocity)
 235        {
 0236            avatarContainer.transform.Rotate(Time.deltaTime * rotationVelocity * Vector3.up);
 0237        }
 238
 239        public void ResetRotation()
 240        {
 0241            avatarContainer.transform.rotation = avatarContainerDefaultRotation;
 0242        }
 243
 244        public void PlayEmote(string emoteId, long timestamp)
 245        {
 1246            avatar.PlayEmote(emoteId, timestamp);
 1247        }
 248
 249        public void Dispose()
 250        {
 48251            Destroy(gameObject);
 48252        }
 253    }
 254}