< Summary

Class:AvatarSystem.AvatarSystemUtils
Assembly:AvatarSystemLoaders
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/Loader/AvatarSystemUtils.cs
Covered lines:9
Uncovered lines:81
Coverable lines:90
Total lines:200
Line coverage:10% (9 of 90)
Covered branches:0
Total branches:0
Covered methods:2
Total methods:9
Method coverage:22.2% (2 of 9)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
IsCategoryRequired(...)0%2100%
UseAssetBundles()0%220100%
GetFacialFeatureTexturesUrls(...)0%7562700%
CopyBones(...)0%30500%
CopyBones(...)0%20400%
PrepareMaterialColors(...)0%5.015091.67%
ExtractBodyShapeParts(...)0%1561200%
GetActiveBodyPartsRenderers(...)0%2101400%
SpawnAvatarLoadedParticles(...)0%6200%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/AvatarSystem/Loader/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
 17        public static bool IsCategoryRequired(string category)
 18        {
 019            return WearableLiterals.Categories.REQUIRED_CATEGORIES.Contains(category);
 20        }
 21
 22        public static bool UseAssetBundles()
 23        {
 224            var featureFlags = DataStore.i.featureFlags.flags.Get();
 225            return featureFlags != null && featureFlags.IsFeatureEnabled(AB_FEATURE_FLAG_NAME);
 26        }
 27
 28        public static (string mainTextureUrl, string maskTextureUrl) GetFacialFeatureTexturesUrls(string bodyshapeId, We
 29        {
 030            if (facialFeature.data.category != WearableLiterals.Categories.EYES && facialFeature.data.category == Wearab
 031                return (null, null);
 32
 033            var representation = facialFeature.GetRepresentation(bodyshapeId);
 034            string mainTextureHash = representation?.contents?.FirstOrDefault(x => x.key == representation?.mainFile)?.h
 35
 036            if (string.IsNullOrEmpty(mainTextureHash))
 037                mainTextureHash = representation?.contents?.FirstOrDefault(x => !x.key.ToLower().Contains("_mask.png"))?
 38
 039            if (string.IsNullOrEmpty(mainTextureHash))
 040                return (null, null);
 41
 042            string maskTextureHash = representation?.contents?.FirstOrDefault(x => x.key.ToLower().Contains("_mask.png")
 43
 044            string mainTextureUrl = facialFeature.baseUrl + mainTextureHash;
 045            string maskTextureUrl = maskTextureHash == null ? null : facialFeature.baseUrl + maskTextureHash;
 46
 047            return (mainTextureUrl, maskTextureUrl);
 48        }
 49
 50        public static void CopyBones(Transform rootBone, Transform[] bones, IEnumerable<SkinnedMeshRenderer> targets)
 51        {
 052            if (rootBone == null || bones == null)
 053                return;
 54
 055            foreach (SkinnedMeshRenderer skinnedMeshRenderer in targets) { CopyBones(rootBone, bones, skinnedMeshRendere
 056        }
 57
 58        public static void CopyBones(Transform rootBone, Transform[] bones, SkinnedMeshRenderer skinnedMeshRenderer)
 59        {
 060            if (rootBone == null || bones == null || skinnedMeshRenderer == null)
 061                return;
 62
 063            skinnedMeshRenderer.rootBone = rootBone;
 064            skinnedMeshRenderer.bones = bones;
 065        }
 66
 67        public static void PrepareMaterialColors(Rendereable rendereable, Color skinColor, Color hairColor)
 68        {
 1269            foreach (Renderer renderer in rendereable.renderers)
 70            {
 1671                foreach (Material material in renderer.materials)
 72                {
 73                    // If this is modified, check DecentralandMaterialGenerator.SetMaterialName,
 74                    // its important for the asset bundles materials to have normalized names but this functionality sho
 475                    string name = material.name.ToLower();
 76
 477                    if (name.Contains("skin"))
 278                        material.SetColor(ShaderUtils.BaseColor, skinColor);
 279                    else if (name.Contains("hair"))
 080                        material.SetColor(ShaderUtils.BaseColor, hairColor);
 81                }
 82            }
 283        }
 84
 85        /// <summary>
 86        /// Extract bodyparts of a Rendereable.
 87        ///
 88        /// Using this on a Rendereable that doesn't comes from a bodyshape might result in unexpected result
 89        /// </summary>
 90        /// <param name="rendereable"></param>
 91        /// <returns></returns>
 92
 93        // TODO: The list of body shapes that need to be extracted should come from an external source,
 94        // in order to make this scalable we should return a Dictionary<string, SkinnedMeshRenderer> with the results
 95        public static (
 96            SkinnedMeshRenderer head,
 97            SkinnedMeshRenderer upperBody,
 98            SkinnedMeshRenderer lowerBody,
 99            SkinnedMeshRenderer feet,
 100            SkinnedMeshRenderer eyes,
 101            SkinnedMeshRenderer eyebrows,
 102            SkinnedMeshRenderer mouth,
 103            SkinnedMeshRenderer hands,
 104            List<SkinnedMeshRenderer> extraParts ) ExtractBodyShapeParts(Rendereable rendereable)
 105        {
 0106            SkinnedMeshRenderer head = null;
 0107            SkinnedMeshRenderer upperBody = null;
 0108            SkinnedMeshRenderer lowerBody = null;
 0109            SkinnedMeshRenderer feet = null;
 0110            SkinnedMeshRenderer eyes = null;
 0111            SkinnedMeshRenderer eyebrows = null;
 0112            SkinnedMeshRenderer mouth = null;
 0113            SkinnedMeshRenderer hands = null;
 0114            var extraParts = new List<SkinnedMeshRenderer>();
 115
 0116            foreach (Renderer r in rendereable.renderers)
 117            {
 0118                if (r is not SkinnedMeshRenderer renderer)
 119                    continue;
 120
 0121                string name = renderer.name.ToLower();
 122
 123                // we still support the old gltf hierarchy for ABs
 0124                if (name.Contains("primitive"))
 0125                    name = renderer.transform.parent.name.ToLower();
 126
 0127                if (name.Contains("head"))
 0128                    head = renderer;
 0129                else if (name.Contains("ubody"))
 0130                    upperBody = renderer;
 0131                else if (name.Contains("lbody"))
 0132                    lowerBody = renderer;
 0133                else if (name.Contains("hands"))
 0134                    hands = renderer;
 0135                else if (name.Contains("feet"))
 0136                    feet = renderer;
 0137                else if (name.Contains("eyes"))
 0138                    eyes = renderer;
 0139                else if (name.Contains("eyebrows"))
 0140                    eyebrows = renderer;
 0141                else if (name.Contains("mouth"))
 0142                    mouth = renderer;
 143                else
 144                {
 0145                    Debug.LogWarning($"{name} has not been set-up as a valid body part", r);
 0146                    extraParts.Add(renderer);
 147                }
 148
 149            }
 150
 0151            return (head, upperBody, lowerBody, feet, eyes, eyebrows, mouth, hands, extraParts);
 152        }
 153
 154        public static List<SkinnedMeshRenderer> GetActiveBodyPartsRenderers(IBodyshapeLoader bodyshapeLoader, string bod
 155        {
 0156            HashSet<string> hiddenList = new HashSet<string>();
 0157            HashSet<string> usedCategories = new HashSet<string>();
 158
 0159            foreach (WearableItem wearable in wearables)
 160            {
 0161                usedCategories.Add(wearable.data.category);
 0162                string[] hiddenByThisWearable = wearable.GetHidesList(bodyShapeId);
 163
 0164                if (hiddenByThisWearable != null)
 0165                    hiddenList.UnionWith(hiddenByThisWearable);
 166            }
 167
 0168            List<SkinnedMeshRenderer> result = new List<SkinnedMeshRenderer>();
 169
 0170            if (!hiddenList.Contains(WearableLiterals.Categories.HEAD) && !usedCategories.Contains(WearableLiterals.Cate
 0171                result.Add(bodyshapeLoader.headRenderer);
 172
 0173            if (!hiddenList.Contains(WearableLiterals.Categories.UPPER_BODY) && !usedCategories.Contains(WearableLiteral
 0174                result.Add(bodyshapeLoader.upperBodyRenderer);
 175
 0176            if (!hiddenList.Contains(WearableLiterals.Categories.LOWER_BODY) && !usedCategories.Contains(WearableLiteral
 0177                result.Add(bodyshapeLoader.lowerBodyRenderer);
 178
 0179            if (!hiddenList.Contains(WearableLiterals.Categories.FEET) && !usedCategories.Contains(WearableLiterals.Cate
 0180                result.Add(bodyshapeLoader.feetRenderer);
 181
 0182            if (!hiddenList.Contains(WearableLiterals.Categories.HANDS) && !usedCategories.Contains(WearableLiterals.Cat
 0183                result.Add(bodyshapeLoader.handsRenderer);
 184
 185            // We dont want to hide new body parts that are not configured yet
 0186            result.AddRange(bodyshapeLoader.extraRenderers);
 187
 0188            return result;
 189        }
 190
 191        public static void SpawnAvatarLoadedParticles(Transform avatarContainer, GameObject particlePrefab)
 192        {
 0193            if (!particlePrefab.TryGetComponent(out DestroyParticlesOnFinish _))
 0194                throw new Exception("A self destructive particles prefab is expected");
 195
 0196            GameObject particles = Object.Instantiate(particlePrefab);
 0197            particles.transform.position = avatarContainer.position + particlePrefab.transform.position;
 0198        }
 199    }
 200}