Merge branch 'master' into fix-spriterender-prefab
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.Windows;
|
||||
using FlaxEngine;
|
||||
|
||||
@@ -68,5 +69,15 @@ namespace FlaxEditor.Content
|
||||
{
|
||||
return new SceneItem(path, id);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||
{
|
||||
var id = ((SceneItem)item).ID;
|
||||
if (Level.FindScene(id) == null)
|
||||
{
|
||||
menu.AddButton("Open (additive)", () => { Editor.Instance.Scene.OpenScene(id, true); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,21 +48,21 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (buildSettings.SkipDotnetPackaging && data.Tools->UseSystemDotnet())
|
||||
{
|
||||
// Use system-installed .Net Runtime
|
||||
// Use system-installed .NET Runtime
|
||||
FileSystem::DeleteDirectory(dstDotnet);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Deploy .Net Runtime files
|
||||
// Deploy .NET Runtime files
|
||||
FileSystem::CreateDirectory(dstDotnet);
|
||||
String srcDotnet = depsRoot / TEXT("Dotnet");
|
||||
if (FileSystem::DirectoryExists(srcDotnet))
|
||||
{
|
||||
// Use prebuilt .Net installation for that platform
|
||||
LOG(Info, "Using .Net Runtime {} at {}", data.Tools->GetName(), srcDotnet);
|
||||
// Use prebuilt .NET installation for that platform
|
||||
LOG(Info, "Using .NET Runtime {} at {}", data.Tools->GetName(), srcDotnet);
|
||||
if (EditorUtilities::CopyDirectoryIfNewer(dstDotnet, srcDotnet, true))
|
||||
{
|
||||
data.Error(TEXT("Failed to copy .Net runtime data files."));
|
||||
data.Error(TEXT("Failed to copy .NET runtime data files."));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (canUseSystemDotnet && (aotMode == DotNetAOTModes::None || aotMode == DotNetAOTModes::ILC))
|
||||
{
|
||||
// Ask Flax.Build to provide .Net SDK location for the current platform
|
||||
// Ask Flax.Build to provide .NET SDK location for the current platform
|
||||
String sdks;
|
||||
bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), GAME_BUILD_DOTNET_VER), data.CacheDirectory);
|
||||
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
||||
@@ -101,7 +101,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (failed || !FileSystem::DirectoryExists(srcDotnet))
|
||||
{
|
||||
data.Error(TEXT("Failed to get .Net SDK location for a current platform."));
|
||||
data.Error(TEXT("Failed to get .NET SDK location for the current host platform."));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -110,19 +110,25 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
FileSystem::GetChildDirectories(versions, srcDotnet / TEXT("host/fxr"));
|
||||
if (versions.Count() == 0)
|
||||
{
|
||||
data.Error(TEXT("Failed to get .Net SDK location for a current platform."));
|
||||
data.Error(TEXT("Failed to find any .NET hostfxr versions for the current host platform."));
|
||||
return true;
|
||||
}
|
||||
for (String& version : versions)
|
||||
{
|
||||
version = String(StringUtils::GetFileName(version));
|
||||
if (!version.StartsWith(TEXT("7.")))
|
||||
if (!version.StartsWith(TEXT("7.")) && !version.StartsWith(TEXT("8."))) // .NET 7 or .NET 8
|
||||
version.Clear();
|
||||
}
|
||||
Sorting::QuickSort(versions);
|
||||
const String version = versions.Last();
|
||||
if (version.IsEmpty())
|
||||
{
|
||||
data.Error(TEXT("Failed to find supported .NET hostfxr version for the current host platform."));
|
||||
return true;
|
||||
}
|
||||
|
||||
FileSystem::NormalizePath(srcDotnet);
|
||||
LOG(Info, "Using .Net Runtime {} at {}", version, srcDotnet);
|
||||
LOG(Info, "Using .NET Runtime {} at {}", version, srcDotnet);
|
||||
|
||||
// Check if previously deployed files are valid (eg. system-installed .NET was updated from version 7.0.3 to 7.0.5)
|
||||
{
|
||||
@@ -158,13 +164,13 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (failed)
|
||||
{
|
||||
data.Error(TEXT("Failed to copy .Net runtime data files."));
|
||||
data.Error(TEXT("Failed to copy .NET runtime data files."));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ask Flax.Build to provide .Net Host Runtime location for the target platform
|
||||
// Ask Flax.Build to provide .NET Host Runtime location for the target platform
|
||||
String sdks;
|
||||
const Char *platformName, *archName;
|
||||
data.GetBuildPlatformName(platformName, archName);
|
||||
@@ -180,11 +186,11 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (failed || !FileSystem::DirectoryExists(srcDotnet))
|
||||
{
|
||||
data.Error(TEXT("Failed to get .Net SDK location for a current platform."));
|
||||
data.Error(TEXT("Failed to get .NET SDK location for the current host platform."));
|
||||
return true;
|
||||
}
|
||||
FileSystem::NormalizePath(srcDotnet);
|
||||
LOG(Info, "Using .Net Runtime {} at {}", TEXT("Host"), srcDotnet);
|
||||
LOG(Info, "Using .NET Runtime {} at {}", TEXT("Host"), srcDotnet);
|
||||
|
||||
// Deploy runtime files
|
||||
const Char* corlibPrivateName = TEXT("System.Private.CoreLib.dll");
|
||||
@@ -249,7 +255,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
DEPLOY_NATIVE_FILE("libmonosgen-2.0.dylib");
|
||||
DEPLOY_NATIVE_FILE("libSystem.IO.Compression.Native.dylib");
|
||||
DEPLOY_NATIVE_FILE("libSystem.Native.dylib");
|
||||
DEPLOY_NATIVE_FILE("libSystem.Net.Security.Native.dylib");
|
||||
DEPLOY_NATIVE_FILE("libSystem.NET.Security.Native.dylib");
|
||||
DEPLOY_NATIVE_FILE("libSystem.Security.Cryptography.Native.Apple.dylib");
|
||||
break;
|
||||
#undef DEPLOY_NATIVE_FILE
|
||||
@@ -257,7 +263,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (failed)
|
||||
{
|
||||
data.Error(TEXT("Failed to copy .Net runtime data files."));
|
||||
data.Error(TEXT("Failed to copy .NET runtime data files."));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -278,7 +284,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (ScriptsBuilder::RunBuildTool(args))
|
||||
{
|
||||
data.Error(TEXT("Failed to optimize .Net class library."));
|
||||
data.Error(TEXT("Failed to optimize .NET class library."));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,13 +404,23 @@ int32 Editor::LoadProduct()
|
||||
|
||||
// Create new project option
|
||||
if (CommandLine::Options.NewProject)
|
||||
{
|
||||
Array<String> projectFiles;
|
||||
FileSystem::DirectoryGetFiles(projectFiles, projectPath, TEXT("*.flaxproj"), DirectorySearchOption::TopDirectoryOnly);
|
||||
if (projectFiles.Count() == 1)
|
||||
{
|
||||
// Skip creating new project if it already exists
|
||||
LOG(Info, "Skip creatinng new project because it already exists");
|
||||
CommandLine::Options.NewProject.Reset();
|
||||
}
|
||||
}
|
||||
if (CommandLine::Options.NewProject)
|
||||
{
|
||||
if (projectPath.IsEmpty())
|
||||
projectPath = Platform::GetWorkingDirectory();
|
||||
else if (!FileSystem::DirectoryExists(projectPath))
|
||||
FileSystem::CreateDirectory(projectPath);
|
||||
FileSystem::NormalizePath(projectPath);
|
||||
|
||||
String folderName = StringUtils::GetFileName(projectPath);
|
||||
String tmpName;
|
||||
for (int32 i = 0; i < folderName.Length(); i++)
|
||||
|
||||
@@ -1343,108 +1343,6 @@ namespace FlaxEditor
|
||||
public float AutoRebuildNavMeshTimeoutMs;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[NativeMarshalling(typeof(VisualScriptLocalMarshaller))]
|
||||
internal struct VisualScriptLocal
|
||||
{
|
||||
public string Value;
|
||||
public string ValueTypeName;
|
||||
public uint NodeId;
|
||||
public int BoxId;
|
||||
}
|
||||
|
||||
[CustomMarshaller(typeof(VisualScriptLocal), MarshalMode.Default, typeof(VisualScriptLocalMarshaller))]
|
||||
internal static class VisualScriptLocalMarshaller
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct VisualScriptLocalNative
|
||||
{
|
||||
public IntPtr Value;
|
||||
public IntPtr ValueTypeName;
|
||||
public uint NodeId;
|
||||
public int BoxId;
|
||||
}
|
||||
|
||||
internal static VisualScriptLocal ConvertToManaged(VisualScriptLocalNative unmanaged) => ToManaged(unmanaged);
|
||||
internal static VisualScriptLocalNative ConvertToUnmanaged(VisualScriptLocal managed) => ToNative(managed);
|
||||
|
||||
internal static VisualScriptLocal ToManaged(VisualScriptLocalNative managed)
|
||||
{
|
||||
return new VisualScriptLocal()
|
||||
{
|
||||
Value = ManagedString.ToManaged(managed.Value),
|
||||
ValueTypeName = ManagedString.ToManaged(managed.ValueTypeName),
|
||||
NodeId = managed.NodeId,
|
||||
BoxId = managed.BoxId,
|
||||
};
|
||||
}
|
||||
|
||||
internal static VisualScriptLocalNative ToNative(VisualScriptLocal managed)
|
||||
{
|
||||
return new VisualScriptLocalNative()
|
||||
{
|
||||
Value = ManagedString.ToNative(managed.Value),
|
||||
ValueTypeName = ManagedString.ToNative(managed.ValueTypeName),
|
||||
NodeId = managed.NodeId,
|
||||
BoxId = managed.BoxId,
|
||||
};
|
||||
}
|
||||
|
||||
internal static void Free(VisualScriptLocalNative unmanaged)
|
||||
{
|
||||
ManagedString.Free(unmanaged.Value);
|
||||
ManagedString.Free(unmanaged.ValueTypeName);
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[NativeMarshalling(typeof(VisualScriptStackFrameMarshaller))]
|
||||
internal struct VisualScriptStackFrame
|
||||
{
|
||||
public VisualScript Script;
|
||||
public uint NodeId;
|
||||
public int BoxId;
|
||||
}
|
||||
|
||||
[CustomMarshaller(typeof(VisualScriptStackFrame), MarshalMode.Default, typeof(VisualScriptStackFrameMarshaller))]
|
||||
internal static class VisualScriptStackFrameMarshaller
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct VisualScriptStackFrameNative
|
||||
{
|
||||
public IntPtr Script;
|
||||
public uint NodeId;
|
||||
public int BoxId;
|
||||
}
|
||||
|
||||
internal static VisualScriptStackFrame ConvertToManaged(VisualScriptStackFrameNative unmanaged) => ToManaged(unmanaged);
|
||||
internal static VisualScriptStackFrameNative ConvertToUnmanaged(VisualScriptStackFrame managed) => ToNative(managed);
|
||||
|
||||
internal static VisualScriptStackFrame ToManaged(VisualScriptStackFrameNative managed)
|
||||
{
|
||||
return new VisualScriptStackFrame()
|
||||
{
|
||||
Script = VisualScriptMarshaller.ConvertToManaged(managed.Script),
|
||||
NodeId = managed.NodeId,
|
||||
BoxId = managed.BoxId,
|
||||
};
|
||||
}
|
||||
|
||||
internal static VisualScriptStackFrameNative ToNative(VisualScriptStackFrame managed)
|
||||
{
|
||||
return new VisualScriptStackFrameNative()
|
||||
{
|
||||
Script = VisualScriptMarshaller.ConvertToUnmanaged(managed.Script),
|
||||
NodeId = managed.NodeId,
|
||||
BoxId = managed.BoxId,
|
||||
};
|
||||
}
|
||||
|
||||
internal static void Free(VisualScriptStackFrameNative unmanaged)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
internal void BuildCommand(string arg)
|
||||
{
|
||||
if (TryBuildCommand(arg))
|
||||
@@ -1723,21 +1621,6 @@ namespace FlaxEditor
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_RunVisualScriptBreakpointLoopTick", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||
internal static partial void Internal_RunVisualScriptBreakpointLoopTick(float deltaTime);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetVisualScriptLocals", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||
[return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "localsCount")]
|
||||
internal static partial VisualScriptLocal[] Internal_GetVisualScriptLocals(out int localsCount);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetVisualScriptStackFrames", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||
[return: MarshalUsing(typeof(FlaxEngine.Interop.ArrayMarshaller<,>), CountElementName = "stackFrameCount")]
|
||||
internal static partial VisualScriptStackFrame[] Internal_GetVisualScriptStackFrames(out int stackFrameCount);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_GetVisualScriptPreviousScopeFrame", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||
internal static partial VisualScriptStackFrame Internal_GetVisualScriptPreviousScopeFrame();
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_EvaluateVisualScriptLocal", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||
[return: MarshalAs(UnmanagedType.U1)]
|
||||
internal static partial bool Internal_EvaluateVisualScriptLocal(IntPtr script, ref VisualScriptLocal local);
|
||||
|
||||
[LibraryImport("FlaxEngine", EntryPoint = "EditorInternal_DeserializeSceneObject", StringMarshalling = StringMarshalling.Custom, StringMarshallingCustomType = typeof(StringMarshaller))]
|
||||
internal static partial void Internal_DeserializeSceneObject(IntPtr sceneObject, string json);
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace FlaxEditor.GUI.Dialogs
|
||||
{
|
||||
@@ -30,6 +32,8 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
private const float HSVMargin = 0.0f;
|
||||
private const float ChannelsMargin = 4.0f;
|
||||
private const float ChannelTextWidth = 12.0f;
|
||||
private const float SavedColorButtonWidth = 20.0f;
|
||||
private const float SavedColorButtonHeight = 20.0f;
|
||||
|
||||
private Color _initialValue;
|
||||
private Color _value;
|
||||
@@ -52,6 +56,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
private Button _cOK;
|
||||
private Button _cEyedropper;
|
||||
|
||||
private List<Color> _savedColors = new List<Color>();
|
||||
private List<Button> _savedColorButtons = new List<Button>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the selected color.
|
||||
/// </summary>
|
||||
@@ -111,6 +118,12 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
_onChanged = colorChanged;
|
||||
_onClosed = pickerClosed;
|
||||
|
||||
// Get saved colors if they exist
|
||||
if (Editor.Instance.ProjectCache.TryGetCustomData("ColorPickerSavedColors", out var savedColors))
|
||||
{
|
||||
_savedColors = JsonSerializer.Deserialize<List<Color>>(savedColors);
|
||||
}
|
||||
|
||||
// Selector
|
||||
_cSelector = new ColorSelectorWithSliders(180, 18)
|
||||
{
|
||||
@@ -195,6 +208,9 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
};
|
||||
_cOK.Clicked += OnSubmit;
|
||||
|
||||
// Create saved color buttons
|
||||
CreateAllSaveButtons();
|
||||
|
||||
// Eyedropper button
|
||||
var style = Style.Current;
|
||||
_cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin)
|
||||
@@ -216,6 +232,50 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
SelectedColor = initialValue;
|
||||
}
|
||||
|
||||
private void OnSavedColorButtonClicked(Button button)
|
||||
{
|
||||
if (button.Tag == null)
|
||||
{
|
||||
// Prevent setting same color 2 times... because why...
|
||||
foreach (var color in _savedColors)
|
||||
{
|
||||
if (color == _value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Set color of button to current value;
|
||||
button.BackgroundColor = _value;
|
||||
button.BackgroundColorHighlighted = _value;
|
||||
button.BackgroundColorSelected = _value.RGBMultiplied(0.8f);
|
||||
button.Text = "";
|
||||
button.Tag = _value;
|
||||
|
||||
// Save new colors
|
||||
_savedColors.Add(_value);
|
||||
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List<Color>));
|
||||
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
|
||||
|
||||
// create new + button
|
||||
if (_savedColorButtons.Count < 8)
|
||||
{
|
||||
var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
Tag = null,
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
_savedColorButtons.Add(savedColorButton);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SelectedColor = (Color)button.Tag;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnColorPicked(Color32 colorPicked)
|
||||
{
|
||||
if (_activeEyedropper)
|
||||
@@ -340,6 +400,111 @@ namespace FlaxEditor.GUI.Dialogs
|
||||
return base.OnKeyDown(key);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||
{
|
||||
if (base.OnMouseUp(location, button))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var child = GetChildAtRecursive(location);
|
||||
if (button == MouseButton.Right && child is Button b && b.Tag is Color c)
|
||||
{
|
||||
// Show menu
|
||||
var menu = new ContextMenu.ContextMenu();
|
||||
var replaceButton = menu.AddButton("Replace");
|
||||
replaceButton.Clicked += () => OnSavedColorReplace(b);
|
||||
var deleteButton = menu.AddButton("Delete");
|
||||
deleteButton.Clicked += () => OnSavedColorDelete(b);
|
||||
_disableEvents = true;
|
||||
menu.Show(this, location);
|
||||
menu.VisibleChanged += (c) => _disableEvents = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void OnSavedColorReplace(Button button)
|
||||
{
|
||||
// Prevent setting same color 2 times... because why...
|
||||
foreach (var color in _savedColors)
|
||||
{
|
||||
if (color == _value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Set new Color in spot
|
||||
for (int i = 0; i < _savedColors.Count; i++)
|
||||
{
|
||||
var color = _savedColors[i];
|
||||
if (color == (Color)button.Tag)
|
||||
{
|
||||
color = _value;
|
||||
}
|
||||
}
|
||||
|
||||
// Set color of button to current value;
|
||||
button.BackgroundColor = _value;
|
||||
button.BackgroundColorHighlighted = _value;
|
||||
button.Text = "";
|
||||
button.Tag = _value;
|
||||
|
||||
// Save new colors
|
||||
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List<Color>));
|
||||
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
|
||||
}
|
||||
|
||||
private void OnSavedColorDelete(Button button)
|
||||
{
|
||||
_savedColors.Remove((Color)button.Tag);
|
||||
|
||||
foreach (var b in _savedColorButtons)
|
||||
{
|
||||
Children.Remove(b);
|
||||
}
|
||||
_savedColorButtons.Clear();
|
||||
|
||||
CreateAllSaveButtons();
|
||||
|
||||
// Save new colors
|
||||
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List<Color>));
|
||||
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
|
||||
}
|
||||
|
||||
private void CreateAllSaveButtons()
|
||||
{
|
||||
// Create saved color buttons
|
||||
for (int i = 0; i < _savedColors.Count; i++)
|
||||
{
|
||||
var savedColor = _savedColors[i];
|
||||
var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
{
|
||||
Parent = this,
|
||||
Tag = savedColor,
|
||||
BackgroundColor = savedColor,
|
||||
BackgroundColorHighlighted = savedColor,
|
||||
BackgroundColorSelected = savedColor.RGBMultiplied(0.8f),
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
_savedColorButtons.Add(savedColorButton);
|
||||
}
|
||||
if (_savedColors.Count < 8)
|
||||
{
|
||||
var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
|
||||
{
|
||||
Text = "+",
|
||||
Parent = this,
|
||||
Tag = null,
|
||||
};
|
||||
savedColorButton.ButtonClicked += (b) => OnSavedColorButtonClicked(b);
|
||||
_savedColorButtons.Add(savedColorButton);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnSubmit()
|
||||
{
|
||||
|
||||
@@ -526,133 +526,6 @@ DEFINE_INTERNAL_CALL(void) EditorInternal_RunVisualScriptBreakpointLoopTick(floa
|
||||
Engine::OnDraw();
|
||||
}
|
||||
|
||||
struct VisualScriptLocalManaged
|
||||
{
|
||||
MString* Value;
|
||||
MString* ValueTypeName;
|
||||
uint32 NodeId;
|
||||
int32 BoxId;
|
||||
};
|
||||
|
||||
DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptLocals(int* localsCount)
|
||||
{
|
||||
MArray* result = nullptr;
|
||||
*localsCount = 0;
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
if (stack && stack->Scope)
|
||||
{
|
||||
const int32 count = stack->Scope->Parameters.Length() + stack->Scope->ReturnedValues.Count();
|
||||
const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptLocal");
|
||||
ASSERT(mclass);
|
||||
result = MCore::Array::New(mclass, count);
|
||||
VisualScriptLocalManaged local;
|
||||
local.NodeId = MAX_uint32;
|
||||
if (stack->Scope->Parameters.Length() != 0)
|
||||
{
|
||||
auto s = stack;
|
||||
while (s->PreviousFrame && s->PreviousFrame->Scope == stack->Scope)
|
||||
s = s->PreviousFrame;
|
||||
if (s)
|
||||
local.NodeId = s->Node->ID;
|
||||
}
|
||||
VisualScriptLocalManaged* resultPtr = MCore::Array::GetAddress<VisualScriptLocalManaged>(result);
|
||||
for (int32 i = 0; i < stack->Scope->Parameters.Length(); i++)
|
||||
{
|
||||
auto& v = stack->Scope->Parameters[i];
|
||||
local.BoxId = i + 1;
|
||||
local.Value = MUtils::ToString(v.ToString());
|
||||
local.ValueTypeName = MUtils::ToString(v.Type.GetTypeName());
|
||||
resultPtr[i] = local;
|
||||
}
|
||||
for (int32 i = 0; i < stack->Scope->ReturnedValues.Count(); i++)
|
||||
{
|
||||
auto& v = stack->Scope->ReturnedValues[i];
|
||||
local.NodeId = v.NodeId;
|
||||
local.BoxId = v.BoxId;
|
||||
local.Value = MUtils::ToString(v.Value.ToString());
|
||||
local.ValueTypeName = MUtils::ToString(v.Value.Type.GetTypeName());
|
||||
resultPtr[stack->Scope->Parameters.Length() + i] = local;
|
||||
}
|
||||
*localsCount = count;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct VisualScriptStackFrameManaged
|
||||
{
|
||||
MObject* Script;
|
||||
uint32 NodeId;
|
||||
int32 BoxId;
|
||||
};
|
||||
|
||||
DEFINE_INTERNAL_CALL(MArray*) EditorInternal_GetVisualScriptStackFrames(int* stackFramesCount)
|
||||
{
|
||||
MArray* result = nullptr;
|
||||
*stackFramesCount = 0;
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
if (stack)
|
||||
{
|
||||
int32 count = 0;
|
||||
auto s = stack;
|
||||
while (s)
|
||||
{
|
||||
s = s->PreviousFrame;
|
||||
count++;
|
||||
}
|
||||
const MClass* mclass = ((NativeBinaryModule*)GetBinaryModuleFlaxEngine())->Assembly->GetClass("FlaxEditor.Editor+VisualScriptStackFrame");
|
||||
ASSERT(mclass);
|
||||
result = MCore::Array::New(mclass, count);
|
||||
VisualScriptStackFrameManaged* resultPtr = MCore::Array::GetAddress<VisualScriptStackFrameManaged>(result);
|
||||
s = stack;
|
||||
count = 0;
|
||||
while (s)
|
||||
{
|
||||
VisualScriptStackFrameManaged frame;
|
||||
frame.Script = s->Script->GetOrCreateManagedInstance();
|
||||
frame.NodeId = s->Node->ID;
|
||||
frame.BoxId = s->Box ? s->Box->ID : MAX_uint32;
|
||||
resultPtr[count] = frame;
|
||||
s = s->PreviousFrame;
|
||||
count++;
|
||||
}
|
||||
*stackFramesCount = count;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(VisualScriptStackFrameManaged) EditorInternal_GetVisualScriptPreviousScopeFrame()
|
||||
{
|
||||
VisualScriptStackFrameManaged frame;
|
||||
Platform::MemoryClear(&frame, sizeof(frame));
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
if (stack)
|
||||
{
|
||||
auto s = stack;
|
||||
while (s->PreviousFrame && s->PreviousFrame->Scope == stack->Scope)
|
||||
s = s->PreviousFrame;
|
||||
if (s && s->PreviousFrame)
|
||||
{
|
||||
s = s->PreviousFrame;
|
||||
frame.Script = s->Script->GetOrCreateManagedInstance();
|
||||
frame.NodeId = s->Node->ID;
|
||||
frame.BoxId = s->Box ? s->Box->ID : MAX_uint32;
|
||||
}
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(bool) EditorInternal_EvaluateVisualScriptLocal(VisualScript* script, VisualScriptLocalManaged* local)
|
||||
{
|
||||
Variant v;
|
||||
if (VisualScripting::Evaluate(script, VisualScripting::GetThreadStackTop()->Instance, local->NodeId, local->BoxId, v))
|
||||
{
|
||||
local->Value = MUtils::ToString(v.ToString());
|
||||
local->ValueTypeName = MUtils::ToString(v.Type.GetTypeName());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
DEFINE_INTERNAL_CALL(void) EditorInternal_DeserializeSceneObject(SceneObject* sceneObject, MString* jsonObj)
|
||||
{
|
||||
PROFILE_CPU_NAMED("DeserializeSceneObject");
|
||||
|
||||
@@ -489,6 +489,95 @@ void ManagedEditor::RequestStartPlayOnEditMode()
|
||||
Internal_RequestStartPlayOnEditMode->Invoke(GetManagedInstance(), nullptr, nullptr);
|
||||
}
|
||||
|
||||
Array<ManagedEditor::VisualScriptStackFrame> ManagedEditor::GetVisualScriptStackFrames()
|
||||
{
|
||||
Array<VisualScriptStackFrame> result;
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
auto s = stack;
|
||||
while (s)
|
||||
{
|
||||
VisualScriptStackFrame& frame = result.AddOne();
|
||||
frame.Script = s->Script;
|
||||
frame.NodeId = s->Node->ID;
|
||||
frame.BoxId = s->Box ? s->Box->ID : MAX_uint32;
|
||||
s = s->PreviousFrame;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ManagedEditor::VisualScriptStackFrame ManagedEditor::GetVisualScriptPreviousScopeFrame()
|
||||
{
|
||||
VisualScriptStackFrame frame;
|
||||
Platform::MemoryClear(&frame, sizeof(frame));
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
if (stack)
|
||||
{
|
||||
auto s = stack;
|
||||
while (s->PreviousFrame && s->PreviousFrame->Scope == stack->Scope)
|
||||
s = s->PreviousFrame;
|
||||
if (s && s->PreviousFrame)
|
||||
{
|
||||
s = s->PreviousFrame;
|
||||
frame.Script = s->Script;
|
||||
frame.NodeId = s->Node->ID;
|
||||
frame.BoxId = s->Box ? s->Box->ID : MAX_uint32;
|
||||
}
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
Array<ManagedEditor::VisualScriptLocal> ManagedEditor::GetVisualScriptLocals()
|
||||
{
|
||||
Array<VisualScriptLocal> result;
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
if (stack && stack->Scope)
|
||||
{
|
||||
const int32 count = stack->Scope->Parameters.Length() + stack->Scope->ReturnedValues.Count();
|
||||
result.Resize(count);
|
||||
VisualScriptLocal local;
|
||||
local.NodeId = MAX_uint32;
|
||||
if (stack->Scope->Parameters.Length() != 0)
|
||||
{
|
||||
auto s = stack;
|
||||
while (s->PreviousFrame && s->PreviousFrame->Scope == stack->Scope)
|
||||
s = s->PreviousFrame;
|
||||
if (s)
|
||||
local.NodeId = s->Node->ID;
|
||||
}
|
||||
for (int32 i = 0; i < stack->Scope->Parameters.Length(); i++)
|
||||
{
|
||||
auto& v = stack->Scope->Parameters[i];
|
||||
local.BoxId = i + 1;
|
||||
local.Value = v.ToString();
|
||||
local.ValueTypeName = v.Type.GetTypeName();
|
||||
result[i] = local;
|
||||
}
|
||||
for (int32 i = 0; i < stack->Scope->ReturnedValues.Count(); i++)
|
||||
{
|
||||
auto& v = stack->Scope->ReturnedValues[i];
|
||||
local.NodeId = v.NodeId;
|
||||
local.BoxId = v.BoxId;
|
||||
local.Value = v.Value.ToString();
|
||||
local.ValueTypeName = v.Value.Type.GetTypeName();
|
||||
result[stack->Scope->Parameters.Length() + i] = local;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ManagedEditor::EvaluateVisualScriptLocal(VisualScript* script, VisualScriptLocal& local)
|
||||
{
|
||||
Variant v;
|
||||
const auto stack = VisualScripting::GetThreadStackTop();
|
||||
if (stack && VisualScripting::Evaluate(script, stack->Instance, local.NodeId, local.BoxId, v))
|
||||
{
|
||||
local.Value = v.ToString();
|
||||
local.ValueTypeName = v.Type.GetTypeName();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ManagedEditor::OnEditorAssemblyLoaded(MAssembly* assembly)
|
||||
{
|
||||
ASSERT(!HasManagedInstance());
|
||||
|
||||
@@ -210,6 +210,31 @@ public:
|
||||
API_FUNCTION() static bool TryRestoreImportOptions(API_PARAM(Ref) AudioTool::Options& options, String assetPath);
|
||||
#endif
|
||||
|
||||
public:
|
||||
API_STRUCT(Internal, NoDefault) struct VisualScriptStackFrame
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(VisualScriptStackFrame);
|
||||
|
||||
API_FIELD() class VisualScript* Script;
|
||||
API_FIELD() uint32 NodeId;
|
||||
API_FIELD() int32 BoxId;
|
||||
};
|
||||
|
||||
API_STRUCT(Internal, NoDefault) struct VisualScriptLocal
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(VisualScriptLocal);
|
||||
|
||||
API_FIELD() String Value;
|
||||
API_FIELD() String ValueTypeName;
|
||||
API_FIELD() uint32 NodeId;
|
||||
API_FIELD() int32 BoxId;
|
||||
};
|
||||
|
||||
API_FUNCTION(Internal) static Array<VisualScriptStackFrame> GetVisualScriptStackFrames();
|
||||
API_FUNCTION(Internal) static VisualScriptStackFrame GetVisualScriptPreviousScopeFrame();
|
||||
API_FUNCTION(Internal) static Array<VisualScriptLocal> GetVisualScriptLocals();
|
||||
API_FUNCTION(Internal) static bool EvaluateVisualScriptLocal(VisualScript* script, API_PARAM(Ref) VisualScriptLocal& local);
|
||||
|
||||
private:
|
||||
void OnEditorAssemblyLoaded(MAssembly* assembly);
|
||||
|
||||
|
||||
@@ -755,6 +755,29 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnDeleted(SurfaceNodeActions action)
|
||||
{
|
||||
// Unlink from the current parent (when deleted by user)
|
||||
var node = Node;
|
||||
if (node != null)
|
||||
{
|
||||
if (action == SurfaceNodeActions.User)
|
||||
{
|
||||
var decorators = node.DecoratorIds;
|
||||
decorators.Remove(ID);
|
||||
node.DecoratorIds = decorators;
|
||||
}
|
||||
else
|
||||
{
|
||||
node._decorators = null;
|
||||
node.ResizeAuto();
|
||||
}
|
||||
}
|
||||
|
||||
base.OnDeleted(action);
|
||||
}
|
||||
|
||||
public override void OnSurfaceCanEditChanged(bool canEdit)
|
||||
{
|
||||
base.OnSurfaceCanEditChanged(canEdit);
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace FlaxEditor.Surface.Undo
|
||||
private ushort _typeId;
|
||||
private Float2 _nodeLocation;
|
||||
private object[] _nodeValues;
|
||||
private SurfaceNodeActions _actionType = SurfaceNodeActions.User; // Action usage flow is first to apply user effect such as removing/adding node, then we use Undo type so node can react to this
|
||||
|
||||
public AddRemoveNodeAction(SurfaceNode node, bool isAdd)
|
||||
{
|
||||
@@ -38,6 +39,7 @@ namespace FlaxEditor.Surface.Undo
|
||||
Add();
|
||||
else
|
||||
Remove();
|
||||
_actionType = SurfaceNodeActions.Undo;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -67,8 +69,8 @@ namespace FlaxEditor.Surface.Undo
|
||||
else if (_nodeValues != null && _nodeValues.Length != 0)
|
||||
throw new InvalidOperationException("Invalid node values.");
|
||||
node.Location = _nodeLocation;
|
||||
context.OnControlLoaded(node, SurfaceNodeActions.Undo);
|
||||
node.OnSurfaceLoaded(SurfaceNodeActions.Undo);
|
||||
context.OnControlLoaded(node, _actionType);
|
||||
node.OnSurfaceLoaded(_actionType);
|
||||
|
||||
context.MarkAsModified();
|
||||
}
|
||||
@@ -89,7 +91,7 @@ namespace FlaxEditor.Surface.Undo
|
||||
|
||||
// Remove node
|
||||
context.Nodes.Remove(node);
|
||||
context.OnControlDeleted(node, SurfaceNodeActions.Undo);
|
||||
context.OnControlDeleted(node, _actionType);
|
||||
|
||||
context.MarkAsModified();
|
||||
}
|
||||
|
||||
@@ -837,7 +837,7 @@ namespace FlaxEditor.Surface
|
||||
actions.Add(action);
|
||||
}
|
||||
|
||||
Undo.AddAction(new MultiUndoAction(actions, nodes.Count == 1 ? "Remove node" : "Remove nodes"));
|
||||
AddBatchedUndoAction(new MultiUndoAction(actions, nodes.Count == 1 ? "Remove node" : "Remove nodes"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,19 @@ namespace FlaxEditor.Surface
|
||||
return true;
|
||||
}
|
||||
|
||||
private string GetBoxDebuggerTooltip(ref Editor.VisualScriptLocal local)
|
||||
{
|
||||
if (string.IsNullOrEmpty(local.ValueTypeName))
|
||||
{
|
||||
if (string.IsNullOrEmpty(local.Value))
|
||||
return string.Empty;
|
||||
return local.Value;
|
||||
}
|
||||
if (string.IsNullOrEmpty(local.Value))
|
||||
return $"({local.ValueTypeName})";
|
||||
return $"{local.Value}\n({local.ValueTypeName})";
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnNodeBreakpointEdited(SurfaceNode node)
|
||||
{
|
||||
@@ -95,7 +108,7 @@ namespace FlaxEditor.Surface
|
||||
ref var local = ref state.Locals[i];
|
||||
if (local.BoxId == box.ID && local.NodeId == box.ParentNode.ID)
|
||||
{
|
||||
text = $"{local.Value ?? string.Empty} ({local.ValueTypeName})";
|
||||
text = GetBoxDebuggerTooltip(ref local);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -107,7 +120,7 @@ namespace FlaxEditor.Surface
|
||||
ref var local = ref state.Locals[i];
|
||||
if (local.BoxId == connectedBox.ID && local.NodeId == connectedBox.ParentNode.ID)
|
||||
{
|
||||
text = $"{local.Value ?? string.Empty} ({local.ValueTypeName})";
|
||||
text = GetBoxDebuggerTooltip(ref local);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -123,9 +136,33 @@ namespace FlaxEditor.Surface
|
||||
BoxId = box.ID,
|
||||
};
|
||||
var script = ((Windows.Assets.VisualScriptWindow)box.Surface.Owner).Asset;
|
||||
if (Editor.Internal_EvaluateVisualScriptLocal(Object.GetUnmanagedPtr(script), ref local))
|
||||
if (Editor.EvaluateVisualScriptLocal(script, ref local))
|
||||
{
|
||||
text = $"{local.Value ?? string.Empty} ({local.ValueTypeName})";
|
||||
// Check if got no value (null)
|
||||
if (string.IsNullOrEmpty(local.ValueTypeName) && string.Equals(local.Value, "null", StringComparison.Ordinal))
|
||||
{
|
||||
var connections = box.Connections;
|
||||
if (connections.Count == 0 && box.Archetype.ValueIndex >= 0 && box.ParentNode.Values != null && box.Archetype.ValueIndex < box.ParentNode.Values.Length)
|
||||
{
|
||||
// Special case when there is no value but the box has no connection and uses default value
|
||||
var defaultValue = box.ParentNode.Values[box.Archetype.ValueIndex];
|
||||
if (defaultValue != null)
|
||||
{
|
||||
local.Value = defaultValue.ToString();
|
||||
local.ValueTypeName = defaultValue.GetType().FullName;
|
||||
}
|
||||
}
|
||||
else if (connections.Count == 1)
|
||||
{
|
||||
// Special case when there is no value but the box has a connection with valid value to try to use it instead
|
||||
box = connections[0];
|
||||
local.NodeId = box.ParentNode.ID;
|
||||
local.BoxId = box.ID;
|
||||
Editor.EvaluateVisualScriptLocal(script, ref local);
|
||||
}
|
||||
}
|
||||
|
||||
text = GetBoxDebuggerTooltip(ref local);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -797,11 +797,12 @@ namespace FlaxEditor.Windows.Assets
|
||||
}
|
||||
|
||||
// Check if any breakpoint was hit
|
||||
for (int i = 0; i < Surface.Breakpoints.Count; i++)
|
||||
var breakpoints = Surface.Breakpoints;
|
||||
for (int i = 0; i < breakpoints.Count; i++)
|
||||
{
|
||||
if (Surface.Breakpoints[i].ID == flowInfo.NodeId)
|
||||
if (breakpoints[i].ID == flowInfo.NodeId)
|
||||
{
|
||||
OnDebugBreakpointHit(ref flowInfo, Surface.Breakpoints[i]);
|
||||
OnDebugBreakpointHit(ref flowInfo, breakpoints[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -820,7 +821,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
var state = (BreakpointHangState)Editor.Instance.Simulation.BreakpointHangTag;
|
||||
if (state.Locals == null)
|
||||
{
|
||||
state.Locals = Editor.Internal_GetVisualScriptLocals(out var _);
|
||||
state.Locals = Editor.GetVisualScriptLocals();
|
||||
Editor.Instance.Simulation.BreakpointHangTag = state;
|
||||
}
|
||||
return state;
|
||||
@@ -831,7 +832,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
var state = (BreakpointHangState)Editor.Instance.Simulation.BreakpointHangTag;
|
||||
if (state.StackFrames == null)
|
||||
{
|
||||
state.StackFrames = Editor.Internal_GetVisualScriptStackFrames(out var _);
|
||||
state.StackFrames = Editor.GetVisualScriptStackFrames();
|
||||
Editor.Instance.Simulation.BreakpointHangTag = state;
|
||||
}
|
||||
return state;
|
||||
@@ -976,7 +977,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
return;
|
||||
|
||||
// Break on any of the output connects from the previous scope node
|
||||
var frame = Editor.Internal_GetVisualScriptPreviousScopeFrame();
|
||||
var frame = Editor.GetVisualScriptPreviousScopeFrame();
|
||||
if (frame.Script != null)
|
||||
{
|
||||
if (_debugStepOutNodesIds == null)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEngine;
|
||||
@@ -146,6 +148,31 @@ namespace FlaxEditor.Windows
|
||||
contextMenu.AddButton("Break Prefab Link", Editor.Prefabs.BreakLinks);
|
||||
}
|
||||
|
||||
// Load additional scenes option
|
||||
|
||||
if (!hasSthSelected)
|
||||
{
|
||||
var allScenes = FlaxEngine.Content.GetAllAssetsByType(typeof(SceneAsset));
|
||||
var loadedSceneIds = Editor.Instance.Scene.Root.ChildNodes.Select(node => node.ID).ToList();
|
||||
var unloadedScenes = allScenes.Where(sceneId => !loadedSceneIds.Contains(sceneId)).ToList();
|
||||
if (unloadedScenes.Count > 0)
|
||||
{
|
||||
contextMenu.AddSeparator();
|
||||
var childCM = contextMenu.GetOrAddChildMenu("Open Scene");
|
||||
foreach (var sceneGuid in unloadedScenes)
|
||||
{
|
||||
if (FlaxEngine.Content.GetAssetInfo(sceneGuid, out var unloadedScene))
|
||||
{
|
||||
var splitPath = unloadedScene.Path.Split('/');
|
||||
var sceneName = splitPath[^1];
|
||||
if (splitPath[^1].EndsWith(".scene"))
|
||||
sceneName = sceneName[..^6];
|
||||
childCM.ContextMenu.AddButton(sceneName, () => { Editor.Instance.Scene.OpenScene(sceneGuid, true); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spawning actors options
|
||||
|
||||
contextMenu.AddSeparator();
|
||||
|
||||
@@ -144,7 +144,10 @@ BehaviorKnowledge::~BehaviorKnowledge()
|
||||
|
||||
void BehaviorKnowledge::InitMemory(BehaviorTree* tree)
|
||||
{
|
||||
ASSERT_LOW_LAYER(!Tree && tree);
|
||||
if (Tree)
|
||||
FreeMemory();
|
||||
if (!tree)
|
||||
return;
|
||||
Tree = tree;
|
||||
Blackboard = Variant::NewValue(tree->Graph.Root->BlackboardType);
|
||||
RelevantNodes.Resize(tree->Graph.NodesCount, false);
|
||||
|
||||
@@ -892,6 +892,11 @@ void VisualScriptExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value&
|
||||
PrintStack(LogType::Error);
|
||||
break;
|
||||
}
|
||||
if (boxBase->ID == 1)
|
||||
{
|
||||
value = instance;
|
||||
break;
|
||||
}
|
||||
// TODO: check if instance is of event type (including inheritance)
|
||||
|
||||
// Add Visual Script method to the event bindings table
|
||||
|
||||
@@ -192,6 +192,9 @@ bool RenderBuffers::Init(int32 width, int32 height)
|
||||
_viewport = Viewport(0, 0, static_cast<float>(width), static_cast<float>(height));
|
||||
LastEyeAdaptationTime = 0;
|
||||
|
||||
// Flush any pool render targets to prevent over-allocating GPU memory when resizing game viewport
|
||||
RenderTargetPool::Flush(false, 4);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,35 +7,31 @@
|
||||
|
||||
struct Entry
|
||||
{
|
||||
bool IsOccupied;
|
||||
GPUTexture* RT;
|
||||
uint64 LastFrameTaken;
|
||||
uint64 LastFrameReleased;
|
||||
uint32 DescriptionHash;
|
||||
bool IsOccupied;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
Array<Entry> TemporaryRTs(64);
|
||||
Array<Entry> TemporaryRTs;
|
||||
}
|
||||
|
||||
void RenderTargetPool::Flush(bool force)
|
||||
void RenderTargetPool::Flush(bool force, int32 framesOffset)
|
||||
{
|
||||
const uint64 framesOffset = 3 * 60;
|
||||
if (framesOffset < 0)
|
||||
framesOffset = 3 * 60; // For how many frames RTs should be cached (by default)
|
||||
const uint64 maxReleaseFrame = Engine::FrameCount - framesOffset;
|
||||
force |= Engine::ShouldExit();
|
||||
|
||||
for (int32 i = 0; i < TemporaryRTs.Count(); i++)
|
||||
{
|
||||
auto& tmp = TemporaryRTs[i];
|
||||
|
||||
if (!tmp.IsOccupied && (force || (tmp.LastFrameReleased < maxReleaseFrame)))
|
||||
const auto& e = TemporaryRTs[i];
|
||||
if (!e.IsOccupied && (force || e.LastFrameReleased < maxReleaseFrame))
|
||||
{
|
||||
// Release
|
||||
tmp.RT->DeleteObjectNow();
|
||||
TemporaryRTs.RemoveAt(i);
|
||||
i--;
|
||||
|
||||
e.RT->DeleteObjectNow();
|
||||
TemporaryRTs.RemoveAt(i--);
|
||||
if (TemporaryRTs.IsEmpty())
|
||||
break;
|
||||
}
|
||||
@@ -48,19 +44,14 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc)
|
||||
const uint32 descHash = GetHash(desc);
|
||||
for (int32 i = 0; i < TemporaryRTs.Count(); i++)
|
||||
{
|
||||
auto& tmp = TemporaryRTs[i];
|
||||
|
||||
if (!tmp.IsOccupied && tmp.DescriptionHash == descHash)
|
||||
auto& e = TemporaryRTs[i];
|
||||
if (!e.IsOccupied && e.DescriptionHash == descHash)
|
||||
{
|
||||
ASSERT(tmp.RT);
|
||||
|
||||
// Mark as used
|
||||
tmp.IsOccupied = true;
|
||||
tmp.LastFrameTaken = Engine::FrameCount;
|
||||
return tmp.RT;
|
||||
e.IsOccupied = true;
|
||||
return e.RT;
|
||||
}
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
if (TemporaryRTs.Count() > 2000)
|
||||
{
|
||||
@@ -71,24 +62,23 @@ GPUTexture* RenderTargetPool::Get(const GPUTextureDescription& desc)
|
||||
|
||||
// Create new rt
|
||||
const String name = TEXT("TemporaryRT_") + StringUtils::ToString(TemporaryRTs.Count());
|
||||
auto newRenderTarget = GPUDevice::Instance->CreateTexture(name);
|
||||
if (newRenderTarget->Init(desc))
|
||||
GPUTexture* rt = GPUDevice::Instance->CreateTexture(name);
|
||||
if (rt->Init(desc))
|
||||
{
|
||||
Delete(newRenderTarget);
|
||||
Delete(rt);
|
||||
LOG(Error, "Cannot create temporary render target. Description: {0}", desc.ToString());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create temporary rt entry
|
||||
Entry entry;
|
||||
entry.IsOccupied = true;
|
||||
entry.LastFrameReleased = 0;
|
||||
entry.LastFrameTaken = Engine::FrameCount;
|
||||
entry.RT = newRenderTarget;
|
||||
entry.DescriptionHash = descHash;
|
||||
TemporaryRTs.Add(entry);
|
||||
Entry e;
|
||||
e.IsOccupied = true;
|
||||
e.LastFrameReleased = 0;
|
||||
e.RT = rt;
|
||||
e.DescriptionHash = descHash;
|
||||
TemporaryRTs.Add(e);
|
||||
|
||||
return newRenderTarget;
|
||||
return rt;
|
||||
}
|
||||
|
||||
void RenderTargetPool::Release(GPUTexture* rt)
|
||||
@@ -98,14 +88,13 @@ void RenderTargetPool::Release(GPUTexture* rt)
|
||||
|
||||
for (int32 i = 0; i < TemporaryRTs.Count(); i++)
|
||||
{
|
||||
auto& tmp = TemporaryRTs[i];
|
||||
|
||||
if (tmp.RT == rt)
|
||||
auto& e = TemporaryRTs[i];
|
||||
if (e.RT == rt)
|
||||
{
|
||||
// Mark as free
|
||||
ASSERT(tmp.IsOccupied);
|
||||
tmp.IsOccupied = false;
|
||||
tmp.LastFrameReleased = Engine::FrameCount;
|
||||
ASSERT(e.IsOccupied);
|
||||
e.IsOccupied = false;
|
||||
e.LastFrameReleased = Engine::FrameCount;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@ public:
|
||||
/// Flushes the temporary render targets.
|
||||
/// </summary>
|
||||
/// <param name="force">True if release unused render targets by force, otherwise will use a few frames of delay.</param>
|
||||
static void Flush(bool force = false);
|
||||
/// <param name="framesOffset">Amount of previous frames that should persist in the pool after flush. Resources used more than given value wil be freed. Use value of -1 to auto pick default duration.</param>
|
||||
static void Flush(bool force = false, int32 framesOffset = -1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a temporary render target.
|
||||
|
||||
@@ -439,7 +439,7 @@ public:
|
||||
|
||||
bool Do() const override
|
||||
{
|
||||
auto scene = Scripting::FindObject<Scene>(TargetScene);
|
||||
auto scene = Level::FindScene(TargetScene);
|
||||
if (!scene)
|
||||
return true;
|
||||
return unloadScene(scene);
|
||||
|
||||
@@ -445,6 +445,8 @@ void SceneObjectsFactory::SetupPrefabInstances(Context& context, const PrefabSyn
|
||||
const SceneObject* obj = data.SceneObjects[i];
|
||||
const Guid id = obj ? obj->GetID() : JsonTools::GetGuid(stream, "ID");
|
||||
auto prefab = Content::LoadAsync<Prefab>(prefabId);
|
||||
if (!prefab)
|
||||
continue;
|
||||
|
||||
// Check if it's parent is in the same prefab
|
||||
int32 index;
|
||||
|
||||
@@ -94,6 +94,7 @@ X11::XcursorImage* CursorsImg[(int32)CursorType::MAX];
|
||||
Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
||||
Array<KeyboardKeys> KeyCodeMap;
|
||||
Delegate<void*> LinuxPlatform::xEventRecieved;
|
||||
Window* MouseTrackingWindow = nullptr;
|
||||
|
||||
// Message boxes configuration
|
||||
#define LINUX_DIALOG_MIN_BUTTON_WIDTH 64
|
||||
@@ -1916,7 +1917,6 @@ bool LinuxPlatform::Init()
|
||||
{
|
||||
if (PlatformBase::Init())
|
||||
return true;
|
||||
|
||||
char fileNameBuffer[1024];
|
||||
|
||||
// Init timing
|
||||
@@ -2398,7 +2398,7 @@ void LinuxPlatform::Tick()
|
||||
// Update input context focus
|
||||
X11::XSetICFocus(IC);
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xfocus.window);
|
||||
if (window)
|
||||
if (window && MouseTrackingWindow == nullptr)
|
||||
{
|
||||
window->OnGotFocus();
|
||||
}
|
||||
@@ -2407,7 +2407,7 @@ void LinuxPlatform::Tick()
|
||||
// Update input context focus
|
||||
X11::XUnsetICFocus(IC);
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xfocus.window);
|
||||
if (window)
|
||||
if (window && MouseTrackingWindow == nullptr)
|
||||
{
|
||||
window->OnLostFocus();
|
||||
}
|
||||
@@ -2514,23 +2514,32 @@ void LinuxPlatform::Tick()
|
||||
break;
|
||||
case ButtonPress:
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xbutton.window);
|
||||
if (window)
|
||||
if (MouseTrackingWindow)
|
||||
MouseTrackingWindow->OnButtonPress(&event.xbutton);
|
||||
else if (window)
|
||||
window->OnButtonPress(&event.xbutton);
|
||||
break;
|
||||
case ButtonRelease:
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xbutton.window);
|
||||
if (window)
|
||||
if (MouseTrackingWindow)
|
||||
MouseTrackingWindow->OnButtonRelease(&event.xbutton);
|
||||
else if (window)
|
||||
window->OnButtonRelease(&event.xbutton);
|
||||
break;
|
||||
case MotionNotify:
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xmotion.window);
|
||||
if (window)
|
||||
if (MouseTrackingWindow)
|
||||
MouseTrackingWindow->OnMotionNotify(&event.xmotion);
|
||||
else if (window)
|
||||
window->OnMotionNotify(&event.xmotion);
|
||||
break;
|
||||
case EnterNotify:
|
||||
// nothing?
|
||||
break;
|
||||
case LeaveNotify:
|
||||
window = WindowsManager::GetByNativePtr((void*)event.xcrossing.window);
|
||||
if (MouseTrackingWindow)
|
||||
MouseTrackingWindow->OnLeaveNotify(&event.xcrossing);
|
||||
if (window)
|
||||
window->OnLeaveNotify(&event.xcrossing);
|
||||
break;
|
||||
|
||||
@@ -40,6 +40,7 @@ extern X11::Atom xAtomWmName;
|
||||
extern Dictionary<StringAnsi, X11::KeyCode> KeyNameMap;
|
||||
extern Array<KeyboardKeys> KeyCodeMap;
|
||||
extern X11::Cursor Cursors[(int32)CursorType::MAX];
|
||||
extern Window* MouseTrackingWindow;
|
||||
|
||||
static constexpr uint32 MouseDoubleClickTime = 500;
|
||||
static constexpr uint32 MaxDoubleClickDistanceSquared = 10;
|
||||
@@ -54,7 +55,7 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
return;
|
||||
auto screen = XDefaultScreen(display);
|
||||
|
||||
// Cache data
|
||||
// Cache data
|
||||
int32 width = Math::TruncToInt(settings.Size.X);
|
||||
int32 height = Math::TruncToInt(settings.Size.Y);
|
||||
_clientSize = Float2((float)width, (float)height);
|
||||
@@ -111,6 +112,12 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
windowAttributes.border_pixel = XBlackPixel(display, screen);
|
||||
windowAttributes.event_mask = KeyPressMask | KeyReleaseMask | StructureNotifyMask | ExposureMask;
|
||||
|
||||
if (!settings.IsRegularWindow)
|
||||
{
|
||||
windowAttributes.save_under = true;
|
||||
windowAttributes.override_redirect = true;
|
||||
}
|
||||
|
||||
// TODO: implement all window settings
|
||||
/*
|
||||
bool Fullscreen;
|
||||
@@ -118,11 +125,16 @@ LinuxWindow::LinuxWindow(const CreateWindowSettings& settings)
|
||||
bool AllowMaximize;
|
||||
*/
|
||||
|
||||
unsigned long valueMask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
|
||||
if (!settings.IsRegularWindow)
|
||||
{
|
||||
valueMask |= CWOverrideRedirect | CWSaveUnder;
|
||||
}
|
||||
const X11::Window window = X11::XCreateWindow(
|
||||
display, X11::XRootWindow(display, screen), x, y,
|
||||
width, height, 0, visualInfo->depth, InputOutput,
|
||||
visualInfo->visual,
|
||||
CWBackPixel | CWBorderPixel | CWEventMask | CWColormap, &windowAttributes);
|
||||
valueMask, &windowAttributes);
|
||||
_window = window;
|
||||
LinuxWindow::SetTitle(settings.Title);
|
||||
|
||||
@@ -811,12 +823,13 @@ void LinuxWindow::SetTitle(const StringView& title)
|
||||
|
||||
void LinuxWindow::StartTrackingMouse(bool useMouseScreenOffset)
|
||||
{
|
||||
// TODO: impl this
|
||||
MouseTrackingWindow = this;
|
||||
}
|
||||
|
||||
void LinuxWindow::EndTrackingMouse()
|
||||
{
|
||||
// TODO: impl this
|
||||
if (MouseTrackingWindow == this)
|
||||
MouseTrackingWindow = nullptr;
|
||||
}
|
||||
|
||||
void LinuxWindow::SetCursor(CursorType type)
|
||||
|
||||
@@ -1235,17 +1235,17 @@ bool ManagedBinaryModule::InvokeMethod(void* method, const Variant& instance, Sp
|
||||
const bool withInterfaces = !mMethod->IsStatic() && mMethod->GetParentClass()->IsInterface();
|
||||
if (!mMethod->IsStatic())
|
||||
{
|
||||
// Box instance into C# object
|
||||
// Box instance into C# object (and validate the type)
|
||||
MObject* instanceObject = MUtils::BoxVariant(instance);
|
||||
const MClass* instanceObjectClass = MCore::Object::GetClass(instanceObject);
|
||||
|
||||
// Validate instance
|
||||
if (!instanceObject || !instanceObjectClass->IsSubClassOf(mMethod->GetParentClass(), withInterfaces))
|
||||
if (!instanceObject)
|
||||
{
|
||||
if (!instanceObject)
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount);
|
||||
else
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, String(MUtils::GetClassFullname(instanceObject)));
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) without object instance", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount);
|
||||
return true;
|
||||
}
|
||||
const MClass* instanceObjectClass = MCore::Object::GetClass(instanceObject);
|
||||
if (!instanceObjectClass->IsSubClassOf(mMethod->GetParentClass(), withInterfaces))
|
||||
{
|
||||
LOG(Error, "Failed to call method '{0}.{1}' (args count: {2}) with invalid object instance of type '{3}'", String(mMethod->GetParentClass()->GetFullName()), String(mMethod->GetName()), parametersCount, String(MUtils::GetClassFullname(instanceObject)));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,8 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
|
||||
DrawCall drawCall;
|
||||
if (TerrainManager::GetChunkGeometry(drawCall, chunkSize, lod))
|
||||
return;
|
||||
if (!_neighbors[0])
|
||||
const_cast<TerrainChunk*>(this)->CacheNeighbors();
|
||||
drawCall.InstanceCount = 1;
|
||||
drawCall.Material = _cachedDrawMaterial;
|
||||
renderContext.View.GetWorldMatrix(_transform, drawCall.World);
|
||||
|
||||
@@ -448,6 +448,8 @@ namespace FlaxEngine.GUI
|
||||
internal virtual void AddChildInternal(Control child)
|
||||
{
|
||||
Assert.IsNotNull(child, "Invalid control.");
|
||||
if (Parent == child)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
// Add child
|
||||
_children.Add(child);
|
||||
@@ -820,13 +822,14 @@ namespace FlaxEngine.GUI
|
||||
protected virtual void DrawChildren()
|
||||
{
|
||||
// Draw all visible child controls
|
||||
var children = _children;
|
||||
if (_cullChildren)
|
||||
{
|
||||
Render2D.PeekClip(out var globalClipping);
|
||||
Render2D.PeekTransform(out var globalTransform);
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
var child = children[i];
|
||||
if (child.Visible)
|
||||
{
|
||||
Matrix3x3.Multiply(ref child._cachedTransform, ref globalTransform, out var globalChildTransform);
|
||||
@@ -842,9 +845,9 @@ namespace FlaxEngine.GUI
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < _children.Count; i++)
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
var child = _children[i];
|
||||
var child = children[i];
|
||||
if (child.Visible)
|
||||
{
|
||||
Render2D.PushTransform(ref child._cachedTransform);
|
||||
|
||||
@@ -58,10 +58,11 @@ namespace FlaxEngine
|
||||
if (containerControl != null && IsActiveInHierarchy)
|
||||
{
|
||||
var children = ChildrenCount;
|
||||
var parent = Parent;
|
||||
for (int i = 0; i < children; i++)
|
||||
{
|
||||
var child = GetChild(i) as UIControl;
|
||||
if (child != null && child.IsActiveInHierarchy && child.HasControl)
|
||||
if (child != null && child.IsActiveInHierarchy && child.HasControl && child != parent)
|
||||
{
|
||||
child.Control.Parent = containerControl;
|
||||
}
|
||||
|
||||
@@ -91,11 +91,6 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
var baseConfiguration = project.Configurations.First();
|
||||
var baseOutputDir = Utilities.MakePathRelativeTo(project.CSharp.OutputPath ?? baseConfiguration.TargetBuildOptions.OutputFolder, projectDirectory);
|
||||
var baseIntermediateOutputPath = Utilities.MakePathRelativeTo(project.CSharp.IntermediateOutputPath ?? Path.Combine(baseConfiguration.TargetBuildOptions.IntermediateFolder, "CSharp"), projectDirectory);
|
||||
|
||||
bool isMainProject = Globals.Project.ProjectFolderPath == project.WorkspaceRootPath && project.Name != "BuildScripts" && (Globals.Project.Name != "Flax" || project.Name != "FlaxEngine");
|
||||
var flaxBuildTargetsFilename = isMainProject ? "Flax.Build.CSharp.targets" : "Flax.Build.CSharp.SkipBuild.targets";
|
||||
var cacheProjectsPath = Utilities.MakePathRelativeTo(Path.Combine(Globals.Root, "Cache", "Projects"), projectDirectory);
|
||||
var flaxBuildTargetsPath = !string.IsNullOrEmpty(cacheProjectsPath) ? Path.Combine(cacheProjectsPath, flaxBuildTargetsFilename) : flaxBuildTargetsFilename;
|
||||
|
||||
csProjectFileContent.AppendLine($" <TargetFramework>net{dotnetSdk.Version.Major}.{dotnetSdk.Version.Minor}</TargetFramework>");
|
||||
csProjectFileContent.AppendLine(" <ImplicitUsings>disable</ImplicitUsings>");
|
||||
@@ -114,7 +109,11 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
|
||||
//csProjectFileContent.AppendLine(" <CopyLocalLockFileAssemblies>false</CopyLocalLockFileAssemblies>"); // TODO: use it to reduce burden of framework libs
|
||||
|
||||
// Custom .targets file for overriding MSBuild build tasks
|
||||
// Custom .targets file for overriding MSBuild build tasks, only invoke Flax.Build once per Flax project
|
||||
bool isMainProject = Globals.Project.IsCSharpOnlyProject && Globals.Project.ProjectFolderPath == project.WorkspaceRootPath && project.Name != "BuildScripts" && (Globals.Project.Name != "Flax" || project.Name != "FlaxEngine");
|
||||
var flaxBuildTargetsFilename = isMainProject ? "Flax.Build.CSharp.targets" : "Flax.Build.CSharp.SkipBuild.targets";
|
||||
var cacheProjectsPath = Utilities.MakePathRelativeTo(Path.Combine(Globals.Root, "Cache", "Projects"), projectDirectory);
|
||||
var flaxBuildTargetsPath = !string.IsNullOrEmpty(cacheProjectsPath) ? Path.Combine(cacheProjectsPath, flaxBuildTargetsFilename) : flaxBuildTargetsFilename;
|
||||
csProjectFileContent.AppendLine(string.Format(" <CustomAfterMicrosoftCommonTargets>$(MSBuildThisFileDirectory){0}</CustomAfterMicrosoftCommonTargets>", flaxBuildTargetsPath));
|
||||
|
||||
// Hide annoying warnings during build
|
||||
|
||||
@@ -702,7 +702,7 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
// Override MSBuild build tasks to run Flax.Build in C#-only projects
|
||||
{
|
||||
// Build command for the build tool
|
||||
var buildToolPath = Path.ChangeExtension(Utilities.MakePathRelativeTo(typeof(Builder).Assembly.Location, Path.GetDirectoryName(solution.MainProject.Path)), null);
|
||||
var buildToolPath = Path.ChangeExtension(typeof(Builder).Assembly.Location, null);
|
||||
|
||||
var targetsFileContent = new StringBuilder();
|
||||
targetsFileContent.AppendLine("<Project>");
|
||||
@@ -724,16 +724,16 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
{
|
||||
foreach (var configuration in solution.MainProject.Configurations)
|
||||
{
|
||||
var cmdLine = string.Format("{0} -log -mutex -workspace={1} -arch={2} -configuration={3} -platform={4} -buildTargets={5}",
|
||||
FixPath(buildToolPath),
|
||||
FixPath(solution.MainProject.WorkspaceRootPath),
|
||||
var cmdLine = string.Format("\"{0}\" -log -mutex -workspace=\"{1}\" -arch={2} -configuration={3} -platform={4} -buildTargets={5}",
|
||||
buildToolPath,
|
||||
solution.MainProject.WorkspaceRootPath,
|
||||
configuration.Architecture,
|
||||
configuration.Configuration,
|
||||
configuration.Platform,
|
||||
configuration.Target);
|
||||
Configuration.PassArgs(ref cmdLine);
|
||||
|
||||
str.AppendLine(string.Format(" <Exec Command=\"{0} {1}\" Condition=\"'$(Configuration)|$(Platform)'=='{2}'\"/>", cmdLine, extraArgs, configuration.Name));
|
||||
str.AppendLine(string.Format(" <Exec Command='{0} {1}' Condition=\"'$(Configuration)|$(Platform)'=='{2}'\"/>", cmdLine, extraArgs, configuration.Name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -774,14 +774,5 @@ namespace Flax.Build.Projects.VisualStudio
|
||||
projects.Add(project);
|
||||
}
|
||||
}
|
||||
|
||||
private static string FixPath(string path)
|
||||
{
|
||||
if (path.Contains(' '))
|
||||
{
|
||||
path = "\"" + path + "\"";
|
||||
}
|
||||
return path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user