< 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:135
Uncovered lines:11
Coverable lines:146
Total lines:323
Line coverage:92.4% (135 of 146)
Covered branches:0
Total branches:0

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity NPath complexity Sequence coverage
SceneBoundsChecker(...)0%220100%
Initialize()0%110100%
SetFeedbackStyle(...)0%110100%
GetFeedbackStyle()0%110100%
GetOriginalMaterials(...)0%2100%
CheckEntities()0%990100%
Restart()0%110100%
Start()0%2.032080%
Stop()0%220100%
Dispose()0%110100%
AddEntityToBeChecked(...)0%220100%
AddPersistent(...)0%2.032080%
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;
 51415        public bool enabled => entitiesCheckRoutine != null;
 65916        public float timeBetweenChecks { get; set; } = 0.5f;
 17
 18        // We use Hashset instead of Queue to be able to have a unique representation of each entity when added.
 63419        HashSet<IDCLEntity> highPrioEntitiesToCheck = new HashSet<IDCLEntity>();
 63420        HashSet<IDCLEntity> entitiesToCheck = new HashSet<IDCLEntity>();
 63421        HashSet<IDCLEntity> checkedEntities = new HashSet<IDCLEntity>();
 22        Coroutine entitiesCheckRoutine = null;
 23        float lastCheckTime;
 63424        private HashSet<IDCLEntity> persistentEntities = new HashSet<IDCLEntity>();
 25
 226        public int entitiesToCheckCount => entitiesToCheck.Count;
 427        public int highPrioEntitiesToCheckCount => highPrioEntitiesToCheck.Count;
 28
 29        private ISceneBoundsFeedbackStyle feedbackStyle;
 30
 31        public void Initialize()
 32        {
 63433            Start();
 63434        }
 35
 63436        public SceneBoundsChecker(ISceneBoundsFeedbackStyle feedbackStyle = null)
 37        {
 63438            this.feedbackStyle = feedbackStyle ?? new SceneBoundsFeedbackStyle_Simple();
 63439        }
 40
 41        public void SetFeedbackStyle(ISceneBoundsFeedbackStyle feedbackStyle)
 42        {
 2943            this.feedbackStyle.CleanFeedback();
 2944            this.feedbackStyle = feedbackStyle;
 2945            Restart();
 2946        }
 47
 4948        public ISceneBoundsFeedbackStyle GetFeedbackStyle() { return feedbackStyle; }
 49
 050        public List<Material> GetOriginalMaterials(MeshesInfo meshesInfo) { return feedbackStyle.GetOriginalMaterials(me
 51
 52
 53        // TODO: Improve MessagingControllersManager.i.timeBudgetCounter usage once we have the centralized budget contr
 54        IEnumerator CheckEntities()
 55        {
 679156            while (true)
 57            {
 745458                float elapsedTime = Time.realtimeSinceStartup - lastCheckTime;
 745459                if ((entitiesToCheck.Count > 0 || highPrioEntitiesToCheck.Count > 0) && (timeBetweenChecks <= 0f || elap
 60                {
 61                    //TODO(Brian): Remove later when we implement a centralized way of handling time budgets
 8762                    var messagingManager = Environment.i.messaging.manager as MessagingControllersManager;
 63
 64                    void processEntitiesList(HashSet<IDCLEntity> entities)
 65                    {
 17466                        if (messagingManager != null && messagingManager.timeBudgetCounter <= 0f)
 067                            return;
 68
 17469                        using HashSet<IDCLEntity>.Enumerator iterator = entities.GetEnumerator();
 27370                        while (iterator.MoveNext())
 71                        {
 9972                            if (messagingManager != null && messagingManager.timeBudgetCounter <= 0f)
 073                                break;
 74
 9975                            float startTime = Time.realtimeSinceStartup;
 76
 9977                            EvaluateEntityPosition(iterator.Current);
 9978                            checkedEntities.Add(iterator.Current);
 79
 9980                            float finishTime = Time.realtimeSinceStartup;
 81
 9982                            if ( messagingManager != null )
 9983                                messagingManager.timeBudgetCounter -= (finishTime - startTime);
 84                        }
 34885                    }
 86
 8787                    processEntitiesList(highPrioEntitiesToCheck);
 8788                    processEntitiesList(entitiesToCheck);
 89
 90                    // As we can't modify the hashset while traversing it, we keep track of the entities that should be 
 8791                    using (var iterator = checkedEntities.GetEnumerator())
 92                    {
 18093                        while (iterator.MoveNext())
 94                        {
 9395                            if (!persistentEntities.Contains(iterator.Current))
 96                            {
 9397                                entitiesToCheck.Remove(iterator.Current);
 9398                                highPrioEntitiesToCheck.Remove(iterator.Current);
 99                            }
 100                        }
 87101                    }
 102
 87103                    checkedEntities.Clear();
 104
 87105                    lastCheckTime = Time.realtimeSinceStartup;
 106                }
 107
 7454108                yield return null;
 109            }
 110        }
 111
 112        public void Restart()
 113        {
 29114            Stop();
 29115            Start();
 29116        }
 117
 118        public void Start()
 119        {
 663120            if (entitiesCheckRoutine != null)
 0121                return;
 122
 663123            lastCheckTime = Time.realtimeSinceStartup;
 663124            entitiesCheckRoutine = CoroutineStarter.Start(CheckEntities());
 663125        }
 126
 127        public void Stop()
 128        {
 684129            if (entitiesCheckRoutine == null)
 21130                return;
 131
 663132            CoroutineStarter.Stop(entitiesCheckRoutine);
 663133            entitiesCheckRoutine = null;
 663134        }
 135
 136        public void Dispose()
 137        {
 634138            Stop();
 634139        }
 140
 141        public void AddEntityToBeChecked(IDCLEntity entity)
 142        {
 346143            if (!enabled)
 18144                return;
 145
 328146            OnAddEntity(entity);
 328147        }
 148
 149        /// <summary>
 150        /// Add an entity that will be consistently checked, until manually removed from the list.
 151        /// </summary>
 152        public void AddPersistent(IDCLEntity entity)
 153        {
 16154            if (!enabled)
 0155                return;
 156
 16157            AddEntityBasedOnPriority(entity);
 158
 16159            persistentEntities.Add(entity);
 16160        }
 161
 162        /// <summary>
 163        /// Returns whether an entity was added to be consistently checked
 164        /// </summary>
 165        ///
 4166        public bool WasAddedAsPersistent(IDCLEntity entity) { return persistentEntities.Contains(entity); }
 167
 168        public void RemoveEntityToBeChecked(IDCLEntity entity)
 169        {
 43170            if (!enabled)
 0171                return;
 172
 43173            OnRemoveEntity(entity);
 43174        }
 175
 176        public void EvaluateEntityPosition(IDCLEntity entity)
 177        {
 118178            if (entity == null || entity.scene == null || entity.gameObject == null)
 0179                return;
 180
 181            // Recursively evaluate entity children as well, we need to check this up front because this entity may not 
 118182            if (entity.children.Count > 0)
 183            {
 5184                using (var iterator = entity.children.GetEnumerator())
 185                {
 10186                    while (iterator.MoveNext())
 187                    {
 5188                        EvaluateEntityPosition(iterator.Current.Value);
 189                    }
 5190                }
 191            }
 192
 118193            if (entity.meshRootGameObject == null || entity.meshesInfo.renderers == null || entity.meshesInfo.renderers.
 194            {
 39195                UpdateComponents(entity, entity.scene.IsInsideSceneBoundaries(entity.gameObject.transform.position + Com
 39196                return;
 197            }
 198
 199            // If the mesh is being loaded we should skip the evaluation (it will be triggered again later when the load
 79200            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>()) // the object's MaterialTransiti
 201            {
 2202                return;
 203            }
 204
 77205            var loadWrapper = LoadableShape.GetLoaderForEntity(entity);
 77206            if (loadWrapper != null && !loadWrapper.alreadyLoaded)
 2207                return;
 208
 75209            EvaluateMeshBounds(entity);
 75210        }
 211
 212        public bool IsEntityInsideSceneBoundaries(IDCLEntity entity)
 213        {
 94214            if (entity.meshesInfo == null || entity.meshesInfo.meshRootGameObject == null || entity.meshesInfo.mergedBou
 3215                return false;
 216
 217            // 1st check (full mesh AABB)
 91218            bool isInsideBoundaries = entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.mergedBounds);
 219
 220            // 2nd check (submeshes AABB)
 91221            if (!isInsideBoundaries)
 222            {
 65223                isInsideBoundaries = AreSubmeshesInsideBoundaries(entity);
 224            }
 225
 91226            return isInsideBoundaries;
 227        }
 228
 229        void EvaluateMeshBounds(IDCLEntity entity)
 230        {
 75231            bool isInsideBoundaries = IsEntityInsideSceneBoundaries(entity);
 75232            if (entity.isInsideBoundaries != isInsideBoundaries)
 233            {
 35234                entity.isInsideBoundaries = isInsideBoundaries;
 35235                OnEntityBoundsCheckerStatusChanged?.Invoke(entity, isInsideBoundaries);
 236            }
 237
 75238            UpdateEntityMeshesValidState(entity.meshesInfo, isInsideBoundaries);
 75239            UpdateEntityCollidersValidState(entity.meshesInfo, isInsideBoundaries);
 75240            UpdateComponents(entity, isInsideBoundaries);
 75241        }
 242
 243        protected bool AreSubmeshesInsideBoundaries(IDCLEntity entity)
 244        {
 130245            for (int i = 0; i < entity.meshesInfo.renderers.Length; i++)
 246            {
 65247                if (entity.meshesInfo.renderers[i] == null)
 248                    continue;
 249
 65250                if (!entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.renderers[i].GetSafeBounds()))
 65251                    return false;
 252            }
 253
 0254            return true;
 255        }
 256
 150257        protected void UpdateEntityMeshesValidState(MeshesInfo meshesInfo, bool isInsideBoundaries) { feedbackStyle.Appl
 258
 259        protected void UpdateEntityCollidersValidState(MeshesInfo meshesInfo, bool isInsideBoundaries)
 260        {
 75261            if (meshesInfo == null || meshesInfo.colliders == null)
 0262                return;
 263
 75264            int collidersCount = meshesInfo.colliders.Count;
 265
 75266            if (collidersCount == 0)
 10267                return;
 268
 65269            if (meshesInfo.colliders[0] == null)
 0270                return;
 271
 65272            if (collidersCount > 0 && isInsideBoundaries != meshesInfo.colliders[0].enabled && meshesInfo.currentShape.H
 273            {
 164274                for (int i = 0; i < collidersCount; i++)
 275                {
 41276                    if (meshesInfo.colliders[i] != null)
 41277                        meshesInfo.colliders[i].enabled = isInsideBoundaries;
 278                }
 279            }
 65280        }
 281
 282        protected void UpdateComponents(IDCLEntity entity, bool isInsideBoundaries)
 283        {
 114284            IOutOfSceneBoundariesHandler[] components = entity.gameObject.GetComponentsInChildren<IOutOfSceneBoundariesH
 285
 236286            for (int i = 0; i < components.Length; i++)
 287            {
 4288                components[i].UpdateOutOfBoundariesState(isInsideBoundaries);
 289            }
 114290        }
 291
 292        protected void OnAddEntity(IDCLEntity entity)
 293        {
 328294            AddEntityBasedOnPriority(entity);
 328295        }
 296
 297        protected void OnRemoveEntity(IDCLEntity entity)
 298        {
 43299            highPrioEntitiesToCheck.Remove(entity);
 43300            entitiesToCheck.Remove(entity);
 43301            persistentEntities.Remove(entity);
 43302            feedbackStyle.ApplyFeedback(entity.meshesInfo, true);
 43303        }
 304
 305        protected void AddEntityBasedOnPriority(IDCLEntity entity)
 306        {
 344307            if (IsHighPrioEntity(entity) && !highPrioEntitiesToCheck.Contains(entity))
 8308                highPrioEntitiesToCheck.Add(entity);
 336309            else if (!entitiesToCheck.Contains(entity))
 203310                entitiesToCheck.Add(entity);
 336311        }
 312
 313        protected bool IsHighPrioEntity(IDCLEntity entity)
 314        {
 344315            if (entity.gameObject == null)
 0316                return false;
 317
 344318            Vector3 scale = entity.gameObject.transform.lossyScale;
 344319            Vector3 position = entity.gameObject.transform.localPosition;
 344320            return scale.x > TRIGGER_HIGHPRIO_VALUE || scale.y > TRIGGER_HIGHPRIO_VALUE || scale.z > TRIGGER_HIGHPRIO_VA
 321        }
 322    }
 323}