< Summary

Class:DCL.Controllers.SceneBoundsChecker
Assembly:DCL.Runtime
File(s):/tmp/workspace/unity-renderer/unity-renderer/Assets/Scripts/MainScripts/DCL/WorldRuntime/SceneBoundariesController/SceneBoundsChecker.cs
Covered lines:139
Uncovered lines:12
Coverable lines:151
Total lines:330
Line coverage:92% (139 of 151)
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%
RemovePersistent(...)0%220100%
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%330100%
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.Models;
 2using UnityEngine;
 3using System.Collections.Generic;
 4using System.Collections;
 5using System;
 6
 7namespace DCL.Controllers
 8{
 9    public class SceneBoundsChecker : ISceneBoundsChecker
 10    {
 11        public const int TRIGGER_HIGHPRIO_VALUE = 1000;
 12        public event Action<IDCLEntity, bool> OnEntityBoundsCheckerStatusChanged;
 98613        public bool enabled => entitiesCheckRoutine != null;
 68814        public float timeBetweenChecks { get; set; } = 0.5f;
 15
 16        // We use Hashset instead of Queue to be able to have a unique representation of each entity when added.
 65917        HashSet<IDCLEntity> highPrioEntitiesToCheck = new HashSet<IDCLEntity>();
 65918        HashSet<IDCLEntity> entitiesToCheck = new HashSet<IDCLEntity>();
 65919        HashSet<IDCLEntity> checkedEntities = new HashSet<IDCLEntity>();
 20        Coroutine entitiesCheckRoutine = null;
 21        float lastCheckTime;
 65922        private HashSet<IDCLEntity> persistentEntities = new HashSet<IDCLEntity>();
 23
 224        public int entitiesToCheckCount => entitiesToCheck.Count;
 425        public int highPrioEntitiesToCheckCount => highPrioEntitiesToCheck.Count;
 26
 27        private ISceneBoundsFeedbackStyle feedbackStyle;
 28
 29        public void Initialize()
 30        {
 65931            Start();
 65932        }
 33
 65934        public SceneBoundsChecker(ISceneBoundsFeedbackStyle feedbackStyle = null)
 35        {
 65936            this.feedbackStyle = feedbackStyle ?? new SceneBoundsFeedbackStyle_Simple();
 65937        }
 38
 39        public void SetFeedbackStyle(ISceneBoundsFeedbackStyle feedbackStyle)
 40        {
 3441            this.feedbackStyle.CleanFeedback();
 3442            this.feedbackStyle = feedbackStyle;
 3443            Restart();
 3444        }
 45
 5746        public ISceneBoundsFeedbackStyle GetFeedbackStyle() { return feedbackStyle; }
 47
 048        public List<Material> GetOriginalMaterials(MeshesInfo meshesInfo) { return feedbackStyle.GetOriginalMaterials(me
 49
 50
 51        // TODO: Improve MessagingControllersManager.i.timeBudgetCounter usage once we have the centralized budget contr
 52        IEnumerator CheckEntities()
 53        {
 682554            while (true)
 55            {
 751856                float elapsedTime = Time.realtimeSinceStartup - lastCheckTime;
 751857                if ((entitiesToCheck.Count > 0 || highPrioEntitiesToCheck.Count > 0) && (timeBetweenChecks <= 0f || elap
 58                {
 59                    //TODO(Brian): Remove later when we implement a centralized way of handling time budgets
 8060                    var messagingManager = Environment.i.messaging.manager as MessagingControllersManager;
 61
 62                    void processEntitiesList(HashSet<IDCLEntity> entities)
 63                    {
 16064                        if (messagingManager != null && messagingManager.timeBudgetCounter <= 0f)
 065                            return;
 66
 16067                        using HashSet<IDCLEntity>.Enumerator iterator = entities.GetEnumerator();
 25368                        while (iterator.MoveNext())
 69                        {
 9370                            if (messagingManager != null && messagingManager.timeBudgetCounter <= 0f)
 071                                break;
 72
 9373                            float startTime = Time.realtimeSinceStartup;
 74
 9375                            EvaluateEntityPosition(iterator.Current);
 9376                            checkedEntities.Add(iterator.Current);
 77
 9378                            float finishTime = Time.realtimeSinceStartup;
 79
 9380                            if ( messagingManager != null )
 9381                                messagingManager.timeBudgetCounter -= (finishTime - startTime);
 82                        }
 32083                    }
 84
 8085                    processEntitiesList(highPrioEntitiesToCheck);
 8086                    processEntitiesList(entitiesToCheck);
 87
 88                    // As we can't modify the hashset while traversing it, we keep track of the entities that should be 
 8089                    using (var iterator = checkedEntities.GetEnumerator())
 90                    {
 16791                        while (iterator.MoveNext())
 92                        {
 8793                            if (!persistentEntities.Contains(iterator.Current))
 94                            {
 8795                                entitiesToCheck.Remove(iterator.Current);
 8796                                highPrioEntitiesToCheck.Remove(iterator.Current);
 97                            }
 98                        }
 8099                    }
 100
 80101                    checkedEntities.Clear();
 102
 80103                    lastCheckTime = Time.realtimeSinceStartup;
 104                }
 105
 7518106                yield return null;
 107            }
 108        }
 109
 110        public void Restart()
 111        {
 34112            Stop();
 34113            Start();
 34114        }
 115
 116        public void Start()
 117        {
 693118            if (entitiesCheckRoutine != null)
 0119                return;
 120
 693121            lastCheckTime = Time.realtimeSinceStartup;
 693122            entitiesCheckRoutine = CoroutineStarter.Start(CheckEntities());
 693123        }
 124
 125        public void Stop()
 126        {
 714127            if (entitiesCheckRoutine == null)
 21128                return;
 129
 693130            CoroutineStarter.Stop(entitiesCheckRoutine);
 693131            entitiesCheckRoutine = null;
 693132        }
 133
 134        public void Dispose()
 135        {
 659136            Stop();
 659137        }
 138
 139        public void AddEntityToBeChecked(IDCLEntity entity)
 140        {
 399141            if (!enabled)
 18142                return;
 143
 381144            OnAddEntity(entity);
 381145        }
 146
 147        /// <summary>
 148        /// Add an entity that will be consistently checked, until manually removed from the list.
 149        /// </summary>
 150        public void AddPersistent(IDCLEntity entity)
 151        {
 38152            if (!enabled)
 0153                return;
 154
 38155            AddEntityBasedOnPriority(entity);
 156
 38157            persistentEntities.Add(entity);
 38158        }
 159
 160        public void RemovePersistent(IDCLEntity entity)
 161        {
 2162            if (persistentEntities.Contains(entity))
 2163                persistentEntities.Remove(entity);
 2164        }
 165
 166        /// <summary>
 167        /// Returns whether an entity was added to be consistently checked
 168        /// </summary>
 169        ///
 7170        public bool WasAddedAsPersistent(IDCLEntity entity) { return persistentEntities.Contains(entity); }
 171
 172        public void RemoveEntityToBeChecked(IDCLEntity entity)
 173        {
 458174            if (!enabled)
 0175                return;
 176
 458177            OnRemoveEntity(entity);
 458178        }
 179
 180        public void EvaluateEntityPosition(IDCLEntity entity)
 181        {
 112182            if (entity == null || entity.scene == null || entity.gameObject == null)
 0183                return;
 184
 185            // Recursively evaluate entity children as well, we need to check this up front because this entity may not 
 112186            if (entity.children.Count > 0)
 187            {
 5188                using (var iterator = entity.children.GetEnumerator())
 189                {
 10190                    while (iterator.MoveNext())
 191                    {
 5192                        EvaluateEntityPosition(iterator.Current.Value);
 193                    }
 5194                }
 195            }
 196
 112197            if (entity.meshRootGameObject == null || entity.meshesInfo.renderers == null || entity.meshesInfo.renderers.
 198            {
 38199                UpdateComponents(entity, entity.scene.IsInsideSceneBoundaries(entity.gameObject.transform.position + Com
 38200                return;
 201            }
 202
 203            // If the mesh is being loaded we should skip the evaluation (it will be triggered again later when the load
 74204            if (entity.meshRootGameObject.GetComponent<MaterialTransitionController>()) // the object's MaterialTransiti
 205            {
 0206                return;
 207            }
 208
 74209            var loadWrapper = Environment.i.world.state.GetLoaderForEntity(entity);
 74210            if (loadWrapper != null && !loadWrapper.alreadyLoaded)
 4211                return;
 212
 70213            EvaluateMeshBounds(entity);
 70214        }
 215
 216        public bool IsEntityInsideSceneBoundaries(IDCLEntity entity)
 217        {
 111218            if (entity.meshesInfo == null || entity.meshesInfo.meshRootGameObject == null || entity.meshesInfo.mergedBou
 25219                return false;
 220
 221            // 1st check (full mesh AABB)
 86222            bool isInsideBoundaries = entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.mergedBounds);
 223
 224            // 2nd check (submeshes AABB)
 86225            if (!isInsideBoundaries)
 226            {
 63227                isInsideBoundaries = AreSubmeshesInsideBoundaries(entity);
 228            }
 229
 86230            return isInsideBoundaries;
 231        }
 232
 233        void EvaluateMeshBounds(IDCLEntity entity)
 234        {
 70235            bool isInsideBoundaries = IsEntityInsideSceneBoundaries(entity);
 70236            if (entity.isInsideBoundaries != isInsideBoundaries)
 237            {
 33238                entity.isInsideBoundaries = isInsideBoundaries;
 33239                OnEntityBoundsCheckerStatusChanged?.Invoke(entity, isInsideBoundaries);
 240            }
 241
 70242            UpdateEntityMeshesValidState(entity.meshesInfo, isInsideBoundaries);
 70243            UpdateEntityCollidersValidState(entity.meshesInfo, isInsideBoundaries);
 70244            UpdateComponents(entity, isInsideBoundaries);
 70245        }
 246
 247        protected bool AreSubmeshesInsideBoundaries(IDCLEntity entity)
 248        {
 126249            for (int i = 0; i < entity.meshesInfo.renderers.Length; i++)
 250            {
 63251                if (entity.meshesInfo.renderers[i] == null)
 252                    continue;
 253
 63254                if (!entity.scene.IsInsideSceneBoundaries(entity.meshesInfo.renderers[i].GetSafeBounds()))
 63255                    return false;
 256            }
 257
 0258            return true;
 259        }
 260
 140261        protected void UpdateEntityMeshesValidState(MeshesInfo meshesInfo, bool isInsideBoundaries) { feedbackStyle.Appl
 262
 263        protected void UpdateEntityCollidersValidState(MeshesInfo meshesInfo, bool isInsideBoundaries)
 264        {
 70265            if (meshesInfo == null || meshesInfo.colliders == null)
 0266                return;
 267
 70268            int collidersCount = meshesInfo.colliders.Count;
 269
 70270            if (collidersCount == 0)
 5271                return;
 272
 65273            if (meshesInfo.colliders[0] == null)
 0274                return;
 275
 65276            if (collidersCount > 0 && isInsideBoundaries != meshesInfo.colliders[0].enabled && meshesInfo.currentShape.H
 277            {
 200278                for (int i = 0; i < collidersCount; i++)
 279                {
 56280                    if (meshesInfo.colliders[i] != null)
 56281                        meshesInfo.colliders[i].enabled = isInsideBoundaries;
 282                }
 283            }
 65284        }
 285
 286        protected void UpdateComponents(IDCLEntity entity, bool isInsideBoundaries)
 287        {
 108288            if(!DataStore.i.sceneBoundariesChecker.componentsCheckSceneBoundaries.ContainsKey(entity.entityId))
 104289                return;
 290
 4291            List<IOutOfSceneBoundariesHandler> components = DataStore.i.sceneBoundariesChecker.componentsCheckSceneBound
 292
 16293            for (int i = 0; i < components.Count; i++)
 294            {
 4295                components[i].UpdateOutOfBoundariesState(isInsideBoundaries);
 296            }
 4297        }
 298
 299        protected void OnAddEntity(IDCLEntity entity)
 300        {
 381301            AddEntityBasedOnPriority(entity);
 381302        }
 303
 304        protected void OnRemoveEntity(IDCLEntity entity)
 305        {
 458306            highPrioEntitiesToCheck.Remove(entity);
 458307            entitiesToCheck.Remove(entity);
 458308            persistentEntities.Remove(entity);
 458309            feedbackStyle.ApplyFeedback(entity.meshesInfo, true);
 458310        }
 311
 312        protected void AddEntityBasedOnPriority(IDCLEntity entity)
 313        {
 419314            if (IsHighPrioEntity(entity) && !highPrioEntitiesToCheck.Contains(entity))
 8315                highPrioEntitiesToCheck.Add(entity);
 411316            else if (!entitiesToCheck.Contains(entity))
 232317                entitiesToCheck.Add(entity);
 411318        }
 319
 320        protected bool IsHighPrioEntity(IDCLEntity entity)
 321        {
 419322            if (entity.gameObject == null)
 0323                return false;
 324
 419325            Vector3 scale = entity.gameObject.transform.lossyScale;
 419326            Vector3 position = entity.gameObject.transform.localPosition;
 419327            return scale.x > TRIGGER_HIGHPRIO_VALUE || scale.y > TRIGGER_HIGHPRIO_VALUE || scale.z > TRIGGER_HIGHPRIO_VA
 328        }
 329    }
 330}