/// <summary> /// Update the current brush object and weights with the current mouse position. /// </summary> /// <param name="mousePosition">current mouse position (from Event)</param> /// <param name="isDrag">optional, is dragging the mouse cursor</param> /// <param name="overridenGO">optional, provides an already selected gameobject (used in unit tests only)</param> /// <param name="overridenRay"> optional, provides a ray already created (used in unit tests only)</param> internal void UpdateBrush(Vector2 mousePosition, bool isUserHoldingControl = false, bool isUserHoldingShift = false, bool isDrag = false, GameObject overridenGO = null, Ray?overridenRay = null) { MirrorSettings mirrorSettings = m_BrushMirrorEditor.settings; // NOTE: Quick fix for the lockBrushToFirst feature, probably need to refactor // some code in order to be able to do it properly if (firstGameObject != null && s_LockBrushToFirst) { Ray mouseRay2 = overridenRay != null ? (Ray)overridenRay : HandleUtility.GUIPointToWorldRay(mousePosition); DoMeshRaycast(mouseRay2, brushTarget, mirrorSettings); OnBrushMove(); SceneView.RepaintAll(); this.Repaint(); return; } // Must check HandleUtility.PickGameObject only during MouseMoveEvents or errors will rain. GameObject go = null; brushTarget = null; if (isDrag && s_LockBrushToFirst && m_LastHoveredGameObject != null) { go = m_LastHoveredGameObject; brushTarget = GetBrushTarget(go); } else if (s_IgnoreUnselected || isDrag) { GameObject cur = null; int max = 0; // safeguard against unforeseen while loop errors crashing unity do { int tmp; // overloaded PickGameObject ignores array of GameObjects, this is used // when there are non-selected gameObjects between the mouse and selected // gameObjects. cur = overridenGO; if (cur == null) { m_IgnoreDrag.RemoveAll(x => x == null); cur = HandleUtility.PickGameObject(mousePosition, m_IgnoreDrag.ToArray(), out tmp); } if (cur != null) { if (!PolyEditorUtility.InSelection(cur)) { if (!m_IgnoreDrag.Contains(cur)) { m_IgnoreDrag.Add(cur); } } else { brushTarget = GetBrushTarget(cur); if (brushTarget != null) { go = cur; } else { m_IgnoreDrag.Add(cur); } } } } while(go == null && cur != null && max++ < 128); } else { go = overridenGO; if (go == null) { go = HandleUtility.PickGameObject(mousePosition, false); } if (go != null && PolyEditorUtility.InSelection(go)) { brushTarget = GetBrushTarget(go); } else { go = null; } } bool mouseHoverTargetChanged = false; Ray mouseRay = overridenRay != null ? (Ray)overridenRay : HandleUtility.GUIPointToWorldRay(mousePosition); // if the mouse hover picked up a valid editable, raycast against that. otherwise // raycast all meshes in selection if (go == null) { foreach (var kvp in m_Hovering) { BrushTarget t = kvp.Value; if (Util.IsValid(t) && DoMeshRaycast(mouseRay, t, mirrorSettings)) { brushTarget = t; go = t.gameObject; break; } } } else { if (!DoMeshRaycast(mouseRay, brushTarget, mirrorSettings)) { if (!isDrag || !s_LockBrushToFirst) { go = null; brushTarget = null; } return; } } // if m_Hovering off another gameobject, call OnBrushExit on that last one and mark the // target as having been changed if (go != m_LastHoveredGameObject) { OnBrushExit(m_LastHoveredGameObject); mouseHoverTargetChanged = true; m_LastHoveredGameObject = go; } SceneView.RepaintAll(); this.Repaint(); if (brushTarget == null) { return; } if (mouseHoverTargetChanged) { brushSettings.isUserHoldingControl = isUserHoldingControl; brushSettings.isUserHoldingShift = isUserHoldingShift; OnBrushEnter(brushTarget, brushSettings); // brush is in use, adding a new object to the undo if (m_ApplyingBrush && !m_UndoQueue.Contains(go)) { int curGroup = Undo.GetCurrentGroup(); brushTarget.editableObject.isDirty = true; OnBrushBeginApply(brushTarget, brushSettings); Undo.CollapseUndoOperations(curGroup); } } OnBrushMove(); SceneView.RepaintAll(); this.Repaint(); }
/// <summary> /// Update the current brush object and weights with the current mouse position. /// </summary> /// <param name="mousePosition">current mouse position (from Event)</param> /// <param name="isDrag">optional, is dragging the mouse cursor</param> /// <param name="overridenGO">optional, provides an already selected gameobject (used in unit tests only)</param> /// <param name="overridenRay"> optional, provides a ray already created (used in unit tests only)</param> internal void UpdateBrush(Vector2 mousePosition, bool isUserHoldingControl = false, bool isUserHoldingShift = false, bool isDrag = false, GameObject overridenGO = null, Ray?overridenRay = null) { MirrorSettings mirrorSettings = m_BrushMirrorEditor.settings; // Must check HandleUtility.PickGameObject only during MouseMoveEvents or errors will rain. GameObject go = null; brushTarget = null; GameObject cur = null; #if UNITY_2021_1_OR_NEWER int materialIndex; cur = HandleUtility.PickGameObject(mousePosition, false, null, Selection.gameObjects, out materialIndex); if (cur != null) { brushTarget = GetOrCreateBrushTarget(cur); } if (brushTarget != null) { go = cur; } #else int max = 0; // safeguard against unforeseen while loop errors crashing unity do { int tmp; // overloaded PickGameObject ignores array of GameObjects, this is used // when there are non-selected gameObjects between the mouse and selected // gameObjects. cur = overridenGO; if (cur == null) { m_IgnoreDrag.RemoveAll(x => x == null); cur = HandleUtility.PickGameObject(mousePosition, m_IgnoreDrag.ToArray(), out tmp); } if (cur != null) { if (!PolyEditorUtility.InSelection(cur)) { if (!m_IgnoreDrag.Contains(cur)) { m_IgnoreDrag.Add(cur); } } else { brushTarget = GetOrCreateBrushTarget(cur); if (brushTarget != null) { go = cur; } else { m_IgnoreDrag.Add(cur); } } } } while(go == null && cur != null && max++ < 128); #endif bool mouseHoverTargetChanged = false; Ray mouseRay = overridenRay != null ? (Ray)overridenRay : HandleUtility.GUIPointToWorldRay(mousePosition); // if the mouse hover picked up a valid editable, raycast against that. otherwise // raycast all meshes in selection if (go == null) { foreach (var kvp in m_Hovering) { BrushTarget t = kvp.Value; if (Util.IsValid(t) && DoMeshRaycast(mouseRay, t, mirrorSettings)) { brushTarget = t; go = t.gameObject; break; } } } else { if (!DoMeshRaycast(mouseRay, brushTarget, mirrorSettings)) { brushTarget = null; return; } } // if m_Hovering off another gameobject, call OnBrushExit on that last one and mark the // target as having been changed if (go != m_LastHoveredGameObject) { if (m_LastHoveredGameObject) { OnBrushExit(m_LastHoveredGameObject); } if (m_ApplyingBrush) { mode.OnBrushFinishApply(brushTarget, brushSettings); } mouseHoverTargetChanged = true; m_LastHoveredGameObject = go; foreach (var secondaryTarget in m_LastSecondaryBrushTargets) { if (!m_SecondaryBrushTargets.Contains(secondaryTarget)) { OnBrushExit(secondaryTarget.gameObject); if (m_ApplyingBrush) { mode.OnBrushFinishApply(brushTarget, brushSettings); } } } } if (brushTarget == null) { SceneView.RepaintAll(); DoRepaint(); m_LastSecondaryBrushTargets.Clear(); m_SecondaryBrushTargets.Clear(); return; } brushSettings.isUserHoldingControl = isUserHoldingControl; brushSettings.isUserHoldingShift = isUserHoldingShift; if (mouseHoverTargetChanged) { foreach (var secondaryTarget in m_SecondaryBrushTargets) { if (!m_LastSecondaryBrushTargets.Contains(secondaryTarget)) { OnBrushEnter(secondaryTarget, brushSettings); if (m_ApplyingBrush) { mode.OnBrushBeginApply(secondaryTarget, brushSettings); } } } //The active brushtarget is the last one to notify the brush OnBrushEnter(brushTarget, brushSettings); // brush is in use, adding a new object to the undo if (m_ApplyingBrush) { if (!m_UndoQueue.Contains(go)) { int curGroup = Undo.GetCurrentGroup(); brushTarget.editableObject.isDirty = true; OnBrushBeginApply(brushTarget, brushSettings); Undo.CollapseUndoOperations(curGroup); } else { mode.OnBrushBeginApply(brushTarget, brushSettings); } } } m_LastSecondaryBrushTargets.Clear(); m_LastSecondaryBrushTargets.AddRange(m_SecondaryBrushTargets); OnBrushMove(); SceneView.RepaintAll(); DoRepaint(); }