Exemple #1
0
        public static void AlignSelectionToAxis(Axis axis)
        {
            int axisIndex = (int)axis;

            List <GameObject> allSelectedObjects = ObjectSelection.Get().GetAllSelectedGameObjects();
            List <GameObject> selectedParents    = GameObjectExtensions.GetParents(allSelectedObjects);

            if (selectedParents.Count == 0)
            {
                return;
            }

            float average = 0.0f;

            foreach (var parent in selectedParents)
            {
                average += parent.transform.position[axisIndex];
            }
            average /= selectedParents.Count;

            GameObjectExtensions.RecordObjectTransformsForUndo(selectedParents);
            foreach (var parent in selectedParents)
            {
                Transform parentTransform = parent.transform;
                Vector3   alignedPosition = parentTransform.position;
                alignedPosition[axisIndex] = average;

                parentTransform.position = alignedPosition;
            }
        }
Exemple #2
0
        private void Rotate(float angle, Vector3 axis)
        {
            var selectedParents = GameObjectExtensions.GetParents(_selectedObjects.HashSet);

            GameObjectExtensions.RecordObjectTransformsForUndo(selectedParents);
            if (Settings.RotateAroundSelectionCenter)
            {
                Vector3 selectionCenter = GetWorldCenter();
                foreach (var parent in selectedParents)
                {
                    parent.RotateHierarchyBoxAroundPoint(angle, axis, selectionCenter);
                }
            }
            else
            {
                foreach (var parent in selectedParents)
                {
                    Box worldAABB = parent.GetHierarchyWorldBox();
                    if (worldAABB.IsValid())
                    {
                        parent.RotateHierarchyBoxAroundPoint(angle, axis, worldAABB.Center);
                    }
                }
            }
        }
Exemple #3
0
        private void SetWorldRotation(Quaternion rotation)
        {
            var selectedParents = GameObjectExtensions.GetParents(_selectedObjects.HashSet);

            GameObjectExtensions.RecordObjectTransformsForUndo(selectedParents);
            foreach (var parent in selectedParents)
            {
                parent.transform.rotation = rotation;
            }
        }
Exemple #4
0
        public override void RenderHandles(TransformGizmoPivotPoint transformPivotPoint)
        {
            if (CanTransformObjects())
            {
                Vector3 newScaleAccumulatedByGizmoInteraction = Handles.ScaleHandle(_scaleAccumulatedByGizmoInteraction, _worldPosition, _worldRotation, HandleUtility.GetHandleSize(_worldPosition));
                if (newScaleAccumulatedByGizmoInteraction != _scaleAccumulatedByGizmoInteraction)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_gameObjectsWhichCanBeTransformed);
                    List <GameObject> topParents = GameObjectExtensions.GetTopParentsFromGameObjectCollection(_gameObjectsWhichCanBeTransformed);

                    Vector3 scaleFactor = CalculateScaleFactorUsedToScaleObjects(newScaleAccumulatedByGizmoInteraction);
                    ScaleObjectsBySpecifiedPivotPoint(transformPivotPoint, scaleFactor, topParents);

                    _scaleAccumulatedByGizmoInteraction = newScaleAccumulatedByGizmoInteraction;
                    GizmoTransformedObjectsMessage.SendToInterestedListeners(this);
                }
            }
        }
Exemple #5
0
        public override void RenderHandles(TransformGizmoPivotPoint transformPivotPoint)
        {
            if (CanTransformObjects())
            {
                Vector3 newGizmoWorldPosition = Handles.PositionHandle(_worldPosition, _worldRotation);
                if (newGizmoWorldPosition != _worldPosition)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_targetObjects);
                    List <GameObject> topParents = GameObjectExtensions.GetParents(_targetObjects);

                    MoveObjects(newGizmoWorldPosition, topParents);

                    UndoEx.RecordForToolAction(this);
                    _worldPosition = newGizmoWorldPosition;
                    GizmoTransformedObjectsMessage.SendToInterestedListeners(this);
                }
            }
        }
Exemple #6
0
        public override void RenderHandles(TransformGizmoPivotPoint transformPivotPoint)
        {
            if (CanTransformObjects())
            {
                Quaternion newWorldRotationAccumulatedByGizmoInteraction = Handles.RotationHandle(_worldRotation, _worldPosition);
                if (newWorldRotationAccumulatedByGizmoInteraction.eulerAngles != _worldRotation.eulerAngles)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_targetObjects);
                    List <GameObject> topParents = GameObjectExtensions.GetParents(_targetObjects);

                    Quaternion rotationAmount = CalculateRotationAmount(newWorldRotationAccumulatedByGizmoInteraction);
                    RotateObjectsAroundSpecifiedPivotPoint(transformPivotPoint, rotationAmount, topParents);

                    UndoEx.RecordForToolAction(this);
                    _worldRotation = newWorldRotationAccumulatedByGizmoInteraction;
                    GizmoTransformedObjectsMessage.SendToInterestedListeners(this);
                }
            }
        }
Exemple #7
0
        public void UpdateForMouseMovement()
        {
            if (_state == State.Inactive)
            {
                return;
            }

            if (MouseButtonStates.Instance.IsMouseButtonDown(MouseButton.Left))
            {
                _state = State.Snap;
            }
            else
            {
                _state = State.SelectPivot;
            }

            if (_state == State.SelectPivot && _selectedParents.Count != 0)
            {
                Camera  camera   = SceneViewCamera.Camera;
                Vector2 mousePos = Event.current.InvMousePos(camera);

                _isPivotAvailable = false;
                float minDistanceSq = float.MaxValue;
                foreach (var parent in _selectedParents)
                {
                    if (parent == null)
                    {
                        continue;
                    }

                    OrientedBox worldOOBB = parent.GetHierarchyWorldOrientedBox();
                    if (worldOOBB.IsValid())
                    {
                        List <Vector3> centerAndCorners = worldOOBB.GetCenterAndCornerPoints();
                        List <Vector2> oobbScreenPts    = Vector2Extensions.GetScreenPoints(centerAndCorners, camera);

                        for (int ptIndex = 0; ptIndex < centerAndCorners.Count; ++ptIndex)
                        {
                            Vector3 worldPt  = centerAndCorners[ptIndex];
                            Vector2 screenPt = oobbScreenPts[ptIndex];
                            float   distSq   = (mousePos - screenPt).sqrMagnitude;
                            if (distSq < minDistanceSq)
                            {
                                minDistanceSq     = distSq;
                                _pivot            = worldPt;
                                _isPivotAvailable = true;
                            }
                        }
                    }
                }
            }
            else
            if (_state == State.Snap && _isPivotAvailable)
            {
                GameObjectExtensions.RecordObjectTransformsForUndo(_selectedParents);
                MouseCursorRayHit cursorHit = MouseCursor.Instance.GetCursorRayHitForGridCell();
                if (cursorHit.WasACellHit)
                {
                    Camera         camera          = SceneViewCamera.Camera;
                    Vector2        mousePos        = Event.current.InvMousePos(camera);
                    GridCellRayHit cellRayHit      = cursorHit.GridCellRayHit;
                    Vector3        snapDestination = Vector3Extensions.GetClosestPointToPoint(cellRayHit.HitCell.Quad.GetCenterAndCornerPoints(), cellRayHit.HitPoint);

                    Vector3 moveVector = snapDestination - _pivot;
                    foreach (var parent in _selectedParents)
                    {
                        if (parent != null)
                        {
                            parent.transform.position += moveVector;
                        }
                    }

                    _pivot = snapDestination;

                    ObjectSelection.Get().ObjectSelectionTransformGizmoSystem.OnObjectSelectionUpdated();
                }
            }
        }
        public void Update()
        {
            if (IsActive)
            {
                _currentCursorRayHit = GetCursorRayHit();
                if (!_currentCursorRayHit.WasAnythingHit)
                {
                    return;
                }

                if (_currentCursorRayHit.WasAnObjectHit)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
                    GameObjectRayHit objectRayHit = _currentCursorRayHit.ClosestObjectRayHit;
                    _surfaceHitPoint = objectRayHit.HitPoint;
                    foreach (var grabbedObject in _grabbedObjects)
                    {
                        if (grabbedObject == null)
                        {
                            continue;
                        }

                        Transform objectTransform = grabbedObject.transform;
                        objectTransform.position = objectRayHit.HitPoint + _objectToPivotDir[grabbedObject];
                        if (objectRayHit.WasTerrainHit)
                        {
                            Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + Vector3.up, -Vector3.up);
                            GameObjectRayHit sitPointHit = null;
                            if (objectRayHit.HitObject.RaycastTerrainReverseIfFail(ray, out sitPointHit))
                            {
                                if (_grabSettings.AlignAxis)
                                {
                                    AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
                                }
                                grabbedObject.PlaceHierarchyOnPlane(new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint));
                                if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis)
                                {
                                    objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
                                }
                                if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis)
                                {
                                    grabbedObject.EmbedInSurfaceByVertex(-Vector3.up, objectRayHit.HitObject);
                                }
                            }
                        }
                        else
                        if (objectRayHit.WasMeshHit)
                        {
                            Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + objectRayHit.HitNormal * 2.0f, -objectRayHit.HitNormal);
                            GameObjectRayHit sitPointHit = null;
                            if (objectRayHit.HitObject.RaycastMeshReverseIfFail(ray, out sitPointHit))
                            {
                                if (_grabSettings.AlignAxis)
                                {
                                    AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
                                }
                                grabbedObject.PlaceHierarchyOnPlane(new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint));
                                if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis)
                                {
                                    objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
                                }
                                if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis)
                                {
                                    grabbedObject.EmbedInSurfaceByVertex(-sitPointHit.HitNormal, objectRayHit.HitObject);
                                }
                            }
                        }
                    }
                }
                else
                if (_currentCursorRayHit.WasACellHit)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
                    GridCellRayHit cellRayHit = _currentCursorRayHit.GridCellRayHit;
                    _surfaceHitPoint = cellRayHit.HitPoint;

                    foreach (var grabbedObject in _grabbedObjects)
                    {
                        if (grabbedObject == null)
                        {
                            continue;
                        }

                        Transform objectTransform = grabbedObject.transform;
                        objectTransform.position = cellRayHit.HitPoint + _objectToPivotDir[grabbedObject];

                        if (_grabSettings.AlignAxis)
                        {
                            AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, cellRayHit.HitNormal);
                        }
                        grabbedObject.PlaceHierarchyOnPlane(new Plane(cellRayHit.HitNormal, cellRayHit.HitPoint));
                        if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis)
                        {
                            objectTransform.position += _grabSettings.OffsetFromSurface * cellRayHit.HitNormal;
                        }
                    }
                }
            }
        }
Exemple #9
0
        public void UpdateOnMouseMove()
        {
            if (_isActive)
            {
                MouseCursorRayHit cursorRayHit = GetCursorRayHit();
                if (cursorRayHit == null || !cursorRayHit.WasAnythingHit)
                {
                    return;
                }

                Vector3 hitPoint  = Vector3.zero;
                Vector3 hitNormal = Vector3.zero;

                if (!cursorRayHit.WasAnObjectHit && cursorRayHit.WasACellHit)
                {
                    hitPoint  = cursorRayHit.GridCellRayHit.HitPoint;
                    hitNormal = cursorRayHit.GridCellRayHit.HitNormal;
                }
                else
                if (cursorRayHit.WasAnObjectHit && !cursorRayHit.WasACellHit)
                {
                    hitPoint  = cursorRayHit.ClosestObjectRayHit.HitPoint;
                    hitNormal = cursorRayHit.ClosestObjectRayHit.HitNormal;
                }
                else
                if (cursorRayHit.WasAnObjectHit && cursorRayHit.WasACellHit)
                {
                    if (cursorRayHit.ClosestObjectRayHit.HitEnter < cursorRayHit.GridCellRayHit.HitEnter)
                    {
                        hitPoint  = cursorRayHit.ClosestObjectRayHit.HitPoint;
                        hitNormal = cursorRayHit.ClosestObjectRayHit.HitNormal;
                    }
                    else
                    {
                        hitPoint  = cursorRayHit.GridCellRayHit.HitPoint;
                        hitNormal = cursorRayHit.GridCellRayHit.HitNormal;
                    }
                }

                Plane hitPlane           = new Plane(hitNormal, hitPoint);
                Box   selectionWorldAABB = ObjectSelection.Get().GetWorldBox();
                if (selectionWorldAABB.IsInvalid())
                {
                    return;
                }

                Vector3 oldCenter = selectionWorldAABB.Center;
                selectionWorldAABB.Center = hitPoint;
                selectionWorldAABB.MoveInFrontOfPlane(hitPlane);
                Vector3 moveVector = selectionWorldAABB.Center - oldCenter;

                float snapEps = ObjectSelection.Get().Settings.Object2ObjectSnapSettings.SnapEps;

                GameObjectExtensions.RecordObjectTransformsForUndo(_selectedParents);
                foreach (var parent in _selectedParents)
                {
                    parent.transform.position += moveVector;
                }

                var ignoreObjects = new List <GameObject>(_allSelectedObjects);
                ignoreObjects.AddRange(ObjectSnapping.Get().ObjectSnapMask.ObjectCollectionMask.GetAllMaskedGameObjects());
                Object2ObjectSnap.Snap(_selectedParents, snapEps, ignoreObjects);
            }
        }
        public void Update()
        {
            if (IsActive)
            {
                if (AllShortcutCombos.Instance.GrabRotateSelection.IsActive())
                {
                    _state = State.Rotating;
                }
                else if (AllShortcutCombos.Instance.GrabScaleSelection.IsActive())
                {
                    if (_state != State.Scaling)
                    {
                        _objectToWorldScale.Clear();
                        foreach (var grabbedObject in _grabbedObjects)
                        {
                            if (grabbedObject != null)
                            {
                                _objectToWorldScale.Add(grabbedObject, grabbedObject.transform.lossyScale);
                            }
                        }
                        _cursorPosAtScaleBegin = MouseCursor.Instance.Position;
                        _state = State.Scaling;
                    }
                }
                else
                {
                    // Need to reset the anchor relationships because the cursor was moved without
                    // the objects following it.
                    if (_state == State.Rotating || _state == State.Scaling)
                    {
                        _objectToPivotDir.Clear();
                        foreach (var grabbedObject in _grabbedObjects)
                        {
                            if (grabbedObject != null)
                            {
                                _objectToPivotDir.Add(grabbedObject, grabbedObject.transform.position - _surfaceHitPoint);
                            }
                        }
                    }
                    _state = State.Moving;
                }

                _currentCursorRayHit = GetCursorRayHit();
                if (!_currentCursorRayHit.WasAnythingHit)
                {
                    return;
                }

                if (_currentCursorRayHit.WasAnythingHit)
                {
                    if (_currentCursorRayHit.WasAnObjectHit)
                    {
                        _surfaceHitPoint = _currentCursorRayHit.ClosestObjectRayHit.HitPoint;
                    }
                    else
                    {
                        _surfaceHitPoint = _currentCursorRayHit.GridCellRayHit.HitPoint;
                    }
                }

                if (_state == State.Moving || _objectToSurfaceInfo.Count == 0)
                {
                    if (_currentCursorRayHit.WasAnObjectHit)
                    {
                        GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
                        GameObjectRayHit objectRayHit = _currentCursorRayHit.ClosestObjectRayHit;
                        foreach (var grabbedObject in _grabbedObjects)
                        {
                            if (grabbedObject == null)
                            {
                                continue;
                            }

                            Transform objectTransform = grabbedObject.transform;
                            objectTransform.position = objectRayHit.HitPoint + _objectToPivotDir[grabbedObject];
                            if (objectRayHit.WasTerrainHit)
                            {
                                Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + Vector3.up, -Vector3.up);
                                GameObjectRayHit sitPointHit = null;
                                if (objectRayHit.HitObject.RaycastTerrainReverseIfFail(ray, out sitPointHit))
                                {
                                    Plane surfacePlane = new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint);
                                    if (_grabSettings.AlignAxis)
                                    {
                                        AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
                                    }
                                    grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
                                    if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis)
                                    {
                                        objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
                                    }
                                    if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis)
                                    {
                                        grabbedObject.EmbedInSurfaceByVertex(-Vector3.up, objectRayHit.HitObject);
                                    }

                                    ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
                                    surfaceInfo.SurfacePlane = surfacePlane;
                                    surfaceInfo.SitPoint     = sitPointHit.HitPoint;
                                    SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
                                }
                            }
                            else
                            if (objectRayHit.WasMeshHit)
                            {
                                Ray ray = new Ray(grabbedObject.GetHierarchyWorldOrientedBox().Center + objectRayHit.HitNormal * 2.0f, -objectRayHit.HitNormal);
                                GameObjectRayHit sitPointHit = null;
                                if (objectRayHit.HitObject.RaycastMeshReverseIfFail(ray, out sitPointHit))
                                {
                                    Plane surfacePlane = new Plane(sitPointHit.HitNormal, sitPointHit.HitPoint);
                                    if (_grabSettings.AlignAxis)
                                    {
                                        AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, sitPointHit.HitNormal);
                                    }
                                    grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
                                    if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis)
                                    {
                                        objectTransform.position += _grabSettings.OffsetFromSurface * sitPointHit.HitNormal;
                                    }
                                    if (_grabSettings.EmbedInSurfaceWhenNoAlign && !_grabSettings.AlignAxis)
                                    {
                                        grabbedObject.EmbedInSurfaceByVertex(-sitPointHit.HitNormal, objectRayHit.HitObject);
                                    }

                                    ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
                                    surfaceInfo.SurfacePlane = surfacePlane;
                                    surfaceInfo.SitPoint     = sitPointHit.HitPoint;
                                    SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
                                }
                            }
                        }
                    }
                    else
                    if (_currentCursorRayHit.WasACellHit)
                    {
                        GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
                        GridCellRayHit cellRayHit = _currentCursorRayHit.GridCellRayHit;
                        foreach (var grabbedObject in _grabbedObjects)
                        {
                            if (grabbedObject == null)
                            {
                                continue;
                            }

                            Transform objectTransform = grabbedObject.transform;
                            objectTransform.position = cellRayHit.HitPoint + _objectToPivotDir[grabbedObject];

                            Plane   surfacePlane = new Plane(cellRayHit.HitNormal, cellRayHit.HitPoint);
                            Vector3 sitPoint     = surfacePlane.ProjectPoint(grabbedObject.GetWorldOrientedBox().Center);

                            if (_grabSettings.AlignAxis)
                            {
                                AxisAlignment.AlignObjectAxis(grabbedObject, _grabSettings.AlignmentAxis, cellRayHit.HitNormal);
                            }
                            grabbedObject.PlaceHierarchyOnPlane(surfacePlane);
                            if (!_grabSettings.EmbedInSurfaceWhenNoAlign || _grabSettings.AlignAxis)
                            {
                                objectTransform.position += _grabSettings.OffsetFromSurface * cellRayHit.HitNormal;
                            }

                            ObjectSurfaceInfo surfaceInfo = new ObjectSurfaceInfo();
                            surfaceInfo.SurfacePlane = surfacePlane;
                            surfaceInfo.SitPoint     = sitPoint;
                            SetObjectSurfaceInfo(grabbedObject, surfaceInfo);
                        }
                    }
                }
                else
                if (_state == State.Rotating)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
                    float rotationSensitivity = _grabSettings.RotationSensitivity;
                    foreach (var grabbedObject in _grabbedObjects)
                    {
                        if (!_objectToSurfaceInfo.ContainsKey(grabbedObject))
                        {
                            continue;
                        }

                        var         surfaceInfo = _objectToSurfaceInfo[grabbedObject];
                        OrientedBox worldOOBB   = grabbedObject.GetHierarchyWorldOrientedBox();
                        grabbedObject.RotateHierarchyBoxAroundPoint(MouseCursor.Instance.OffsetSinceLastMouseMove.x * rotationSensitivity, _grabSettings.AlignAxis ? surfaceInfo.SurfacePlane.normal : Vector3.up, worldOOBB.Center);
                    }
                }
                else
                if (_state == State.Scaling)
                {
                    GameObjectExtensions.RecordObjectTransformsForUndo(_grabbedObjects);
                    Vector2 currentCursorPos           = MouseCursor.Instance.Position;
                    Vector2 cursorOffsetFromScaleBegin = currentCursorPos - _cursorPosAtScaleBegin;

                    float scaleFactor = 1.0f + _grabSettings.ScaleSensitivity * cursorOffsetFromScaleBegin.x;
                    foreach (var grabbedObject in _grabbedObjects)
                    {
                        if (!_objectToSurfaceInfo.ContainsKey(grabbedObject) ||
                            !_objectToWorldScale.ContainsKey(grabbedObject))
                        {
                            continue;
                        }

                        var surfaceInfo = _objectToSurfaceInfo[grabbedObject];
                        grabbedObject.SetHierarchyWorldScaleByPivotPoint(_objectToWorldScale[grabbedObject] * scaleFactor, surfaceInfo.SitPoint);
                    }
                }
            }
        }
        public override void RenderHandles(TransformGizmoPivotPoint transformPivotPoint)
        {
            Box targetWorldAABB = Box.FromObjectWorldAABB(_targetObjects);

            //Color[] axesColors = new Color[] { Handles.xAxisColor, Handles.xAxisColor, Handles.yAxisColor, Handles.yAxisColor, Handles.zAxisColor, Handles.zAxisColor };
            //Vector3[] axesDirs = new Vector3[] { Vector3.right, -Vector3.right, Vector3.up, -Vector3.up, Vector3.forward, -Vector3.forward };
            //float[] snapValues = new float[] { targetWorldAABB.Size.x, targetWorldAABB.Size.x, targetWorldAABB.Size.y, targetWorldAABB.Size.y, targetWorldAABB.Size.z, targetWorldAABB.Size.z };

            Color[]   axesColors = new Color[] { Handles.xAxisColor, Handles.yAxisColor, Handles.zAxisColor };
            Vector3[] axesDirs   = new Vector3[] { Vector3.right, Vector3.up, Vector3.forward };
            float[]   snapValues = new float[] { targetWorldAABB.Size.x *_snapSteps[0], targetWorldAABB.Size.y *_snapSteps[1], targetWorldAABB.Size.z *_snapSteps[2] };

            Vector3 oldPosition = WorldPosition;

            for (int axisIndex = 0; axisIndex < 3; ++axisIndex)
            {
                Handles.color = axesColors[axisIndex];
                Vector3 newGizmoPosition = Handles.Slider(oldPosition, axesDirs[axisIndex], HandleUtility.GetHandleSize(oldPosition), Handles.ArrowHandleCap, snapValues[axisIndex]);
                if (newGizmoPosition != oldPosition)
                {
                    Vector3 moveOffset = (newGizmoPosition - oldPosition);
                    WorldPosition = newGizmoPosition;
                    oldPosition   = newGizmoPosition;

                    if (Event.current.control)
                    {
                        float absNumGroups   = Mathf.Abs(moveOffset[axisIndex] / snapValues[axisIndex]);
                        float absFractional  = absNumGroups - (int)absNumGroups;
                        int   numCloneGroups = Mathf.FloorToInt(absNumGroups);
                        if (Mathf.Abs(absFractional - 1.0f) < 1e-4f)
                        {
                            ++numCloneGroups;
                        }

                        for (int cloneGroupIndex = 0; cloneGroupIndex < numCloneGroups; ++cloneGroupIndex)
                        {
                            var clonedRoots = Octave3DWorldBuilder.ActiveInstance.GetRoots(ObjectActions.Duplicate(_targetObjects));
                            ObjectHierarchyRootsWerePlacedInSceneMessage.SendToInterestedListeners(clonedRoots, ObjectHierarchyRootsWerePlacedInSceneMessage.PlacementType.Selection);
                            ObjectHierarchyRootsWerePlacedInSceneMessage.SendToInterestedListeners(ObjectSelection.Get().Mirror.MirrorGameObjectHierarchies(clonedRoots), ObjectHierarchyRootsWerePlacedInSceneMessage.PlacementType.Selection);

                            if (cloneGroupIndex != 0)
                            {
                                Vector3 offset = axesDirs[axisIndex] * snapValues[axisIndex] * cloneGroupIndex *Mathf.Sign(moveOffset[axisIndex]);

                                foreach (var root in clonedRoots)
                                {
                                    root.transform.position += offset;
                                }
                            }
                        }
                    }

                    GameObjectExtensions.RecordObjectTransformsForUndo(_targetObjects);
                    var targetParents = GameObjectExtensions.GetParents(_targetObjects);
                    foreach (var parent in targetParents)
                    {
                        parent.transform.position += moveOffset;
                    }
                }
            }

            Handles.BeginGUI();
            GUI.BeginGroup(new Rect(0.0f, -15.0f, 200.0f, 200.0f));

            var content = new GUIContent();

            string[] snapStepLabels = new string[] { "Snap step X", "Snap step Y", "Snap step Z" };

            for (int axisIndex = 0; axisIndex < 3; ++axisIndex)
            {
                EditorGUILayout.BeginHorizontal();
                content.text = snapStepLabels[axisIndex];
                EditorGUILayout.LabelField(content, GUILayout.Width(80.0f));
                int newInt = EditorGUILayout.IntField("", _snapSteps[axisIndex], GUILayout.Width(50.0f));
                if (newInt != _snapSteps[axisIndex])
                {
                    UndoEx.RecordForToolAction(this);
                    SetSnapStep(axisIndex, newInt);
                }
                EditorGUILayout.EndHorizontal();
            }

            GUI.EndGroup();
            Handles.EndGUI();
        }