< 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:56
Coverable lines:150
Total lines:347
Line coverage:62.6% (94 of 150)
Covered branches:0
Total branches:0

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%220100%
LoadAsset(...)0%28.0812051.85%
UpdateBackgroundColor(...)0%2.062075%
FetchNFTContents()0%110100%
FetchNftInfoSuccess(...)0%110100%
FetchNFTInfoFail()0%2100%
PrepareFrame(...)0%220100%
LoadNFTAssetCoroutine()0%440100%
FinishLoading(...)0%4.594066.67%
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.Components;
 2using DCL.Helpers.NFT;
 3using System.Collections;
 4using System.Text.RegularExpressions;
 5using UnityEngine;
 6using DCL;
 7using NFTShape_Internal;
 8
 9public interface INFTShapeLoaderController
 10{
 11    BoxCollider nftCollider { get; }
 12    Transform transform { get; }
 13}
 14
 15public class NFTShapeLoaderController : MonoBehaviour, INFTShapeLoaderController
 16{
 17    internal const string COULD_NOT_FETCH_DAR_URL = "Couldn't fetch DAR url '{0}' for NFTShape.";
 18    internal const string ACCEPTED_URL_FORMAT = "The accepted format is 'ethereum://ContractAddress/TokenID'.";
 19    internal const string SUPPORTED_PROTOCOL = "The only protocol currently supported is 'ethereum'.";
 20    internal const string DOES_NOT_SUPPORT_POLYGON = "Warning: OpenSea API does not support fetching Polygon assets.";
 21    internal const string COULD_NOT_FETCH_NFT_FROM_API = "Couldn't fetch NFT: '{0}/{1}'.";
 22    internal const string COULD_NOT_FETCH_NFT_IMAGE = "Couldn't fetch NFT image for: '{0}/{1}': {2}.";
 23
 24    public enum NoiseType
 25    {
 26        ClassicPerlin,
 27        PeriodicPerlin,
 28        Simplex,
 29        SimplexNumericalGrad,
 30        SimplexAnalyticalGrad,
 31        None
 32    }
 33
 34    public MeshRenderer meshRenderer;
 35    public new BoxCollider collider;
 36    public Color backgroundColor;
 37    public GameObject spinner;
 38    public GameObject errorFeedback;
 39
 40    [HideInInspector] public bool alreadyLoadedAsset = false;
 41    private INFTInfoRetriever nftInfoRetriever;
 42    private INFTAssetRetriever nftAssetRetriever;
 43    private NFTShapeHQImageHandler hqTextureHandler = null;
 44    private Coroutine loadNftAssetCoroutine;
 45
 46    public event System.Action OnLoadingAssetSuccess;
 47    public event System.Action OnLoadingAssetFail;
 48
 49    [SerializeField]
 50    NFTShapeMaterial[] materials;
 51
 52    [Header("Noise Shader")]
 53    [SerializeField]
 3554    NoiseType noiseType = NoiseType.Simplex;
 55
 56    [SerializeField] bool noiseIs3D = false;
 57    [SerializeField] bool noiseIsFractal = false;
 58
 59    System.Action<LoadWrapper> OnSuccess;
 60    System.Action<LoadWrapper> OnFail;
 61    private string darURLProtocol;
 62    private string darURLRegistry;
 63    private string darURLAsset;
 10164    public BoxCollider nftCollider => this.collider;
 65
 066    public Material frameMaterial { private set; get; } = null;
 067    public Material imageMaterial { private set; get; } = null;
 068    public Material backgroundMaterial { private set; get; } = null;
 69
 170    static readonly int BASEMAP_SHADER_PROPERTY = Shader.PropertyToID("_BaseMap");
 171    static readonly int COLOR_SHADER_PROPERTY = Shader.PropertyToID("_BaseColor");
 72
 73    void Awake()
 74    {
 75        // NOTE: we use half scale to keep backward compatibility cause we are using 512px to normalize the scale with a
 1276        meshRenderer.transform.localScale = new Vector3(0.5f, 0.5f, 1);
 1277        InitializeMaterials();
 1278    }
 79
 80    public void Initialize(INFTInfoRetriever nftInfoRetriever = null, INFTAssetRetriever nftAssetRetriever = null)
 81    {
 2082        if (nftInfoRetriever == null)
 1283            nftInfoRetriever = new NFTInfoRetriever();
 84
 2085        if (nftAssetRetriever == null)
 1286            nftAssetRetriever = new NFTAssetRetriever();
 87
 2088        this.nftInfoRetriever = nftInfoRetriever;
 2089        this.nftAssetRetriever = nftAssetRetriever;
 90
 2091        nftInfoRetriever.OnFetchInfoSuccess += FetchNftInfoSuccess;
 2092        nftInfoRetriever.OnFetchInfoFail += FetchNFTInfoFail;
 2093    }
 94
 95    private void OnEnable()
 96    {
 1297        Initialize();
 1298        nftInfoRetriever.OnFetchInfoSuccess += FetchNftInfoSuccess;
 1299        nftInfoRetriever.OnFetchInfoFail += FetchNFTInfoFail;
 12100    }
 101
 102    private void OnDisable()
 103    {
 12104        nftInfoRetriever.OnFetchInfoSuccess -= FetchNftInfoSuccess;
 12105        nftInfoRetriever.OnFetchInfoFail -= FetchNFTInfoFail;
 12106    }
 107
 14108    private void Start() { spinner.layer = LayerMask.NameToLayer("ViewportCullingIgnored"); }
 109
 35110    void Update() { hqTextureHandler?.Update(); }
 111
 112    public void LoadAsset(string url, bool loadEvenIfAlreadyLoaded = false)
 113    {
 8114        if (string.IsNullOrEmpty(url) || (!loadEvenIfAlreadyLoaded && alreadyLoadedAsset))
 0115            return;
 116
 8117        ShowErrorFeedback(false);
 8118        UpdateBackgroundColor(backgroundColor);
 119
 120        // Check the src follows the needed format e.g.: 'ethereum://0x06012c8cf97BEaD5deAe237070F9587f8E7A266d/558536'
 8121        var regexMatches = Regex.Matches(url, "(?<protocol>[^:]+)://(?<registry>0x([A-Fa-f0-9])+)(?:/(?<asset>.+))?");
 8122        if (regexMatches.Count == 0)
 123        {
 0124            Debug.LogError(string.Format(COULD_NOT_FETCH_DAR_URL + " " + ACCEPTED_URL_FORMAT, url));
 0125            ShowErrorFeedback(true);
 0126            OnLoadingAssetFail?.Invoke();
 127
 0128            return;
 129        }
 130
 8131        Match match = regexMatches[0];
 8132        if (match.Groups["protocol"] == null || match.Groups["registry"] == null || match.Groups["asset"] == null)
 133        {
 0134            Debug.LogError(string.Format(COULD_NOT_FETCH_DAR_URL + " " + ACCEPTED_URL_FORMAT, url));
 0135            ShowErrorFeedback(true);
 0136            OnLoadingAssetFail?.Invoke();
 137
 0138            return;
 139        }
 140
 8141        darURLProtocol = match.Groups["protocol"].ToString();
 8142        if (darURLProtocol != "ethereum")
 143        {
 0144            Debug.LogError(string.Format(COULD_NOT_FETCH_DAR_URL + " " + SUPPORTED_PROTOCOL + " " + ACCEPTED_URL_FORMAT,
 0145            ShowErrorFeedback(true);
 0146            OnLoadingAssetFail?.Invoke();
 147
 0148            return;
 149        }
 150
 8151        darURLRegistry = match.Groups["registry"].ToString();
 8152        darURLAsset = match.Groups["asset"].ToString();
 153
 8154        alreadyLoadedAsset = false;
 155
 8156        FetchNFTContents();
 8157    }
 158
 159    public void UpdateBackgroundColor(Color newColor)
 160    {
 12161        if (backgroundMaterial == null)
 0162            return;
 163
 12164        backgroundMaterial.SetColor(COLOR_SHADER_PROPERTY, newColor);
 12165    }
 166
 167    private void FetchNFTContents()
 168    {
 8169        ShowLoading(true);
 8170        nftInfoRetriever.FetchNFTInfo(darURLRegistry, darURLAsset);
 8171    }
 172
 173    private void FetchNftInfoSuccess(NFTInfo nftInfo)
 174    {
 8175        loadNftAssetCoroutine = StartCoroutine(LoadNFTAssetCoroutine(nftInfo));
 8176    }
 177
 178    private void FetchNFTInfoFail()
 179    {
 0180        ShowErrorFeedback(true);
 0181        FinishLoading(false);
 0182    }
 183
 184    private void PrepareFrame(INFTAsset nftAsset, string nftName, string nftImageUrl)
 185    {
 7186        if (nftAsset.previewAsset != null)
 7187            SetFrameImage(nftAsset.previewAsset.texture, resizeFrameMesh: true);
 188
 7189        var hqImageHandlerConfig = new NFTShapeHQImageConfig()
 190        {
 191            controller = this,
 192            name = nftName,
 193            imageUrl = nftImageUrl,
 194            asset = nftAsset
 195        };
 196
 7197        hqTextureHandler = NFTShapeHQImageHandler.Create(hqImageHandlerConfig);
 7198        nftAsset.OnTextureUpdate += UpdateTexture;
 7199    }
 200
 201    internal IEnumerator LoadNFTAssetCoroutine(NFTInfo nftInfo)
 202    {
 8203        var config = DataStore.i.Get<DataStore_NFTShape>();
 16204        yield return new DCL.WaitUntil(() => (CommonScriptableObjects.playerUnityPosition - transform.position).sqrMagni
 205
 206        // We download the "preview" 256px image
 7207        yield return nftAssetRetriever.LoadNFTAsset(
 208            nftInfo.previewImageUrl,
 209            (result) =>
 210            {
 7211                PrepareFrame(result, nftInfo.name, nftInfo.imageUrl);
 7212                FinishLoading(true);
 7213            },
 214            (exc) =>
 215            {
 0216                Debug.LogError(string.Format(COULD_NOT_FETCH_NFT_IMAGE, darURLRegistry, darURLAsset,
 217                    nftInfo.previewImageUrl));
 218
 0219                ShowErrorFeedback(true);
 0220                OnLoadingAssetFail?.Invoke();
 0221                FinishLoading(false);
 0222            });
 6223    }
 224
 225    void FinishLoading(bool loadedSuccessfully)
 226    {
 7227        if (loadedSuccessfully)
 228        {
 7229            ShowLoading(false);
 7230            OnLoadingAssetSuccess?.Invoke();
 7231        }
 232        else
 233        {
 0234            OnLoadingAssetFail?.Invoke();
 235        }
 0236    }
 237
 238    void SetFrameImage(Texture2D texture, bool resizeFrameMesh = false)
 239    {
 7240        if (texture == null)
 7241            return;
 242
 0243        UpdateTexture(texture);
 244
 0245        if (resizeFrameMesh && meshRenderer != null)
 246        {
 247            float w, h;
 0248            w = h = 0.5f;
 0249            if (texture.width > texture.height)
 0250                h *= texture.height / (float) texture.width;
 0251            else if (texture.width < texture.height)
 0252                w *= texture.width / (float) texture.height;
 0253            Vector3 newScale = new Vector3(w, h, 1f);
 254
 0255            meshRenderer.transform.localScale = newScale;
 256        }
 0257    }
 258
 259    public void UpdateTexture(Texture2D texture)
 260    {
 0261        if (imageMaterial == null)
 0262            return;
 263
 0264        imageMaterial.SetTexture(BASEMAP_SHADER_PROPERTY, texture);
 0265        imageMaterial.SetColor(COLOR_SHADER_PROPERTY, Color.white);
 0266    }
 267
 268    private void ShowLoading(bool isVisible)
 269    {
 15270        if (spinner == null)
 0271            return;
 272
 15273        spinner.SetActive(isVisible);
 15274    }
 275
 276    private void ShowErrorFeedback(bool isVisible)
 277    {
 8278        if (errorFeedback == null)
 0279            return;
 280
 8281        if (isVisible)
 0282            ShowLoading(false);
 283
 8284        errorFeedback.SetActive(isVisible);
 8285    }
 286
 287    void InitializeMaterials()
 288    {
 12289        Material[] meshMaterials = new Material[materials.Length];
 96290        for (int i = 0; i < materials.Length; i++)
 291        {
 36292            switch (materials[i].type)
 293            {
 294                case NFTShapeMaterial.MaterialType.BACKGROUND:
 12295                    backgroundMaterial = new Material(materials[i].material);
 12296                    meshMaterials[i] = backgroundMaterial;
 12297                    break;
 298                case NFTShapeMaterial.MaterialType.FRAME:
 12299                    frameMaterial = materials[i].material;
 12300                    meshMaterials[i] = frameMaterial;
 12301                    break;
 302                case NFTShapeMaterial.MaterialType.IMAGE:
 12303                    imageMaterial = new Material(materials[i].material);
 12304                    meshMaterials[i] = imageMaterial;
 305                    break;
 306            }
 307        }
 308
 309
 12310        meshRenderer.materials = meshMaterials;
 311
 12312        if (frameMaterial == null)
 0313            return;
 314
 12315        frameMaterial.shaderKeywords = null;
 316
 12317        if (noiseType == NoiseType.None)
 0318            return;
 319
 12320        switch (noiseType)
 321        {
 322            case NoiseType.ClassicPerlin:
 0323                frameMaterial.EnableKeyword("CNOISE");
 0324                break;
 325            case NoiseType.PeriodicPerlin:
 0326                frameMaterial.EnableKeyword("PNOISE");
 0327                break;
 328            case NoiseType.Simplex:
 12329                frameMaterial.EnableKeyword("SNOISE");
 12330                break;
 331            case NoiseType.SimplexNumericalGrad:
 0332                frameMaterial.EnableKeyword("SNOISE_NGRAD");
 0333                break;
 334            default: // SimplexAnalyticalGrad
 0335                frameMaterial.EnableKeyword("SNOISE_AGRAD");
 336                break;
 337        }
 338
 12339        if (noiseIs3D)
 0340            frameMaterial.EnableKeyword("THREED");
 341
 12342        if (noiseIsFractal)
 0343            frameMaterial.EnableKeyword("FRACTAL");
 12344    }
 345
 346
 347}