// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
using System;
using System.Collections.Generic;
using FlaxEditor.Modules;
using FlaxEditor.SceneGraph;
using FlaxEngine;
namespace FlaxEditor
{
///
/// Implementation of used to transform a selection of .
/// The same logic could be achieved using but it would be slower.
/// Since we use this kind of action very ofter (for operations) it's better to provide faster implementation.
///
///
[Serializable]
public sealed class TransformObjectsAction : UndoActionBase, ISceneEditAction
{
///
/// The undo data.
///
[Serializable]
public struct DataStorage
{
///
/// The scene of the selected objects.
///
public Scene Scene;
///
/// The selection pool.
///
public SceneGraphNode[] Selection;
///
/// The 'before' state.
///
public Transform[] Before;
///
/// The 'after' state.
///
public Transform[] After;
///
/// The cached bounding box that contains all selected items in 'before' state.
///
public BoundingBox BeforeBounds;
///
/// The cached bounding box that contains all selected items in 'after' state.
///
public BoundingBox AfterBounds;
///
/// True if navigation system has been modified during editing the selected objects (navmesh auto-rebuild is required).
///
public bool NavigationDirty;
}
internal TransformObjectsAction(List selection, List before, ref BoundingBox boundsBefore, bool navigationDirty)
{
var after = Utilities.Utils.GetTransformsAndBounds(selection, out var afterBounds);
// TODO: support moving objects from more than one scene
var scene = selection[0].ParentScene?.Scene;
var data = new DataStorage
{
Scene = scene,
Selection = selection.ToArray(),
After = after,
Before = before.ToArray(),
BeforeBounds = boundsBefore,
AfterBounds = afterBounds,
NavigationDirty = navigationDirty,
};
Data = data;
InvalidateBounds(ref data);
}
///
public override string ActionString => "Transform object(s)";
///
public override void Do()
{
var data = Data;
for (int i = 0; i < data.Selection.Length; i++)
{
data.Selection[i].Transform = data.After[i];
}
InvalidateBounds(ref data);
}
///
public override void Undo()
{
var data = Data;
for (int i = 0; i < data.Selection.Length; i++)
{
data.Selection[i].Transform = data.Before[i];
}
InvalidateBounds(ref data);
}
private void InvalidateBounds(ref DataStorage data)
{
if (!data.NavigationDirty)
return;
var editor = Editor.Instance;
bool isPlayMode = editor.StateMachine.IsPlayMode;
var options = editor.Options.Options;
// Auto NavMesh rebuild
if (!isPlayMode && options.General.AutoRebuildNavMesh && data.Scene != null)
{
// Handle simple case where objects were moved just a little and use one navmesh build request to improve performance
if (data.BeforeBounds.Intersects(ref data.AfterBounds))
{
Navigation.BuildNavMesh(data.Scene, BoundingBox.Merge(data.BeforeBounds, data.AfterBounds), options.General.AutoRebuildNavMeshTimeoutMs);
}
else
{
Navigation.BuildNavMesh(data.Scene, data.BeforeBounds, options.General.AutoRebuildNavMeshTimeoutMs);
Navigation.BuildNavMesh(data.Scene, data.AfterBounds, options.General.AutoRebuildNavMeshTimeoutMs);
}
}
}
void ISceneEditAction.MarkSceneEdited(SceneModule sceneModule)
{
var data = Data;
for (int i = 0; i < data.Selection.Length; i++)
{
sceneModule.MarkSceneEdited(data.Selection[i].ParentScene);
}
}
}
}