< Summary

Class:NFTShapeLoaderController
Assembly:DCL.Components.LoadableShapes
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/LoadableShapes/NFTShape/NFTShapeLoaderController.cs
Covered lines:94
Uncovered lines:50
Coverable lines:144
Total lines:340
Line coverage:65.2% (94 of 144)
Covered branches:0
Total branches:0
Covered methods:25
Total methods:28
Method coverage:89.2% (25 of 28)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
NFTShapeLoaderController()0%110100%
NFTShapeLoaderController()0%110100%
Awake()0%110100%
Initialize(...)0%330100%
OnEnable()0%110100%
OnDisable()0%110100%
Start()0%110100%
Update()0%2.52050%
LoadAsset(...)0%16.8510059.09%
UpdateBackgroundColor(...)0%2.062075%
FetchNFTContents()0%110100%
FetchNftInfoSuccess(...)0%110100%
FetchNFTInfoFail()0%2100%
PrepareFrame(...)0%220100%
LoadNFTAssetCoroutine()0%440100%
FinishLoading(...)0%5.024060%
SetFrameImage(...)0%26.836016.67%
UpdateTexture(...)0%6200%
ShowLoading(...)0%2.062075%
ShowErrorFeedback(...)0%3.333066.67%
InitializeMaterials()0%20.6414067.65%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/LoadableShapes/NFTShape/NFTShapeLoaderController.cs

#LineLine coverage
 1using DCL;
 2using MainScripts.DCL.ServiceProviders.OpenSea.Interfaces;
 3using NFTShape_Internal;
 4using System;
 5using System.Collections;
 6using System.Text.RegularExpressions;
 7using UnityEngine;
 8using WaitUntil = DCL.WaitUntil;
 9
 10public interface INFTShapeLoaderController
 11{
 12    BoxCollider nftCollider { get; }
 13    Transform transform { get; }
 14}
 15
 16public class NFTShapeLoaderController : MonoBehaviour, INFTShapeLoaderController
 17{
 18    internal const string COULD_NOT_FETCH_DAR_URL = "Couldn't fetch DAR url '{0}' for NFTShape.";
 19    internal const string ACCEPTED_URL_FORMAT = "The accepted format is 'ethereum://ContractAddress/TokenID'.";
 20    internal const string SUPPORTED_PROTOCOL = "The only protocol currently supported is 'ethereum'.";
 21    internal const string DOES_NOT_SUPPORT_POLYGON = "Warning: OpenSea API does not support fetching Polygon assets.";
 22    internal const string COULD_NOT_FETCH_NFT_FROM_API = "Couldn't fetch NFT: '{0}/{1}'.";
 23    internal const string COULD_NOT_FETCH_NFT_IMAGE = "Couldn't fetch NFT image for: '{0}/{1}': {2}";
 24
 25    public enum NoiseType
 26    {
 27        ClassicPerlin,
 28        PeriodicPerlin,
 29        Simplex,
 30        SimplexNumericalGrad,
 31        SimplexAnalyticalGrad,
 32        None
 33    }
 34
 35    public MeshRenderer meshRenderer;
 36    public new BoxCollider collider;
 37    public Color backgroundColor;
 38    public GameObject spinner;
 39    public GameObject errorFeedback;
 40
 41    [HideInInspector] public bool alreadyLoadedAsset = false;
 42    private INFTInfoRetriever nftInfoRetriever;
 43    private INFTAssetRetriever nftAssetRetriever;
 44    private NFTShapeHQImageHandler hqTextureHandler = null;
 45    private Coroutine loadNftAssetCoroutine;
 46
 47    public event Action OnLoadingAssetSuccess;
 48    public event Action OnLoadingAssetFail;
 49
 50    [SerializeField]
 51    NFTShapeMaterial[] materials;
 52
 53    [Header("Noise Shader")]
 54    [SerializeField]
 3055    NoiseType noiseType = NoiseType.Simplex;
 56
 57    [SerializeField] bool noiseIs3D = false;
 58    [SerializeField] bool noiseIsFractal = false;
 59
 60    Action<LoadWrapper> OnSuccess;
 61    Action<LoadWrapper> OnFail;
 62    private string darURLProtocol;
 63    private string darURLRegistry;
 64    private string darURLAsset;
 065    public BoxCollider nftCollider => this.collider;
 66
 3567    public Material frameMaterial { private set; get; } = null;
 1468    public Material imageMaterial { private set; get; } = null;
 3169    public Material backgroundMaterial { private set; get; } = null;
 70
 171    static readonly int BASEMAP_SHADER_PROPERTY = Shader.PropertyToID("_BaseMap");
 172    static readonly int COLOR_SHADER_PROPERTY = Shader.PropertyToID("_BaseColor");
 73
 74    void Awake()
 75    {
 76        // NOTE: we use half scale to keep backward compatibility cause we are using 512px to normalize the scale with a
 777        meshRenderer.transform.localScale = new Vector3(0.5f, 0.5f, 1);
 778        InitializeMaterials();
 779    }
 80
 81    public void Initialize(INFTInfoRetriever nftInfoRetriever = null, INFTAssetRetriever nftAssetRetriever = null)
 82    {
 1483        if (nftInfoRetriever == null)
 784            nftInfoRetriever = new NFTInfoRetriever();
 85
 1486        if (nftAssetRetriever == null)
 787            nftAssetRetriever = new NFTAssetRetriever();
 88
 1489        this.nftInfoRetriever = nftInfoRetriever;
 1490        this.nftAssetRetriever = nftAssetRetriever;
 91
 1492        nftInfoRetriever.OnFetchInfoSuccess += FetchNftInfoSuccess;
 1493        nftInfoRetriever.OnFetchInfoFail += FetchNFTInfoFail;
 1494    }
 95
 96    private void OnEnable()
 97    {
 798        Initialize();
 799        nftInfoRetriever.OnFetchInfoSuccess += FetchNftInfoSuccess;
 7100        nftInfoRetriever.OnFetchInfoFail += FetchNFTInfoFail;
 7101    }
 102
 103    private void OnDisable()
 104    {
 7105        nftInfoRetriever.OnFetchInfoSuccess -= FetchNftInfoSuccess;
 7106        nftInfoRetriever.OnFetchInfoFail -= FetchNFTInfoFail;
 7107    }
 108
 14109    private void Start() { spinner.layer = LayerMask.NameToLayer("ViewportCullingIgnored"); }
 110
 88111    void Update() { hqTextureHandler?.Update(); }
 112
 113    public void LoadAsset(string url, bool loadEvenIfAlreadyLoaded = false)
 114    {
 7115        if (string.IsNullOrEmpty(url) || (!loadEvenIfAlreadyLoaded && alreadyLoadedAsset))
 0116            return;
 117
 7118        ShowErrorFeedback(false);
 7119        UpdateBackgroundColor(backgroundColor);
 120
 121        // Check the src follows the needed format e.g.: 'ethereum://0x06012c8cf97BEaD5deAe237070F9587f8E7A266d/558536'
 7122        var regexMatches = Regex.Matches(url, "(?<protocol>[^:]+)://(?<registry>0x([A-Fa-f0-9])+)(?:/(?<asset>.+))?");
 7123        if (regexMatches.Count == 0)
 124        {
 0125            Debug.LogError(string.Format(COULD_NOT_FETCH_DAR_URL + " " + ACCEPTED_URL_FORMAT, url));
 0126            ShowErrorFeedback(true);
 0127            OnLoadingAssetFail?.Invoke();
 128
 0129            return;
 130        }
 131
 7132        Match match = regexMatches[0];
 7133        if (match.Groups["protocol"] == null || match.Groups["registry"] == null || match.Groups["asset"] == null)
 134        {
 0135            Debug.LogError(string.Format(COULD_NOT_FETCH_DAR_URL + " " + ACCEPTED_URL_FORMAT, url));
 0136            ShowErrorFeedback(true);
 0137            OnLoadingAssetFail?.Invoke();
 138
 0139            return;
 140        }
 141
 7142        darURLProtocol = match.Groups["protocol"].ToString();
 7143        darURLRegistry = match.Groups["registry"].ToString();
 7144        darURLAsset = match.Groups["asset"].ToString();
 145
 7146        alreadyLoadedAsset = false;
 147
 7148        FetchNFTContents();
 7149    }
 150
 151    public void UpdateBackgroundColor(Color newColor)
 152    {
 8153        if (backgroundMaterial == null)
 0154            return;
 155
 8156        backgroundMaterial.SetColor(COLOR_SHADER_PROPERTY, newColor);
 8157    }
 158
 159    private void FetchNFTContents()
 160    {
 7161        ShowLoading(true);
 7162        nftInfoRetriever.FetchNFTInfo(darURLProtocol, darURLRegistry, darURLAsset);
 7163    }
 164
 165    private void FetchNftInfoSuccess(NFTInfo nftInfo)
 166    {
 7167        loadNftAssetCoroutine = StartCoroutine(LoadNFTAssetCoroutine(nftInfo));
 7168    }
 169
 170    private void FetchNFTInfoFail()
 171    {
 0172        ShowErrorFeedback(true);
 0173        FinishLoading(false);
 0174    }
 175
 176    private void PrepareFrame(INFTAsset nftAsset, string nftName, string nftImageUrl)
 177    {
 7178        if (nftAsset.previewAsset != null)
 7179            SetFrameImage(nftAsset.previewAsset.texture, resizeFrameMesh: true);
 180
 7181        var hqImageHandlerConfig = new NFTShapeHQImageConfig()
 182        {
 183            collider = collider,
 184            transform = transform,
 185            name = nftName,
 186            imageUrl = nftImageUrl,
 187            asset = nftAsset
 188        };
 189
 7190        hqTextureHandler = NFTShapeHQImageHandler.Create(hqImageHandlerConfig);
 7191        nftAsset.OnTextureUpdate += UpdateTexture;
 7192    }
 193
 194    internal IEnumerator LoadNFTAssetCoroutine(NFTInfo nftInfo)
 195    {
 7196        var config = DataStore.i.Get<DataStore_NFTShape>();
 14197        yield return new WaitUntil(() => (CommonScriptableObjects.playerUnityPosition - transform.position).sqrMagnitude
 198
 199        // We download the "preview" 256px image
 7200        yield return nftAssetRetriever.LoadNFTAsset(
 201            nftInfo.previewImageUrl,
 202            (result) =>
 203            {
 7204                PrepareFrame(result, nftInfo.name, nftInfo.imageUrl);
 7205                FinishLoading(true);
 7206            },
 207            _ =>
 208            {
 0209                Debug.LogError(string.Format(COULD_NOT_FETCH_NFT_IMAGE, darURLRegistry, darURLAsset,
 210                    nftInfo.previewImageUrl));
 211
 0212                ShowErrorFeedback(true);
 0213                OnLoadingAssetFail?.Invoke();
 0214                FinishLoading(false);
 0215            });
 6216    }
 217
 218    void FinishLoading(bool loadedSuccessfully)
 219    {
 7220        if (loadedSuccessfully)
 221        {
 7222            ShowLoading(false);
 7223            OnLoadingAssetSuccess?.Invoke();
 224        }
 225        else
 226        {
 0227            OnLoadingAssetFail?.Invoke();
 228        }
 0229    }
 230
 231    void SetFrameImage(Texture2D texture, bool resizeFrameMesh = false)
 232    {
 7233        if (texture == null)
 7234            return;
 235
 0236        UpdateTexture(texture);
 237
 0238        if (resizeFrameMesh && meshRenderer != null)
 239        {
 240            float w, h;
 0241            w = h = 0.5f;
 0242            if (texture.width > texture.height)
 0243                h *= texture.height / (float) texture.width;
 0244            else if (texture.width < texture.height)
 0245                w *= texture.width / (float) texture.height;
 0246            Vector3 newScale = new Vector3(w, h, 1f);
 247
 0248            meshRenderer.transform.localScale = newScale;
 249        }
 0250    }
 251
 252    public void UpdateTexture(Texture2D texture)
 253    {
 0254        if (imageMaterial == null)
 0255            return;
 256
 0257        imageMaterial.SetTexture(BASEMAP_SHADER_PROPERTY, texture);
 0258        imageMaterial.SetColor(COLOR_SHADER_PROPERTY, Color.white);
 0259    }
 260
 261    private void ShowLoading(bool isVisible)
 262    {
 14263        if (spinner == null)
 0264            return;
 265
 14266        spinner.SetActive(isVisible);
 14267    }
 268
 269    private void ShowErrorFeedback(bool isVisible)
 270    {
 7271        if (errorFeedback == null)
 0272            return;
 273
 7274        if (isVisible)
 0275            ShowLoading(false);
 276
 7277        errorFeedback.SetActive(isVisible);
 7278    }
 279
 280    private void InitializeMaterials()
 281    {
 7282        Material[] meshMaterials = new Material[materials.Length];
 56283        for (int i = 0; i < materials.Length; i++)
 284        {
 21285            switch (materials[i].type)
 286            {
 287                case NFTShapeMaterial.MaterialType.BACKGROUND:
 7288                    backgroundMaterial = new Material(materials[i].material);
 7289                    meshMaterials[i] = backgroundMaterial;
 7290                    break;
 291                case NFTShapeMaterial.MaterialType.FRAME:
 7292                    frameMaterial = materials[i].material;
 7293                    meshMaterials[i] = frameMaterial;
 7294                    break;
 295                case NFTShapeMaterial.MaterialType.IMAGE:
 7296                    imageMaterial = new Material(materials[i].material);
 7297                    meshMaterials[i] = imageMaterial;
 298                    break;
 299            }
 300        }
 301
 302
 7303        meshRenderer.materials = meshMaterials;
 304
 7305        if (frameMaterial == null)
 0306            return;
 307
 7308        frameMaterial.shaderKeywords = null;
 309
 7310        if (noiseType == NoiseType.None)
 0311            return;
 312
 7313        switch (noiseType)
 314        {
 315            case NoiseType.ClassicPerlin:
 0316                frameMaterial.EnableKeyword("CNOISE");
 0317                break;
 318            case NoiseType.PeriodicPerlin:
 0319                frameMaterial.EnableKeyword("PNOISE");
 0320                break;
 321            case NoiseType.Simplex:
 7322                frameMaterial.EnableKeyword("SNOISE");
 7323                break;
 324            case NoiseType.SimplexNumericalGrad:
 0325                frameMaterial.EnableKeyword("SNOISE_NGRAD");
 0326                break;
 327            default: // SimplexAnalyticalGrad
 0328                frameMaterial.EnableKeyword("SNOISE_AGRAD");
 329                break;
 330        }
 331
 7332        if (noiseIs3D)
 0333            frameMaterial.EnableKeyword("THREED");
 334
 7335        if (noiseIsFractal)
 0336            frameMaterial.EnableKeyword("FRACTAL");
 7337    }
 338
 339
 340}