Example #1
0
        internal void CleanupEditor()
        {
            bool selectAvatarAsset = StageUtility.GetCurrentStageHandle() == StageUtility.GetMainStageHandle();

            m_EditMode = EditMode.Stopping;

            DestroyEditor();

            ChangeInspectorLock(m_InspectorLocked);

            EditorApplication.CallbackFunction CleanUpOnDestroy = null;
            CleanUpOnDestroy = () =>
            {
                // Make sure that we restore the "original" selection if we exit Avatar Editing mode
                // from the avatar tooling itself (e.g by clicking done).
                if (selectAvatarAsset)
                {
                    SelectAsset();
                }

                if (!m_CameFromImportSettings)
                {
                    m_EditMode = EditMode.NotEditing;
                }

                EditorApplication.update -= CleanUpOnDestroy;
            };

            EditorApplication.update += CleanUpOnDestroy;

            // Reset back the Edit Mode specific states (they probably should be better encapsulated)
            m_GameObject = null;
            m_ModelBones = null;
        }
Example #2
0
        // Helper function that returns a Solver GameObject; preferably a parent of the selection, or other existing Canvas.
        private static GameObject GetOrCreateSolverObject()
        {
            GameObject selectedGo = Selection.activeGameObject;

            // Try to find a gameobject that is the selected GO or one if its parents.
            ObiSolver solver = (selectedGo != null) ? selectedGo.GetComponentInParent <ObiSolver>() : null;

            if (IsValidSolver(solver))
            {
                return(solver.gameObject);
            }

            // No solver in selection or its parents? Then use any valid solver.
            // We have to find all loaded solvers, not just the ones in main scenes.
            ObiSolver[] solverArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType <ObiSolver>();
            for (int i = 0; i < solverArray.Length; i++)
            {
                if (IsValidSolver(solverArray[i]))
                {
                    return(solverArray[i].gameObject);
                }
            }

            // No solver in the scene at all? Then create a new one.
            return(CreateNewSolver());
        }
        protected override void OnEnable()
        {
            base.OnEnable();

            m_PickupAction = (PickupAction)m_Action;

            m_EffectProp = serializedObject.FindProperty("m_Effect");

            // Collect Pickup Triggers that depend on this Pickup Action.
            m_DependentTriggers.Clear();

            var pickupTriggers = StageUtility.GetCurrentStageHandle().FindComponentsOfType <PickupTrigger>();

            foreach (var trigger in pickupTriggers)
            {
                if (trigger.GetMode() == PickupTrigger.Mode.SpecificPickups)
                {
                    var specificPickups = trigger.GetSpecificPickupActions();
                    if (specificPickups.Contains(m_PickupAction))
                    {
                        m_DependentTriggers.Add(trigger);
                    }
                }
            }
        }
Example #4
0
        private static void CreateEventSystem(bool select, GameObject parent)
        {
            StageHandle stage = parent == null?StageUtility.GetCurrentStageHandle() : StageUtility.GetStageHandle(parent);

            var esys = stage.FindComponentOfType <EventSystem>();

            if (esys == null)
            {
                var eventSystem = ObjectFactory.CreateGameObject("EventSystem");
                if (parent == null)
                {
                    StageUtility.PlaceGameObjectInCurrentStage(eventSystem);
                }
                else
                {
                    SetParentAndAlign(eventSystem, parent);
                }
                esys = ObjectFactory.AddComponent <EventSystem>(eventSystem);
                ObjectFactory.AddComponent <StandaloneInputModule>(eventSystem);

                Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name);
            }

            if (select && esys != null)
            {
                Selection.activeGameObject = esys.gameObject;
            }
        }
    // UnityEditor.UI.MenuOptions
    public static GameObject GetOrCreateCanvasGameObject()
    {
        GameObject activeGameObject = Selection.activeGameObject;
        Canvas     canvas           = (!(activeGameObject != null)) ? null : activeGameObject.GetComponentInParent <Canvas>();
        GameObject result;

        if (SpringGUIMenuOptions.IsValidCanvas(canvas))
        {
            result = canvas.gameObject;
        }
        else
        {
            Canvas[] array = StageUtility.GetCurrentStageHandle().FindComponentsOfType <Canvas>();
            for (int i = 0; i < array.Length; i++)
            {
                if (SpringGUIMenuOptions.IsValidCanvas(array[i]))
                {
                    result = array[i].gameObject;
                    return(result);
                }
            }
            result = SpringGUIMenuOptions.CreateNewUI();
        }
        return(result);
    }
Example #6
0
        // Helper function that returns a Canvas GameObject; preferably a parent of the selection, or other existing Canvas.
        static public GameObject GetOrCreateCanvasGameObject()
        {
            GameObject selectedGo = Selection.activeGameObject;

            // Try to find a gameobject that is the selected GO or one if its parents.
            Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent <Canvas>() : null;

            if (IsValidCanvas(canvas))
            {
                return(canvas.gameObject);
            }

            // No canvas in selection or its parents? Then use any valid canvas.
            // We have to find all loaded Canvases, not just the ones in main scenes.
            Canvas[] canvasArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType <Canvas>();
            for (int i = 0; i < canvasArray.Length; i++)
            {
                if (IsValidCanvas(canvasArray[i]))
                {
                    return(canvasArray[i].gameObject);
                }
            }

            // No canvas in the scene at all? Then create a new one.
            return(CreateNewUI());
        }
#pragma warning disable IDE0051 // Remove unused private members -- Editor Menu Item
        static void CreateXRUICanvas(MenuCommand menuCommand)
#pragma warning restore IDE0051
        {
            var parentOfNewGameObject = menuCommand.GetContextTransform();

            var editingPrefabStage = (StageUtility.GetCurrentStageHandle() != StageUtility.GetMainStageHandle());

            var canvasGO = CreateAndPlaceGameObject("Canvas", parentOfNewGameObject,
                                                    typeof(Canvas),
                                                    typeof(CanvasScaler),
                                                    typeof(GraphicRaycaster),
                                                    typeof(TrackedDeviceGraphicRaycaster));

            // Either inherit the layer of the parent object, or use the same default that GameObject/UI/Canvas uses.
            if (parentOfNewGameObject == null)
            {
                Undo.RegisterCompleteObjectUndo(canvasGO, "Change Layer");
                canvasGO.layer = LayerMask.NameToLayer(k_UILayerName);
            }

            var canvas = canvasGO.GetComponent <Canvas>();

            Undo.RecordObject(canvas, "Configure Canvas");
            canvas.renderMode = RenderMode.WorldSpace;

            if (!editingPrefabStage)
            {
                canvas.worldCamera = Camera.main;
            }
            else
            {
                Debug.LogWarning("You have just added an XR UI Canvas to a prefab." +
                                 $" To function properly with an {nameof(XRRayInteractor)}, you must also set the Canvas component's worldCamera field in your scene.",
                                 canvasGO);
            }

            // Ensure there is at least one EventSystem setup properly
            var inputModule = Object.FindObjectOfType <XRUIInputModule>();

            if (inputModule == null)
            {
                if (!editingPrefabStage)
                {
                    CreateXRUIEventSystem(menuCommand);
                }
                else
                {
                    Debug.LogWarning("You have just added an XR UI Canvas to a prefab." +
                                     $" To function properly with an {nameof(XRRayInteractor)}, you must also add an XR UI EventSystem to your scene.",
                                     canvasGO);
                }
            }

            // May need to set this again since creating the XR UI EventSystem would have overwritten this
            Undo.SetCurrentGroupName("Create " + canvasGO.name);
            Selection.activeGameObject = canvasGO;
        }
Example #8
0
        internal void FindSkinComponents()
        {
            var currentStage = StageUtility.GetCurrentStageHandle();
            var skins        = currentStage.FindComponentsOfType <SpriteSkin>().Where(x => x.gameObject.scene.isLoaded).ToArray();

            m_SkinComponents.Clear();
            m_SkinComponents.AddRange(skins);

            SceneView.RepaintAll();
        }
Example #9
0
     public static bool AreInPrefabMode()
     {
 #if UNITY_2018_3_OR_NEWER && UNITY_EDITOR
         var mainStage          = StageUtility.GetMainStageHandle();
         var currentStageHandle = StageUtility.GetCurrentStageHandle();
         if (mainStage != currentStageHandle)
         {
             return(true);
         }
 #endif
         return(false);
     }
Example #10
0
     public static CSGNode[] GetNodesInPrefabMode()
     {
 #if UNITY_2018_3_OR_NEWER && UNITY_EDITOR
         var mainStage          = StageUtility.GetMainStageHandle();
         var currentStageHandle = StageUtility.GetCurrentStageHandle();
         if (mainStage != currentStageHandle)
         {
             return(currentStageHandle.FindComponentsOfType <CSGNode>());
         }
 #endif
         return(null);
     }
Example #11
0
        private static Canvas GetCanvas()
        {
            StageHandle handle = StageUtility.GetCurrentStageHandle();

            if (!handle.FindComponentOfType <Canvas>())
            {
                EditorApplication.ExecuteMenuItem("GameObject/UI/Canvas");
            }

            Canvas c = handle.FindComponentOfType <Canvas>();

            return(c);
        }
Example #12
0
        static Scene GetLastRootScene()
        {
            if (StageUtility.GetCurrentStageHandle() == StageUtility.GetMainStageHandle())
            {
                for (int i = SceneManager.sceneCount - 1; i >= 0; i--)
                {
                    var scene = SceneManager.GetSceneAt(i);
                    if (scene.isLoaded && !scene.isSubScene)
                    {
                        return(scene);
                    }
                }
            }

            return(default);
Example #13
0
    private static void CreateEventSystem()
    {
        StageHandle stage = StageUtility.GetCurrentStageHandle();
        var         esys  = stage.FindComponentOfType <EventSystem>();

        if (esys == null)
        {
            var eventSystem = ObjectFactory.CreateGameObject("EventSystem");
            StageUtility.PlaceGameObjectInCurrentStage(eventSystem);
            esys = ObjectFactory.AddComponent <EventSystem>(eventSystem);
            ObjectFactory.AddComponent <StandaloneInputModule>(eventSystem);

            Undo.RegisterCreatedObjectUndo(eventSystem, "Create " + eventSystem.name);
        }
    }
        /// <summary>
        /// Returns the first active loaded object of the given type in the same Scene.
        /// </summary>
        /// <typeparam name="T">The Component type to find.</typeparam>
        /// <param name="scene">The <see cref="Scene"/> to search.</param>
        /// <returns>
        /// Returns the object that matches the specified type in the Scene.
        /// Otherwise, returns <see langword="null"/> if no object matches the type in the Scene.
        /// </returns>
        /// <remarks>
        /// This method can be used when finding a Component to reference in the same Scene
        /// since serialization of cross scene references are not supported.
        /// </remarks>
        public static T FindSceneComponentOfType <T>(Scene scene) where T : Component
        {
            var currentStage = StageUtility.GetCurrentStageHandle();
            var components   = currentStage.FindComponentsOfType <T>();

            foreach (var component in components)
            {
                if (component.gameObject.scene == scene)
                {
                    return(component);
                }
            }

            return(null);
        }
        /// <summary>
        /// Create the <see cref="XRInteractionManager"/> if necessary, and select it in the Hierarchy.
        /// </summary>
        /// <param name="parent">The parent <see cref="Transform"/> to use.</param>
        static void CreateInteractionManager(Transform parent = null)
        {
            var currentStage = StageUtility.GetCurrentStageHandle();

            var interactionManager = currentStage.FindComponentOfType <XRInteractionManager>();

            if (interactionManager == null)
            {
                CreateAndPlaceGameObject("XR Interaction Manager", parent, typeof(XRInteractionManager));
            }
            else
            {
                Selection.activeGameObject = interactionManager.gameObject;
            }
        }
Example #16
0
    public static string[] OnWillSaveAssets(string[] paths)
    {
        foreach (string path in paths)
        {
            if (path.Contains("MechaComponent_"))
            {
                MechaComponent[] components = StageUtility.GetCurrentStageHandle().FindComponentsOfType <MechaComponent>();
                foreach (MechaComponent component in components)
                {
                    ProcessMechaComponentPrefab(component);
                }
            }
        }

        return(paths);
    }
Example #17
0
        static bool IsValidCanvas(Canvas canvas)
        {
            if (canvas == null || !canvas.gameObject.activeInHierarchy)
            {
                return(false);
            }

            // It's important that the non-editable canvas from a prefab scene won't be rejected,
            // but canvases not visible in the Hierarchy at all do. Don't check for HideAndDontSave.
            if (EditorUtility.IsPersistent(canvas) || (canvas.hideFlags & HideFlags.HideInHierarchy) != 0)
            {
                return(false);
            }

            return(StageUtility.GetStageHandle(canvas.gameObject) == StageUtility.GetCurrentStageHandle());
        }
Example #18
0
        public static ActionPicker Show(Behaviours.Actions.Action originalAction, Type actionType, Action <Behaviours.Actions.Action> actionPicked)
        {
            s_ActionPicked   = actionPicked;
            s_OriginalAction = originalAction;
            s_SelectedAction = null;

            // Find all Actions, filter them to only include subtypes the specified type, and sort them by name.
            s_Actions = StageUtility.GetCurrentStageHandle().FindComponentsOfType <Behaviours.Actions.Action>();
            var filteredActions = new List <Behaviours.Actions.Action>(s_Actions);

            filteredActions = filteredActions.FindAll(a => a.GetType().IsSubclassOf(actionType) || a.GetType() == actionType);
            filteredActions.Sort((a, b) => a.name.CompareTo(b.name));
            s_Actions = filteredActions.ToArray();

            var window = GetWindow <ActionPicker>(true, "Action Picker", true);

            return(window);
        }
Example #19
0
        // Helper function that returns a Canvas GameObject; preferably a parent of the selection, or other existing Canvas.
        public static GameObject GetOrCreateCanvasGameObject()
        {
            var selectedGo = Selection.activeGameObject;

            // Try to find a gameobject that is the selected GO or one if its parents.
            var canvas = (selectedGo != null) ? selectedGo.GetComponentInParent <Canvas>() : null;

            if (IsValidCanvas(canvas))
            {
                return(canvas.gameObject);
            }

            // No canvas in selection or its parents? Then use any valid canvas.
            // We have to find all loaded Canvases, not just the ones in main scenes.
            var canvasArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType <Canvas>();

            return(canvasArray.Where(x => IsValidCanvas(x)).FirstOrDefault()?.gameObject ?? CreateNewUI());
        }
Example #20
0
        static bool IsValidCanvas(Canvas canvas)
        {
            if (canvas == null || !canvas.gameObject.activeInHierarchy)
            {
                return(false);
            }

            if (EditorUtility.IsPersistent(canvas) || (canvas.hideFlags & HideFlags.HideInHierarchy) != 0)
            {
                return(false);
            }

            if (StageUtility.GetStageHandle(canvas.gameObject) != StageUtility.GetCurrentStageHandle())
            {
                return(false);
            }

            return(true);
        }
Example #21
0
        static bool IsValidSolver(ObiSolver solver)
        {
            if (solver == null || !solver.gameObject.activeInHierarchy)
            {
                return(false);
            }

            if (EditorUtility.IsPersistent(solver) || (solver.hideFlags & HideFlags.HideInHierarchy) != 0)
            {
                return(false);
            }

            if (StageUtility.GetStageHandle(solver.gameObject) != StageUtility.GetCurrentStageHandle())
            {
                return(false);
            }

            return(true);
        }
Example #22
0
    static bool UpdateSceneViewSkybox()
    {
        if (StageUtility.GetCurrentStageHandle() != StageUtility.GetMainStageHandle())
        {
            return(false);
        }

        SceneView sceneView = SceneView.lastActiveSceneView;

        if (sceneView == null)
        {
            return(false);
        }

        SceneView.SceneViewState state = sceneView.sceneViewState;
        state.showSkybox         = false;
        sceneView.sceneViewState = state;

        return(true);
    }
Example #23
0
        private void OnTireAndRimSelected(GameObject selected)
        {
            if (selected == null)
            {
                Debug.LogError("Unable to configure TwoBodyTire - selected object is null.");
                return;
            }

            var rb = selected.GetComponentInParent <RigidBody>();

            if (rb == null)
            {
                Debug.LogError("Unable to configure TwoBodyTire - selected object isn't part of a RigidBody.");
                return;
            }

#if UNITY_2019_1_OR_NEWER
            // StageUtility is used when a prefab is open in "Open Prefab" tab.
            var allConstraints = StageUtility.GetCurrentStageHandle().Contains(rb.gameObject) ?
                                 StageUtility.GetCurrentStageHandle().FindComponentsOfType <Constraint>() :
                                 Object.FindObjectsOfType <Constraint>();
#else
            var allConstraints = Object.FindObjectsOfType <Constraint>();
#endif
            var tireConstraints = (from constraint
                                   in allConstraints
                                   where constraint.AttachmentPair.Contains(rb)
                                   select constraint).ToArray();
            if (tireConstraints.Length != 1)
            {
                Debug.LogError("Unable to configure TwoBodyTire - " +
                               rb.name +
                               " has " +
                               tireConstraints.Length +
                               " constraints != 1. Exact one expected.");
                return;
            }

            Undo.RecordObject(Tire, "Configuring TwoBodyTire");
            Tire.Configure(tireConstraints[0], rb);
        }
Example #24
0
        public static bool IsEditedInPrefabMode(CSGModel model)
        {
    #if UNITY_2018_3_OR_NEWER && UNITY_EDITOR
            if (!model)
            {
                return(false);
            }

            if (!AreInPrefabMode())
            {
                return(false);
            }

            var currentStageHandle = StageUtility.GetCurrentStageHandle();
            if (currentStageHandle.Contains(model.gameObject))
            {
                return(true);
            }
    #endif
            return(false);
        }
Example #25
0
        public RigidBodyTool(Object[] targets)
            : base(targets)
        {
#if UNITY_2019_1_OR_NEWER
            var allConstraints = StageUtility.GetCurrentStageHandle().Contains(RigidBody.gameObject) ?
                                 StageUtility.GetCurrentStageHandle().FindComponentsOfType <Constraint>() :
                                 Object.FindObjectsOfType <Constraint>();
#else
            var allConstraints = Object.FindObjectsOfType <Constraint>();
#endif
            foreach (var constraint in allConstraints)
            {
                foreach (var rb in GetTargets <RigidBody>())
                {
                    if (constraint.AttachmentPair.Contains(rb))
                    {
                        m_constraints.Add(constraint);
                    }
                }
            }
        }
Example #26
0
        private static GameObject GetOrCreateCanvasGameObject()
        {
            GameObject selectedGo = Selection.activeGameObject;

            Canvas canvas = (selectedGo != null) ? selectedGo.GetComponentInParent <Canvas>() : null;

            if (IsValidCanvas(canvas))
            {
                return(canvas.gameObject);
            }

            Canvas[] canvasArray = StageUtility.GetCurrentStageHandle().FindComponentsOfType <Canvas>();
            for (int i = 0; i < canvasArray.Length; i++)
            {
                if (IsValidCanvas(canvasArray[i]))
                {
                    return(canvasArray[i].gameObject);
                }
            }

            return(CreateNewUI());
        }
Example #27
0
        public void Initialize()
        {
            m_IKManagers.Clear();
            m_IKSolvers.Clear();
            m_DirtyManagers.Clear();
            m_ChainPositionOverrides.Clear();

            var currentStage = StageUtility.GetCurrentStageHandle();
            var managers     = currentStage.FindComponentsOfType <IKManager2D>().Where(x => x.gameObject.scene.isLoaded).ToArray();

            m_IKManagers.AddRange(managers);

            foreach (IKManager2D manager in m_IKManagers)
            {
                foreach (Solver2D solver in manager.solvers)
                {
                    if (solver)
                    {
                        m_IKSolvers.Add(solver);
                    }
                }
            }
        }
#pragma warning disable IDE0051 // Remove unused private members -- Editor Menu Item
        static void CreateXRUIEventSystem(MenuCommand menuCommand)
#pragma warning restore IDE0051
        {
            var currentStage = StageUtility.GetCurrentStageHandle();

            // Ensure there is at least one EventSystem setup properly
            var inputModule = currentStage.FindComponentOfType <XRUIInputModule>();

            if (inputModule == null)
            {
                var        eventSystem = currentStage.FindComponentOfType <EventSystem>();
                GameObject eventSystemGO;
                if (eventSystem == null)
                {
                    eventSystemGO = CreateAndPlaceGameObject("EventSystem", menuCommand.GetContextTransform(),
                                                             typeof(EventSystem),
                                                             typeof(XRUIInputModule));
                }
                else
                {
                    eventSystemGO = eventSystem.gameObject;

                    // Remove the Standalone Input Module if already implemented, since it will block the XRUIInputModule
                    var standaloneInputModule = eventSystemGO.GetComponent <StandaloneInputModule>();
                    if (standaloneInputModule != null)
                    {
                        Undo.DestroyObjectImmediate(standaloneInputModule);
                    }

                    Undo.AddComponent <XRUIInputModule>(eventSystemGO);
                }

                inputModule = eventSystemGO.GetComponent <XRUIInputModule>();
            }

            Selection.activeGameObject = inputModule.gameObject;
        }
Example #29
0
        public static GameObject CreateNewSolver()
        {
            // Root for the actors.
            var       root   = new GameObject("Obi Solver");
            ObiSolver solver = root.AddComponent <ObiSolver>();

            // Try to find a fixed updater in the scene (though other kinds of updaters can exist, updating in FixedUpdate is the preferred option).
            ObiFixedUpdater updater = StageUtility.GetCurrentStageHandle().FindComponentOfType <ObiFixedUpdater>();

            // If we could not find an fixed updater in the scene, add one to the solver object.
            if (updater == null)
            {
                updater = root.AddComponent <ObiFixedUpdater>();
            }

            // Add the solver to the updater:
            updater.solvers.Add(solver);

            // Works for all stages.
            StageUtility.PlaceGameObjectInCurrentStage(root);
            Undo.RegisterCreatedObjectUndo(root, "Create " + root.name);

            return(root);
        }
        /// <summary>
        /// Recompute the model and model group hierarchy.
        /// Some rules:
        /// 1. A brick will always be in a model group, which will always be in a model.
        /// 2. Empty models and model groups are destroyed.
        /// 3. Bricks can not contain bricks, model groups can not contain model groups and models can not contain models. So they are flattened.
        /// </summary>
        /// <param name="bricks">The bricks that we need to check the hierarchy for</param>
        /// <param name="alignRotation">Whether or not we also align rotation when we recompute pivot</param>
        /// <param name="undoBehavior">Whether or not to register undo for the recomputation of hierarchy</param>
        public static void RecomputeHierarchy(IEnumerable <Brick> bricks, bool alignRotation = true, UndoBehavior undoBehavior = UndoBehavior.withUndo)
        {
            // Group numbers are numbers in parentheses
            // So group 1 with three bricks is shown as (1, 1, 1)
            // In case the group is part of a prefab it is suffixed with a p like (1p, 1p)
            // In case one of the bricks in a group is an override it is noted with a + as in (1p, 1p+)

            // Cases for splitting:
            // (1, 1) -> (1) (1)
            // (1, 1) (2) -> (1) (1, 2) -> (1) (2, 2)

            // Cases for unpacking:
            // (1p) (2p) -> (1, 2p) -> (2p+, 2p)
            // (1p, 1p+) (2p) -> (1, 1, 2p) -> (2p+, 2p+, 2p)
            // (1p, 1p) -> (1p) (1p) -> (1) (1)

            // Only set parent:
            // (1) (2) -> (1, 2) -> (2, 2)
            // (1p, 1p+) (2p) -> (1p) (1p+, 2p) -> (1p) (2p+, 2p)
            // (1p, 1p+) (2) -> (1p) (1p+, 2) -> (1p) (2, 2)

            if (PrefabStageUtility.GetCurrentPrefabStage() != null)
            {
                var rootObject = PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot;
                var brick      = rootObject.GetComponent <Brick>();
                if (brick)
                {
                    return;
                }
            }

            // First we flatten models
            var modelsToCheck = new HashSet <Model>();
            var groupsToCheck = new HashSet <ModelGroup>();
            var bricksToCheck = new HashSet <Brick>();

            foreach (var brick in bricks)
            {
                var bricksInParent = brick.GetComponentsInParent <Brick>(true);
                if (bricksInParent.Length > 1)
                {
                    bricksToCheck.Add(brick);
                }
                var modelsInParent = brick.GetComponentsInParent <Model>(true);
                if (modelsInParent.Length > 1)
                {
                    modelsToCheck.UnionWith(modelsInParent);
                }
                var groupsInParent = brick.GetComponentsInParent <ModelGroup>(true);
                if (groupsInParent.Length > 1)
                {
                    groupsToCheck.UnionWith(groupsInParent);
                }
            }

            foreach (var model in modelsToCheck)
            {
                var modelsInModel = model.GetComponentsInChildren <Model>(true);
                foreach (var inModel in modelsInModel)
                {
                    if (inModel == model)
                    {
                        continue;
                    }

                    var groupsInModel = inModel.GetComponentsInChildren <ModelGroup>(true);
                    foreach (var group in groupsInModel)
                    {
                        SetParent(group.transform, model.transform, undoBehavior);
                    }
                }
            }

            // Now flatten groups
            foreach (var group in groupsToCheck)
            {
                var groupsInGroup = group.GetComponentsInChildren <ModelGroup>(true);
                foreach (var inGroup in groupsInGroup)
                {
                    if (inGroup == group)
                    {
                        continue;
                    }

                    var bricksInGroup = inGroup.GetComponentsInChildren <Brick>(true);
                    foreach (var brick in bricksInGroup)
                    {
                        SetParent(brick.transform, group.transform, undoBehavior);
                    }
                }
            }

            // Now flatten bricks
            foreach (var brick in bricksToCheck)
            {
                var group = GetGroupInParent(brick.transform);
                if (group)
                {
                    SetParent(brick.transform, group.transform, undoBehavior);
                }
            }

            var connectedClusters = new List <HashSet <Brick> >();

            // Collect all connected brick lists
            foreach (var brick in bricks)
            {
                if (connectedClusters.Any(x => x.Contains(brick)))
                {
                    continue;
                }

                if (!brick.HasConnectivity())
                {
                    var group = GetGroupInParent(brick.transform);
                    if (!group)
                    {
                        connectedClusters.Add(new HashSet <Brick> {
                            brick
                        });
                    }
                    else
                    {
                        var model = GetModelInParent(brick.transform);
                        if (!model)
                        {
                            var bricksInGroup = group.GetComponentsInChildren <Brick>(true);
                            var connected     = new HashSet <Brick>();
                            connected.Add(brick);
                            connectedClusters.Add(connected);
                        }
                    }
                }
                else
                {
                    var connected = brick.GetConnectedBricks();
                    connected.Add(brick);
                    connectedClusters.Add(connected);
                }
            }

            // Now find all groups for each cluster
            var groupsPerCluster = new List <(HashSet <Brick>, HashSet <ModelGroup>, HashSet <Brick>)>();

            foreach (var cluster in connectedClusters)
            {
                if (cluster.Count == 0)
                {
                    continue;
                }

                var bricksNotInGroup = new HashSet <Brick>();
                var groups           = new HashSet <ModelGroup>();
                foreach (var brick in cluster)
                {
                    var group = GetGroupInParent(brick.transform);
                    if (group)
                    {
                        groups.Add(group);
                    }
                    else
                    {
                        bricksNotInGroup.Add(brick);
                    }
                }
                groupsPerCluster.Add((cluster, groups, bricksNotInGroup));
            }

            // Sorting makes sure we merge before we split. Merging will make it easier to see what we need to split later.
            groupsPerCluster = groupsPerCluster.OrderByDescending(x => x.Item2.Count).ToList();

            // Check through each of these groups in the cluster
            foreach (var groupPerCluster in groupsPerCluster)
            {
                var cluster    = groupPerCluster.Item1;
                var groups     = groupPerCluster.Item2;
                var notInGroup = groupPerCluster.Item3;

                // If the cluster has more than one group, we need to merge them
                if (groups.Count > 1)
                {
                    // Merge some groups
                    ModelGroup largestGroup     = null;
                    int        largestGroupSize = 0;
                    foreach (var group in groups)
                    {
                        var bricksInGroup   = group.GetComponentsInChildren <Brick>(true);
                        var bricksInCluster = bricksInGroup.Count(x => cluster.Contains(x));
                        if (bricksInCluster >= largestGroupSize)
                        {
                            largestGroup     = group;
                            largestGroupSize = bricksInCluster;
                        }
                    }

                    foreach (var brick in cluster)
                    {
                        if (brick.transform.parent == largestGroup.transform)
                        {
                            continue;
                        }

                        if (IsPartOfPrefab(brick))
                        {
                            UnpackPrefab(brick, undoBehavior);
                        }
                        SetParent(brick.transform, largestGroup.transform, undoBehavior);
                    }

                    RecomputePivot(largestGroup, alignRotation, undoBehavior);
                    var modelGO = largestGroup.transform.parent;
                    var model   = modelGO.GetComponent <Model>();
                    if (model)
                    {
                        RecomputePivot(model, alignRotation, undoBehavior);
                    }
                }
                else if (groups.Count == 1) // In case the cluster only has one group, we need to check if the group contains bricks not in this cluster
                {
                    var group = groups.FirstOrDefault();
                    if (!group)
                    {
                        continue;
                    }

                    var bricksInGroup    = group.GetComponentsInChildren <Brick>(true);
                    var clustersForGroup = new HashSet <HashSet <Brick> >();

                    // If this group contains more than one cluster, split
                    foreach (var brick in bricksInGroup)
                    {
                        if (!clustersForGroup.Any(x => x.Contains(brick)))
                        {
                            if (brick.HasConnectivity())
                            {
                                var connected = brick.GetConnectedBricks();
                                connected.Add(brick);
                                clustersForGroup.Add(connected);
                            }
                        }
                    }

                    // Only split if we found multiple clusters in group
                    if (clustersForGroup.Count > 1)
                    {
                        // Get the model for the group
                        var model = GetModelInParent(group.transform);

                        // Find all prefabs we need to unpack by looking through the clusters in the group
                        foreach (var clusterInGroup in clustersForGroup)
                        {
                            // Look through each brick in the cluster
                            foreach (var brick in clusterInGroup)
                            {
                                // First check if there is a brick in this cluster that is part of a prefab and not an override
                                // If there is, then check if there is another cluster containing a prefab that is not an override
                                // In that case, we have to unpack, because we are changing the parents of gameobjects in a prefab
                                if (IsPartOfPrefab(brick))
                                {
                                    if (clustersForGroup.Any(clust => clust != clusterInGroup && clust.Any(obj => !PrefabUtility.IsAddedGameObjectOverride(obj.gameObject))))
                                    {
                                        UnpackPrefab(brick, undoBehavior);
                                        break;
                                    }
                                }
                            }
                        }

                        // Optimization: Check for largest group containing only a single cluster and keep that group intact
                        HashSet <Brick> largestGroupBricks   = null;
                        ModelGroup      largestGroup         = null;
                        int             largestGroupSize     = 0;
                        var             sharedParentClusters = new HashSet <ModelGroup>();

                        foreach (var clusterInGroup in clustersForGroup)
                        {
                            var parent     = clusterInGroup.First().transform.parent;
                            var modelGroup = parent.GetComponent <ModelGroup>();
                            if (!modelGroup)
                            {
                                continue;
                            }

                            var skip = false;
                            foreach (var brick in clusterInGroup)
                            {
                                if (brick.transform.parent != parent)
                                {
                                    skip = true;
                                    break;
                                }
                            }
                            if (skip)
                            {
                                continue;
                            }

                            if (largestGroupSize < clusterInGroup.Count())
                            {
                                largestGroupSize   = clusterInGroup.Count();
                                largestGroupBricks = clusterInGroup;
                                largestGroup       = modelGroup;
                            }
                        }

                        if (largestGroupBricks != null)
                        {
                            clustersForGroup.Remove(largestGroupBricks);
                        }

                        foreach (var clusterInGroup in clustersForGroup)
                        {
                            var newObject = new GameObject();
                            var newGroup  = newObject.AddComponent <ModelGroup>();

                            // Add LEGOModelGroupAsset component.
                            newObject.AddComponent <LEGOModelGroupAsset>();

                            if (undoBehavior == UndoBehavior.withUndo)
                            {
                                Undo.RegisterCreatedObjectUndo(newObject, "Created new group");
                            }

                            newGroup.transform.position = group.transform.position;

                            if (model)
                            {
                                SetParent(newGroup.transform, model.transform, undoBehavior);
                            }

                            newGroup.name             = group.groupName;
                            newGroup.groupName        = group.groupName;
                            newGroup.parentName       = group.parentName;
                            newGroup.optimizations    = group.optimizations;
                            newGroup.randomizeNormals = group.randomizeNormals;
                            foreach (var view in group.views)
                            {
                                newGroup.views.Add(new CullingCameraConfig()
                                {
                                    name        = view.name,
                                    perspective = view.perspective,
                                    position    = view.position,
                                    rotation    = view.rotation,
                                    fov         = view.fov,
                                    size        = view.size,
                                    minRange    = view.minRange,
                                    maxRange    = view.maxRange,
                                    aspect      = view.aspect
                                });
                            }
                            newGroup.autoGenerated = true;

                            foreach (var brick in clusterInGroup)
                            {
                                SetParent(brick.transform, newGroup.transform, undoBehavior);
                            }
                            RecomputePivot(newGroup, alignRotation, undoBehavior);
                        }

                        if (model)
                        {
                            RecomputePivot(model, alignRotation, undoBehavior);
                        }
                    }
                    else if (notInGroup.Count > 0)
                    {
                        foreach (var brick in notInGroup)
                        {
                            if (IsPartOfPrefab(brick))
                            {
                                UnpackPrefab(brick, undoBehavior);
                            }
                            SetParent(brick.transform, group.transform, undoBehavior);
                        }
                    }
                    else
                    {
                        RecomputePivot(group, alignRotation, undoBehavior);

                        var   modelGO        = group.transform.parent;
                        Model model          = null;
                        bool  createNewModel = (PrefabStageUtility.GetCurrentPrefabStage() != null && modelGO && !modelGO.GetComponent <Model>()) ||
                                               (PrefabStageUtility.GetCurrentPrefabStage() == null && (!modelGO || !modelGO.GetComponent <Model>()));
                        if (createNewModel)
                        {
                            model = CreateNewDefaultModel(group.name, undoBehavior);
                            if (modelGO != null)
                            {
                                if (PrefabStageUtility.GetCurrentPrefabStage() == null && PrefabUtility.IsPartOfAnyPrefab(modelGO))
                                {
                                    UnpackPrefab(modelGO.gameObject, undoBehavior);
                                }
                                SetParent(model.transform, modelGO.transform, undoBehavior);
                            }
                            SetParent(group.transform, model.transform, undoBehavior);
                            EditorGUIUtility.PingObject(group.gameObject);
                        }
                        else
                        {
                            model = modelGO.GetComponent <Model>();
                        }

                        if (model)
                        {
                            RecomputePivot(model, alignRotation, undoBehavior);
                        }
                    }
                }
                else // No groups.
                {
                    var   name  = cluster.FirstOrDefault()?.name;
                    Model model = null;

                    if (PrefabStageUtility.GetCurrentPrefabStage() != null)
                    {
                        var rootObject = PrefabStageUtility.GetCurrentPrefabStage().prefabContentsRoot;
                        model = rootObject.GetComponent <Model>();
                        if (!model)
                        {
                            model = CreateNewDefaultModel(name);
                            SetParent(model.transform, rootObject.transform, undoBehavior);
                        }
                    }
                    else
                    {
                        model = CreateNewDefaultModel(name);
                    }

                    ModelGroup newGroup = CreateNewDefaultModelGroup(name);

                    SetParent(newGroup.transform, model.transform, undoBehavior);
                    var bounds = BrickBuildingUtility.ComputeBounds(cluster, Matrix4x4.identity);
                    model.transform.position = new Vector3(bounds.center.x, bounds.min.y, bounds.center.z);

                    Transform originalParent = null;
                    foreach (var brick in cluster)
                    {
                        if (!originalParent)
                        {
                            originalParent = brick.transform.parent;
                        }
                        if (brick.transform.parent != originalParent)
                        {
                            originalParent = null;
                            break;
                        }
                    }

                    if (originalParent)
                    {
                        SetParent(model.transform, originalParent, undoBehavior);
                    }

                    foreach (var brick in cluster)
                    {
                        SetParent(brick.transform, newGroup.transform, undoBehavior);
                        EditorGUIUtility.PingObject(brick.gameObject);
                    }
                }
            }

            var modelsInScene = StageUtility.GetCurrentStageHandle().FindComponentsOfType <Model>();

            foreach (var model in modelsInScene)
            {
                var children = model.GetComponentsInChildren <Brick>(true);
                if (children.Length == 0)
                {
                    if (undoBehavior == UndoBehavior.withUndo)
                    {
                        Undo.DestroyObjectImmediate(model.gameObject);
                    }
                    else
                    {
                        Object.DestroyImmediate(model.gameObject);
                    }
                }
            }

            var groupsInScene = StageUtility.GetCurrentStageHandle().FindComponentsOfType <ModelGroup>();

            foreach (var group in groupsInScene)
            {
                var children = group.GetComponentsInChildren <Brick>(true);
                if (children.Length == 0)
                {
                    if (undoBehavior == UndoBehavior.withUndo)
                    {
                        Undo.DestroyObjectImmediate(group.gameObject);
                    }
                    else
                    {
                        Object.DestroyImmediate(group.gameObject);
                    }
                }
            }
        }