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; }
// 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); } } } }
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); }
// 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; }
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(); }
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); }
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); }
private static Canvas GetCanvas() { StageHandle handle = StageUtility.GetCurrentStageHandle(); if (!handle.FindComponentOfType <Canvas>()) { EditorApplication.ExecuteMenuItem("GameObject/UI/Canvas"); } Canvas c = handle.FindComponentOfType <Canvas>(); return(c); }
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);
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; } }
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); }
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()); }
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); }
// 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()); }
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); }
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); }
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); }
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); }
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); }
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); } } } }
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()); }
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; }
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); } } } }