< Summary

Class:AvatarSystem.BodyShapeLoader
Assembly:AvatarSystemLoaders
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/Loader/BodyShapeLoader.cs
Covered lines:0
Uncovered lines:120
Coverable lines:120
Total lines:218
Line coverage:0% (0 of 120)
Covered branches:0
Total branches:0
Covered methods:0
Total methods:36
Method coverage:0% (0 of 36)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
BodyShapeLoader(...)0%2100%
Load()0%1101000%
SetBones(...)0%2100%
LoadMouth()0%42600%
LoadEyebrows()0%42600%
LoadEyes()0%42600%
UpdateColors(...)0%56700%
LoadWearable()0%20400%
IsValid(...)0%3801900%
DisableFacialRenderers()0%20400%
Dispose()0%90900%

File(s)

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

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using System;
 3using System.Collections.Generic;
 4using System.Linq;
 5using System.Threading;
 6using DCL;
 7using DCL.Shaders;
 8using UnityEngine;
 9using Object = UnityEngine.Object;
 10
 11namespace AvatarSystem
 12{
 13    public class BodyShapeLoader : IBodyshapeLoader
 14    {
 015        public WearableItem bodyShape { get; }
 016        public Rendereable rendereable => bodyShapeRetriever?.rendereable;
 017        public IWearableLoader.Status status { get; private set; }
 018        public WearableItem eyes { get; }
 019        public WearableItem eyebrows { get; }
 020        public WearableItem mouth { get; }
 021        public SkinnedMeshRenderer eyesRenderer { get; internal set; }
 022        public SkinnedMeshRenderer eyebrowsRenderer { get; internal set; }
 023        public SkinnedMeshRenderer mouthRenderer { get; internal set; }
 024        public SkinnedMeshRenderer headRenderer { get; private set; }
 025        public SkinnedMeshRenderer feetRenderer { get; private set; }
 026        public SkinnedMeshRenderer handsRenderer { get; private set; }
 027        public SkinnedMeshRenderer upperBodyRenderer { get; private set; }
 028        public SkinnedMeshRenderer lowerBodyRenderer { get; private set; }
 29
 30        // This list contains uncategorized body parts that also have their own renderers
 31        // this enables us to modify the body shape with new parts without ignoring them during the mesh combine process
 032        public IList<SkinnedMeshRenderer> extraRenderers { get; set; }
 33
 34        internal readonly IWearableRetriever bodyShapeRetriever;
 35        internal readonly IFacialFeatureRetriever eyesRetriever;
 36        internal readonly IFacialFeatureRetriever eyebrowsRetriever;
 37        internal readonly IFacialFeatureRetriever mouthRetriever;
 38
 039        private readonly Dictionary<SkinnedMeshRenderer, (Transform rootBone, Transform[] bones)> originalBones = new ()
 40
 041        public BodyShapeLoader(IRetrieverFactory retrieverFactory, BodyWearables bodyWearables)
 42        {
 043            this.bodyShape = bodyWearables.BodyShape;
 044            this.eyes = bodyWearables.Eyes;
 045            this.eyebrows = bodyWearables.Eyebrows;
 046            this.mouth = bodyWearables.Mouth;
 47
 048            bodyShapeRetriever = retrieverFactory.GetWearableRetriever();
 049            eyesRetriever = retrieverFactory.GetFacialFeatureRetriever();
 050            eyebrowsRetriever = retrieverFactory.GetFacialFeatureRetriever();
 051            mouthRetriever = retrieverFactory.GetFacialFeatureRetriever();
 052        }
 53
 54        public async UniTask Load(GameObject container, AvatarSettings avatarSettings, CancellationToken ct = default)
 55        {
 056            ct.ThrowIfCancellationRequested();
 57
 58            try
 59            {
 060                if (status == IWearableLoader.Status.Succeeded)
 61                {
 062                    UpdateColors(avatarSettings);
 063                    return;
 64                }
 65
 066                status = IWearableLoader.Status.Idle;
 067                await LoadWearable(container, ct);
 68
 69                // Store the original bones.
 070                originalBones.Clear();
 71
 072                foreach (SkinnedMeshRenderer skm in bodyShapeRetriever.rendereable.renderers.OfType<SkinnedMeshRenderer>
 73
 074                (headRenderer, upperBodyRenderer, lowerBodyRenderer, feetRenderer, eyesRenderer, eyebrowsRenderer, mouth
 75
 076                await (LoadEyes(ct), LoadEyebrows(ct), LoadMouth(ct));
 77
 078                UpdateColors(avatarSettings);
 079                status = IWearableLoader.Status.Succeeded;
 080            }
 081            catch (Exception)
 82            {
 083                status = IWearableLoader.Status.Failed;
 084                Dispose();
 085                throw;
 86            }
 087        }
 88
 89        public void SetBones(Transform rootBone, Transform[] bones)
 90        {
 091            AvatarSystemUtils.CopyBones(rootBone, bones, rendereable.renderers.OfType<SkinnedMeshRenderer>());
 092        }
 93
 94        private async UniTask LoadMouth(CancellationToken ct)
 95        {
 096            if (mouth == null) return;
 097            (Texture main, Texture mask) = await mouthRetriever.Retrieve(mouth, bodyShape.id, ct);
 098            mouthRenderer.material = new Material(Resources.Load<Material>("Mouth Material"));
 99
 0100            if (main == null)
 0101                throw new Exception($"Couldn't fetch main texture for {mouth.id}");
 102
 0103            mouthRenderer.material.SetTexture(ShaderUtils.BaseMap, main);
 104
 0105            if (mask != null)
 0106                mouthRenderer.material.SetTexture(ShaderUtils.TintMask, mask);
 0107        }
 108
 109        private async UniTask LoadEyebrows(CancellationToken ct)
 110        {
 0111            if (eyebrows == null) return;
 0112            (Texture main, Texture mask) = await eyebrowsRetriever.Retrieve(eyebrows, bodyShape.id, ct);
 0113            eyebrowsRenderer.material = new Material(Resources.Load<Material>("Eyebrow Material"));
 114
 0115            if (main == null)
 0116                throw new Exception($"Couldn't fetch main texture for {eyebrows.id}");
 117
 0118            eyebrowsRenderer.material.SetTexture(ShaderUtils.BaseMap, main);
 119
 0120            if (mask != null)
 0121                eyebrowsRenderer.material.SetTexture(ShaderUtils.BaseMap, mask);
 0122        }
 123
 124        private async UniTask LoadEyes(CancellationToken ct)
 125        {
 0126            if (eyes == null) return;
 0127            (Texture main, Texture mask) = await eyesRetriever.Retrieve(eyes, bodyShape.id, ct);
 0128            eyesRenderer.material = new Material(Resources.Load<Material>("Eye Material"));
 129
 0130            if (main == null)
 0131                throw new Exception($"Couldn't fetch main texture for {eyes.id}");
 132
 0133            eyesRenderer.material.SetTexture(ShaderUtils.EyesTexture, main);
 134
 0135            if (mask != null)
 0136                eyesRenderer.material.SetTexture(ShaderUtils.IrisMask, mask);
 0137        }
 138
 139        private void UpdateColors(AvatarSettings avatarSettings)
 140        {
 0141            AvatarSystemUtils.PrepareMaterialColors(rendereable, avatarSettings.skinColor, avatarSettings.hairColor);
 0142            eyesRenderer?.material?.SetColor(ShaderUtils.EyeTint, avatarSettings.eyesColor);
 0143            eyebrowsRenderer?.material?.SetColor(ShaderUtils.BaseColor, avatarSettings.hairColor);
 0144            mouthRenderer?.material?.SetColor(ShaderUtils.BaseColor, avatarSettings.skinColor);
 0145        }
 146
 147        private async UniTask<Rendereable> LoadWearable(GameObject container, CancellationToken ct)
 148        {
 0149            ct.ThrowIfCancellationRequested();
 150
 0151            bodyShapeRetriever.Dispose();
 152
 0153            Rendereable bodyshapeRenderable = await bodyShapeRetriever.Retrieve(container, bodyShape, bodyShape.id, ct);
 154
 0155            if (bodyshapeRenderable == null) // fail safe, we shouldnt reach this since .Retrieve should throw if anythi
 0156                throw new Exception("Couldn't load bodyshape");
 157
 0158            return bodyshapeRenderable;
 0159        }
 160
 161        public bool IsValid(BodyWearables bodyWearables)
 162        {
 0163            if (bodyShape.id != bodyWearables.BodyShape?.id) return false;
 0164            if (this.eyebrows?.id != bodyWearables.Eyebrows?.id) return false;
 0165            if (this.mouth?.id != bodyWearables.Mouth?.id) return false;
 0166            if (this.eyes?.id != bodyWearables.Eyes?.id) return false;
 0167            return true;
 168        }
 169
 170        public void DisableFacialRenderers()
 171        {
 0172            if (eyesRenderer != null)
 0173                eyesRenderer.enabled = false;
 174
 0175            if (eyebrowsRenderer != null)
 0176                eyebrowsRenderer.enabled = false;
 177
 0178            if (mouthRenderer != null)
 0179                mouthRenderer.enabled = false;
 0180        }
 181
 182        public void Dispose()
 183        {
 0184            status = IWearableLoader.Status.Idle;
 185
 186            //Restore bones
 0187            foreach ((SkinnedMeshRenderer skm, (Transform rootBone, Transform[] bones)) in originalBones)
 188            {
 0189                skm.rootBone = rootBone;
 0190                skm.bones = bones;
 191            }
 192
 0193            originalBones.Clear();
 194
 0195            bodyShapeRetriever?.Dispose();
 0196            eyesRetriever?.Dispose();
 0197            eyebrowsRetriever?.Dispose();
 0198            mouthRetriever?.Dispose();
 199
 0200            if (eyesRenderer != null)
 0201                Object.Destroy(eyesRenderer.material);
 202
 0203            if (eyebrowsRenderer != null)
 0204                Object.Destroy(eyebrowsRenderer.material);
 205
 0206            if (mouthRenderer != null)
 0207                Object.Destroy(mouthRenderer.material);
 208
 0209            upperBodyRenderer = null;
 0210            lowerBodyRenderer = null;
 0211            feetRenderer = null;
 0212            headRenderer = null;
 0213            eyesRenderer = null;
 0214            eyebrowsRenderer = null;
 0215            mouthRenderer = null;
 0216        }
 217    }
 218}