Merge remote-tracking branch 'origin/master' into 1.8
This commit is contained in:
@@ -280,17 +280,25 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
const Char* gradlew = TEXT("gradlew");
|
||||
#endif
|
||||
#if PLATFORM_LINUX
|
||||
Platform::RunProcess(String::Format(TEXT("chmod +x \"{0}/gradlew\""), data.OriginalOutputPath), data.OriginalOutputPath, Dictionary<String, String>(), true);
|
||||
{
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = String::Format(TEXT("chmod +x \"{0}/gradlew\""), data.OriginalOutputPath);
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
procSettings.HiddenWindow = true;
|
||||
Platform::CreateProcess(procSettings);
|
||||
}
|
||||
#endif
|
||||
const bool distributionPackage = buildSettings->ForDistribution;
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
const int32 result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result));
|
||||
return true;
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = String::Format(TEXT("\"{0}\" {1}"), data.OriginalOutputPath / gradlew, distributionPackage ? TEXT("assemble") : TEXT("assembleDebug"));
|
||||
procSettings.WorkingDirectory = data.OriginalOutputPath;
|
||||
const int32 result = Platform::CreateProcess(procSettings);
|
||||
if (result != 0)
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to build Gradle project into package (result code: {0}). See log for more info."), result));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Copy result package
|
||||
|
||||
@@ -37,41 +37,32 @@ public class MissingScriptEditor : GenericEditor
|
||||
Parent = _dropPanel,
|
||||
Height = 64,
|
||||
};
|
||||
|
||||
_replaceScriptButton = new Button
|
||||
{
|
||||
Text = "Replace Script",
|
||||
TooltipText = "Replaces the missing script with a given script type",
|
||||
AnchorPreset = AnchorPresets.TopCenter,
|
||||
Width = 240,
|
||||
Height = 24,
|
||||
X = -120,
|
||||
Y = 0,
|
||||
Bounds = new Rectangle(-120, 0, 240, 24),
|
||||
Parent = replaceScriptPanel,
|
||||
};
|
||||
_replaceScriptButton.Clicked += OnReplaceScriptButtonClicked;
|
||||
|
||||
var replaceAllLabel = new Label
|
||||
{
|
||||
Text = "Replace all matching missing scripts",
|
||||
TooltipText = "Whether or not to apply this script change to all scripts missing the same type.",
|
||||
AnchorPreset = AnchorPresets.BottomCenter,
|
||||
Y = -34,
|
||||
Y = -38,
|
||||
Parent = replaceScriptPanel,
|
||||
};
|
||||
replaceAllLabel.X -= FlaxEngine.GUI.Style.Current.FontSmall.MeasureText(replaceAllLabel.Text).X;
|
||||
|
||||
_shouldReplaceAllCheckbox = new CheckBox
|
||||
{
|
||||
TooltipText = replaceAllLabel.TooltipText,
|
||||
AnchorPreset = AnchorPresets.BottomCenter,
|
||||
Y = -34,
|
||||
Y = -38,
|
||||
Parent = replaceScriptPanel,
|
||||
};
|
||||
|
||||
float centerDifference = (_shouldReplaceAllCheckbox.Right - replaceAllLabel.Left) / 2;
|
||||
replaceAllLabel.X += centerDifference;
|
||||
_shouldReplaceAllCheckbox.X += centerDifference;
|
||||
_shouldReplaceAllCheckbox.X -= _replaceScriptButton.Width * 0.5f + 0.5f;
|
||||
replaceAllLabel.X -= 52;
|
||||
|
||||
base.Initialize(layout);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
public class DockPanelProxy : ContainerControl
|
||||
{
|
||||
private DockPanel _panel;
|
||||
private double _dragEnterTime = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The is mouse down flag (left button).
|
||||
@@ -256,8 +257,8 @@ namespace FlaxEditor.GUI.Docking
|
||||
else
|
||||
{
|
||||
tabColor = style.BackgroundHighlighted;
|
||||
Render2D.DrawLine(tabRect.BottomLeft - new Float2(0 , 1), tabRect.UpperLeft, tabColor);
|
||||
Render2D.DrawLine(tabRect.BottomRight - new Float2(0 , 1), tabRect.UpperRight, tabColor);
|
||||
Render2D.DrawLine(tabRect.BottomLeft - new Float2(0, 1), tabRect.UpperLeft, tabColor);
|
||||
Render2D.DrawLine(tabRect.BottomRight - new Float2(0, 1), tabRect.UpperRight, tabColor);
|
||||
}
|
||||
|
||||
if (tab.Icon.IsValid)
|
||||
@@ -477,11 +478,7 @@ namespace FlaxEditor.GUI.Docking
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
if (TrySelectTabUnderLocation(ref location))
|
||||
return DragDropEffect.Move;
|
||||
|
||||
return DragDropEffect.None;
|
||||
return TrySelectTabUnderLocation(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -490,11 +487,15 @@ namespace FlaxEditor.GUI.Docking
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
return TrySelectTabUnderLocation(ref location);
|
||||
}
|
||||
|
||||
if (TrySelectTabUnderLocation(ref location))
|
||||
return DragDropEffect.Move;
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragEnterTime = -1;
|
||||
|
||||
return DragDropEffect.None;
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -503,17 +504,25 @@ namespace FlaxEditor.GUI.Docking
|
||||
rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight);
|
||||
}
|
||||
|
||||
private bool TrySelectTabUnderLocation(ref Float2 location)
|
||||
private DragDropEffect TrySelectTabUnderLocation(ref Float2 location)
|
||||
{
|
||||
var tab = GetTabAtPos(location, out _);
|
||||
if (tab != null)
|
||||
{
|
||||
// Auto-select tab only if drag takes some time
|
||||
var time = Platform.TimeSeconds;
|
||||
if (_dragEnterTime < 0)
|
||||
_dragEnterTime = time;
|
||||
if (time - _dragEnterTime < 0.3f)
|
||||
return DragDropEffect.Link;
|
||||
_dragEnterTime = -1;
|
||||
|
||||
_panel.SelectTab(tab);
|
||||
Update(0); // Fake update
|
||||
return true;
|
||||
return DragDropEffect.Move;
|
||||
}
|
||||
|
||||
return false;
|
||||
_dragEnterTime = -1;
|
||||
return DragDropEffect.None;
|
||||
}
|
||||
|
||||
private void ShowContextMenu(DockWindow tab, ref Float2 location)
|
||||
|
||||
@@ -106,5 +106,11 @@ namespace FlaxEditor.Gizmo
|
||||
/// </summary>
|
||||
/// <param name="nodes">The nodes to select</param>
|
||||
void Select(List<SceneGraph.SceneGraphNode> nodes);
|
||||
|
||||
/// <summary>
|
||||
/// Spawns the actor in the viewport hierarchy.
|
||||
/// </summary>
|
||||
/// <param name="actor">The new actor to spawn.</param>
|
||||
void Spawn(Actor actor);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -242,7 +242,6 @@ namespace FlaxEditor.Modules
|
||||
/// <param name="additive">True if don't close opened scenes and just add new scene to them, otherwise will release current scenes and load single one.</param>
|
||||
public void OpenScene(Guid sceneId, bool additive = false)
|
||||
{
|
||||
// Check if cannot change scene now
|
||||
if (!Editor.StateMachine.CurrentState.CanChangeScene)
|
||||
return;
|
||||
|
||||
@@ -271,11 +270,21 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public void ReloadScenes()
|
||||
{
|
||||
if (!Editor.StateMachine.CurrentState.CanChangeScene)
|
||||
return;
|
||||
|
||||
if (!Editor.IsPlayMode)
|
||||
{
|
||||
if (CheckSaveBeforeClose())
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload scenes
|
||||
foreach (var scene in Level.Scenes)
|
||||
{
|
||||
var sceneId = scene.ID;
|
||||
if (!Level.UnloadScene(scene))
|
||||
Level.LoadScene(sceneId);
|
||||
Level.UnloadScene(scene);
|
||||
Level.LoadScene(sceneId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,7 +294,6 @@ namespace FlaxEditor.Modules
|
||||
/// <param name="scene">The scene.</param>
|
||||
public void CloseScene(Scene scene)
|
||||
{
|
||||
// Check if cannot change scene now
|
||||
if (!Editor.StateMachine.CurrentState.CanChangeScene)
|
||||
return;
|
||||
|
||||
@@ -309,7 +317,6 @@ namespace FlaxEditor.Modules
|
||||
/// </summary>
|
||||
public void CloseAllScenes()
|
||||
{
|
||||
// Check if cannot change scene now
|
||||
if (!Editor.StateMachine.CurrentState.CanChangeScene)
|
||||
return;
|
||||
|
||||
@@ -334,7 +341,6 @@ namespace FlaxEditor.Modules
|
||||
/// <param name="scene">The scene to not close.</param>
|
||||
public void CloseAllScenesExcept(Scene scene)
|
||||
{
|
||||
// Check if cannot change scene now
|
||||
if (!Editor.StateMachine.CurrentState.CanChangeScene)
|
||||
return;
|
||||
|
||||
|
||||
@@ -78,7 +78,10 @@ void VisualStudioCodeEditor::FindEditors(Array<CodeEditor*>* output)
|
||||
|
||||
// Detect Flatpak installations
|
||||
{
|
||||
if (Platform::RunProcess(TEXT("/bin/bash -c \"flatpak list --app --columns=application | grep com.visualstudio.code -c\""), String::Empty) == 0)
|
||||
CreateProcessSettings procSettings;
|
||||
procSettings.FileName = TEXT("/bin/bash -c \"flatpak list --app --columns=application | grep com.visualstudio.code -c\"");
|
||||
procSettings.HiddenWindow = true;
|
||||
if (Platform::CreateProcess(procSettings) == 0)
|
||||
{
|
||||
const String runPath(TEXT("flatpak run com.visualstudio.code"));
|
||||
output->Add(New<VisualStudioCodeEditor>(runPath, false));
|
||||
|
||||
@@ -1084,7 +1084,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
},
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(void), 0),
|
||||
NodeElementArchetype.Factory.Input(0, string.Empty, false, typeof(void), 0),
|
||||
NodeElementArchetype.Factory.Input(1, string.Empty, true, ScriptType.Null, 1, 1),
|
||||
NodeElementArchetype.Factory.Output(0, string.Empty, typeof(void), 2, true),
|
||||
NodeElementArchetype.Factory.ComboBox(2 + 20, 0, 116)
|
||||
|
||||
@@ -86,6 +86,9 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public abstract void Select(List<SceneGraphNode> nodes);
|
||||
|
||||
/// <inheritdoc />
|
||||
public abstract void Spawn(Actor actor);
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
||||
|
||||
|
||||
@@ -5,9 +5,7 @@ using System.Collections.Generic;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Modes;
|
||||
@@ -37,28 +35,8 @@ namespace FlaxEditor.Viewport
|
||||
private readonly ViewportWidgetButton _rotateSnapping;
|
||||
private readonly ViewportWidgetButton _scaleSnapping;
|
||||
|
||||
private readonly DragAssets<DragDropEventArgs> _dragAssets;
|
||||
private readonly DragActorType<DragDropEventArgs> _dragActorType = new DragActorType<DragDropEventArgs>(ValidateDragActorType);
|
||||
|
||||
private SelectionOutline _customSelectionOutline;
|
||||
|
||||
/// <summary>
|
||||
/// The custom drag drop event arguments.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Drag.DragEventArgs" />
|
||||
public class DragDropEventArgs : DragEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The hit.
|
||||
/// </summary>
|
||||
public SceneGraphNode Hit;
|
||||
|
||||
/// <summary>
|
||||
/// The hit location.
|
||||
/// </summary>
|
||||
public Vector3 HitLocation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The editor sprites rendering effect.
|
||||
/// </summary>
|
||||
@@ -137,15 +115,12 @@ namespace FlaxEditor.Viewport
|
||||
private bool _lockedFocus;
|
||||
private double _lockedFocusOffset;
|
||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||
private StaticModel _previewStaticModel;
|
||||
private int _previewModelEntryIndex;
|
||||
private BrushSurface _previewBrushSurface;
|
||||
private EditorSpritesRenderer _editorSpritesRenderer;
|
||||
|
||||
/// <summary>
|
||||
/// Drag and drop handlers
|
||||
/// </summary>
|
||||
public readonly DragHandlers DragHandlers = new DragHandlers();
|
||||
public readonly ViewportDragHandlers DragHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// The transform gizmo.
|
||||
@@ -219,7 +194,7 @@ namespace FlaxEditor.Viewport
|
||||
: base(Object.New<SceneRenderTask>(), editor.Undo, editor.Scene.Root)
|
||||
{
|
||||
_editor = editor;
|
||||
_dragAssets = new DragAssets<DragDropEventArgs>(ValidateDragItem);
|
||||
DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType);
|
||||
var inputOptions = editor.Options.Options.Input;
|
||||
|
||||
// Prepare rendering task
|
||||
@@ -408,9 +383,6 @@ namespace FlaxEditor.Viewport
|
||||
ViewWidgetButtonMenu.AddSeparator();
|
||||
ViewWidgetButtonMenu.AddButton("Create camera here", CreateCameraAtView);
|
||||
|
||||
DragHandlers.Add(_dragActorType);
|
||||
DragHandlers.Add(_dragAssets);
|
||||
|
||||
// Init gizmo modes
|
||||
{
|
||||
// Add default modes used by the editor
|
||||
@@ -430,7 +402,11 @@ namespace FlaxEditor.Viewport
|
||||
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
||||
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
||||
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
||||
InputActions.Add(options => options.ToggleTransformSpace, () => { OnTransformSpaceToggle(transformSpaceToggle); transformSpaceToggle.Checked = !transformSpaceToggle.Checked; });
|
||||
InputActions.Add(options => options.ToggleTransformSpace, () =>
|
||||
{
|
||||
OnTransformSpaceToggle(transformSpaceToggle);
|
||||
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
|
||||
});
|
||||
InputActions.Add(options => options.LockFocusSelection, LockFocusSelection);
|
||||
InputActions.Add(options => options.FocusSelection, FocusSelection);
|
||||
InputActions.Add(options => options.RotateSelection, RotateSelection);
|
||||
@@ -530,20 +506,9 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private void OnCollectDrawCalls(ref RenderContext renderContext)
|
||||
{
|
||||
if (_previewStaticModel)
|
||||
{
|
||||
_debugDrawData.HighlightModel(_previewStaticModel, _previewModelEntryIndex);
|
||||
}
|
||||
if (_previewBrushSurface.Brush != null)
|
||||
{
|
||||
_debugDrawData.HighlightBrushSurface(_previewBrushSurface);
|
||||
}
|
||||
|
||||
DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext);
|
||||
if (ShowNavigation)
|
||||
{
|
||||
Editor.Internal_DrawNavMesh();
|
||||
}
|
||||
|
||||
_debugDrawData.OnDraw(ref renderContext);
|
||||
}
|
||||
|
||||
@@ -942,78 +907,14 @@ namespace FlaxEditor.Viewport
|
||||
base.OnLeftMouseButtonUp();
|
||||
}
|
||||
|
||||
private void GetHitLocation(ref Float2 location, out SceneGraphNode hit, out Vector3 hitLocation, out Vector3 hitNormal)
|
||||
{
|
||||
// Get mouse ray and try to hit any object
|
||||
var ray = ConvertMouseToRay(ref location);
|
||||
var view = new Ray(ViewPosition, ViewDirection);
|
||||
var gridPlane = new Plane(Vector3.Zero, Vector3.Up);
|
||||
var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives;
|
||||
hit = Editor.Instance.Scene.Root.RayCast(ref ray, ref view, out var closest, out var normal, flags);
|
||||
if (hit != null)
|
||||
{
|
||||
// Use hit location
|
||||
hitLocation = ray.Position + ray.Direction * closest;
|
||||
hitNormal = normal;
|
||||
}
|
||||
else if (Grid.Enabled && CollisionsHelper.RayIntersectsPlane(ref ray, ref gridPlane, out closest) && closest < 4000.0f)
|
||||
{
|
||||
// Use grid location
|
||||
hitLocation = ray.Position + ray.Direction * closest;
|
||||
hitNormal = Vector3.Up;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use area in front of the viewport
|
||||
hitLocation = ViewPosition + ViewDirection * 100;
|
||||
hitNormal = Vector3.Up;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetDragEffects(ref Float2 location)
|
||||
{
|
||||
if (_dragAssets.HasValidDrag && _dragAssets.Objects[0].IsOfType<MaterialBase>())
|
||||
{
|
||||
GetHitLocation(ref location, out var hit, out _, out _);
|
||||
ClearDragEffects();
|
||||
var material = FlaxEngine.Content.LoadAsync<MaterialBase>(_dragAssets.Objects[0].ID);
|
||||
if (material.IsDecal)
|
||||
return;
|
||||
|
||||
if (hit is StaticModelNode staticModelNode)
|
||||
{
|
||||
_previewStaticModel = (StaticModel)staticModelNode.Actor;
|
||||
var ray = ConvertMouseToRay(ref location);
|
||||
_previewStaticModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex);
|
||||
}
|
||||
else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode)
|
||||
{
|
||||
_previewBrushSurface = brushSurfaceNode.Surface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearDragEffects()
|
||||
{
|
||||
_previewStaticModel = null;
|
||||
_previewModelEntryIndex = -1;
|
||||
_previewBrushSurface = new BrushSurface();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
ClearDragEffects();
|
||||
|
||||
DragHandlers.ClearDragEffects();
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
result = DragHandlers.OnDragEnter(data);
|
||||
|
||||
SetDragEffects(ref location);
|
||||
|
||||
return result;
|
||||
return DragHandlers.DragEnter(ref location, data);
|
||||
}
|
||||
|
||||
private bool ValidateDragItem(ContentItem contentItem)
|
||||
@@ -1042,167 +943,29 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
ClearDragEffects();
|
||||
|
||||
DragHandlers.ClearDragEffects();
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
SetDragEffects(ref location);
|
||||
|
||||
return DragHandlers.Effect;
|
||||
return DragHandlers.DragEnter(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
ClearDragEffects();
|
||||
|
||||
DragHandlers.ClearDragEffects();
|
||||
DragHandlers.OnDragLeave();
|
||||
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
private Vector3 PostProcessSpawnedActorLocation(Actor actor, ref Vector3 hitLocation)
|
||||
{
|
||||
// Refresh actor position to ensure that cached bounds are valid
|
||||
actor.Position = Vector3.One;
|
||||
actor.Position = Vector3.Zero;
|
||||
|
||||
// Place the object
|
||||
//var location = hitLocation - (box.Size.Length * 0.5f) * ViewDirection;
|
||||
var editorBounds = actor.EditorBoxChildren;
|
||||
var bottomToCenter = actor.Position.Y - editorBounds.Minimum.Y;
|
||||
var location = hitLocation + new Vector3(0, bottomToCenter, 0);
|
||||
|
||||
// Apply grid snapping if enabled
|
||||
if (UseSnapping || TransformGizmo.TranslationSnapEnable)
|
||||
{
|
||||
float snapValue = TransformGizmo.TranslationSnapValue;
|
||||
location = new Vector3(
|
||||
(int)(location.X / snapValue) * snapValue,
|
||||
(int)(location.Y / snapValue) * snapValue,
|
||||
(int)(location.Z / snapValue) * snapValue);
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
private void Spawn(Actor actor, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
{
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
var parent = actor.Parent ?? Level.GetScene(0);
|
||||
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
Focus();
|
||||
}
|
||||
|
||||
private void Spawn(AssetItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
{
|
||||
if (item.IsOfType<MaterialBase>())
|
||||
{
|
||||
var material = FlaxEngine.Content.LoadAsync<MaterialBase>(item.ID);
|
||||
if (material && !material.WaitForLoaded(100) && material.IsDecal)
|
||||
{
|
||||
var actor = new Decal
|
||||
{
|
||||
Material = material,
|
||||
LocalOrientation = RootNode.RaycastNormalRotation(ref hitNormal),
|
||||
Name = item.ShortName
|
||||
};
|
||||
DebugDraw.DrawWireArrow(PostProcessSpawnedActorLocation(actor, ref hitNormal), actor.LocalOrientation, 1.0f, Color.Red, 1000000);
|
||||
Spawn(actor, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
else if (hit is StaticModelNode staticModelNode)
|
||||
{
|
||||
var staticModel = (StaticModel)staticModelNode.Actor;
|
||||
var ray = ConvertMouseToRay(ref location);
|
||||
if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex))
|
||||
{
|
||||
using (new UndoBlock(Undo, staticModel, "Change material"))
|
||||
staticModel.SetMaterial(entryIndex, material);
|
||||
}
|
||||
}
|
||||
else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode)
|
||||
{
|
||||
using (new UndoBlock(Undo, brushSurfaceNode.Brush, "Change material"))
|
||||
{
|
||||
var surface = brushSurfaceNode.Surface;
|
||||
surface.Material = material;
|
||||
brushSurfaceNode.Surface = surface;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (item.IsOfType<SceneAsset>())
|
||||
{
|
||||
Editor.Instance.Scene.OpenScene(item.ID, true);
|
||||
return;
|
||||
}
|
||||
{
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Spawn(actor, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
}
|
||||
|
||||
private void Spawn(ScriptType item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
{
|
||||
var actor = item.CreateInstance() as Actor;
|
||||
if (actor == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
|
||||
return;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Spawn(actor, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
ClearDragEffects();
|
||||
|
||||
DragHandlers.ClearDragEffects();
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
// Check if drag sth
|
||||
Vector3 hitLocation = ViewPosition, hitNormal = -ViewDirection;
|
||||
SceneGraphNode hit = null;
|
||||
if (DragHandlers.HasValidDrag)
|
||||
{
|
||||
GetHitLocation(ref location, out hit, out hitLocation, out hitNormal);
|
||||
}
|
||||
|
||||
// Drag assets
|
||||
if (_dragAssets.HasValidDrag)
|
||||
{
|
||||
result = _dragAssets.Effect;
|
||||
|
||||
// Process items
|
||||
for (int i = 0; i < _dragAssets.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragAssets.Objects[i];
|
||||
Spawn(item, hit, ref location, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
}
|
||||
// Drag actor type
|
||||
else if (_dragActorType.HasValidDrag)
|
||||
{
|
||||
result = _dragActorType.Effect;
|
||||
|
||||
// Process items
|
||||
for (int i = 0; i < _dragActorType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragActorType.Objects[i];
|
||||
Spawn(item, hit, ref location, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
}
|
||||
|
||||
DragHandlers.OnDragDrop(new DragDropEventArgs { Hit = hit, HitLocation = hitLocation });
|
||||
|
||||
return result;
|
||||
return DragHandlers.DragDrop(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -1211,6 +974,14 @@ namespace FlaxEditor.Viewport
|
||||
_editor.SceneEditing.Select(nodes);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Spawn(Actor actor)
|
||||
{
|
||||
var parent = actor.Parent ?? Level.GetScene(0);
|
||||
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parent.GetChild(x) == null);
|
||||
Editor.Instance.SceneEditing.Spawn(actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDestroy()
|
||||
{
|
||||
|
||||
@@ -5,9 +5,7 @@ using System.Collections.Generic;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Viewport.Cameras;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
@@ -56,9 +54,11 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private readonly ViewportDebugDrawData _debugDrawData = new ViewportDebugDrawData(32);
|
||||
private PrefabSpritesRenderer _spritesRenderer;
|
||||
private readonly DragAssets _dragAssets;
|
||||
private readonly DragActorType _dragActorType = new DragActorType(ValidateDragActorType);
|
||||
private readonly DragHandlers _dragHandlers = new DragHandlers();
|
||||
|
||||
/// <summary>
|
||||
/// Drag and drop handlers
|
||||
/// </summary>
|
||||
public readonly ViewportDragHandlers DragHandlers;
|
||||
|
||||
/// <summary>
|
||||
/// The transform gizmo.
|
||||
@@ -81,7 +81,7 @@ namespace FlaxEditor.Viewport
|
||||
_window.SelectionChanged += OnSelectionChanged;
|
||||
Undo = window.Undo;
|
||||
ViewportCamera = new FPSCamera();
|
||||
_dragAssets = new DragAssets(ValidateDragItem);
|
||||
DragHandlers = new ViewportDragHandlers(this, this, ValidateDragItem, ValidateDragActorType);
|
||||
ShowDebugDraw = true;
|
||||
ShowEditorPrimitives = true;
|
||||
Gizmos = new GizmosCollection(this);
|
||||
@@ -228,14 +228,15 @@ namespace FlaxEditor.Viewport
|
||||
_gizmoModeScale.Toggled += OnGizmoModeToggle;
|
||||
gizmoMode.Parent = this;
|
||||
|
||||
_dragHandlers.Add(_dragActorType);
|
||||
_dragHandlers.Add(_dragAssets);
|
||||
|
||||
// Setup input actions
|
||||
InputActions.Add(options => options.TranslateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Translate);
|
||||
InputActions.Add(options => options.RotateMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Rotate);
|
||||
InputActions.Add(options => options.ScaleMode, () => TransformGizmo.ActiveMode = TransformGizmoBase.Mode.Scale);
|
||||
InputActions.Add(options => options.ToggleTransformSpace, () => { OnTransformSpaceToggle(transformSpaceToggle); transformSpaceToggle.Checked = !transformSpaceToggle.Checked; });
|
||||
InputActions.Add(options => options.ToggleTransformSpace, () =>
|
||||
{
|
||||
OnTransformSpaceToggle(transformSpaceToggle);
|
||||
transformSpaceToggle.Checked = !transformSpaceToggle.Checked;
|
||||
});
|
||||
InputActions.Add(options => options.FocusSelection, ShowSelectedActors);
|
||||
|
||||
SetUpdate(ref _update, OnUpdate);
|
||||
@@ -267,6 +268,7 @@ namespace FlaxEditor.Viewport
|
||||
|
||||
private void OnCollectDrawCalls(ref RenderContext renderContext)
|
||||
{
|
||||
DragHandlers.CollectDrawCalls(_debugDrawData, ref renderContext);
|
||||
_debugDrawData.OnDraw(ref renderContext);
|
||||
}
|
||||
|
||||
@@ -306,6 +308,7 @@ namespace FlaxEditor.Viewport
|
||||
var orient = ViewOrientation;
|
||||
((FPSCamera)ViewportCamera).ShowActors(TransformGizmo.SelectedParents, ref orient);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public EditorViewport Viewport => this;
|
||||
|
||||
@@ -354,6 +357,12 @@ namespace FlaxEditor.Viewport
|
||||
_window.Select(nodes);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Spawn(Actor actor)
|
||||
{
|
||||
_window.Spawn(actor);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool IsControllingMouse => Gizmos.Active?.IsControllingMouse ?? false;
|
||||
|
||||
@@ -669,11 +678,11 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
DragHandlers.ClearDragEffects();
|
||||
var result = base.OnDragEnter(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
return _dragHandlers.OnDragEnter(data);
|
||||
return DragHandlers.DragEnter(ref location, data);
|
||||
}
|
||||
|
||||
private bool ValidateDragItem(ContentItem contentItem)
|
||||
@@ -685,7 +694,6 @@ namespace FlaxEditor.Viewport
|
||||
if (assetItem.IsOfType<MaterialBase>())
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -697,86 +705,21 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
DragHandlers.ClearDragEffects();
|
||||
var result = base.OnDragMove(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
return _dragHandlers.Effect;
|
||||
return DragHandlers.DragEnter(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDragLeave()
|
||||
{
|
||||
_dragHandlers.OnDragLeave();
|
||||
|
||||
DragHandlers.ClearDragEffects();
|
||||
DragHandlers.OnDragLeave();
|
||||
base.OnDragLeave();
|
||||
}
|
||||
|
||||
private Vector3 PostProcessSpawnedActorLocation(Actor actor, ref Vector3 hitLocation)
|
||||
{
|
||||
// Place the object
|
||||
//var location = hitLocation - (box.Size.Length * 0.5f) * ViewDirection;
|
||||
var location = hitLocation;
|
||||
|
||||
// Apply grid snapping if enabled
|
||||
if (UseSnapping || TransformGizmo.TranslationSnapEnable)
|
||||
{
|
||||
float snapValue = TransformGizmo.TranslationSnapValue;
|
||||
location = new Vector3(
|
||||
(int)(location.X / snapValue) * snapValue,
|
||||
(int)(location.Y / snapValue) * snapValue,
|
||||
(int)(location.Z / snapValue) * snapValue);
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
private void Spawn(AssetItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation)
|
||||
{
|
||||
if (item is BinaryAssetItem binaryAssetItem)
|
||||
{
|
||||
if (typeof(MaterialBase).IsAssignableFrom(binaryAssetItem.Type))
|
||||
{
|
||||
if (hit is StaticModelNode staticModelNode)
|
||||
{
|
||||
var staticModel = (StaticModel)staticModelNode.Actor;
|
||||
var ray = ConvertMouseToRay(ref location);
|
||||
if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex))
|
||||
{
|
||||
var material = FlaxEngine.Content.LoadAsync<MaterialBase>(item.ID);
|
||||
using (new UndoBlock(Undo, staticModel, "Change material"))
|
||||
staticModel.SetMaterial(entryIndex, material);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Spawn(actor, ref hitLocation);
|
||||
}
|
||||
}
|
||||
|
||||
private void Spawn(Actor actor, ref Vector3 hitLocation)
|
||||
{
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
_window.Spawn(actor);
|
||||
Focus();
|
||||
}
|
||||
|
||||
private void Spawn(ScriptType item, SceneGraphNode hit, ref Vector3 hitLocation)
|
||||
{
|
||||
var actor = item.CreateInstance() as Actor;
|
||||
if (actor == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
|
||||
return;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Spawn(actor, ref hitLocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Focuses the viewport on the current selection of the gizmo.
|
||||
/// </summary>
|
||||
@@ -814,57 +757,11 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public override DragDropEffect OnDragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
DragHandlers.ClearDragEffects();
|
||||
var result = base.OnDragDrop(ref location, data);
|
||||
if (result != DragDropEffect.None)
|
||||
return result;
|
||||
|
||||
// Check if drag sth
|
||||
Vector3 hitLocation = ViewPosition;
|
||||
SceneGraphNode hit = null;
|
||||
if (_dragHandlers.HasValidDrag)
|
||||
{
|
||||
// Get mouse ray and try to hit any object
|
||||
var ray = ConvertMouseToRay(ref location);
|
||||
var view = new Ray(ViewPosition, ViewDirection);
|
||||
hit = _window.Graph.Root.RayCast(ref ray, ref view, out var closest, SceneGraphNode.RayCastData.FlagTypes.SkipColliders);
|
||||
if (hit != null)
|
||||
{
|
||||
// Use hit location
|
||||
hitLocation = ray.Position + ray.Direction * closest;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use area in front of the viewport
|
||||
hitLocation = ViewPosition + ViewDirection * 100;
|
||||
}
|
||||
}
|
||||
|
||||
// Drag assets
|
||||
if (_dragAssets.HasValidDrag)
|
||||
{
|
||||
result = _dragAssets.Effect;
|
||||
|
||||
// Process items
|
||||
for (int i = 0; i < _dragAssets.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragAssets.Objects[i];
|
||||
Spawn(item, hit, ref location, ref hitLocation);
|
||||
}
|
||||
}
|
||||
// Drag actor type
|
||||
else if (_dragActorType.HasValidDrag)
|
||||
{
|
||||
result = _dragActorType.Effect;
|
||||
|
||||
// Process items
|
||||
for (int i = 0; i < _dragActorType.Objects.Count; i++)
|
||||
{
|
||||
var item = _dragActorType.Objects[i];
|
||||
Spawn(item, hit, ref hitLocation);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return DragHandlers.DragDrop(ref location, data);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
257
Source/Editor/Viewport/ViewportDraggingHelper.cs
Normal file
257
Source/Editor/Viewport/ViewportDraggingHelper.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.GUI.Drag;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.Viewport
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility to help managing dragging assets, actors and other objects into the editor viewport.
|
||||
/// </summary>
|
||||
public class ViewportDragHandlers : DragHandlers
|
||||
{
|
||||
/// <summary>
|
||||
/// The custom drag drop event arguments.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.GUI.Drag.DragEventArgs" />
|
||||
public class DragDropEventArgs : DragEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// The hit.
|
||||
/// </summary>
|
||||
public SceneGraphNode Hit;
|
||||
|
||||
/// <summary>
|
||||
/// The hit location.
|
||||
/// </summary>
|
||||
public Vector3 HitLocation;
|
||||
}
|
||||
|
||||
private readonly IGizmoOwner _owner;
|
||||
private readonly EditorViewport _viewport;
|
||||
private readonly DragAssets<DragDropEventArgs> _dragAssets;
|
||||
private readonly DragActorType<DragDropEventArgs> _dragActorType;
|
||||
|
||||
private StaticModel _previewStaticModel;
|
||||
private int _previewModelEntryIndex;
|
||||
private BrushSurface _previewBrushSurface;
|
||||
|
||||
internal ViewportDragHandlers(IGizmoOwner owner, EditorViewport viewport, Func<AssetItem, bool> validateAsset, Func<ScriptType, bool> validateDragActorType)
|
||||
{
|
||||
_owner = owner;
|
||||
_viewport = viewport;
|
||||
Add(_dragAssets = new DragAssets<DragDropEventArgs>(validateAsset));
|
||||
Add(_dragActorType = new DragActorType<DragDropEventArgs>(validateDragActorType));
|
||||
}
|
||||
|
||||
internal void ClearDragEffects()
|
||||
{
|
||||
_previewStaticModel = null;
|
||||
_previewModelEntryIndex = -1;
|
||||
_previewBrushSurface = new BrushSurface();
|
||||
}
|
||||
|
||||
internal void CollectDrawCalls(ViewportDebugDrawData debugDrawData, ref RenderContext renderContext)
|
||||
{
|
||||
if (_previewStaticModel)
|
||||
debugDrawData.HighlightModel(_previewStaticModel, _previewModelEntryIndex);
|
||||
if (_previewBrushSurface.Brush != null)
|
||||
debugDrawData.HighlightBrushSurface(_previewBrushSurface);
|
||||
}
|
||||
|
||||
internal DragDropEffect DragEnter(ref Float2 location, DragData data)
|
||||
{
|
||||
var result = OnDragEnter(data);
|
||||
SetDragEffects(ref location);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal DragDropEffect DragMove(ref Float2 location, DragData data)
|
||||
{
|
||||
SetDragEffects(ref location);
|
||||
return Effect;
|
||||
}
|
||||
|
||||
internal DragDropEffect DragDrop(ref Float2 location, DragData data)
|
||||
{
|
||||
Vector3 hitLocation = _viewport.ViewPosition, hitNormal = -_viewport.ViewDirection;
|
||||
SceneGraphNode hit = null;
|
||||
if (HasValidDrag)
|
||||
{
|
||||
GetHitLocation(ref location, out hit, out hitLocation, out hitNormal);
|
||||
}
|
||||
|
||||
var result = DragDropEffect.None;
|
||||
if (_dragAssets.HasValidDrag)
|
||||
{
|
||||
result = _dragAssets.Effect;
|
||||
foreach (var asset in _dragAssets.Objects)
|
||||
Spawn(asset, hit, ref location, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
else if (_dragActorType.HasValidDrag)
|
||||
{
|
||||
result = _dragActorType.Effect;
|
||||
foreach (var actorType in _dragActorType.Objects)
|
||||
Spawn(actorType, hit, ref location, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
|
||||
OnDragDrop(new DragDropEventArgs { Hit = hit, HitLocation = hitLocation });
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void SetDragEffects(ref Float2 location)
|
||||
{
|
||||
if (_dragAssets.HasValidDrag && _dragAssets.Objects[0].IsOfType<MaterialBase>())
|
||||
{
|
||||
GetHitLocation(ref location, out var hit, out _, out _);
|
||||
ClearDragEffects();
|
||||
var material = FlaxEngine.Content.LoadAsync<MaterialBase>(_dragAssets.Objects[0].ID);
|
||||
if (material.IsDecal)
|
||||
return;
|
||||
|
||||
if (hit is StaticModelNode staticModelNode)
|
||||
{
|
||||
_previewStaticModel = (StaticModel)staticModelNode.Actor;
|
||||
var ray = _viewport.ConvertMouseToRay(ref location);
|
||||
_previewStaticModel.IntersectsEntry(ref ray, out _, out _, out _previewModelEntryIndex);
|
||||
}
|
||||
else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode)
|
||||
{
|
||||
_previewBrushSurface = brushSurfaceNode.Surface;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GetHitLocation(ref Float2 location, out SceneGraphNode hit, out Vector3 hitLocation, out Vector3 hitNormal)
|
||||
{
|
||||
// Get mouse ray and try to hit any object
|
||||
var ray = _viewport.ConvertMouseToRay(ref location);
|
||||
var view = new Ray(_viewport.ViewPosition, _viewport.ViewDirection);
|
||||
var gridPlane = new Plane(Vector3.Zero, Vector3.Up);
|
||||
var flags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders | SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives;
|
||||
hit = _owner.SceneGraphRoot.RayCast(ref ray, ref view, out var closest, out var normal, flags);
|
||||
var girdGizmo = (GridGizmo)_owner.Gizmos.FirstOrDefault(x => x is GridGizmo);
|
||||
if (hit != null)
|
||||
{
|
||||
// Use hit location
|
||||
hitLocation = ray.Position + ray.Direction * closest;
|
||||
hitNormal = normal;
|
||||
}
|
||||
else if (girdGizmo != null && girdGizmo.Enabled && CollisionsHelper.RayIntersectsPlane(ref ray, ref gridPlane, out closest) && closest < 4000.0f)
|
||||
{
|
||||
// Use grid location
|
||||
hitLocation = ray.Position + ray.Direction * closest;
|
||||
hitNormal = Vector3.Up;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use area in front of the viewport
|
||||
hitLocation = view.GetPoint(100);
|
||||
hitNormal = Vector3.Up;
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 PostProcessSpawnedActorLocation(Actor actor, ref Vector3 hitLocation)
|
||||
{
|
||||
// Refresh actor position to ensure that cached bounds are valid
|
||||
actor.Position = Vector3.One;
|
||||
actor.Position = Vector3.Zero;
|
||||
|
||||
// Place the object
|
||||
//var location = hitLocation - (box.Size.Length * 0.5f) * ViewDirection;
|
||||
var editorBounds = actor.EditorBoxChildren;
|
||||
var bottomToCenter = actor.Position.Y - editorBounds.Minimum.Y;
|
||||
var location = hitLocation + new Vector3(0, bottomToCenter, 0);
|
||||
|
||||
// Apply grid snapping if enabled
|
||||
var transformGizmo = (TransformGizmo)_owner.Gizmos.FirstOrDefault(x => x is TransformGizmo);
|
||||
if (transformGizmo != null && (_owner.UseSnapping || transformGizmo.TranslationSnapEnable))
|
||||
{
|
||||
float snapValue = transformGizmo.TranslationSnapValue;
|
||||
location = new Vector3(
|
||||
(int)(location.X / snapValue) * snapValue,
|
||||
(int)(location.Y / snapValue) * snapValue,
|
||||
(int)(location.Z / snapValue) * snapValue);
|
||||
}
|
||||
|
||||
return location;
|
||||
}
|
||||
|
||||
private void Spawn(Actor actor, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
{
|
||||
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
|
||||
_owner.Spawn(actor);
|
||||
_viewport.Focus();
|
||||
}
|
||||
|
||||
private void Spawn(ScriptType item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
{
|
||||
var actor = item.CreateInstance() as Actor;
|
||||
if (actor == null)
|
||||
{
|
||||
Editor.LogWarning("Failed to spawn actor of type " + item.TypeName);
|
||||
return;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Spawn(actor, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
|
||||
private void Spawn(AssetItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal)
|
||||
{
|
||||
if (item.IsOfType<MaterialBase>())
|
||||
{
|
||||
var material = FlaxEngine.Content.LoadAsync<MaterialBase>(item.ID);
|
||||
if (material && !material.WaitForLoaded(500) && material.IsDecal)
|
||||
{
|
||||
var actor = new Decal
|
||||
{
|
||||
Material = material,
|
||||
LocalOrientation = RootNode.RaycastNormalRotation(ref hitNormal),
|
||||
Name = item.ShortName
|
||||
};
|
||||
DebugDraw.DrawWireArrow(PostProcessSpawnedActorLocation(actor, ref hitNormal), actor.LocalOrientation, 1.0f, Color.Red, 1000000);
|
||||
Spawn(actor, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
else if (hit is StaticModelNode staticModelNode)
|
||||
{
|
||||
var staticModel = (StaticModel)staticModelNode.Actor;
|
||||
var ray = _viewport.ConvertMouseToRay(ref location);
|
||||
if (staticModel.IntersectsEntry(ref ray, out _, out _, out var entryIndex))
|
||||
{
|
||||
using (new UndoBlock(_owner.Undo, staticModel, "Change material"))
|
||||
staticModel.SetMaterial(entryIndex, material);
|
||||
}
|
||||
}
|
||||
else if (hit is BoxBrushNode.SideLinkNode brushSurfaceNode)
|
||||
{
|
||||
using (new UndoBlock(_owner.Undo, brushSurfaceNode.Brush, "Change material"))
|
||||
{
|
||||
var surface = brushSurfaceNode.Surface;
|
||||
surface.Material = material;
|
||||
brushSurfaceNode.Surface = surface;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (item.IsOfType<SceneAsset>())
|
||||
{
|
||||
Editor.Instance.Scene.OpenScene(item.ID, true);
|
||||
return;
|
||||
}
|
||||
{
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Spawn(actor, ref hitLocation, ref hitNormal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +190,7 @@ namespace FlaxEditor.Windows
|
||||
};
|
||||
_addPluginProjectButton = new Button
|
||||
{
|
||||
Text = "Create Plugin Project",
|
||||
Text = "Create Project",
|
||||
TooltipText = "Add new plugin project.",
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
LocalLocation = new Float2(70, 18),
|
||||
@@ -201,7 +201,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
_cloneProjectButton = new Button
|
||||
{
|
||||
Text = "Clone Plugin Project",
|
||||
Text = "Clone Project",
|
||||
TooltipText = "Git Clone a plugin project.",
|
||||
AnchorPreset = AnchorPresets.TopLeft,
|
||||
LocalLocation = new Float2(70 + _addPluginProjectButton.Size.X + 8, 18),
|
||||
|
||||
@@ -423,6 +423,7 @@ namespace FlaxEditor.Windows
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
@@ -440,6 +441,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ namespace FlaxEngine.Windows.Search
|
||||
/// <summary>
|
||||
/// The content searching window. Allows to search inside Visual Scripts, Materials, Particles and other assets.
|
||||
/// </summary>
|
||||
[HideInEditor]
|
||||
internal class ContentSearchWindow : EditorWindow
|
||||
{
|
||||
/// <summary>
|
||||
@@ -115,6 +116,7 @@ namespace FlaxEngine.Windows.Search
|
||||
}
|
||||
}
|
||||
|
||||
[HideInEditor]
|
||||
private sealed class SearchResultTreeNode : TreeNode
|
||||
{
|
||||
public Action<SearchResultTreeNode> Navigate;
|
||||
|
||||
@@ -728,9 +728,7 @@ namespace Math
|
||||
/// <summary>
|
||||
/// Returns value based on comparand. The main purpose of this function is to avoid branching based on floating point comparison which can be avoided via compiler intrinsics.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.
|
||||
/// </remarks>
|
||||
/// <remarks>Please note that this doesn't define what happens in the case of NaNs as there might be platform specific differences.</remarks>
|
||||
/// <param name="comparand">Comparand the results are based on.</param>
|
||||
/// <param name="valueGEZero">The result value if comparand >= 0.</param>
|
||||
/// <param name="valueLTZero">The result value if comparand < 0.</param>
|
||||
@@ -891,6 +889,18 @@ namespace Math
|
||||
return Lerp<T>(a, b, alpha < 0.5f ? InterpCircularIn(0.f, 1.f, alpha * 2.f) * 0.5f : InterpCircularOut(0.f, 1.f, alpha * 2.f - 1.f) * 0.5f + 0.5f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping pongs the value <paramref name="t"/>, so that it is never larger than <paramref name="length"/> and never smaller than 0.
|
||||
/// </summary>
|
||||
/// <param name="t"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <returns></returns>
|
||||
template<class T>
|
||||
static FORCE_INLINE T PingPong(const T& t, T length)
|
||||
{
|
||||
return length - Abs(Repeat(t, length * 2.0f) - length);
|
||||
}
|
||||
|
||||
// Rotates position about the given axis by the given angle, in radians, and returns the offset to position
|
||||
Vector3 FLAXENGINE_API RotateAboutAxis(const Vector3& normalizedRotationAxis, float angle, const Vector3& positionOnAxis, const Vector3& position);
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "String.h"
|
||||
#include "StringView.h"
|
||||
#include "Engine/Core/Collections/Array.h"
|
||||
|
||||
/// <summary>
|
||||
@@ -138,6 +139,11 @@ public:
|
||||
_data.Add(*str, str.Length());
|
||||
return *this;
|
||||
}
|
||||
StringBuilder& Append(const StringView& str)
|
||||
{
|
||||
_data.Add(*str, str.Length());
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Append int to the string
|
||||
// @param val Value to append
|
||||
|
||||
@@ -591,37 +591,37 @@ API_ENUM() enum class Quality : byte
|
||||
API_ENUM() enum class MaterialPostFxLocation : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// The 'after' post processing pass using LDR input frame.
|
||||
/// Render the material after the post processing pass using *LDR* input frame.
|
||||
/// </summary>
|
||||
AfterPostProcessingPass = 0,
|
||||
|
||||
/// <summary>
|
||||
/// The 'before' post processing pass using HDR input frame.
|
||||
/// Render the material before the post processing pass using *HDR* input frame.
|
||||
/// </summary>
|
||||
BeforePostProcessingPass = 1,
|
||||
|
||||
/// <summary>
|
||||
/// The 'before' forward pass but after GBuffer with HDR input frame.
|
||||
/// Render the material before the forward pass but after *GBuffer* with *HDR* input frame.
|
||||
/// </summary>
|
||||
BeforeForwardPass = 2,
|
||||
|
||||
/// <summary>
|
||||
/// The 'after' custom post effects.
|
||||
/// Render the material after custom post effects (scripted).
|
||||
/// </summary>
|
||||
AfterCustomPostEffects = 3,
|
||||
|
||||
/// <summary>
|
||||
/// The 'before' Reflections pass. After the Light pass. Can be used to implement a custom light types that accumulate lighting to the light buffer.
|
||||
/// Render the material before the reflections pass but after the lighting pass using *HDR* input frame. It can be used to implement a custom light types that accumulate lighting to the light buffer.
|
||||
/// </summary>
|
||||
BeforeReflectionsPass = 4,
|
||||
|
||||
/// <summary>
|
||||
/// The 'after' AA filter pass. Rendering is done to the output backbuffer.
|
||||
/// Render the material after anti-aliasing into the output backbuffer.
|
||||
/// </summary>
|
||||
AfterAntiAliasingPass = 5,
|
||||
|
||||
/// <summary>
|
||||
/// The 'after' forward pass but before any post processing.
|
||||
/// Render the material after the forward pass but before any post processing.
|
||||
/// </summary>
|
||||
AfterForwardPass = 6,
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ void GraphicsSettings::Apply()
|
||||
Graphics::AllowCSMBlending = AllowCSMBlending;
|
||||
Graphics::GlobalSDFQuality = GlobalSDFQuality;
|
||||
Graphics::GIQuality = GIQuality;
|
||||
Graphics::PostProcessSettings = PostProcessSettings;
|
||||
Graphics::PostProcessSettings = ::PostProcessSettings();
|
||||
Graphics::PostProcessSettings.BlendWith(PostProcessSettings, 1.0f);
|
||||
}
|
||||
|
||||
void Graphics::DisposeDevice()
|
||||
|
||||
@@ -447,13 +447,13 @@ API_STRUCT() struct FLAXENGINE_API BloomSettings : ISerializable
|
||||
bool Enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// Bloom effect strength. Value 0 disabled is, while higher values increase the effect.
|
||||
/// Bloom effect strength. Set a value of 0 to disabled it, while higher values increase the effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 20.0f, 0.01f), EditorOrder(1), PostProcessSetting((int)BloomSettingsOverride.Intensity)")
|
||||
float Intensity = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum pixel brightness value to start blowing. Values below the threshold are skipped.
|
||||
/// Minimum pixel brightness value to start blooming. Values below this threshold are skipped.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 15.0f, 0.01f), EditorOrder(2), PostProcessSetting((int)BloomSettingsOverride.Threshold)")
|
||||
float Threshold = 3.0f;
|
||||
@@ -987,13 +987,13 @@ API_STRUCT() struct FLAXENGINE_API EyeAdaptationSettings : ISerializable
|
||||
float MaxBrightness = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The lower bound for the luminance histogram of the scene color. Value is in percent and limits the pixels below this brightness. Use values from range 60-80. Used only in AutomaticHistogram mode.
|
||||
/// The lower bound for the luminance histogram of the scene color. This value is in percent and limits the pixels below this brightness. Use values in the range of 60-80. Used only in AutomaticHistogram mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramLowPercent)")
|
||||
float HistogramLowPercent = 70.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The upper bound for the luminance histogram of the scene color. Value is in percent and limits the pixels above this brightness. Use values from range 80-95. Used only in AutomaticHistogram mode.
|
||||
/// The upper bound for the luminance histogram of the scene color. This value is in percent and limits the pixels above this brightness. Use values in the range of 80-95. Used only in AutomaticHistogram mode.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(1, 99, 0.001f), EditorOrder(3), PostProcessSetting((int)EyeAdaptationSettingsOverride.HistogramHighPercent)")
|
||||
float HistogramHighPercent = 98.0f;
|
||||
@@ -1091,13 +1091,13 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
|
||||
Float3 VignetteColor = Float3(0, 0, 0.001f);
|
||||
|
||||
/// <summary>
|
||||
/// Controls shape of the vignette. Values near 0 produce rectangle shape. Higher values result in round shape. The default value is 0.125.
|
||||
/// Controls the shape of the vignette. Values near 0 produce a rectangular shape. Higher values result in a rounder shape. The default value is 0.125.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0001f, 2.0f, 0.001f), EditorOrder(2), PostProcessSetting((int)CameraArtifactsSettingsOverride.VignetteShapeFactor)")
|
||||
float VignetteShapeFactor = 0.125f;
|
||||
|
||||
/// <summary>
|
||||
/// Intensity of the grain filter. Value 0 hides it. The default value is 0.005.
|
||||
/// Intensity of the grain filter. A value of 0 hides it. The default value is 0.005.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 2.0f, 0.005f), EditorOrder(3), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainAmount)")
|
||||
float GrainAmount = 0.006f;
|
||||
@@ -1109,19 +1109,19 @@ API_STRUCT() struct FLAXENGINE_API CameraArtifactsSettings : ISerializable
|
||||
float GrainParticleSize = 1.6f;
|
||||
|
||||
/// <summary>
|
||||
/// Speed of the grain particles animation.
|
||||
/// Speed of the grain particle animation.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 10.0f, 0.01f), EditorOrder(5), PostProcessSetting((int)CameraArtifactsSettingsOverride.GrainSpeed)")
|
||||
float GrainSpeed = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls chromatic aberration effect strength. Value 0 hides it.
|
||||
/// Controls the chromatic aberration effect strength. A value of 0 hides it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0.0f, 1.0f, 0.01f), EditorOrder(6), PostProcessSetting((int)CameraArtifactsSettingsOverride.ChromaticDistortion)")
|
||||
float ChromaticDistortion = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Screen tint color (alpha channel defines the blending factor).
|
||||
/// Screen tint color (the alpha channel defines the blending factor).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DefaultValue(typeof(Color), \"0,0,0,0\"), EditorOrder(7), PostProcessSetting((int)CameraArtifactsSettingsOverride.ScreenFadeColor)")
|
||||
Color ScreenFadeColor = Color::Transparent;
|
||||
@@ -1227,7 +1227,7 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
LensFlaresSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// Strength of the effect. Value 0 disabled it.
|
||||
/// Strength of the effect. A value of 0 disables it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 10.0f, 0.01f), EditorOrder(0), PostProcessSetting((int)LensFlaresSettingsOverride.Intensity)")
|
||||
float Intensity = 1.0f;
|
||||
@@ -1281,7 +1281,7 @@ API_STRUCT() struct FLAXENGINE_API LensFlaresSettings : ISerializable
|
||||
SoftAssetReference<Texture> LensDirt;
|
||||
|
||||
/// <summary>
|
||||
/// Fullscreen lens dirt intensity parameter. Allows to tune dirt visibility.
|
||||
/// Fullscreen lens dirt intensity parameter. Allows tuning dirt visibility.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 100, 0.01f), EditorOrder(9), PostProcessSetting((int)LensFlaresSettingsOverride.LensDirtIntensity)")
|
||||
float LensDirtIntensity = 1.0f;
|
||||
@@ -1419,13 +1419,13 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
DepthOfFieldSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, depth of field effect will be visible.
|
||||
/// If checked, the depth of field effect will be visible.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)DepthOfFieldSettingsOverride.Enabled)")
|
||||
bool Enabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// The blur intensity in the out-of-focus areas. Allows reducing blur amount by scaling down the Gaussian Blur radius. Normalized to range 0-1.
|
||||
/// The blur intensity in the out-of-focus areas. Allows reducing the blur amount by scaling down the Gaussian Blur radius. Normalized to range 0-1.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1, 0.01f), EditorOrder(1), PostProcessSetting((int)DepthOfFieldSettingsOverride.BlurStrength)")
|
||||
float BlurStrength = 1.0f;
|
||||
@@ -1479,7 +1479,7 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
float BokehBrightness = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Defines bokeh shapes type.
|
||||
/// Defines the type of the bokeh shapes.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehShape)")
|
||||
BokehShapeType BokehShape = BokehShapeType::Octagon;
|
||||
@@ -1491,19 +1491,19 @@ API_STRUCT() struct FLAXENGINE_API DepthOfFieldSettings : ISerializable
|
||||
SoftAssetReference<Texture> BokehShapeCustom;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum pixel brightness to create bokeh. Pixels with lower brightness will be skipped.
|
||||
/// The minimum pixel brightness to create the bokeh. Pixels with lower brightness will be skipped.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 10000.0f, 0.01f), EditorOrder(12), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehBrightnessThreshold)")
|
||||
float BokehBrightnessThreshold = 3.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Depth of Field bokeh shapes blur threshold.
|
||||
/// Depth of Field bokeh shape blur threshold.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 1.0f, 0.001f), EditorOrder(13), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehBlurThreshold)")
|
||||
float BokehBlurThreshold = 0.05f;
|
||||
|
||||
/// <summary>
|
||||
/// Controls bokeh shapes brightness falloff. Higher values reduce bokeh visibility.
|
||||
/// Controls bokeh shape brightness falloff. Higher values reduce bokeh visibility.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 2.0f, 0.001f), EditorOrder(14), PostProcessSetting((int)DepthOfFieldSettingsOverride.BokehFalloff)")
|
||||
float BokehFalloff = 0.5f;
|
||||
@@ -1575,25 +1575,25 @@ API_STRUCT() struct FLAXENGINE_API MotionBlurSettings : ISerializable
|
||||
MotionBlurSettingsOverride OverrideFlags = Override::None;
|
||||
|
||||
/// <summary>
|
||||
/// If checked, motion blur effect will be rendered.
|
||||
/// If checked, the motion blur effect will be rendered.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(0), PostProcessSetting((int)MotionBlurSettingsOverride.Enabled)")
|
||||
bool Enabled = true;
|
||||
|
||||
/// <summary>
|
||||
/// The blur effect strength. Value 0 disabled is, while higher values increase the effect.
|
||||
/// The blur effect strength. A value of 0 disables it, while higher values increase the effect.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 5, 0.01f), EditorOrder(1), PostProcessSetting((int)MotionBlurSettingsOverride.Scale)")
|
||||
float Scale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of sample points used during motion blur rendering. It affects quality and performance.
|
||||
/// The amount of sample points used during motion blur rendering. It affects blur quality and performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(4, 32, 0.1f), EditorOrder(2), PostProcessSetting((int)MotionBlurSettingsOverride.SampleCount)")
|
||||
int32 SampleCount = 10;
|
||||
|
||||
/// <summary>
|
||||
/// The motion vectors texture resolution. Motion blur uses per-pixel motion vectors buffer that contains objects movement information. Use lower resolution to improve performance.
|
||||
/// The motion vectors texture resolution. Motion blur uses a per-pixel motion vector buffer that contains an objects movement information. Use a lower resolution to improve performance.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(3), PostProcessSetting((int)MotionBlurSettingsOverride.MotionVectorsResolution)")
|
||||
ResolutionMode MotionVectorsResolution = ResolutionMode::Half;
|
||||
@@ -1898,13 +1898,13 @@ API_STRUCT() struct FLAXENGINE_API AntiAliasingSettings : ISerializable
|
||||
float TAA_Sharpness = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The blend coefficient for stationary fragments. Controls the percentage of history sample blended into final color for fragments with minimal active motion.
|
||||
/// The blend coefficient for stationary fragments. Controls the percentage of history samples blended into the final color for fragments with minimal active motion.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(3), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_StationaryBlending), EditorDisplay(null, \"TAA Stationary Blending\")")
|
||||
float TAA_StationaryBlending = 0.95f;
|
||||
|
||||
/// <summary>
|
||||
/// The blending coefficient for moving fragments. Controls the percentage of history sample blended into the final color for fragments with significant active motion.
|
||||
/// The blending coefficient for moving fragments. Controls the percentage of history samples blended into the final color for fragments with significant active motion.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0, 0.99f, 0.001f), EditorOrder(4), PostProcessSetting((int)AntiAliasingSettingsOverride.TAA_MotionBlending), EditorDisplay(null, \"TAA Motion Blending\")")
|
||||
float TAA_MotionBlending = 0.7f;
|
||||
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
float FogDensity = 0.02f;
|
||||
|
||||
/// <summary>
|
||||
/// The fog height density factor that controls how the density increases as height decreases. The smaller values produce more visible transition larger.
|
||||
/// The fog height density factor that controls how the density increases as height decreases. Smaller values produce a more visible transition layer.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(0.2f), Limit(0.0001f, 10.0f, 0.001f), EditorDisplay(\"Exponential Height Fog\")")
|
||||
float FogHeightFalloff = 0.2f;
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
float StartDistance = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in.
|
||||
/// Scene elements past this distance will not have fog applied. This is useful for excluding skyboxes which already have fog baked in. Setting this value to 0 disables it.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(60), DefaultValue(0.0f), Limit(0), EditorDisplay(\"Exponential Height Fog\")")
|
||||
float FogCutoffDistance = 0.0f;
|
||||
@@ -111,7 +111,7 @@ public:
|
||||
Color VolumetricFogAlbedo = Color::White;
|
||||
|
||||
/// <summary>
|
||||
/// Light emitted by height fog. This is a density so more light is emitted the further you are looking through the fog.
|
||||
/// Light emitted by height fog. This is a density value so more light is emitted the further you are looking through the fog.
|
||||
/// In most cases using a Skylight is a better choice, however, it may be useful in certain scenarios.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(330), DefaultValue(typeof(Color), \"0,0,0,1\"), EditorDisplay(\"Volumetric Fog\", \"Emissive\")")
|
||||
|
||||
@@ -31,19 +31,19 @@ public:
|
||||
/// <summary>
|
||||
/// Directional light that is used to simulate the sun.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sun\")")
|
||||
API_FIELD(Attributes="EditorOrder(10), DefaultValue(null), EditorDisplay(\"Sky\")")
|
||||
ScriptingObjectReference<DirectionalLight> SunLight;
|
||||
|
||||
/// <summary>
|
||||
/// The sun disc scale.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sun\"), Limit(0, 100, 0.01f)")
|
||||
API_FIELD(Attributes="EditorOrder(20), DefaultValue(2.0f), EditorDisplay(\"Sky\"), Limit(0, 100, 0.01f)")
|
||||
float SunDiscScale = 2.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The sun power.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sun\"), Limit(0, 1000, 0.01f)")
|
||||
API_FIELD(Attributes="EditorOrder(30), DefaultValue(8.0f), EditorDisplay(\"Sky\"), Limit(0, 1000, 0.01f)")
|
||||
float SunPower = 8.0f;
|
||||
|
||||
private:
|
||||
|
||||
@@ -42,24 +42,7 @@ public:
|
||||
/// <summary>
|
||||
/// Field for assigning new script to transfer data to.
|
||||
/// </summary>
|
||||
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value)
|
||||
{
|
||||
_referenceScript = value;
|
||||
if (Data.IsEmpty())
|
||||
return;
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(Data.ToStringAnsi().GetText());
|
||||
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
_referenceScript->Deserialize(document, modifier.Value);
|
||||
|
||||
DeleteObject();
|
||||
}
|
||||
API_PROPERTY() void SetReferenceScript(const ScriptingObjectReference<Script>& value);
|
||||
};
|
||||
|
||||
inline MissingScript::MissingScript(const SpawnParams& params)
|
||||
: Script(params)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1064,6 +1064,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
}
|
||||
}
|
||||
}
|
||||
prefabSyncData.InitNewObjects();
|
||||
}
|
||||
|
||||
// /\ all above this has to be done on an any thread
|
||||
@@ -1108,7 +1109,7 @@ bool LevelImpl::saveScene(Scene* scene)
|
||||
|
||||
bool LevelImpl::saveScene(Scene* scene, const String& path)
|
||||
{
|
||||
ASSERT(scene);
|
||||
ASSERT(scene && EnumHasNoneFlags(scene->Flags, ObjectFlags::WasMarkedToDelete));
|
||||
auto sceneId = scene->GetID();
|
||||
|
||||
LOG(Info, "Saving scene {0} to \'{1}\'", scene->GetName(), path);
|
||||
|
||||
@@ -253,7 +253,7 @@ void PrefabInstanceData::SerializePrefabInstances(PrefabInstancesData& prefabIns
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
SceneObject* obj = sceneObjects.Value->At(i);
|
||||
instance.PrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
|
||||
instance.PrefabInstanceIdToDataIndex[obj->GetSceneObjectId()] = i;
|
||||
}
|
||||
}
|
||||
tmpBuffer.Clear();
|
||||
@@ -313,15 +313,13 @@ bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabI
|
||||
continue;
|
||||
}
|
||||
|
||||
modifier.Value->IdsMapping.Add(obj->GetPrefabObjectID(), obj->GetSceneObjectId());
|
||||
modifier.Value->IdsMapping[obj->GetPrefabObjectID()] = obj->GetSceneObjectId();
|
||||
}
|
||||
}
|
||||
|
||||
// Generate new IDs for the added objects (objects in prefab has to have a unique Ids, other than the targetActor instance objects to prevent Id collisions)
|
||||
for (int32 i = 0; i < newPrefabObjectIds.Count(); i++)
|
||||
{
|
||||
modifier->IdsMapping.Add(newPrefabObjectIds[i], Guid::New());
|
||||
}
|
||||
modifier->IdsMapping[newPrefabObjectIds[i]] = Guid::New();
|
||||
|
||||
// Create new objects added to prefab
|
||||
int32 deserializeSceneObjectIndex = sceneObjects->Count();
|
||||
@@ -786,7 +784,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
}
|
||||
|
||||
// Cache connection for fast lookup
|
||||
diffPrefabObjectIdToDataIndex.Add(obj->GetPrefabObjectID(), i);
|
||||
diffPrefabObjectIdToDataIndex[obj->GetPrefabObjectID()] = i;
|
||||
|
||||
// Strip unwanted data
|
||||
data.RemoveMember("ID");
|
||||
@@ -796,7 +794,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
else
|
||||
{
|
||||
// Object if a new thing
|
||||
newPrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
|
||||
newPrefabInstanceIdToDataIndex[obj->GetSceneObjectId()] = i;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,8 +834,8 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr
|
||||
for (auto i = newPrefabInstanceIdToDataIndex.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
const auto prefabObjectId = Guid::New();
|
||||
newPrefabInstanceIdToPrefabObjectId.Add(i->Key, prefabObjectId);
|
||||
modifier->IdsMapping.Add(i->Key, prefabObjectId);
|
||||
newPrefabInstanceIdToPrefabObjectId[i->Key] = prefabObjectId;
|
||||
modifier->IdsMapping[i->Key] = prefabObjectId;
|
||||
}
|
||||
|
||||
// Add inverse IDs mapping to link added objects and references inside them to the prefab objects
|
||||
|
||||
@@ -374,13 +374,13 @@ bool PrefabManager::CreatePrefab(Actor* targetActor, const StringView& outputPat
|
||||
if (targetActor->HasParent())
|
||||
{
|
||||
// Unlink from parent actor
|
||||
objectInstanceIdToPrefabObjectId.Add(targetActor->GetParent()->GetID(), Guid::Empty);
|
||||
objectInstanceIdToPrefabObjectId[targetActor->GetParent()->GetID()] = Guid::Empty;
|
||||
}
|
||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
||||
{
|
||||
// Generate new IDs for the prefab objects (other than reference instance used to create prefab)
|
||||
const SceneObject* obj = sceneObjects->At(i);
|
||||
objectInstanceIdToPrefabObjectId.Add(obj->GetSceneObjectId(), Guid::New());
|
||||
objectInstanceIdToPrefabObjectId[obj->GetSceneObjectId()] = Guid::New();
|
||||
}
|
||||
{
|
||||
// Parse json to DOM document
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "SceneObjectsFactory.h"
|
||||
#include "Components/MissingScript.h"
|
||||
#include "Engine/Level/Actor.h"
|
||||
#include "Engine/Level/Prefabs/Prefab.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
@@ -17,11 +16,41 @@
|
||||
#if !BUILD_RELEASE || USE_EDITOR
|
||||
#include "Engine/Level/Level.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Level/Components/MissingScript.h"
|
||||
#endif
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
MissingScript::MissingScript(const SpawnParams& params)
|
||||
: Script(params)
|
||||
{
|
||||
}
|
||||
|
||||
void MissingScript::SetReferenceScript(const ScriptingObjectReference<Script>& value)
|
||||
{
|
||||
if (_referenceScript == value)
|
||||
return;
|
||||
_referenceScript = value;
|
||||
if (Data.IsEmpty() || !_referenceScript)
|
||||
return;
|
||||
rapidjson_flax::Document document;
|
||||
document.Parse(Data.ToStringAnsi().GetText());
|
||||
document.RemoveMember("ParentID"); // Prevent changing parent
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
const auto idsMapping = Scripting::ObjectsLookupIdMapping.Get();
|
||||
if (idsMapping)
|
||||
modifier->IdsMapping = *idsMapping;
|
||||
_referenceScript->Deserialize(document, modifier.Value);
|
||||
DeleteObject();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
SceneObjectsFactory::Context::Context(ISerializeModifier* modifier)
|
||||
: Modifier(modifier)
|
||||
{
|
||||
// Override the main thread value to not create it again in GetModifier() if called from the same thread
|
||||
Modifiers.Set(modifier);
|
||||
}
|
||||
|
||||
SceneObjectsFactory::Context::~Context()
|
||||
@@ -31,7 +60,11 @@ SceneObjectsFactory::Context::~Context()
|
||||
Array<ISerializeModifier*, FixedAllocation<PLATFORM_THREADS_LIMIT>> modifiers;
|
||||
Modifiers.GetValues(modifiers);
|
||||
for (ISerializeModifier* e : modifiers)
|
||||
{
|
||||
if (e == Modifier)
|
||||
continue;
|
||||
Cache::ISerializeModifier.Put(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,15 +90,15 @@ ISerializeModifier* SceneObjectsFactory::Context::GetModifier()
|
||||
return modifier;
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier)
|
||||
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier) const
|
||||
{
|
||||
int32 instanceIndex;
|
||||
if (ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != modifier->CurrentInstance)
|
||||
{
|
||||
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
|
||||
modifier->CurrentInstance = instanceIndex;
|
||||
auto& instance = Instances[instanceIndex];
|
||||
for (auto& e : instance.IdsMapping)
|
||||
const auto& instance = Instances[instanceIndex];
|
||||
for (const auto& e : instance.IdsMapping)
|
||||
modifier->IdsMapping[e.Key] = e.Value;
|
||||
}
|
||||
}
|
||||
@@ -370,6 +403,15 @@ SceneObjectsFactory::PrefabSyncData::PrefabSyncData(Array<SceneObject*>& sceneOb
|
||||
{
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::PrefabSyncData::InitNewObjects()
|
||||
{
|
||||
for (int32 i = 0; i < NewObjects.Count(); i++)
|
||||
{
|
||||
SceneObject* obj = SceneObjects[InitialCount + i];
|
||||
obj->Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyncData& data)
|
||||
{
|
||||
PROFILE_CPU_NAMED("SetupPrefabInstances");
|
||||
@@ -459,9 +501,6 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
|
||||
continue;
|
||||
const Guid actorParentId = JsonTools::GetGuid(stream, "ParentID");
|
||||
|
||||
// Map prefab object id to this actor so the new objects gets added to it
|
||||
data.Modifier->IdsMapping[actorPrefabObjectId] = actor->GetID();
|
||||
|
||||
// Load prefab
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (prefab == nullptr)
|
||||
@@ -527,8 +566,12 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
|
||||
if (spawned)
|
||||
continue;
|
||||
|
||||
// Create instance (including all children)
|
||||
// Map prefab object id to this actor's prefab instance so the new objects gets added to it
|
||||
context.SetupIdsMapping(actor, data.Modifier);
|
||||
data.Modifier->IdsMapping[actorPrefabObjectId] = actor->GetID();
|
||||
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
|
||||
|
||||
// Create instance (including all children)
|
||||
SynchronizeNewPrefabInstance(context, data, prefab, actor, prefabObjectId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
~Context();
|
||||
|
||||
ISerializeModifier* GetModifier();
|
||||
void SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier);
|
||||
void SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier) const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
ISerializeModifier* Modifier;
|
||||
|
||||
PrefabSyncData(Array<SceneObject*>& sceneObjects, const ISerializable::DeserializeStream& data, ISerializeModifier* modifier);
|
||||
void InitNewObjects();
|
||||
|
||||
private:
|
||||
struct NewObj
|
||||
|
||||
@@ -1067,6 +1067,12 @@ void NavMeshBuilder::Update()
|
||||
|
||||
void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
|
||||
{
|
||||
if (!scene)
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh without scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Early out if scene is not using navigation
|
||||
if (scene->Navigation.Volumes.IsEmpty())
|
||||
{
|
||||
@@ -1098,6 +1104,12 @@ void NavMeshBuilder::Build(Scene* scene, float timeoutMs)
|
||||
|
||||
void NavMeshBuilder::Build(Scene* scene, const BoundingBox& dirtyBounds, float timeoutMs)
|
||||
{
|
||||
if (!scene)
|
||||
{
|
||||
LOG(Warning, "Could not generate navmesh without scene.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Early out if scene is not using navigation
|
||||
if (scene->Navigation.Volumes.IsEmpty())
|
||||
{
|
||||
|
||||
@@ -51,6 +51,33 @@
|
||||
#include <android/window.h>
|
||||
#include <android/versioning.h>
|
||||
|
||||
#if CRASH_LOG_ENABLE
|
||||
|
||||
#include <unwind.h>
|
||||
#include <cxxabi.h>
|
||||
|
||||
struct AndroidBacktraceState
|
||||
{
|
||||
void** Current;
|
||||
void** End;
|
||||
};
|
||||
|
||||
_Unwind_Reason_Code AndroidUnwindCallback(struct _Unwind_Context* context, void* arg)
|
||||
{
|
||||
AndroidBacktraceState* state = (AndroidBacktraceState*)arg;
|
||||
uintptr_t pc = _Unwind_GetIP(context);
|
||||
if (pc)
|
||||
{
|
||||
if (state->Current == state->End)
|
||||
return _URC_END_OF_STACK;
|
||||
else
|
||||
*state->Current++ = reinterpret_cast<void*>(pc);
|
||||
}
|
||||
return _URC_NO_REASON;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct AndroidKeyEventType
|
||||
{
|
||||
uint32_t KeyCode;
|
||||
@@ -1066,4 +1093,45 @@ void* AndroidPlatform::GetProcAddress(void* handle, const char* symbol)
|
||||
return dlsym(handle, symbol);
|
||||
}
|
||||
|
||||
Array<AndroidPlatform::StackFrame> AndroidPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
|
||||
{
|
||||
Array<StackFrame> result;
|
||||
#if CRASH_LOG_ENABLE
|
||||
// Reference: https://stackoverflow.com/questions/8115192/android-ndk-getting-the-backtrace
|
||||
void* callstack[120];
|
||||
skipCount = Math::Min<int32>(skipCount, ARRAY_COUNT(callstack));
|
||||
int32 maxCount = Math::Min<int32>(ARRAY_COUNT(callstack), skipCount + maxDepth);
|
||||
AndroidBacktraceState state;
|
||||
state.Current = callstack;
|
||||
state.End = callstack + maxCount;
|
||||
_Unwind_Backtrace(AndroidUnwindCallback, &state);
|
||||
int32 count = (int32)(state.Current - callstack);
|
||||
int32 useCount = count - skipCount;
|
||||
if (useCount > 0)
|
||||
{
|
||||
result.Resize(useCount);
|
||||
for (int32 i = 0; i < useCount; i++)
|
||||
{
|
||||
StackFrame& frame = result[i];
|
||||
frame.ProgramCounter = callstack[skipCount + i];
|
||||
frame.ModuleName[0] = 0;
|
||||
frame.FileName[0] = 0;
|
||||
frame.LineNumber = 0;
|
||||
Dl_info info;
|
||||
const char* symbol = "";
|
||||
if (dladdr(frame.ProgramCounter, &info) && info.dli_sname)
|
||||
symbol = info.dli_sname;
|
||||
int status = 0;
|
||||
char* demangled = __cxxabiv1::__cxa_demangle(symbol, 0, 0, &status);
|
||||
int32 nameLen = Math::Min<int32>(StringUtils::Length(demangled), ARRAY_COUNT(frame.FunctionName) - 1);
|
||||
Platform::MemoryCopy(frame.FunctionName, demangled, nameLen);
|
||||
frame.FunctionName[nameLen] = 0;
|
||||
if (demangled)
|
||||
free(demangled);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -132,6 +132,7 @@ public:
|
||||
static void* LoadLibrary(const Char* filename);
|
||||
static void FreeLibrary(void* handle);
|
||||
static void* GetProcAddress(void* handle, const char* symbol);
|
||||
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include <dlfcn.h>
|
||||
#if CRASH_LOG_ENABLE
|
||||
#include <execinfo.h>
|
||||
#include <cxxabi.h>
|
||||
#endif
|
||||
|
||||
CPUInfo Cpu;
|
||||
@@ -502,20 +503,42 @@ Array<ApplePlatform::StackFrame> ApplePlatform::GetStackFrames(int32 skipCount,
|
||||
{
|
||||
char** names = backtrace_symbols(callstack + skipCount, useCount);
|
||||
result.Resize(useCount);
|
||||
Array<StringAnsi> parts;
|
||||
int32 len;
|
||||
#define COPY_STR(str, dst) \
|
||||
len = Math::Min<int32>(str.Length(), ARRAY_COUNT(frame.dst) - 1); \
|
||||
Platform::MemoryCopy(frame.dst, str.Get(), len); \
|
||||
frame.dst[len] = 0
|
||||
for (int32 i = 0; i < useCount; i++)
|
||||
{
|
||||
char* name = names[i];
|
||||
const StringAnsi name(names[i]);
|
||||
StackFrame& frame = result[i];
|
||||
frame.ProgramCounter = callstack[skipCount + i];
|
||||
frame.ModuleName[0] = 0;
|
||||
frame.FileName[0] = 0;
|
||||
frame.LineNumber = 0;
|
||||
int32 nameLen = Math::Min<int32>(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1);
|
||||
Platform::MemoryCopy(frame.FunctionName, name, nameLen);
|
||||
frame.FunctionName[nameLen] = 0;
|
||||
|
||||
|
||||
// Decode name
|
||||
parts.Clear();
|
||||
name.Split(' ', parts);
|
||||
if (parts.Count() == 6)
|
||||
{
|
||||
COPY_STR(parts[1], ModuleName);
|
||||
const StringAnsiView toDemangle(parts[3]);
|
||||
int status = 0;
|
||||
char* demangled = __cxxabiv1::__cxa_demangle(*toDemangle, 0, 0, &status);
|
||||
const StringAnsiView toCopy = demangled && status == 0 ? StringAnsiView(demangled) : StringAnsiView(toDemangle);
|
||||
COPY_STR(toCopy, FunctionName);
|
||||
if (demangled)
|
||||
free(demangled);
|
||||
}
|
||||
else
|
||||
{
|
||||
COPY_STR(name, FunctionName);
|
||||
}
|
||||
}
|
||||
free(names);
|
||||
#undef COPY_STR
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
|
||||
@@ -633,14 +633,21 @@ String PlatformBase::GetStackTrace(int32 skipCount, int32 maxDepth, void* contex
|
||||
for (const auto& frame : stackFrames)
|
||||
{
|
||||
StringAsUTF16<ARRAY_COUNT(StackFrame::FunctionName)> functionName(frame.FunctionName);
|
||||
const StringView functionNameStr(functionName.Get());
|
||||
if (StringUtils::Length(frame.FileName) != 0)
|
||||
{
|
||||
StringAsUTF16<ARRAY_COUNT(StackFrame::FileName)> fileName(frame.FileName);
|
||||
result.AppendFormat(TEXT(" at {0}() in {1}:line {2}\n"), functionName.Get(), fileName.Get(), frame.LineNumber);
|
||||
result.Append(TEXT(" at ")).Append(functionNameStr);
|
||||
if (!functionNameStr.EndsWith(')'))
|
||||
result.Append(TEXT("()"));
|
||||
result.AppendFormat(TEXT("in {0}:line {1}\n"),fileName.Get(), frame.LineNumber);
|
||||
}
|
||||
else if (StringUtils::Length(frame.FunctionName) != 0)
|
||||
{
|
||||
result.AppendFormat(TEXT(" at {0}()\n"), functionName.Get());
|
||||
result.Append(TEXT(" at ")).Append(functionNameStr);
|
||||
if (!functionNameStr.EndsWith(')'))
|
||||
result.Append(TEXT("()"));
|
||||
result.Append('\n');
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -50,6 +50,10 @@
|
||||
#include <pwd.h>
|
||||
#include <inttypes.h>
|
||||
#include <dlfcn.h>
|
||||
#if CRASH_LOG_ENABLE
|
||||
#include <signal.h>
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
CPUInfo UnixCpu;
|
||||
int ClockSource;
|
||||
@@ -1868,6 +1872,46 @@ void LinuxPlatform::GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int3
|
||||
millisecond = time.tv_usec / 1000;
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
|
||||
bool LinuxPlatform::IsDebuggerPresent()
|
||||
{
|
||||
static int32 CachedState = -1;
|
||||
if (CachedState == -1)
|
||||
{
|
||||
CachedState = 0;
|
||||
|
||||
// Reference: https://stackoverflow.com/questions/3596781/how-to-detect-if-the-current-process-is-being-run-by-gdb
|
||||
char buf[4096];
|
||||
const int status_fd = open("/proc/self/status", O_RDONLY);
|
||||
if (status_fd == -1)
|
||||
return false;
|
||||
const ssize_t num_read = read(status_fd, buf, sizeof(buf) - 1);
|
||||
close(status_fd);
|
||||
if (num_read <= 0)
|
||||
return false;
|
||||
buf[num_read] = '\0';
|
||||
constexpr char tracerPidString[] = "TracerPid:";
|
||||
const auto tracer_pid_ptr = strstr(buf, tracerPidString);
|
||||
if (!tracer_pid_ptr)
|
||||
return false;
|
||||
for (const char* characterPtr = tracer_pid_ptr + sizeof(tracerPidString) - 1; characterPtr <= buf + num_read; ++characterPtr)
|
||||
{
|
||||
if (StringUtils::IsWhitespace(*characterPtr))
|
||||
continue;
|
||||
else
|
||||
{
|
||||
if (StringUtils::IsDigit(*characterPtr) && *characterPtr != '0')
|
||||
CachedState = 1;
|
||||
return CachedState == 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return CachedState == 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool LinuxPlatform::Init()
|
||||
{
|
||||
if (PlatformBase::Init())
|
||||
@@ -2992,4 +3036,36 @@ void* LinuxPlatform::GetProcAddress(void* handle, const char* symbol)
|
||||
return dlsym(handle, symbol);
|
||||
}
|
||||
|
||||
Array<LinuxPlatform::StackFrame> LinuxPlatform::GetStackFrames(int32 skipCount, int32 maxDepth, void* context)
|
||||
{
|
||||
Array<StackFrame> result;
|
||||
#if CRASH_LOG_ENABLE
|
||||
void* callstack[120];
|
||||
skipCount = Math::Min<int32>(skipCount, ARRAY_COUNT(callstack));
|
||||
int32 maxCount = Math::Min<int32>(ARRAY_COUNT(callstack), skipCount + maxDepth);
|
||||
int32 count = backtrace(callstack, maxCount);
|
||||
int32 useCount = count - skipCount;
|
||||
if (useCount > 0)
|
||||
{
|
||||
char** names = backtrace_symbols(callstack + skipCount, useCount);
|
||||
result.Resize(useCount);
|
||||
for (int32 i = 0; i < useCount; i++)
|
||||
{
|
||||
char* name = names[i];
|
||||
StackFrame& frame = result[i];
|
||||
frame.ProgramCounter = callstack[skipCount + i];
|
||||
frame.ModuleName[0] = 0;
|
||||
frame.FileName[0] = 0;
|
||||
frame.LineNumber = 0;
|
||||
int32 nameLen = Math::Min<int32>(StringUtils::Length(name), ARRAY_COUNT(frame.FunctionName) - 1);
|
||||
Platform::MemoryCopy(frame.FunctionName, name, nameLen);
|
||||
frame.FunctionName[nameLen] = 0;
|
||||
|
||||
}
|
||||
free(names);
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -114,6 +114,9 @@ public:
|
||||
}
|
||||
static void GetSystemTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
static void GetUTCTime(int32& year, int32& month, int32& dayOfWeek, int32& day, int32& hour, int32& minute, int32& second, int32& millisecond);
|
||||
#if !BUILD_RELEASE
|
||||
static bool IsDebuggerPresent();
|
||||
#endif
|
||||
static bool Init();
|
||||
static void BeforeRun();
|
||||
static void Tick();
|
||||
@@ -143,6 +146,7 @@ public:
|
||||
static void* LoadLibrary(const Char* filename);
|
||||
static void FreeLibrary(void* handle);
|
||||
static void* GetProcAddress(void* handle, const char* symbol);
|
||||
static Array<StackFrame, HeapAllocation> GetStackFrames(int32 skipCount = 0, int32 maxDepth = 60, void* context = nullptr);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -565,7 +565,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
_vertexBuffer->Data.EnsureCapacity(_dirtyObjectsBuffer.Count() * 6 * sizeof(AtlasTileVertex));
|
||||
for (void* actorObject : _dirtyObjectsBuffer)
|
||||
{
|
||||
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actorObject];
|
||||
const GlobalSurfaceAtlasObject* objectPtr = surfaceAtlasData.Objects.TryGet(actorObject);
|
||||
if (!objectPtr)
|
||||
continue;
|
||||
const GlobalSurfaceAtlasObject& object = *objectPtr;
|
||||
for (int32 tileIndex = 0; tileIndex < 6; tileIndex++)
|
||||
{
|
||||
auto* tile = object.Tiles[tileIndex];
|
||||
@@ -587,7 +590,10 @@ bool GlobalSurfaceAtlasPass::Render(RenderContext& renderContext, GPUContext* co
|
||||
int32 tilesDrawn = 0;
|
||||
for (void* actorObject : _dirtyObjectsBuffer)
|
||||
{
|
||||
const auto& object = ((const Dictionary<Actor*, GlobalSurfaceAtlasObject>&)surfaceAtlasData.Objects)[actorObject];
|
||||
const GlobalSurfaceAtlasObject* objectPtr = surfaceAtlasData.Objects.TryGet(actorObject);
|
||||
if (!objectPtr)
|
||||
continue;
|
||||
const GlobalSurfaceAtlasObject& object = *objectPtr;
|
||||
|
||||
// Clear draw calls list
|
||||
renderContextTiles.List->DrawCalls.Clear();
|
||||
|
||||
@@ -693,6 +693,14 @@ void BinaryModule::Destroy(bool isReloading)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any scripting events
|
||||
for (auto i = ScriptingEvents::EventsTable.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
const ScriptingTypeHandle type = i->Key.First;
|
||||
if (type.Module == this)
|
||||
ScriptingEvents::EventsTable.Remove(i);
|
||||
}
|
||||
|
||||
// Unregister
|
||||
GetModules().RemoveKeepOrder(this);
|
||||
}
|
||||
|
||||
@@ -1026,30 +1026,28 @@ bool Scripting::IsTypeFromGameScripts(MClass* type)
|
||||
|
||||
void Scripting::RegisterObject(ScriptingObject* obj)
|
||||
{
|
||||
const Guid id = obj->GetID();
|
||||
ScopeLock lock(_objectsLocker);
|
||||
|
||||
//ASSERT(!_objectsDictionary.ContainsValue(obj));
|
||||
#if ENABLE_ASSERTION
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
ScriptingObjectData other;
|
||||
if (_objectsDictionary.TryGet(obj->GetID(), other))
|
||||
if (_objectsDictionary.TryGet(id, other))
|
||||
#else
|
||||
ScriptingObject* other;
|
||||
if (_objectsDictionary.TryGet(obj->GetID(), other))
|
||||
if (_objectsDictionary.TryGet(id, other))
|
||||
#endif
|
||||
{
|
||||
// Something went wrong...
|
||||
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", obj->GetID(), obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
|
||||
_objectsDictionary.Remove(obj->GetID());
|
||||
LOG(Error, "Objects registry already contains object with ID={0} (type '{3}')! Trying to register object {1} (type '{2}').", id, obj->ToString(), String(obj->GetClass()->GetFullName()), String(other->GetClass()->GetFullName()));
|
||||
}
|
||||
#else
|
||||
ASSERT(!_objectsDictionary.ContainsKey(obj->_id));
|
||||
#endif
|
||||
|
||||
#if USE_OBJECTS_DISPOSE_CRASHES_DEBUGGING
|
||||
LOG(Info, "[RegisterObject] obj = 0x{0:x}, {1}", (uint64)obj, String(ScriptingObjectData(obj).TypeName));
|
||||
#endif
|
||||
_objectsDictionary.Add(obj->GetID(), obj);
|
||||
_objectsDictionary[id] = obj;
|
||||
}
|
||||
|
||||
void Scripting::UnregisterObject(ScriptingObject* obj)
|
||||
|
||||
@@ -487,9 +487,12 @@ namespace FlaxEngine.GUI
|
||||
public void UpdateTransform()
|
||||
{
|
||||
// Actual pivot and negative pivot
|
||||
Float2.Multiply(ref _pivot, ref _bounds.Size, out var v1);
|
||||
Float2.Negate(ref v1, out var v2);
|
||||
Float2.Add(ref v1, ref _bounds.Location, out v1);
|
||||
//Float2.Multiply(ref _pivot, ref _bounds.Size, out var v1);
|
||||
//Float2.Negate(ref v1, out var v2);
|
||||
//Float2.Add(ref v1, ref _bounds.Location, out v1);
|
||||
var v1 = _pivot * _bounds.Size;
|
||||
var v2 = -v1;
|
||||
v1 += _bounds.Location;
|
||||
|
||||
// ------ Matrix3x3 based version:
|
||||
|
||||
@@ -518,18 +521,42 @@ namespace FlaxEngine.GUI
|
||||
// ------ Matrix2x2 based version:
|
||||
|
||||
// 2D transformation
|
||||
Matrix2x2.Scale(ref _scale, out Matrix2x2 m1);
|
||||
Matrix2x2.Shear(ref _shear, out Matrix2x2 m2);
|
||||
Matrix2x2.Multiply(ref m1, ref m2, out m1);
|
||||
Matrix2x2.Rotation(Mathf.DegreesToRadians * _rotation, out m2);
|
||||
Matrix2x2.Multiply(ref m1, ref m2, out m1);
|
||||
|
||||
//Matrix2x2.Scale(ref _scale, out Matrix2x2 m1);
|
||||
//Matrix2x2.Shear(ref _shear, out Matrix2x2 m2);
|
||||
//Matrix2x2.Multiply(ref m1, ref m2, out m1);
|
||||
|
||||
// Scale and Shear
|
||||
Matrix3x3 m1 = new Matrix3x3
|
||||
(
|
||||
_scale.X,
|
||||
_scale.X * (_shear.Y == 0 ? 0 : (1.0f / Mathf.Tan(Mathf.DegreesToRadians * (90 - Mathf.Clamp(_shear.Y, -89.0f, 89.0f))))),
|
||||
0,
|
||||
_scale.Y * (_shear.X == 0 ? 0 : (1.0f / Mathf.Tan(Mathf.DegreesToRadians * (90 - Mathf.Clamp(_shear.X, -89.0f, 89.0f))))),
|
||||
_scale.Y,
|
||||
0, 0, 0, 1
|
||||
);
|
||||
|
||||
|
||||
//Matrix2x2.Rotation(Mathf.DegreesToRadians * _rotation, out m2);
|
||||
float sin = Mathf.Sin(Mathf.DegreesToRadians * _rotation);
|
||||
float cos = Mathf.Cos(Mathf.DegreesToRadians * _rotation);
|
||||
|
||||
//Matrix2x2.Multiply(ref m1, ref m2, out m1);
|
||||
m1.M11 = (_scale.X * cos) + (m1.M12 * -sin);
|
||||
m1.M12 = (_scale.X * sin) + (m1.M12 * cos);
|
||||
float m21 = (m1.M21 * cos) + (_scale.Y * -sin);
|
||||
m1.M22 = (m1.M21 * sin) + (_scale.Y * cos);
|
||||
m1.M21 = m21;
|
||||
// Mix all the stuff
|
||||
Matrix3x3.Translation2D(ref v2, out Matrix3x3 m3);
|
||||
Matrix3x3 m4 = (Matrix3x3)m1;
|
||||
Matrix3x3.Multiply(ref m3, ref m4, out m3);
|
||||
Matrix3x3.Translation2D(ref v1, out m4);
|
||||
Matrix3x3.Multiply(ref m3, ref m4, out _cachedTransform);
|
||||
//Matrix3x3.Translation2D(ref v2, out Matrix3x3 m3);
|
||||
//Matrix3x3 m4 = (Matrix3x3)m1;
|
||||
//Matrix3x3.Multiply(ref m3, ref m4, out m3);
|
||||
//Matrix3x3.Translation2D(ref v1, out m4);
|
||||
//Matrix3x3.Multiply(ref m3, ref m4, out _cachedTransform);
|
||||
m1.M31 = (v2.X * m1.M11) + (v2.Y * m1.M21) + v1.X;
|
||||
m1.M32 = (v2.X * m1.M12) + (v2.Y * m1.M22) + v1.Y;
|
||||
_cachedTransform = m1;
|
||||
|
||||
// Cache inverted transform
|
||||
Matrix3x3.Invert(ref _cachedTransform, out _cachedTransformInv);
|
||||
|
||||
@@ -249,11 +249,17 @@ namespace Flax.Build
|
||||
if (dotnetSdkVersions == null)
|
||||
dotnetSdkVersions = GetVersions(Path.Combine(dotnetPath, "sdk"));
|
||||
if (dotnetRuntimeVersions == null)
|
||||
dotnetRuntimeVersions = GetVersions(Path.Combine(dotnetPath, "shared/Microsoft.NETCore.App"));
|
||||
dotnetRuntimeVersions = GetVersions(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App"));
|
||||
|
||||
dotnetSdkVersions = dotnetSdkVersions.Where(x => IsValidVersion(Path.Combine(dotnetPath, "sdk", x)));
|
||||
dotnetRuntimeVersions = dotnetRuntimeVersions.Where(x => IsValidVersion(Path.Combine(dotnetPath, "shared", "Microsoft.NETCore.App", x)));
|
||||
|
||||
dotnetSdkVersions = dotnetSdkVersions.OrderByDescending(ParseVersion);
|
||||
dotnetRuntimeVersions = dotnetRuntimeVersions.OrderByDescending(ParseVersion);
|
||||
|
||||
Log.Verbose($"Found the following .NET SDK versions: {string.Join(", ", dotnetSdkVersions)}");
|
||||
Log.Verbose($"Found the following .NET runtime versions: {string.Join(", ", dotnetRuntimeVersions)}");
|
||||
|
||||
string dotnetSdkVersion = dotnetSdkVersions.FirstOrDefault(x => ParseVersion(x).Major >= MinimumVersion.Major && ParseVersion(x).Major <= MaximumVersion.Major);
|
||||
string dotnetRuntimeVersion = dotnetRuntimeVersions.FirstOrDefault(x => ParseVersion(x).Major >= MinimumVersion.Major && ParseVersion(x).Major <= MaximumVersion.Major);
|
||||
if (string.IsNullOrEmpty(dotnetSdkVersion))
|
||||
@@ -276,7 +282,7 @@ namespace Flax.Build
|
||||
// Pick SDK runtime
|
||||
if (!TryAddHostRuntime(platform, architecture, rid) && !TryAddHostRuntime(platform, architecture, ridFallback))
|
||||
{
|
||||
var path = Path.Combine(RootPath, $"packs/Microsoft.NETCore.App.Host.{rid}/{RuntimeVersionName}/runtimes/{rid}/native");
|
||||
var path = Path.Combine(RootPath, "packs", $"Microsoft.NETCore.App.Host.{rid}", RuntimeVersionName, "runtimes", rid, "native");
|
||||
Log.Warning($"Missing .NET SDK host runtime for {platform} {architecture} ({path}).");
|
||||
return;
|
||||
}
|
||||
@@ -465,6 +471,11 @@ namespace Flax.Build
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private static bool IsValidVersion(string versionPath)
|
||||
{
|
||||
return File.Exists(Path.Combine(versionPath, ".version"));
|
||||
}
|
||||
|
||||
private static string SearchForDotnetLocationLinux()
|
||||
{
|
||||
if (File.Exists("/etc/dotnet/install_location")) // Officially recommended dotnet location file
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "7.0.0",
|
||||
"rollForward": "latestMinor"
|
||||
"rollForward": "latestMajor"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user