< 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:128
Uncovered lines:11
Coverable lines:139
Total lines:309
Line coverage:92% (128 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.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%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
 107316        public bool enabled => entitiesCheckRoutine != null;
 17
 70218        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.
 67121        HashSet<IDCLEntity> highPrioEntitiesToCheck = new HashSet<IDCLEntity>();
 67122        HashSet<IDCLEntity> entitiesToCheck = new HashSet<IDCLEntity>();
 67123        HashSet<IDCLEntity> checkedEntities = new HashSet<IDCLEntity>();
 24        Coroutine entitiesCheckRoutine = null;
 25        float lastCheckTime;
 67126        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
 201333        public SceneBoundsChecker(ISceneBoundsFeedbackStyle feedbackStyle = null) { this.feedbackStyle = feedbackStyle ?
 34
 35        public void SetFeedbackStyle(ISceneBoundsFeedbackStyle feedbackStyle)
 36        {
 3837            this.feedbackStyle.CleanFeedback();
 3838            this.feedbackStyle = feedbackStyle;
 3839            Restart();
 3840        }
 41
 6242        public ISceneBoundsFeedbackStyle GetFeedbackStyle() { return feedbackStyle; }
 43
 52144        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        {
 752349            while (true)
 50            {
 823251                float elapsedTime = Time.realtimeSinceStartup - lastCheckTime;
 823252                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
 11355                    var messagingManager = Environment.i.messaging.manager as MessagingControllersManager;
 56
 11357                    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                    {
 22665                        if (messagingManager.timeBudgetCounter <= 0f)
 066                            return;
 67
 22668                        using HashSet<IDCLEntity>.Enumerator iterator = entities.GetEnumerator();
 35269                        while (iterator.MoveNext())
 70                        {
 12671                            if (messagingManager.timeBudgetCounter <= 0f)
 072                                break;
 73
 12674                            float startTime = Time.realtimeSinceStartup;
 75
 12676                            EvaluateEntityPosition(iterator.Current);
 12677                            checkedEntities.Add(iterator.Current);
 78
 12679                            float finishTime = Time.realtimeSinceStartup;
 12680                            messagingManager.timeBudgetCounter -= (finishTime - startTime);
 81                        }
 45282                    }
 83
 11384                    processEntitiesList(highPrioEntitiesToCheck);
 11385                    processEntitiesList(entitiesToCheck);
 86
 87                    // As we can't modify the hashset while traversing it, we keep track of the entities that should be 
 11388                    using (var iterator = checkedEntities.GetEnumerator())
 89                    {
 23390                        while (iterator.MoveNext())
 91                        {
 12092                            if (!persistentEntities.Contains(iterator.Current))
 93                            {
 11794                                entitiesToCheck.Remove(iterator.Current);
 11795                                highPrioEntitiesToCheck.Remove(iterator.Current);
 96                            }
 97                        }
 11398                    }
 11399                    checkedEntities.Clear();
 100
 113101                    lastCheckTime = Time.realtimeSinceStartup;
 102                }
 103
 8232104                yield return null;
 105            }
 106        }
 107
 108        public void Restart()
 109        {
 38110            Stop();
 38111            Start();
 38112        }
 113
 114        public void Start()
 115        {
 709116            if (entitiesCheckRoutine != null)
 0117                return;
 118
 709119            lastCheckTime = Time.realtimeSinceStartup;
 709120            entitiesCheckRoutine = CoroutineStarter.Start(CheckEntities());
 709121        }
 122
 123        public void Stop()
 124        {
 785125            if (entitiesCheckRoutine == null)
 76126                return;
 127
 709128            CoroutineStarter.Stop(entitiesCheckRoutine);
 709129            entitiesCheckRoutine = null;
 709130        }
 131
 132        public void AddEntityToBeChecked(IDCLEntity entity)
 133        {
 553134            if (!enabled)
 170135                return;
 136
 383137            OnAddEntity(entity);
 383138        }
 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        {
 39145            if (!enabled)
 23146                return;
 147
 16148            if (IsHighPrioEntity(entity))
 0149                highPrioEntitiesToCheck.Add(entity);
 150            else
 16151                entitiesToCheck.Add(entity);
 152
 16153            persistentEntities.Add(entity);
 16154        }
 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        {
 52164            if (!enabled)
 0165                return;
 166
 52167            OnRemoveEntity(entity);
 52168        }
 169
 170        public void EvaluateEntityPosition(IDCLEntity entity)
 171        {
 150172            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 
 150176            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
 150187            if (entity.meshRootGameObject == null || entity.meshesInfo.renderers == null || entity.meshesInfo.renderers.
 188            {
 63189                UpdateComponents(entity, entity.scene.IsInsideSceneBoundaries(entity.gameObject.transform.position + Com
 63190                return;
 191            }
 192
 193            // If the mesh is being loaded we should skip the evaluation (it will be triggered again later when the load
 87194            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>()) // the object's MaterialTransiti
 195            {
 1196                return;
 197            }
 198
 86199            var loadWrapper = LoadableShape.GetLoaderForEntity(entity);
 86200            if (loadWrapper != null && !loadWrapper.alreadyLoaded)
 6201                return;
 202
 80203            EvaluateMeshBounds(entity);
 80204        }
 205
 206        public bool IsEntityInsideSceneBoundaries(IDCLEntity entity)
 207        {
 122208            if (entity.meshesInfo == null || entity.meshesInfo.meshRootGameObject == null || entity.meshesInfo.mergedBou
 26209                return false;
 210
 211            // 1st check (full mesh AABB)
 96212            bool isInsideBoundaries = entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.mergedBounds);
 213
 214            // 2nd check (submeshes AABB)
 96215            if (!isInsideBoundaries)
 216            {
 67217                isInsideBoundaries = AreSubmeshesInsideBoundaries(entity);
 218            }
 219
 96220            return isInsideBoundaries;
 221        }
 222
 223        void EvaluateMeshBounds(IDCLEntity entity)
 224        {
 80225            bool isInsideBoundaries = IsEntityInsideSceneBoundaries(entity);
 80226            if (entity.isInsideBoundaries != isInsideBoundaries)
 227            {
 38228                entity.isInsideBoundaries = isInsideBoundaries;
 38229                OnEntityBoundsCheckerStatusChanged?.Invoke(entity, isInsideBoundaries);
 230            }
 231
 80232            UpdateEntityMeshesValidState(entity.meshesInfo, isInsideBoundaries);
 80233            UpdateEntityCollidersValidState(entity.meshesInfo, isInsideBoundaries);
 80234            UpdateComponents(entity, isInsideBoundaries);
 80235        }
 236
 237        protected bool AreSubmeshesInsideBoundaries(IDCLEntity entity)
 238        {
 134239            for (int i = 0; i < entity.meshesInfo.renderers.Length; i++)
 240            {
 67241                if (entity.meshesInfo.renderers[i] == null)
 242                    continue;
 243
 67244                if (!entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.renderers[i].GetSafeBounds()))
 67245                    return false;
 246            }
 247
 0248            return true;
 249        }
 250
 160251        protected void UpdateEntityMeshesValidState(MeshesInfo meshesInfo, bool isInsideBoundaries) { feedbackStyle.Appl
 252
 253        protected void UpdateEntityCollidersValidState(MeshesInfo meshesInfo, bool isInsideBoundaries)
 254        {
 80255            if (meshesInfo == null || meshesInfo.colliders == null)
 0256                return;
 257
 80258            int collidersCount = meshesInfo.colliders.Count;
 259
 80260            if (collidersCount == 0)
 8261                return;
 262
 72263            if (meshesInfo.colliders[0] == null)
 0264                return;
 265
 72266            if (collidersCount > 0 && isInsideBoundaries != meshesInfo.colliders[0].enabled && meshesInfo.currentShape.H
 267            {
 240268                for (int i = 0; i < collidersCount; i++)
 269                {
 72270                    if (meshesInfo.colliders[i] != null)
 72271                        meshesInfo.colliders[i].enabled = isInsideBoundaries;
 272                }
 273            }
 72274        }
 275
 276        protected void UpdateComponents(IDCLEntity entity, bool isInsideBoundaries)
 277        {
 143278            IOutOfSceneBoundariesHandler[] components = entity.gameObject.GetComponentsInChildren<IOutOfSceneBoundariesH
 279
 294280            for (int i = 0; i < components.Length; i++)
 281            {
 4282                components[i].UpdateOutOfBoundariesState(isInsideBoundaries);
 283            }
 143284        }
 285
 286        protected void OnAddEntity(IDCLEntity entity)
 287        {
 383288            if (IsHighPrioEntity(entity))
 8289                highPrioEntitiesToCheck.Add(entity);
 290            else
 375291                entitiesToCheck.Add(entity);
 375292        }
 293
 294        protected void OnRemoveEntity(IDCLEntity entity)
 295        {
 52296            highPrioEntitiesToCheck.Remove(entity);
 52297            entitiesToCheck.Remove(entity);
 52298            persistentEntities.Remove(entity);
 52299            feedbackStyle.ApplyFeedback(entity.meshesInfo, true);
 52300        }
 301
 302        protected bool IsHighPrioEntity(IDCLEntity entity)
 303        {
 399304            Vector3 scale = entity.gameObject.transform.lossyScale;
 399305            Vector3 position = entity.gameObject.transform.localPosition;
 399306            return scale.x > TRIGGER_HIGHPRIO_VALUE || scale.y > TRIGGER_HIGHPRIO_VALUE || scale.z > TRIGGER_HIGHPRIO_VA
 307        }
 308    }
 309}