< Summary

Class:AvatarSystem.AvatarSystemUtils
Assembly:AvatarSystem
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/AvatarSystemUtils.cs
Covered lines:62
Uncovered lines:52
Coverable lines:114
Total lines:240
Line coverage:54.3% (62 of 114)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
IsCategoryRequired(...)0%110100%
UseAssetBundles()0%220100%
GetFacialFeatureTexturesUrls(...)0%7562700%
CopyBones(...)0%30500%
CopyBones(...)0%20400%
PrepareMaterialColors(...)0%550100%
SplitWearables(...)0%56700%
ExtractBodyshapeParts(...)0%1111096.55%
FilterHiddenWearables(...)0%20400%
GetActiveBodyParts(...)0%880100%
GetActiveBodyPartsRenderers(...)0%550100%
SpawnAvatarLoadedParticles(...)0%6200%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using DCL;
 5using DCL.Helpers;
 6using DCL.Shaders;
 7using UnityEngine;
 8using Object = UnityEngine.Object;
 9
 10namespace AvatarSystem
 11{
 12    public static class AvatarSystemUtils
 13    {
 14        public const float AVATAR_Y_OFFSET = 0.75f;
 15        private const string AB_FEATURE_FLAG_NAME = "wearable_asset_bundles";
 16
 1217        public static bool IsCategoryRequired(string category) { return WearableLiterals.Categories.REQUIRED_CATEGORIES.
 18
 19        public static bool UseAssetBundles()
 20        {
 821            var featureFlags = DataStore.i.featureFlags.flags.Get();
 822            return featureFlags != null && featureFlags.IsFeatureEnabled(AB_FEATURE_FLAG_NAME);
 23        }
 24
 25        public static (string mainTextureUrl, string maskTextureUrl) GetFacialFeatureTexturesUrls(string bodyshapeId, We
 26        {
 027            if (facialFeature.data.category != WearableLiterals.Categories.EYES && facialFeature.data.category == Wearab
 028                return (null, null);
 29
 030            var representation = facialFeature.GetRepresentation(bodyshapeId);
 031            string mainTextureHash = representation?.contents?.FirstOrDefault(x => x.key == representation?.mainFile)?.h
 032            if (string.IsNullOrEmpty(mainTextureHash))
 033                mainTextureHash = representation?.contents?.FirstOrDefault(x => !x.key.ToLower().Contains("_mask.png"))?
 034            if (string.IsNullOrEmpty(mainTextureHash))
 035                return (null, null);
 36
 037            string maskTextureHash = representation?.contents?.FirstOrDefault(x => x.key.ToLower().Contains("_mask.png")
 38
 039            string mainTextureUrl = facialFeature.baseUrl + mainTextureHash;
 040            string maskTextureUrl = maskTextureHash == null ? null : facialFeature.baseUrl + maskTextureHash;
 41
 042            return (mainTextureUrl, maskTextureUrl);
 43        }
 44
 45        public static void CopyBones(Transform rootBone, Transform[] bones, IEnumerable<SkinnedMeshRenderer> targets)
 46        {
 047            if (rootBone == null || bones == null)
 048                return;
 49
 050            foreach (SkinnedMeshRenderer skinnedMeshRenderer in targets)
 51            {
 052                CopyBones(rootBone, bones, skinnedMeshRenderer);
 53            }
 054        }
 55
 56        public static void CopyBones(Transform rootBone, Transform[] bones, SkinnedMeshRenderer skinnedMeshRenderer)
 57        {
 058            if (rootBone == null || bones == null || skinnedMeshRenderer == null)
 059                return;
 60
 061            skinnedMeshRenderer.rootBone = rootBone;
 062            skinnedMeshRenderer.bones = bones;
 063        }
 64
 65        public static void PrepareMaterialColors(Rendereable rendereable, Color skinColor, Color hairColor)
 66        {
 8867            foreach (Renderer renderer in rendereable.renderers)
 68            {
 12869                foreach (Material material in renderer.materials)
 70                {
 3271                    if (material.name.ToLower().Contains("skin"))
 772                        material.SetColor(ShaderUtils.BaseColor, skinColor);
 2573                    else if (material.name.ToLower().Contains("hair"))
 574                        material.SetColor(ShaderUtils.BaseColor, hairColor);
 75                }
 76            }
 1277        }
 78
 79        public static (WearableItem bodyshape, WearableItem eyes, WearableItem eyebrows, WearableItem mouth, List<Wearab
 80        {
 081            WearableItem bodyshape = null;
 082            WearableItem eyes = null;
 083            WearableItem eyebrows = null;
 084            WearableItem mouth = null;
 085            List<WearableItem> resultWearables = new List<WearableItem>();
 086            foreach (WearableItem wearable in wearables)
 87            {
 088                switch (wearable.data.category)
 89                {
 90                    case WearableLiterals.Categories.BODY_SHAPE:
 091                        bodyshape = wearable;
 092                        break;
 93                    case WearableLiterals.Categories.EYES:
 094                        eyes = wearable;
 095                        break;
 96                    case WearableLiterals.Categories.EYEBROWS:
 097                        eyebrows = wearable;
 098                        break;
 99                    case WearableLiterals.Categories.MOUTH:
 0100                        mouth = wearable;
 0101                        break;
 102                    default:
 0103                        resultWearables.Add(wearable);
 104                        break;
 105                }
 106            }
 107
 0108            return (bodyshape, eyes, eyebrows, mouth, resultWearables);
 109        }
 110
 111        /// <summary>
 112        /// Extract bodyparts of a Rendereable.
 113        ///
 114        /// Using this on a Rendereable that doesn't comes from a bodyshape might result in unexpected result
 115        /// </summary>
 116        /// <param name="rendereable"></param>
 117        /// <returns></returns>
 118        public static (
 119            SkinnedMeshRenderer head,
 120            SkinnedMeshRenderer upperBody,
 121            SkinnedMeshRenderer lowerBody,
 122            SkinnedMeshRenderer feet,
 123            SkinnedMeshRenderer eyes,
 124            SkinnedMeshRenderer eyebrows,
 125            SkinnedMeshRenderer mouth
 126            ) ExtractBodyshapeParts(Rendereable rendereable)
 127        {
 1128            SkinnedMeshRenderer head = null;
 1129            SkinnedMeshRenderer upperBody = null;
 1130            SkinnedMeshRenderer lowerBody = null;
 1131            SkinnedMeshRenderer feet = null;
 1132            SkinnedMeshRenderer eyes = null;
 1133            SkinnedMeshRenderer eyebrows = null;
 1134            SkinnedMeshRenderer mouth = null;
 135
 16136            foreach (Renderer r in rendereable.renderers)
 137            {
 7138                if (!(r is SkinnedMeshRenderer renderer))
 139                    continue;
 140
 7141                string name = "";
 142
 143                // Note (Kinerius) Since GLTFast builds the GLTF differently, we use the renderer name instead
 7144                name = rendereable.isGLTFast ? renderer.name.ToLower() : renderer.transform.parent.name.ToLower();
 145
 7146                if (name.Contains("head"))
 1147                    head = renderer;
 6148                else if (name.Contains("ubody"))
 1149                    upperBody = renderer;
 5150                else if (name.Contains("lbody"))
 1151                    lowerBody = renderer;
 4152                else if (name.Contains("feet"))
 1153                    feet = renderer;
 3154                else if (name.Contains("eyes"))
 1155                    eyes = renderer;
 2156                else if (name.Contains("eyebrows"))
 1157                    eyebrows = renderer;
 1158                else if (name.Contains("mouth"))
 1159                    mouth = renderer;
 160                else
 0161                    Debug.LogWarning($"{name} is not a body part?", r);
 162            }
 163
 1164            return (head, upperBody, lowerBody, feet, eyes, eyebrows, mouth);
 165        }
 166
 167        /// <summary>
 168        /// Filters hidden wearables.
 169        /// Conflicts will be resolved by order in the array
 170        /// </summary>
 171        /// <param name="bodyshapeId"></param>
 172        /// <param name="wearables"></param>
 173        /// <returns></returns>
 174        public static List<WearableItem> FilterHiddenWearables(string bodyshapeId, IEnumerable<WearableItem> wearables)
 175        {
 0176            HashSet<string> hiddenCategories = new HashSet<string>();
 0177            List<WearableItem> filteredWearables = new List<WearableItem>();
 0178            foreach (WearableItem wearable in wearables)
 179            {
 0180                if (hiddenCategories.Contains(wearable.data.category))
 181                {
 182                    continue;
 183                }
 184
 0185                filteredWearables.Add(wearable);
 0186                hiddenCategories.UnionWith(wearable.GetHidesList(bodyshapeId));
 187            }
 188
 0189            return filteredWearables;
 190        }
 191
 192        public static (bool headVisible, bool upperBodyVisible, bool lowerBodyVisible, bool feetVisible) GetActiveBodyPa
 193        {
 5194            bool headVisible = true;
 5195            bool upperBodyVisible = true;
 5196            bool lowerBodyVisible = true;
 5197            bool feetVisible = true;
 198
 5199            HashSet<string> hiddenList = new HashSet<string>();
 5200            HashSet<string> usedCategories = new HashSet<string>();
 201
 52202            foreach (WearableItem wearable in wearables)
 203            {
 21204                usedCategories.Add(wearable.data.category);
 21205                string[] hiddenByThisWearable = wearable.GetHidesList(bodyshapeId);
 21206                if (hiddenByThisWearable != null)
 21207                    hiddenList.UnionWith(hiddenByThisWearable);
 208            }
 209
 5210            headVisible = !hiddenList.Contains(WearableLiterals.Misc.HEAD) && !usedCategories.Contains(WearableLiterals.
 5211            upperBodyVisible = !hiddenList.Contains(WearableLiterals.Categories.UPPER_BODY) && !usedCategories.Contains(
 5212            lowerBodyVisible = !hiddenList.Contains(WearableLiterals.Categories.LOWER_BODY) && !usedCategories.Contains(
 5213            feetVisible = !hiddenList.Contains(WearableLiterals.Categories.FEET) && !usedCategories.Contains(WearableLit
 5214            return (headVisible, upperBodyVisible, lowerBodyVisible, feetVisible);
 215        }
 216
 217        public static List<SkinnedMeshRenderer> GetActiveBodyPartsRenderers(IBodyshapeLoader bodyshapeLoader, bool headV
 218        {
 5219            List<SkinnedMeshRenderer> result = new List<SkinnedMeshRenderer>();
 5220            if (headVisible)
 4221                result.Add(bodyshapeLoader.headRenderer);
 5222            if (upperBodyVisible)
 1223                result.Add(bodyshapeLoader.upperBodyRenderer);
 5224            if (lowerBodyVisible)
 1225                result.Add(bodyshapeLoader.lowerBodyRenderer);
 5226            if (feetVisible)
 1227                result.Add(bodyshapeLoader.feetRenderer);
 5228            return result;
 229        }
 230
 231        public static void SpawnAvatarLoadedParticles(Transform avatarContainer, GameObject particlePrefab)
 232        {
 0233            if (!particlePrefab.TryGetComponent(out DestroyParticlesOnFinish selfDestroyScript))
 0234                throw new Exception("A self destructive particles prefab is expected");
 235
 0236            GameObject particles = Object.Instantiate(particlePrefab);
 0237            particles.transform.position = avatarContainer.position + particlePrefab.transform.position;
 0238        }
 239    }
 240}