Files
sbox-public/game/addons/tools/Code/Scene/ObjectTool/RotationEditorTool.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

120 lines
3.2 KiB
C#

using Sandbox;
using System.Reflection.PortableExecutable;
namespace Editor;
/// <summary>
/// Rotate selected GameObjects.<br/> <br/>
/// <b>Ctrl</b> - toggle snap to grid
/// </summary>
[Title( "Rotate" )]
[Icon( "360" )]
[Alias( "tools.rotate-tool" )]
[Group( "1" )]
[Order( 1 )]
public class RotationEditorTool : EditorTool
{
Dictionary<GameObject, Transform> startPoints = new Dictionary<GameObject, Transform>();
Angles moveDelta;
Vector3 handlePosition;
Rotation handleRotation;
IDisposable undoScope;
public override void OnUpdate()
{
var nonSceneGos = Selection.OfType<GameObject>().Where( go => go.GetType() != typeof( Sandbox.Scene ) );
if ( nonSceneGos.Count() == 0 ) return;
var bbox = BBox.FromPoints( nonSceneGos.Select( x => x.WorldPosition ) );
if ( !Gizmo.Pressed.Any && Gizmo.HasMouseFocus )
{
startPoints.Clear();
moveDelta = Rotation.Identity;
handleRotation = nonSceneGos.FirstOrDefault().WorldRotation;
handlePosition = bbox.Center;
undoScope?.Dispose();
undoScope = null;
}
var basis = Gizmo.Settings.GlobalSpace ? Rotation.Identity : handleRotation;
// Stop updating the handle position if we're dragging
var origin = Gizmo.Pressed.Any ? handlePosition : bbox.Center;
using ( Gizmo.Scope( "Tool", new Transform( origin, basis ) ) )
{
Gizmo.Hitbox.DepthBias = 0.01f;
if ( Gizmo.Control.Rotate( "rotation", out var angleDelta ) )
{
StartDrag( nonSceneGos );
moveDelta += angleDelta;
var snapped = Gizmo.Snap( moveDelta, angleDelta );
foreach ( var entry in startPoints )
{
var rot = basis * snapped * basis.Inverse;
var position = entry.Value.Position - handlePosition;
position *= rot;
position += handlePosition;
rot *= entry.Value.Rotation;
var scale = entry.Value.Scale;
var oldPosition = entry.Key.LocalPosition;
entry.Key.BreakProceduralBone();
entry.Key.WorldTransform = new Transform( position, rot, scale );
var newPosition = entry.Key.LocalPosition;
// Only dispatch position event if it actually changed
if ( !newPosition.AlmostEqual( oldPosition ) )
{
entry.Key.DispatchEdited( nameof( GameObject.LocalPosition ) );
}
entry.Key.DispatchEdited( nameof( GameObject.LocalRotation ) );
}
}
}
}
private void StartDrag( IEnumerable<GameObject> selectedGos )
{
if ( startPoints.Any() ) return;
if ( Gizmo.IsShiftPressed )
{
undoScope ??= SceneEditorSession.Active.UndoScope( "Duplicate Object(s)" ).WithGameObjectCreations().Push();
DuplicateSelection();
}
else
{
undoScope ??= SceneEditorSession.Active.UndoScope( "Transform Object(s)" ).WithGameObjectChanges( selectedGos, GameObjectUndoFlags.Properties ).Push();
selectedGos.DispatchPreEdited( nameof( GameObject.LocalPosition ) );
selectedGos.DispatchPreEdited( nameof( GameObject.LocalRotation ) );
}
foreach ( var entry in selectedGos )
{
startPoints[entry] = entry.WorldTransform;
}
}
[Shortcut( "tools.rotate-tool", "e", typeof( SceneViewportWidget ) )]
public static void ActivateSubTool()
{
if ( !(EditorToolManager.CurrentModeName == nameof( ObjectEditorTool ) || EditorToolManager.CurrentModeName == "object") ) return;
EditorToolManager.SetSubTool( nameof( RotationEditorTool ) );
}
}