< Summary

Class:DCL.Controllers.SceneBoundsChecker
Assembly:MainScripts
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/SceneBoundariesController/SceneBoundsChecker.cs
Covered lines:131
Uncovered lines:9
Coverable lines:140
Total lines:312
Line coverage:93.5% (131 of 140)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SceneBoundsChecker(...)0%220100%
SetFeedbackStyle(...)0%110100%
GetFeedbackStyle()0%110100%
GetOriginalMaterials(...)0%110100%
CheckEntities()0%990100%
Restart()0%110100%
Start()0%2.032080%
Stop()0%220100%
AddEntityToBeChecked(...)0%220100%
AddPersistent(...)0%220100%
WasAddedAsPersistent(...)0%110100%
RemoveEntityToBeChecked(...)0%2.062075%
EvaluateEntityPosition(...)0%12.0312094.12%
IsEntityInsideSceneBoundaries(...)0%440100%
EvaluateMeshBounds(...)0%330100%
AreSubmeshesInsideBoundaries(...)0%4.374071.43%
UpdateEntityMeshesValidState(...)0%110100%
UpdateEntityCollidersValidState(...)0%10.2910085.71%
UpdateComponents(...)0%220100%
OnAddEntity(...)0%110100%
OnRemoveEntity(...)0%110100%
AddEntityBasedOnPriority(...)0%440100%
IsHighPrioEntity(...)0%7.397080%

File(s)

/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/SceneBoundariesController/SceneBoundsChecker.cs

#LineLine coverage
 1using DCL.Components;
 2using DCL.Models;
 3using UnityEngine;
 4using System.Collections.Generic;
 5using System.Collections;
 6using System;
 7using DCL.Helpers;
 8
 9namespace DCL.Controllers
 10{
 11    public class SceneBoundsChecker : ISceneBoundsChecker
 12    {
 13        public const int TRIGGER_HIGHPRIO_VALUE = 1000;
 14        public event Action<IDCLEntity, bool> OnEntityBoundsCheckerStatusChanged;
 15
 107816        public bool enabled => entitiesCheckRoutine != null;
 17
 70718        public float timeBetweenChecks { get; set; } = 0.5f;
 19
 20        // We use Hashset instead of Queue to be able to have a unique representation of each entity when added.
 67621        HashSet<IDCLEntity> highPrioEntitiesToCheck = new HashSet<IDCLEntity>();
 67622        HashSet<IDCLEntity> entitiesToCheck = new HashSet<IDCLEntity>();
 67623        HashSet<IDCLEntity> checkedEntities = new HashSet<IDCLEntity>();
 24        Coroutine entitiesCheckRoutine = null;
 25        float lastCheckTime;
 67626        private HashSet<IDCLEntity> persistentEntities = new HashSet<IDCLEntity>();
 27
 228        public int entitiesToCheckCount => entitiesToCheck.Count;
 429        public int highPrioEntitiesToCheckCount => highPrioEntitiesToCheck.Count;
 30
 31        private ISceneBoundsFeedbackStyle feedbackStyle;
 32
 202833        public SceneBoundsChecker(ISceneBoundsFeedbackStyle feedbackStyle = null) { this.feedbackStyle = feedbackStyle ?
 34
 35        public void SetFeedbackStyle(ISceneBoundsFeedbackStyle feedbackStyle)
 36        {
 3737            this.feedbackStyle.CleanFeedback();
 3738            this.feedbackStyle = feedbackStyle;
 3739            Restart();
 3740        }
 41
 56542        public ISceneBoundsFeedbackStyle GetFeedbackStyle() { return feedbackStyle; }
 43
 53044        public List<Material> GetOriginalMaterials(MeshesInfo meshesInfo) { return feedbackStyle.GetOriginalMaterials(me
 45
 46
 47        // TODO: Improve MessagingControllersManager.i.timeBudgetCounter usage once we have the centralized budget contr
 48        IEnumerator CheckEntities()
 49        {
 1069850            while (true)
 51            {
 1141152                float elapsedTime = Time.realtimeSinceStartup - lastCheckTime;
 1141153                if ((entitiesToCheck.Count > 0 || highPrioEntitiesToCheck.Count > 0) && (timeBetweenChecks <= 0f || elap
 54                {
 55                    //TODO(Brian): Remove later when we implement a centralized way of handling time budgets
 10156                    var messagingManager = Environment.i.messaging.manager as MessagingControllersManager;
 57
 58                    void processEntitiesList(HashSet<IDCLEntity> entities)
 59                    {
 20260                        if (messagingManager != null && messagingManager.timeBudgetCounter <= 0f)
 061                            return;
 62
 20263                        using HashSet<IDCLEntity>.Enumerator iterator = entities.GetEnumerator();
 31564                        while (iterator.MoveNext())
 65                        {
 11366                            if (messagingManager != null && messagingManager.timeBudgetCounter <= 0f)
 067                                break;
 68
 11369                            float startTime = Time.realtimeSinceStartup;
 70
 11371                            EvaluateEntityPosition(iterator.Current);
 11372                            checkedEntities.Add(iterator.Current);
 73
 11374                            float finishTime = Time.realtimeSinceStartup;
 75
 11376                            if ( messagingManager != null )
 11177                                messagingManager.timeBudgetCounter -= (finishTime - startTime);
 78                        }
 40479                    }
 80
 10181                    processEntitiesList(highPrioEntitiesToCheck);
 10182                    processEntitiesList(entitiesToCheck);
 83
 84                    // As we can't modify the hashset while traversing it, we keep track of the entities that should be 
 10185                    using (var iterator = checkedEntities.GetEnumerator())
 86                    {
 20887                        while (iterator.MoveNext())
 88                        {
 10789                            if (!persistentEntities.Contains(iterator.Current))
 90                            {
 10491                                entitiesToCheck.Remove(iterator.Current);
 10492                                highPrioEntitiesToCheck.Remove(iterator.Current);
 93                            }
 94                        }
 10195                    }
 96
 10197                    checkedEntities.Clear();
 98
 10199                    lastCheckTime = Time.realtimeSinceStartup;
 100                }
 101
 11411102                yield return null;
 103            }
 104        }
 105
 106        public void Restart()
 107        {
 37108            Stop();
 37109            Start();
 37110        }
 111
 112        public void Start()
 113        {
 713114            if (entitiesCheckRoutine != null)
 0115                return;
 116
 713117            lastCheckTime = Time.realtimeSinceStartup;
 713118            entitiesCheckRoutine = CoroutineStarter.Start(CheckEntities());
 713119        }
 120
 121        public void Stop()
 122        {
 791123            if (entitiesCheckRoutine == null)
 78124                return;
 125
 713126            CoroutineStarter.Stop(entitiesCheckRoutine);
 713127            entitiesCheckRoutine = null;
 713128        }
 129
 130        public void AddEntityToBeChecked(IDCLEntity entity)
 131        {
 565132            if (!enabled)
 167133                return;
 134
 398135            OnAddEntity(entity);
 398136        }
 137
 138        /// <summary>
 139        /// Add an entity that will be consistently checked, until manually removed from the list.
 140        /// </summary>
 141        public void AddPersistent(IDCLEntity entity)
 142        {
 49143            if (!enabled)
 22144                return;
 145
 27146            AddEntityBasedOnPriority(entity);
 147
 27148            persistentEntities.Add(entity);
 27149        }
 150
 151        /// <summary>
 152        /// Returns whether an entity was added to be consistently checked
 153        /// </summary>
 154        ///
 4155        public bool WasAddedAsPersistent(IDCLEntity entity) { return persistentEntities.Contains(entity); }
 156
 157        public void RemoveEntityToBeChecked(IDCLEntity entity)
 158        {
 56159            if (!enabled)
 0160                return;
 161
 56162            OnRemoveEntity(entity);
 56163        }
 164
 165        public void EvaluateEntityPosition(IDCLEntity entity)
 166        {
 136167            if (entity == null || entity.scene == null || entity.gameObject == null)
 0168                return;
 169
 170            // Recursively evaluate entity children as well, we need to check this up front because this entity may not 
 136171            if (entity.children.Count > 0)
 172            {
 5173                using (var iterator = entity.children.GetEnumerator())
 174                {
 10175                    while (iterator.MoveNext())
 176                    {
 5177                        EvaluateEntityPosition(iterator.Current.Value);
 178                    }
 5179                }
 180            }
 181
 136182            if (entity.meshRootGameObject == null || entity.meshesInfo.renderers == null || entity.meshesInfo.renderers.
 183            {
 55184                UpdateComponents(entity, entity.scene.IsInsideSceneBoundaries(entity.gameObject.transform.position + Com
 55185                return;
 186            }
 187
 188            // If the mesh is being loaded we should skip the evaluation (it will be triggered again later when the load
 81189            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>()) // the object's MaterialTransiti
 190            {
 1191                return;
 192            }
 193
 80194            var loadWrapper = LoadableShape.GetLoaderForEntity(entity);
 80195            if (loadWrapper != null && !loadWrapper.alreadyLoaded)
 7196                return;
 197
 73198            EvaluateMeshBounds(entity);
 73199        }
 200
 201        public bool IsEntityInsideSceneBoundaries(IDCLEntity entity)
 202        {
 125203            if (entity.meshesInfo == null || entity.meshesInfo.meshRootGameObject == null || entity.meshesInfo.mergedBou
 25204                return false;
 205
 206            // 1st check (full mesh AABB)
 100207            bool isInsideBoundaries = entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.mergedBounds);
 208
 209            // 2nd check (submeshes AABB)
 100210            if (!isInsideBoundaries)
 211            {
 71212                isInsideBoundaries = AreSubmeshesInsideBoundaries(entity);
 213            }
 214
 100215            return isInsideBoundaries;
 216        }
 217
 218        void EvaluateMeshBounds(IDCLEntity entity)
 219        {
 73220            bool isInsideBoundaries = IsEntityInsideSceneBoundaries(entity);
 73221            if (entity.isInsideBoundaries != isInsideBoundaries)
 222            {
 35223                entity.isInsideBoundaries = isInsideBoundaries;
 35224                OnEntityBoundsCheckerStatusChanged?.Invoke(entity, isInsideBoundaries);
 225            }
 226
 73227            UpdateEntityMeshesValidState(entity.meshesInfo, isInsideBoundaries);
 73228            UpdateEntityCollidersValidState(entity.meshesInfo, isInsideBoundaries);
 73229            UpdateComponents(entity, isInsideBoundaries);
 73230        }
 231
 232        protected bool AreSubmeshesInsideBoundaries(IDCLEntity entity)
 233        {
 142234            for (int i = 0; i < entity.meshesInfo.renderers.Length; i++)
 235            {
 71236                if (entity.meshesInfo.renderers[i] == null)
 237                    continue;
 238
 71239                if (!entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.renderers[i].GetSafeBounds()))
 71240                    return false;
 241            }
 242
 0243            return true;
 244        }
 245
 146246        protected void UpdateEntityMeshesValidState(MeshesInfo meshesInfo, bool isInsideBoundaries) { feedbackStyle.Appl
 247
 248        protected void UpdateEntityCollidersValidState(MeshesInfo meshesInfo, bool isInsideBoundaries)
 249        {
 73250            if (meshesInfo == null || meshesInfo.colliders == null)
 0251                return;
 252
 73253            int collidersCount = meshesInfo.colliders.Count;
 254
 73255            if (collidersCount == 0)
 8256                return;
 257
 65258            if (meshesInfo.colliders[0] == null)
 0259                return;
 260
 65261            if (collidersCount > 0 && isInsideBoundaries != meshesInfo.colliders[0].enabled && meshesInfo.currentShape.H
 262            {
 234263                for (int i = 0; i < collidersCount; i++)
 264                {
 72265                    if (meshesInfo.colliders[i] != null)
 72266                        meshesInfo.colliders[i].enabled = isInsideBoundaries;
 267                }
 268            }
 65269        }
 270
 271        protected void UpdateComponents(IDCLEntity entity, bool isInsideBoundaries)
 272        {
 128273            IOutOfSceneBoundariesHandler[] components = entity.gameObject.GetComponentsInChildren<IOutOfSceneBoundariesH
 274
 264275            for (int i = 0; i < components.Length; i++)
 276            {
 4277                components[i].UpdateOutOfBoundariesState(isInsideBoundaries);
 278            }
 128279        }
 280
 281        protected void OnAddEntity(IDCLEntity entity)
 282        {
 398283            AddEntityBasedOnPriority(entity);
 398284        }
 285
 286        protected void OnRemoveEntity(IDCLEntity entity)
 287        {
 56288            highPrioEntitiesToCheck.Remove(entity);
 56289            entitiesToCheck.Remove(entity);
 56290            persistentEntities.Remove(entity);
 56291            feedbackStyle.ApplyFeedback(entity.meshesInfo, true);
 56292        }
 293
 294        protected void AddEntityBasedOnPriority(IDCLEntity entity)
 295        {
 425296            if (IsHighPrioEntity(entity) && !highPrioEntitiesToCheck.Contains(entity))
 8297                highPrioEntitiesToCheck.Add(entity);
 417298            else if (!entitiesToCheck.Contains(entity))
 230299                entitiesToCheck.Add(entity);
 417300        }
 301
 302        protected bool IsHighPrioEntity(IDCLEntity entity)
 303        {
 425304            if (entity.gameObject == null)
 0305                return false;
 306
 425307            Vector3 scale = entity.gameObject.transform.lossyScale;
 425308            Vector3 position = entity.gameObject.transform.localPosition;
 425309            return scale.x > TRIGGER_HIGHPRIO_VALUE || scale.y > TRIGGER_HIGHPRIO_VALUE || scale.z > TRIGGER_HIGHPRIO_VA
 310        }
 311    }
 312}