namespace Editor;
///
/// Move selected Gameobjects.
/// Ctrl - toggle snap to grid
/// Shift - duplicate selection
///
[Title( "Move/Position" )]
[Icon( "control_camera" )]
[Alias( "tools.position-tool" )]
[Group( "1" )]
[Order( 0 )]
public class PositionEditorTool : EditorTool
{
readonly Dictionary startPoints = [];
readonly HashSet bodies = [];
Vector3 moveDelta;
Vector3 handlePosition;
IDisposable undoScope;
public override void OnDisabled()
{
base.OnDisabled();
ClearBodies();
}
private void ClearBodies()
{
foreach ( var body in bodies )
{
if ( !body.IsValid() )
continue;
body.SetTargetTransform( null );
}
bodies.Clear();
}
public override void OnUpdate()
{
var nonSceneGos = Selection.OfType().Where( go => go.GetType() != typeof( Sandbox.Scene ) );
if ( nonSceneGos.Count() == 0 ) return;
var bbox = BBox.FromPoints( nonSceneGos.Select( x => x.WorldPosition ) );
var handleRotation = Gizmo.Settings.GlobalSpace ? Rotation.Identity : nonSceneGos.FirstOrDefault().WorldRotation;
if ( !Gizmo.Pressed.Any && Gizmo.HasMouseFocus )
{
ClearBodies();
startPoints.Clear();
moveDelta = default;
handlePosition = bbox.Center;
undoScope?.Dispose();
undoScope = null;
}
using ( Gizmo.Scope( "Tool", new Transform( bbox.Center ) ) )
{
Gizmo.Hitbox.DepthBias = 0.01f;
if ( Gizmo.Control.Position( "position", Vector3.Zero, out var delta, handleRotation ) )
{
moveDelta += delta;
StartDrag( nonSceneGos );
var offset = (moveDelta + handlePosition) * handleRotation.Inverse;
offset = Gizmo.Snap( offset, moveDelta * handleRotation.Inverse );
offset *= handleRotation;
offset -= handlePosition;
foreach ( var entry in startPoints )
{
OnMoveObject( entry.Key, entry.Value.Add( offset, true ) );
}
}
}
}
private void StartDrag( IEnumerable selectedGos )
{
if ( startPoints.Count != 0 )
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 ) );
}
foreach ( var entry in selectedGos )
{
startPoints[entry] = entry.WorldTransform;
}
}
private void OnMoveObject( GameObject gameObject, Transform transform )
{
if ( !gameObject.IsValid() )
return;
if ( !Scene.IsEditor )
{
var rb = gameObject.GetComponent();
if ( rb.IsValid() && rb.MotionEnabled )
{
bodies.Add( rb );
rb.SetTargetTransform( transform );
return;
}
}
gameObject.BreakProceduralBone();
gameObject.WorldTransform = transform;
gameObject.DispatchEdited( nameof( GameObject.LocalPosition ) );
}
[Shortcut( "tools.position-tool", "w", typeof( SceneViewportWidget ) )]
public static void ActivateSubTool()
{
if ( !(EditorToolManager.CurrentModeName == nameof( ObjectEditorTool ) || EditorToolManager.CurrentModeName == "object") ) return;
EditorToolManager.SetSubTool( nameof( PositionEditorTool ) );
}
}