< 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:95
Uncovered lines:54
Coverable lines:149
Total lines:348
Line coverage:63.7% (95 of 149)
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%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.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;
 064    public BoxCollider nftCollider => this.collider;
 65
 5566    public Material frameMaterial { private set; get; } = null;
 2267    public Material imageMaterial { private set; get; } = null;
 3968    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
 1176        meshRenderer.transform.localScale = new Vector3(0.5f, 0.5f, 1);
 1177        InitializeMaterials();
 1178    }
 79
 80    public void Initialize(INFTInfoRetriever nftInfoRetriever = null, INFTAssetRetriever nftAssetRetriever = null)
 81    {
 1882        if (nftInfoRetriever == null)
 1183            nftInfoRetriever = new NFTInfoRetriever();
 84
 1885        if (nftAssetRetriever == null)
 1186            nftAssetRetriever = new NFTAssetRetriever();
 87
 1888        this.nftInfoRetriever = nftInfoRetriever;
 1889        this.nftAssetRetriever = nftAssetRetriever;
 90
 1891        nftInfoRetriever.OnFetchInfoSuccess += FetchNftInfoSuccess;
 1892        nftInfoRetriever.OnFetchInfoFail += FetchNFTInfoFail;
 1893    }
 94
 95    private void OnEnable()
 96    {
 1197        Initialize();
 1198        nftInfoRetriever.OnFetchInfoSuccess += FetchNftInfoSuccess;
 1199        nftInfoRetriever.OnFetchInfoFail += FetchNFTInfoFail;
 11100    }
 101
 102    private void OnDisable()
 103    {
 11104        nftInfoRetriever.OnFetchInfoSuccess -= FetchNftInfoSuccess;
 11105        nftInfoRetriever.OnFetchInfoFail -= FetchNFTInfoFail;
 11106    }
 107
 14108    private void Start() { spinner.layer = LayerMask.NameToLayer("ViewportCullingIgnored"); }
 109
 39110    void Update() { hqTextureHandler?.Update(); }
 111
 112    public void LoadAsset(string url, bool loadEvenIfAlreadyLoaded = false)
 113    {
 7114        if (string.IsNullOrEmpty(url) || (!loadEvenIfAlreadyLoaded && alreadyLoadedAsset))
 0115            return;
 116
 7117        ShowErrorFeedback(false);
 7118        UpdateBackgroundColor(backgroundColor);
 119
 120        // Check the src follows the needed format e.g.: 'ethereum://0x06012c8cf97BEaD5deAe237070F9587f8E7A266d/558536'
 7121        var regexMatches = Regex.Matches(url, "(?<protocol>[^:]+)://(?<registry>0x([A-Fa-f0-9])+)(?:/(?<asset>.+))?");
 7122        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
 7131        Match match = regexMatches[0];
 7132        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
 7141        darURLProtocol = match.Groups["protocol"].ToString();
 7142        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
 7151        darURLRegistry = match.Groups["registry"].ToString();
 7152        darURLAsset = match.Groups["asset"].ToString();
 153
 7154        alreadyLoadedAsset = false;
 155
 7156        FetchNFTContents();
 7157    }
 158
 159    public void UpdateBackgroundColor(Color newColor)
 160    {
 8161        if (backgroundMaterial == null)
 0162            return;
 163
 8164        backgroundMaterial.SetColor(COLOR_SHADER_PROPERTY, newColor);
 8165    }
 166
 167    private void FetchNFTContents()
 168    {
 7169        ShowLoading(true);
 7170        nftInfoRetriever.FetchNFTInfo(darURLRegistry, darURLAsset);
 7171    }
 172
 173    private void FetchNftInfoSuccess(NFTInfo nftInfo)
 174    {
 7175        loadNftAssetCoroutine = StartCoroutine(LoadNFTAssetCoroutine(nftInfo));
 7176    }
 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            collider = collider,
 192            transform = transform,
 193            name = nftName,
 194            imageUrl = nftImageUrl,
 195            asset = nftAsset
 196        };
 197
 7198        hqTextureHandler = NFTShapeHQImageHandler.Create(hqImageHandlerConfig);
 7199        nftAsset.OnTextureUpdate += UpdateTexture;
 7200    }
 201
 202    internal IEnumerator LoadNFTAssetCoroutine(NFTInfo nftInfo)
 203    {
 7204        var config = DataStore.i.Get<DataStore_NFTShape>();
 14205        yield return new DCL.WaitUntil(() => (CommonScriptableObjects.playerUnityPosition - transform.position).sqrMagni
 206
 207        // We download the "preview" 256px image
 7208        yield return nftAssetRetriever.LoadNFTAsset(
 209            nftInfo.previewImageUrl,
 210            (result) =>
 211            {
 7212                PrepareFrame(result, nftInfo.name, nftInfo.imageUrl);
 7213                FinishLoading(true);
 7214            },
 215            (exc) =>
 216            {
 0217                Debug.LogError(string.Format(COULD_NOT_FETCH_NFT_IMAGE, darURLRegistry, darURLAsset,
 218                    nftInfo.previewImageUrl));
 219
 0220                ShowErrorFeedback(true);
 0221                OnLoadingAssetFail?.Invoke();
 0222                FinishLoading(false);
 0223            });
 6224    }
 225
 226    void FinishLoading(bool loadedSuccessfully)
 227    {
 7228        if (loadedSuccessfully)
 229        {
 7230            ShowLoading(false);
 7231            OnLoadingAssetSuccess?.Invoke();
 232        }
 233        else
 234        {
 0235            OnLoadingAssetFail?.Invoke();
 236        }
 0237    }
 238
 239    void SetFrameImage(Texture2D texture, bool resizeFrameMesh = false)
 240    {
 7241        if (texture == null)
 7242            return;
 243
 0244        UpdateTexture(texture);
 245
 0246        if (resizeFrameMesh && meshRenderer != null)
 247        {
 248            float w, h;
 0249            w = h = 0.5f;
 0250            if (texture.width > texture.height)
 0251                h *= texture.height / (float) texture.width;
 0252            else if (texture.width < texture.height)
 0253                w *= texture.width / (float) texture.height;
 0254            Vector3 newScale = new Vector3(w, h, 1f);
 255
 0256            meshRenderer.transform.localScale = newScale;
 257        }
 0258    }
 259
 260    public void UpdateTexture(Texture2D texture)
 261    {
 0262        if (imageMaterial == null)
 0263            return;
 264
 0265        imageMaterial.SetTexture(BASEMAP_SHADER_PROPERTY, texture);
 0266        imageMaterial.SetColor(COLOR_SHADER_PROPERTY, Color.white);
 0267    }
 268
 269    private void ShowLoading(bool isVisible)
 270    {
 14271        if (spinner == null)
 0272            return;
 273
 14274        spinner.SetActive(isVisible);
 14275    }
 276
 277    private void ShowErrorFeedback(bool isVisible)
 278    {
 7279        if (errorFeedback == null)
 0280            return;
 281
 7282        if (isVisible)
 0283            ShowLoading(false);
 284
 7285        errorFeedback.SetActive(isVisible);
 7286    }
 287
 288    void InitializeMaterials()
 289    {
 11290        Material[] meshMaterials = new Material[materials.Length];
 88291        for (int i = 0; i < materials.Length; i++)
 292        {
 33293            switch (materials[i].type)
 294            {
 295                case NFTShapeMaterial.MaterialType.BACKGROUND:
 11296                    backgroundMaterial = new Material(materials[i].material);
 11297                    meshMaterials[i] = backgroundMaterial;
 11298                    break;
 299                case NFTShapeMaterial.MaterialType.FRAME:
 11300                    frameMaterial = materials[i].material;
 11301                    meshMaterials[i] = frameMaterial;
 11302                    break;
 303                case NFTShapeMaterial.MaterialType.IMAGE:
 11304                    imageMaterial = new Material(materials[i].material);
 11305                    meshMaterials[i] = imageMaterial;
 306                    break;
 307            }
 308        }
 309
 310
 11311        meshRenderer.materials = meshMaterials;
 312
 11313        if (frameMaterial == null)
 0314            return;
 315
 11316        frameMaterial.shaderKeywords = null;
 317
 11318        if (noiseType == NoiseType.None)
 0319            return;
 320
 11321        switch (noiseType)
 322        {
 323            case NoiseType.ClassicPerlin:
 0324                frameMaterial.EnableKeyword("CNOISE");
 0325                break;
 326            case NoiseType.PeriodicPerlin:
 0327                frameMaterial.EnableKeyword("PNOISE");
 0328                break;
 329            case NoiseType.Simplex:
 11330                frameMaterial.EnableKeyword("SNOISE");
 11331                break;
 332            case NoiseType.SimplexNumericalGrad:
 0333                frameMaterial.EnableKeyword("SNOISE_NGRAD");
 0334                break;
 335            default: // SimplexAnalyticalGrad
 0336                frameMaterial.EnableKeyword("SNOISE_AGRAD");
 337                break;
 338        }
 339
 11340        if (noiseIs3D)
 0341            frameMaterial.EnableKeyword("THREED");
 342
 11343        if (noiseIsFractal)
 0344            frameMaterial.EnableKeyword("FRACTAL");
 11345    }
 346
 347
 348}