< Summary

Class:DCL.Components.PBRMaterial
Assembly:DCL.Components.Materials
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Materials/PBRMaterial.cs
Covered lines:136
Uncovered lines:45
Coverable lines:181
Total lines:385
Line coverage:75.1% (136 of 181)
Covered branches:0
Total branches:0
Covered methods:18
Total methods:20
Method coverage:90% (18 of 20)

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
Model()0%110100%
GetDataFromJSON(...)0%110100%
GetDataFromPb(...)0%3421800%
PBRMaterial()0%110100%
GetModel()0%110100%
GetClassId()0%2100%
AttachTo(...)0%2.032080%
ApplyChanges()0%770100%
SetupTransparencyMode()0%14.218054.05%
LoadMaterial(...)0%4.054085.71%
OnMaterialAttached(...)0%330100%
InitMaterial(...)0%6.016093.75%
OnShapeUpdated(...)0%220100%
OnMaterialDetached(...)0%440100%
FetchTexture(...)0%440100%
AreSameTextureComponent(...)0%220100%
SwitchTextureComponent(...)0%220100%
Dispose()0%660100%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/Components/Materials/PBRMaterial.cs

#LineLine coverage
 1using Cysharp.Threading.Tasks;
 2using DCL.Helpers;
 3using DCL.Models;
 4using System.Collections;
 5using System.Collections.Generic;
 6using System.Linq;
 7using UnityEngine;
 8using UnityEngine.Rendering;
 9using DCL.Shaders;
 10using Decentraland.Sdk.Ecs6;
 11using MainScripts.DCL.Components;
 12using System;
 13using Object = UnityEngine.Object;
 14
 15namespace DCL.Components
 16{
 17    public class PBRMaterial : BaseDisposable
 18    {
 19        [System.Serializable]
 20        public class Model : BaseModel
 21        {
 22            [Range(0f, 1f)]
 8923            public float alphaTest = 0.5f;
 24
 8925            public Color albedoColor = Color.white;
 26            public string albedoTexture;
 8927            public float metallic = 0.5f;
 8928            public float roughness = 0.5f;
 8929            public float microSurface = 1f; // Glossiness
 8930            public float specularIntensity = 1f;
 31
 32            public string alphaTexture;
 33            public string emissiveTexture;
 8934            public Color emissiveColor = Color.black;
 8935            public float emissiveIntensity = 2f;
 8936            public Color reflectivityColor = Color.white;
 8937            public float directIntensity = 1f;
 38            public string bumpTexture;
 8939            public bool castShadows = true;
 40
 41            [Range(0, 4)]
 8942            public int transparencyMode = 4; // 0: OPAQUE; 1: ALPHATEST; 2: ALPHBLEND; 3: ALPHATESTANDBLEND; 4: AUTO (En
 43
 3144            public override BaseModel GetDataFromJSON(string json) { return Utils.SafeFromJson<Model>(json); }
 45
 46            public override BaseModel GetDataFromPb(ComponentBodyPayload pbModel)
 47            {
 048                if (pbModel.PayloadCase != ComponentBodyPayload.PayloadOneofCase.Material)
 049                    return Utils.SafeUnimplemented<PBRMaterial, Model>(expected: ComponentBodyPayload.PayloadOneofCase.M
 50
 051                var pb = new Model();
 052                if (pbModel.Material.HasMetallic) pb.metallic = pbModel.Material.Metallic;
 053                if (pbModel.Material.HasRoughness) pb.roughness = pbModel.Material.Roughness;
 054                if (pbModel.Material.HasAlphaTest) pb.alphaTest = pbModel.Material.AlphaTest;
 055                if (pbModel.Material.HasDirectIntensity) pb.directIntensity = pbModel.Material.DirectIntensity;
 056                if (pbModel.Material.HasMicroSurface) pb.microSurface = pbModel.Material.MicroSurface;
 057                if (pbModel.Material.HasSpecularIntensity) pb.specularIntensity = pbModel.Material.SpecularIntensity;
 058                if (pbModel.Material.HasAlbedoTexture) pb.albedoTexture = pbModel.Material.AlbedoTexture;
 059                if (pbModel.Material.HasAlphaTexture) pb.alphaTexture = pbModel.Material.AlphaTexture;
 060                if (pbModel.Material.HasBumpTexture) pb.bumpTexture = pbModel.Material.BumpTexture;
 061                if (pbModel.Material.HasCastShadows) pb.castShadows = pbModel.Material.CastShadows;
 062                if (pbModel.Material.HasEmissiveIntensity) pb.emissiveIntensity = pbModel.Material.EmissiveIntensity;
 063                if (pbModel.Material.HasEmissiveTexture) pb.emissiveTexture = pbModel.Material.EmissiveTexture;
 064                if (pbModel.Material.HasTransparencyMode) pb.transparencyMode = (int)pbModel.Material.TransparencyMode;
 065                if (pbModel.Material.AlbedoColor != null) pb.albedoColor = pbModel.Material.AlbedoColor.AsUnityColor();
 066                if (pbModel.Material.EmissiveColor != null) pb.emissiveColor = pbModel.Material.EmissiveColor.AsUnityCol
 067                if (pbModel.Material.ReflectivityColor != null) pb.reflectivityColor = pbModel.Material.ReflectivityColo
 68
 069                return pb;
 70            }
 71        }
 72
 73        enum TransparencyMode
 74        {
 75            OPAQUE,
 76            ALPHA_TEST,
 77            ALPHA_BLEND,
 78            ALPHA_TEST_AND_BLEND,
 79            AUTO
 80        }
 81
 82        private enum TextureType
 83        {
 84            Albedo,
 85            Alpha,
 86            Emissive,
 87            Bump
 88        }
 89
 95890        public Material material { get; private set; }
 91        private string currentMaterialResourcesFilename;
 92
 93        const string MATERIAL_RESOURCES_PATH = "Materials/";
 94        const string PBR_MATERIAL_NAME = "ShapeMaterial";
 95
 2796        private readonly DCLTexture[] textures = new DCLTexture[4];
 97
 2798        private readonly DCLTexture.Fetcher[] dclTextureFetcher = new DCLTexture.Fetcher[]
 99        {
 100            new DCLTexture.Fetcher(),
 101            new DCLTexture.Fetcher(),
 102            new DCLTexture.Fetcher(),
 103            new DCLTexture.Fetcher()
 104        };
 105
 27106        public PBRMaterial()
 107        {
 27108            model = new Model();
 109
 27110            LoadMaterial(PBR_MATERIAL_NAME);
 111
 27112            OnAttach += OnMaterialAttached;
 27113            OnDetach += OnMaterialDetached;
 27114        }
 115
 8116        new public Model GetModel() { return (Model) model; }
 117
 0118        public override int GetClassId() { return (int) CLASS_ID.PBR_MATERIAL; }
 119
 120        public override void AttachTo(IDCLEntity entity, System.Type overridenAttachedType = null)
 121        {
 27122            if (attachedEntities.Contains(entity))
 123            {
 0124                return;
 125            }
 126
 27127            scene.componentsManagerLegacy.RemoveSharedComponent(entity, (typeof(BasicMaterial)));
 27128            base.AttachTo(entity);
 27129        }
 130
 131        public override IEnumerator ApplyChanges(BaseModel newModel)
 132        {
 31133            Model model = (Model) newModel;
 134
 31135            LoadMaterial(PBR_MATERIAL_NAME);
 136
 31137            material.SetColor(ShaderUtils.BaseColor, model.albedoColor);
 138
 31139            if (model.emissiveColor != Color.clear && model.emissiveColor != Color.black)
 140            {
 4141                material.EnableKeyword("_EMISSION");
 142            }
 143
 144            // METALLIC/SPECULAR CONFIGURATIONS
 31145            material.SetColor(ShaderUtils.EmissionColor, model.emissiveColor * model.emissiveIntensity);
 31146            material.SetColor(ShaderUtils.SpecColor, model.reflectivityColor);
 147
 31148            material.SetFloat(ShaderUtils.Metallic, model.metallic);
 31149            material.SetFloat(ShaderUtils.Smoothness, 1 - model.roughness);
 31150            material.SetFloat(ShaderUtils.EnvironmentReflections, model.microSurface);
 31151            material.SetFloat(ShaderUtils.SpecularHighlights, model.specularIntensity * model.directIntensity);
 152
 153
 154            // FETCH AND LOAD EMISSIVE TEXTURE
 31155            var fetchEmission = FetchTexture(ShaderUtils.EmissionMap, model.emissiveTexture, (int)TextureType.Emissive);
 156
 31157            SetupTransparencyMode();
 158
 159            // FETCH AND LOAD TEXTURES
 31160            var fetchBaseMap = FetchTexture(ShaderUtils.BaseMap, model.albedoTexture, (int)TextureType.Albedo);
 31161            var fetchAlpha = FetchTexture(ShaderUtils.AlphaTexture, model.alphaTexture, (int)TextureType.Alpha);
 31162            var fetchBump = FetchTexture(ShaderUtils.BumpMap, model.bumpTexture, (int)TextureType.Bump);
 163
 31164            yield return UniTask.WhenAll(fetchEmission, fetchBaseMap, fetchAlpha, fetchBump).ToCoroutine(e =>
 165            {
 0166                if (e is not OperationCanceledException)
 0167                    throw e;
 0168            });
 169
 118170            foreach (IDCLEntity entity in attachedEntities)
 28171                InitMaterial(entity);
 31172        }
 173
 174        private void SetupTransparencyMode()
 175        {
 31176            Model model = (Model) this.model;
 177
 178            // Reset shader keywords
 31179            material.DisableKeyword("_ALPHATEST_ON"); // Cut Out Transparency
 31180            material.DisableKeyword("_ALPHABLEND_ON"); // Fade Transparency
 31181            material.DisableKeyword("_ALPHAPREMULTIPLY_ON"); // Transparent
 182
 31183            TransparencyMode transparencyMode = (TransparencyMode) model.transparencyMode;
 184
 31185            if (transparencyMode == TransparencyMode.AUTO)
 186            {
 23187                if (!string.IsNullOrEmpty(model.alphaTexture) || model.albedoColor.a < 1f) //AlphaBlend
 188                {
 5189                    transparencyMode = TransparencyMode.ALPHA_BLEND;
 190                }
 191                else // Opaque
 192                {
 18193                    transparencyMode = TransparencyMode.OPAQUE;
 194                }
 195            }
 196
 197            switch (transparencyMode)
 198            {
 199                case TransparencyMode.OPAQUE:
 18200                    material.renderQueue = (int) UnityEngine.Rendering.RenderQueue.Geometry;
 18201                    material.SetFloat(ShaderUtils.AlphaClip, 0);
 18202                    break;
 203                case TransparencyMode.ALPHA_TEST: // ALPHATEST
 0204                    material.EnableKeyword("_ALPHATEST_ON");
 205
 0206                    material.SetInt(ShaderUtils.SrcBlend, (int) UnityEngine.Rendering.BlendMode.One);
 0207                    material.SetInt(ShaderUtils.DstBlend, (int) UnityEngine.Rendering.BlendMode.Zero);
 0208                    material.SetInt(ShaderUtils.ZWrite, 1);
 0209                    material.SetFloat(ShaderUtils.AlphaClip, 1);
 0210                    material.SetFloat(ShaderUtils.Cutoff, model.alphaTest);
 0211                    material.SetInt("_Surface", 0);
 0212                    material.renderQueue = (int) UnityEngine.Rendering.RenderQueue.AlphaTest;
 0213                    break;
 214                case TransparencyMode.ALPHA_BLEND: // ALPHABLEND
 13215                    material.EnableKeyword("_ALPHABLEND_ON");
 216
 13217                    material.SetInt(ShaderUtils.SrcBlend, (int) UnityEngine.Rendering.BlendMode.SrcAlpha);
 13218                    material.SetInt(ShaderUtils.DstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
 13219                    material.SetInt(ShaderUtils.ZWrite, 0);
 13220                    material.SetFloat(ShaderUtils.AlphaClip, 0);
 13221                    material.renderQueue = (int) UnityEngine.Rendering.RenderQueue.Transparent;
 13222                    material.SetInt("_Surface", 1);
 13223                    break;
 224                case TransparencyMode.ALPHA_TEST_AND_BLEND:
 0225                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
 226
 0227                    material.SetInt(ShaderUtils.SrcBlend, (int) UnityEngine.Rendering.BlendMode.One);
 0228                    material.SetInt(ShaderUtils.DstBlend, (int) UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
 0229                    material.SetInt(ShaderUtils.ZWrite, 0);
 0230                    material.SetFloat(ShaderUtils.AlphaClip, 1);
 0231                    material.renderQueue = (int) UnityEngine.Rendering.RenderQueue.Transparent;
 0232                    material.SetInt("_Surface", 1);
 233                    break;
 234            }
 0235        }
 236
 237        private void LoadMaterial(string resourcesFilename)
 238        {
 58239            if (material == null || currentMaterialResourcesFilename != resourcesFilename)
 240            {
 27241                if (material != null)
 0242                    Object.Destroy(material);
 243
 27244                material = new Material(Utils.EnsureResourcesMaterial(MATERIAL_RESOURCES_PATH + resourcesFilename));
 245#if UNITY_EDITOR
 27246                material.name = "PBRMaterial_" + id;
 247#endif
 27248                currentMaterialResourcesFilename = resourcesFilename;
 249            }
 58250        }
 251
 252        void OnMaterialAttached(IDCLEntity entity)
 253        {
 27254            entity.OnShapeUpdated -= OnShapeUpdated;
 27255            entity.OnShapeUpdated += OnShapeUpdated;
 256
 27257            if (entity.meshRootGameObject != null)
 258            {
 22259                var meshRenderer = entity.meshRootGameObject.GetComponent<MeshRenderer>();
 260
 22261                if (meshRenderer != null)
 22262                    InitMaterial(entity);
 263            }
 27264        }
 265
 266        void InitMaterial(IDCLEntity entity)
 267        {
 84268            var meshGameObject = entity.meshRootGameObject;
 269
 84270            if (meshGameObject == null)
 14271                return;
 272
 70273            var meshRenderer = meshGameObject.GetComponent<MeshRenderer>();
 274
 70275            if (meshRenderer == null)
 0276                return;
 277
 70278            Model model = (Model) this.model;
 279
 70280            meshRenderer.shadowCastingMode = model.castShadows ? ShadowCastingMode.On : ShadowCastingMode.Off;
 281
 70282            if (meshRenderer.sharedMaterial == material)
 47283                return;
 284
 23285            Material oldMaterial = meshRenderer.sharedMaterial;
 23286            meshRenderer.sharedMaterial = material;
 23287            SRPBatchingHelper.OptimizeMaterial(material);
 288
 23289            DataStore.i.sceneWorldObjects.RemoveMaterial(scene.sceneData.sceneNumber, entity.entityId, oldMaterial);
 23290            DataStore.i.sceneWorldObjects.AddMaterial(scene.sceneData.sceneNumber, entity.entityId, material);
 23291        }
 292
 293        private void OnShapeUpdated(IDCLEntity entity)
 294        {
 34295            if (entity != null)
 34296                InitMaterial(entity);
 34297        }
 298
 299        private void OnMaterialDetached(IDCLEntity entity)
 300        {
 20301            if (entity.meshRootGameObject != null)
 302            {
 5303                entity.OnShapeUpdated -= OnShapeUpdated;
 304
 5305                var meshRenderer = entity.meshRootGameObject.GetComponent<MeshRenderer>();
 306
 5307                if (meshRenderer && meshRenderer.sharedMaterial == material)
 5308                    meshRenderer.sharedMaterial = null;
 309            }
 20310            DataStore.i.sceneWorldObjects.RemoveMaterial(scene.sceneData.sceneNumber, entity.entityId, material);
 20311        }
 312
 313        private UniTask FetchTexture(int materialPropertyId, string textureComponentId, int textureType)
 314        {
 124315            if (!string.IsNullOrEmpty(textureComponentId))
 316            {
 39317                if (!AreSameTextureComponent(textureType, textureComponentId))
 318                {
 39319                    return dclTextureFetcher[textureType]
 320                       .Fetch(scene, textureComponentId,
 321                            fetchedDCLTexture =>
 322                            {
 39323                                if (material == null)
 0324                                    return false;
 325
 39326                                material.SetTexture(materialPropertyId, fetchedDCLTexture.texture);
 39327                                SwitchTextureComponent(textureType, fetchedDCLTexture);
 39328                                return true;
 329                            });
 330                }
 331            }
 332            else
 333            {
 85334                material.SetTexture(materialPropertyId, null);
 85335                textures[textureType]?.DetachFrom(this);
 85336                textures[textureType] = null;
 337            }
 338
 85339            return new UniTask();
 340        }
 341
 342        bool AreSameTextureComponent(int textureType, string textureId)
 343        {
 39344            DCLTexture dclTexture = textures[textureType];
 39345            if (dclTexture == null)
 35346                return false;
 4347            return dclTexture.id == textureId;
 348        }
 349
 350        void SwitchTextureComponent(int textureType, DCLTexture newTexture)
 351        {
 39352            DCLTexture dclTexture = textures[textureType];
 39353            dclTexture?.DetachFrom(this);
 39354            textures[textureType] = newTexture;
 39355            newTexture.AttachTo(this);
 39356        }
 357
 358        public override void Dispose()
 359        {
 110360            for (int i = 0; i < dclTextureFetcher.Length; i++)
 361            {
 44362                dclTextureFetcher[i].Dispose();
 363            }
 364
 110365            for (int i = 0; i < textures.Length; i++)
 366            {
 44367                textures[i]?.DetachFrom(this);
 44368                textures[i] = null;
 369            }
 370
 11371            if (material != null)
 372            {
 373                // we make sure we detach this material from every entity
 374                // before disposing it
 13375                while ( attachedEntities.Count > 0 )
 376                {
 5377                    DetachFrom(attachedEntities.First());
 378                }
 8379                Utils.SafeDestroy(material);
 380            }
 381
 11382            base.Dispose();
 11383        }
 384    }
 385}