< 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:127
Uncovered lines:12
Coverable lines:139
Total lines:309
Line coverage:91.3% (127 of 139)
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%10.1410088.89%
Restart()0%110100%
Start()0%2.032080%
Stop()0%220100%
AddEntityToBeChecked(...)0%220100%
AddPersistent(...)0%3.033085.71%
WasAddedAsPersistent(...)0%110100%
RemoveEntityToBeChecked(...)0%2.062075%
EvaluateEntityPosition(...)0%12.2312088.24%
IsEntityInsideSceneBoundaries(...)0%440100%
EvaluateMeshBounds(...)0%330100%
AreSubmeshesInsideBoundaries(...)0%4.374071.43%
UpdateEntityMeshesValidState(...)0%110100%
UpdateEntityCollidersValidState(...)0%10.2910085.71%
UpdateComponents(...)0%220100%
OnAddEntity(...)0%220100%
OnRemoveEntity(...)0%110100%
IsHighPrioEntity(...)0%660100%

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
 92616        public bool enabled => entitiesCheckRoutine != null;
 17
 56018        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.
 53621        HashSet<IDCLEntity> highPrioEntitiesToCheck = new HashSet<IDCLEntity>();
 53622        HashSet<IDCLEntity> entitiesToCheck = new HashSet<IDCLEntity>();
 53623        HashSet<IDCLEntity> checkedEntities = new HashSet<IDCLEntity>();
 24        Coroutine entitiesCheckRoutine = null;
 25        float lastCheckTime;
 53626        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
 160833        public SceneBoundsChecker(ISceneBoundsFeedbackStyle feedbackStyle = null) { this.feedbackStyle = feedbackStyle ?
 34
 35        public void SetFeedbackStyle(ISceneBoundsFeedbackStyle feedbackStyle)
 36        {
 2437            this.feedbackStyle.CleanFeedback();
 2438            this.feedbackStyle = feedbackStyle;
 2439            Restart();
 2440        }
 41
 4742        public ISceneBoundsFeedbackStyle GetFeedbackStyle() { return feedbackStyle; }
 43
 46044        public List<Material> GetOriginalMaterials(MeshesInfo meshesInfo) { return feedbackStyle.GetOriginalMaterials(me
 45
 46        // TODO: Improve MessagingControllersManager.i.timeBudgetCounter usage once we have the centralized budget contr
 47        IEnumerator CheckEntities()
 48        {
 664049            while (true)
 50            {
 720051                float elapsedTime = Time.realtimeSinceStartup - lastCheckTime;
 720052                if ((entitiesToCheck.Count > 0 || highPrioEntitiesToCheck.Count > 0) && (timeBetweenChecks <= 0f || elap
 53                {
 54                    //TODO(Brian): Remove later when we implement a centralized way of handling time budgets
 5955                    var messagingManager = Environment.i.messaging.manager as MessagingControllersManager;
 56
 5957                    if (messagingManager == null)
 58                    {
 059                        Debug.LogWarning("MessagingControllersManager is null! This shouldn't happen!");
 060                        continue;
 61                    }
 62
 63                    void processEntitiesList(HashSet<IDCLEntity> entities)
 64                    {
 11865                        if (messagingManager.timeBudgetCounter <= 0f)
 066                            return;
 67
 11868                        using HashSet<IDCLEntity>.Enumerator iterator = entities.GetEnumerator();
 18969                        while (iterator.MoveNext())
 70                        {
 7171                            if (messagingManager.timeBudgetCounter <= 0f)
 072                                break;
 73
 7174                            float startTime = Time.realtimeSinceStartup;
 75
 7176                            EvaluateEntityPosition(iterator.Current);
 7177                            checkedEntities.Add(iterator.Current);
 78
 7179                            float finishTime = Time.realtimeSinceStartup;
 7180                            messagingManager.timeBudgetCounter -= (finishTime - startTime);
 81                        }
 23682                    }
 83
 5984                    processEntitiesList(highPrioEntitiesToCheck);
 5985                    processEntitiesList(entitiesToCheck);
 86
 87                    // As we can't modify the hashset while traversing it, we keep track of the entities that should be 
 5988                    using (var iterator = checkedEntities.GetEnumerator())
 89                    {
 12490                        while (iterator.MoveNext())
 91                        {
 6592                            if (!persistentEntities.Contains(iterator.Current))
 93                            {
 6594                                entitiesToCheck.Remove(iterator.Current);
 6595                                highPrioEntitiesToCheck.Remove(iterator.Current);
 96                            }
 97                        }
 5998                    }
 5999                    checkedEntities.Clear();
 100
 59101                    lastCheckTime = Time.realtimeSinceStartup;
 102                }
 103
 7200104                yield return null;
 105            }
 106        }
 107
 108        public void Restart()
 109        {
 24110            Stop();
 24111            Start();
 24112        }
 113
 114        public void Start()
 115        {
 560116            if (entitiesCheckRoutine != null)
 0117                return;
 118
 560119            lastCheckTime = Time.realtimeSinceStartup;
 560120            entitiesCheckRoutine = CoroutineStarter.Start(CheckEntities());
 560121        }
 122
 123        public void Stop()
 124        {
 608125            if (entitiesCheckRoutine == null)
 48126                return;
 127
 560128            CoroutineStarter.Stop(entitiesCheckRoutine);
 560129            entitiesCheckRoutine = null;
 560130        }
 131
 132        public void AddEntityToBeChecked(IDCLEntity entity)
 133        {
 481134            if (!enabled)
 138135                return;
 136
 343137            OnAddEntity(entity);
 343138        }
 139
 140        /// <summary>
 141        /// Add an entity that will be consistently checked, until manually removed from the list.
 142        /// </summary>
 143        public void AddPersistent(IDCLEntity entity)
 144        {
 23145            if (!enabled)
 15146                return;
 147
 8148            if (IsHighPrioEntity(entity))
 0149                highPrioEntitiesToCheck.Add(entity);
 150            else
 8151                entitiesToCheck.Add(entity);
 152
 8153            persistentEntities.Add(entity);
 8154        }
 155
 156        /// <summary>
 157        /// Returns whether an entity was added to be consistently checked
 158        /// </summary>
 159        ///
 4160        public bool WasAddedAsPersistent(IDCLEntity entity) { return persistentEntities.Contains(entity); }
 161
 162        public void RemoveEntityToBeChecked(IDCLEntity entity)
 163        {
 38164            if (!enabled)
 0165                return;
 166
 38167            OnRemoveEntity(entity);
 38168        }
 169
 170        public void EvaluateEntityPosition(IDCLEntity entity)
 171        {
 86172            if (entity == null || entity.scene == null || entity.gameObject == null)
 0173                return;
 174
 175            // Recursively evaluate entity children as well, we need to check this up front because this entity may not 
 86176            if (entity.children.Count > 0)
 177            {
 6178                using (var iterator = entity.children.GetEnumerator())
 179                {
 12180                    while (iterator.MoveNext())
 181                    {
 6182                        EvaluateEntityPosition(iterator.Current.Value);
 183                    }
 6184                }
 185            }
 186
 86187            if (entity.meshRootGameObject == null || entity.meshesInfo.renderers == null || entity.meshesInfo.renderers.
 188            {
 28189                UpdateComponents(entity, entity.scene.IsInsideSceneBoundaries(entity.gameObject.transform.position + Com
 28190                return;
 191            }
 192
 193            // If the mesh is being loaded we should skip the evaluation (it will be triggered again later when the load
 58194            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>()) // the object's MaterialTransiti
 195            {
 1196                return;
 197            }
 198
 57199            var loadWrapper = LoadableShape.GetLoaderForEntity(entity);
 57200            if (loadWrapper != null && !loadWrapper.alreadyLoaded)
 0201                return;
 202
 57203            EvaluateMeshBounds(entity);
 57204        }
 205
 206        public bool IsEntityInsideSceneBoundaries(IDCLEntity entity)
 207        {
 80208            if (entity.meshesInfo == null || entity.meshesInfo.meshRootGameObject == null || entity.meshesInfo.mergedBou
 16209                return false;
 210
 211            // 1st check (full mesh AABB)
 64212            bool isInsideBoundaries = entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.mergedBounds);
 213
 214            // 2nd check (submeshes AABB)
 64215            if (!isInsideBoundaries)
 216            {
 46217                isInsideBoundaries = AreSubmeshesInsideBoundaries(entity);
 218            }
 219
 64220            return isInsideBoundaries;
 221        }
 222
 223        void EvaluateMeshBounds(IDCLEntity entity)
 224        {
 57225            bool isInsideBoundaries = IsEntityInsideSceneBoundaries(entity);
 57226            if (entity.isInsideBoundaries != isInsideBoundaries)
 227            {
 26228                entity.isInsideBoundaries = isInsideBoundaries;
 26229                OnEntityBoundsCheckerStatusChanged?.Invoke(entity, isInsideBoundaries);
 230            }
 231
 57232            UpdateEntityMeshesValidState(entity.meshesInfo, isInsideBoundaries);
 57233            UpdateEntityCollidersValidState(entity.meshesInfo, isInsideBoundaries);
 57234            UpdateComponents(entity, isInsideBoundaries);
 57235        }
 236
 237        protected bool AreSubmeshesInsideBoundaries(IDCLEntity entity)
 238        {
 92239            for (int i = 0; i < entity.meshesInfo.renderers.Length; i++)
 240            {
 46241                if (entity.meshesInfo.renderers[i] == null)
 242                    continue;
 243
 46244                if (!entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.renderers[i].GetSafeBounds()))
 46245                    return false;
 246            }
 247
 0248            return true;
 249        }
 250
 114251        protected void UpdateEntityMeshesValidState(MeshesInfo meshesInfo, bool isInsideBoundaries) { feedbackStyle.Appl
 252
 253        protected void UpdateEntityCollidersValidState(MeshesInfo meshesInfo, bool isInsideBoundaries)
 254        {
 57255            if (meshesInfo == null || meshesInfo.colliders == null)
 0256                return;
 257
 57258            int collidersCount = meshesInfo.colliders.Count;
 259
 57260            if (collidersCount == 0)
 4261                return;
 262
 53263            if (meshesInfo.colliders[0] == null)
 0264                return;
 265
 53266            if (collidersCount > 0 && isInsideBoundaries != meshesInfo.colliders[0].enabled && meshesInfo.currentShape.H
 267            {
 140268                for (int i = 0; i < collidersCount; i++)
 269                {
 35270                    if (meshesInfo.colliders[i] != null)
 35271                        meshesInfo.colliders[i].enabled = isInsideBoundaries;
 272                }
 273            }
 53274        }
 275
 276        protected void UpdateComponents(IDCLEntity entity, bool isInsideBoundaries)
 277        {
 85278            IOutOfSceneBoundariesHandler[] components = entity.gameObject.GetComponentsInChildren<IOutOfSceneBoundariesH
 279
 178280            for (int i = 0; i < components.Length; i++)
 281            {
 4282                components[i].UpdateOutOfBoundariesState(isInsideBoundaries);
 283            }
 85284        }
 285
 286        protected void OnAddEntity(IDCLEntity entity)
 287        {
 343288            if (IsHighPrioEntity(entity))
 6289                highPrioEntitiesToCheck.Add(entity);
 290            else
 337291                entitiesToCheck.Add(entity);
 337292        }
 293
 294        protected void OnRemoveEntity(IDCLEntity entity)
 295        {
 38296            highPrioEntitiesToCheck.Remove(entity);
 38297            entitiesToCheck.Remove(entity);
 38298            persistentEntities.Remove(entity);
 38299            feedbackStyle.ApplyFeedback(entity.meshesInfo, true);
 38300        }
 301
 302        protected bool IsHighPrioEntity(IDCLEntity entity)
 303        {
 351304            Vector3 scale = entity.gameObject.transform.lossyScale;
 351305            Vector3 position = entity.gameObject.transform.localPosition;
 351306            return scale.x > TRIGGER_HIGHPRIO_VALUE || scale.y > TRIGGER_HIGHPRIO_VALUE || scale.z > TRIGGER_HIGHPRIO_VA
 307        }
 308    }
 309}