Merge branch 'master' into 1.12
This commit is contained in:
2
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
2
.github/ISSUE_TEMPLATE/1-bug.yaml
vendored
@@ -31,7 +31,7 @@ body:
|
||||
- '1.10'
|
||||
- '1.11'
|
||||
- master branch
|
||||
default: 2
|
||||
default: 3
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
|
||||
3
.github/workflows/build_linux.yml
vendored
3
.github/workflows/build_linux.yml
vendored
@@ -16,7 +16,8 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
6
.github/workflows/cd.yml
vendored
6
.github/workflows/cd.yml
vendored
@@ -87,7 +87,8 @@ jobs:
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
@@ -118,7 +119,8 @@ jobs:
|
||||
git ${{ env.GIT_LFS_PULL_OPTIONS }} lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
- name: Setup Vulkan
|
||||
uses: ./.github/actions/vulkan
|
||||
- name: Setup .NET
|
||||
|
||||
3
.github/workflows/tests.yml
vendored
3
.github/workflows/tests.yml
vendored
@@ -28,7 +28,8 @@ jobs:
|
||||
git lfs pull
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y --fix-missing libx11-dev libxcursor-dev libxinerama-dev build-essential gettext libtool libtool-bin libpulse-dev libasound2-dev libjack-dev portaudio19-dev
|
||||
- name: Build
|
||||
run: |
|
||||
./GenerateProjectFiles.sh -vs2022 -log -verbose -printSDKs -dotnet=8
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Shaders/GI/DDGI.flax
LFS
BIN
Content/Shaders/GI/DDGI.flax
LFS
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Content/Shaders/MotionBlur.flax
LFS
BIN
Content/Shaders/MotionBlur.flax
LFS
Binary file not shown.
BIN
Content/Shaders/Reflections.flax
LFS
BIN
Content/Shaders/Reflections.flax
LFS
Binary file not shown.
BIN
Content/Shaders/SDF.flax
LFS
BIN
Content/Shaders/SDF.flax
LFS
Binary file not shown.
BIN
Content/Shaders/SSR.flax
LFS
BIN
Content/Shaders/SSR.flax
LFS
Binary file not shown.
Binary file not shown.
@@ -4,7 +4,7 @@
|
||||
"Major": 1,
|
||||
"Minor": 11,
|
||||
"Revision": 0,
|
||||
"Build": 6801
|
||||
"Build": 6805
|
||||
},
|
||||
"Company": "Flax",
|
||||
"Copyright": "Copyright (c) 2012-2025 Wojciech Figat. All rights reserved.",
|
||||
|
||||
@@ -49,7 +49,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Fedora: `sudo dnf install dotnet-sdk-8.0`
|
||||
* Arch: `sudo pacman -S dotnet-sdk-8.0 dotnet-runtime-8.0 dotnet-targeting-pack-8.0 dotnet-host`
|
||||
* Install Vulkan SDK ([https://vulkan.lunarg.com/](https://vulkan.lunarg.com/))
|
||||
* Ubuntu: `sudo apt install vulkan-sdk`
|
||||
* Ubuntu: `sudo apt install vulkan-sdk` (deprecated, follow official docs)
|
||||
* Fedora: `sudo dnf install vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Arch: `sudo pacman -S vulkan-headers vulkan-tools vulkan-validation-layers`
|
||||
* Install Git with LFS
|
||||
@@ -60,7 +60,7 @@ Follow the instructions below to compile and run the engine from source.
|
||||
* Ubuntu: `sudo apt-get install libx11-dev libxcursor-dev libxinerama-dev zlib1g-dev`
|
||||
* Fedora: `sudo dnf install libX11-devel libXcursor-devel libXinerama-devel ghc-zlib-devel`
|
||||
* Arch: `sudo pacman -S base-devel libx11 libxcursor libxinerama zlib`
|
||||
* Install Clang compiler (version 6 or later):
|
||||
* Install Clang compiler (version 14 or later):
|
||||
* Ubuntu: `sudo apt-get install clang lldb lld`
|
||||
* Fedora: `sudo dnf install clang llvm lldb lld`
|
||||
* Arch: `sudo pacman -S clang lldb lld`
|
||||
|
||||
@@ -281,6 +281,13 @@ namespace FlaxEditor.Content
|
||||
|
||||
private void CacheData()
|
||||
{
|
||||
if (!_asset)
|
||||
{
|
||||
_parameters = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_methods = Utils.GetEmptyArray<ScriptMemberInfo>();
|
||||
_attributes = Utils.GetEmptyArray<Attribute>();
|
||||
return;
|
||||
}
|
||||
if (_parameters != null)
|
||||
return;
|
||||
if (_asset.WaitForLoaded())
|
||||
@@ -344,13 +351,13 @@ namespace FlaxEditor.Content
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Name => Path.GetFileNameWithoutExtension(_asset.Path);
|
||||
public string Name => _asset ? Path.GetFileNameWithoutExtension(_asset.Path) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Namespace => string.Empty;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string TypeName => JsonSerializer.GetStringID(_asset.ID);
|
||||
public string TypeName => _asset ? JsonSerializer.GetStringID(_asset.ID) : null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsPublic => true;
|
||||
|
||||
@@ -130,6 +130,11 @@ namespace FlaxEditor.Content
|
||||
eyeAdaptation.Mode = EyeAdaptationMode.None;
|
||||
eyeAdaptation.OverrideFlags |= EyeAdaptationSettingsOverride.Mode;
|
||||
preview.PostFxVolume.EyeAdaptation = eyeAdaptation;
|
||||
|
||||
var antiAliasing = preview.PostFxVolume.AntiAliasing;
|
||||
antiAliasing.Mode = AntialiasingMode.FastApproximateAntialiasing;
|
||||
antiAliasing.OverrideFlags |= AntiAliasingSettingsOverride.Mode;
|
||||
preview.PostFxVolume.AntiAliasing = antiAliasing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,26 +15,32 @@
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
String GetGDK()
|
||||
{
|
||||
// Find GDK
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
String gdk;
|
||||
Platform::GetEnvironmentVariable(TEXT("GameDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
{
|
||||
_gdkPath.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), _gdkPath);
|
||||
if (_gdkPath.IsEmpty() || !FileSystem::DirectoryExists(_gdkPath))
|
||||
gdk.Clear();
|
||||
Platform::GetEnvironmentVariable(TEXT("GRDKLatest"), gdk);
|
||||
if (gdk.IsEmpty() || !FileSystem::DirectoryExists(gdk))
|
||||
{
|
||||
_gdkPath.Clear();
|
||||
gdk.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_gdkPath.EndsWith(TEXT("GRDK\\")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 6);
|
||||
else if (_gdkPath.EndsWith(TEXT("GRDK")))
|
||||
_gdkPath.Remove(_gdkPath.Length() - 5);
|
||||
if (gdk.EndsWith(TEXT("GRDK\\")))
|
||||
gdk.Remove(gdk.Length() - 6);
|
||||
else if (gdk.EndsWith(TEXT("GRDK")))
|
||||
gdk.Remove(gdk.Length() - 5);
|
||||
}
|
||||
}
|
||||
return gdk;
|
||||
}
|
||||
|
||||
GDKPlatformTools::GDKPlatformTools()
|
||||
{
|
||||
_gdkPath = GetGDK();
|
||||
}
|
||||
|
||||
DotNetAOTModes GDKPlatformTools::UseAOT() const
|
||||
@@ -121,7 +127,7 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
|
||||
validName.Add('\0');
|
||||
|
||||
sb.Append(TEXT("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"0\">\n"));
|
||||
sb.Append(TEXT("<Game configVersion=\"1\">\n"));
|
||||
sb.AppendFormat(TEXT(" <Identity Name=\"{0}\" Publisher=\"{1}\" Version=\"{2}\"/>\n"),
|
||||
validName.Get(),
|
||||
platformSettings->PublisherName.HasChars() ? platformSettings->PublisherName : TEXT("CN=") + gameSettings->CompanyName,
|
||||
|
||||
@@ -10,9 +10,10 @@
|
||||
#include "Engine/Serialization/JsonTools.h"
|
||||
#include "Engine/Serialization/JsonWriters.h"
|
||||
#include "Editor/Cooker/PlatformTools.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Editor/Editor.h"
|
||||
#include "Editor/ProjectInfo.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Editor/Utilities/EditorUtilities.h"
|
||||
#if PLATFORM_MAC
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
@@ -127,7 +128,7 @@ bool CompileScriptsStep::DeployBinaries(CookingData& data, const String& path, c
|
||||
const String dst = dstPath / StringUtils::GetFileName(file);
|
||||
if (dst == file)
|
||||
continue;
|
||||
if (FileSystem::CopyFile(dst, file))
|
||||
if (EditorUtilities::CopyFileIfNewer(dst, file))
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to copy file from {0} to {1}."), file, dst));
|
||||
return true;
|
||||
|
||||
@@ -526,6 +526,7 @@ bool ProcessShaderBase(CookAssetsStep::AssetCookData& data, ShaderAssetBase* ass
|
||||
#if PLATFORM_TOOLS_XBOX_SCARLETT
|
||||
case BuildPlatform::XboxScarlett:
|
||||
{
|
||||
options.Platform = PlatformType::XboxScarlett;
|
||||
const char* platformDefineName = "PLATFORM_XBOX_SCARLETT";
|
||||
COMPILE_PROFILE(DirectX_SM6, SHADER_FILE_CHUNK_INTERNAL_D3D_SM6_CACHE);
|
||||
break;
|
||||
@@ -1367,7 +1368,10 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
typeName = e.TypeName;
|
||||
}
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
if (e.Count == 1)
|
||||
LOG(Info, "{0}: 1 asset of total size {1}", typeName, Utilities::BytesToText(e.ContentSize));
|
||||
else
|
||||
LOG(Info, "{0}: {1:>4} assets of total size {2}", typeName, e.Count, Utilities::BytesToText(e.ContentSize));
|
||||
}
|
||||
LOG(Info, "");
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
||||
}
|
||||
if (version.IsEmpty())
|
||||
{
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer));
|
||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for {} platform."), maxVer, minVer, platformName));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +59,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
|
||||
data.StepProgress(infoMsg, 0);
|
||||
|
||||
// Override Newtonsoft.Json with AOT-version (one that doesn't use System.Reflection.Emit)
|
||||
// TODO: remove it since EngineModule does properly reference AOT lib now
|
||||
EditorUtilities::CopyFileIfNewer(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.dll"), Globals::StartupFolder / TEXT("Source/Platforms/DotNet/AOT/Newtonsoft.Json.dll"));
|
||||
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.xml"));
|
||||
FileSystem::DeleteFile(data.ManagedCodeOutputPath / TEXT("Newtonsoft.Json.pdb"));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -11,7 +12,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
[CustomEditor(typeof(EnvironmentProbe)), DefaultEditor]
|
||||
public class EnvironmentProbeEditor : ActorEditor
|
||||
{
|
||||
private FlaxEngine.GUI.Button _bake;
|
||||
private Button _bake;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
@@ -20,8 +21,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
layout.Space(10);
|
||||
_bake = layout.Button("Bake").Button;
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
_bake = group.Button("Bake").Button;
|
||||
_bake.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
@@ -914,9 +914,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
// Remove drop down arrows and containment lines if no objects in the group
|
||||
if (group.Children.Count == 0)
|
||||
{
|
||||
group.Panel.Close();
|
||||
group.Panel.ArrowImageOpened = null;
|
||||
group.Panel.ArrowImageClosed = null;
|
||||
group.Panel.EnableContainmentLines = false;
|
||||
group.Panel.CanOpenClose = false;
|
||||
}
|
||||
|
||||
// Scripts arrange bar
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated
|
||||
{
|
||||
@@ -19,8 +20,9 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
if (Values.HasDifferentTypes == false)
|
||||
{
|
||||
// Add 'Bake' button
|
||||
layout.Space(10);
|
||||
var button = layout.Button("Bake");
|
||||
var group = layout.Group("Bake");
|
||||
group.Panel.ItemsMargin = new Margin(Utilities.Constants.UIMargin * 2);
|
||||
var button = group.Button("Bake");
|
||||
button.Button.Clicked += BakeButtonClicked;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,8 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
base.Refresh();
|
||||
|
||||
if (Picker == null)
|
||||
return;
|
||||
var differentValues = HasDifferentValues;
|
||||
Picker.DifferentValues = differentValues;
|
||||
if (!differentValues)
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
b = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -407,7 +407,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
|
||||
menu.AddButton("Copy", linkedEditor.Copy);
|
||||
var b = menu.AddButton("Duplicate", () => Editor.Duplicate(Index));
|
||||
b.Enabled = linkedEditor.CanPaste && !Editor._readOnly && Editor._canResize;
|
||||
b.Enabled = !Editor._readOnly && Editor._canResize;
|
||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||
paste.Enabled = linkedEditor.CanPaste && !Editor._readOnly;
|
||||
|
||||
@@ -650,7 +650,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
panel.Panel.Size = new Float2(0, 18);
|
||||
panel.Panel.Margin = new Margin(0, 0, Utilities.Constants.UIMargin, 0);
|
||||
|
||||
var removeButton = panel.Button("-", "Remove the last item");
|
||||
var removeButton = panel.Button("-", "Remove the last item.");
|
||||
removeButton.Button.Size = new Float2(16, 16);
|
||||
removeButton.Button.Enabled = size > _minCount;
|
||||
removeButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
@@ -661,7 +661,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
Resize(Count - 1);
|
||||
};
|
||||
|
||||
var addButton = panel.Button("+", "Add a new item");
|
||||
var addButton = panel.Button("+", "Add a new item.");
|
||||
addButton.Button.Size = new Float2(16, 16);
|
||||
addButton.Button.Enabled = (!NotNullItems || size > 0) && size < _maxCount;
|
||||
addButton.Button.AnchorPreset = AnchorPresets.TopRight;
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
public event Action<TypePickerControl> TypePickerValueChanged;
|
||||
|
||||
/// <summary>
|
||||
/// The custom callback for types validation. Cane be used to implement a rule for types to pick.
|
||||
/// The custom callback for types validation. Can be used to implement a rule for types to pick.
|
||||
/// </summary>
|
||||
public Func<ScriptType, bool> CheckValid;
|
||||
|
||||
@@ -353,7 +353,13 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
if (!string.IsNullOrEmpty(typeReference.CheckMethod))
|
||||
{
|
||||
var parentType = ParentEditor.Values[0].GetType();
|
||||
var parentEditor = ParentEditor;
|
||||
// Find actual parent editor if parent editor is collection editor
|
||||
while (parentEditor.GetType().IsAssignableTo(typeof(CollectionEditor)))
|
||||
parentEditor = parentEditor.ParentEditor;
|
||||
|
||||
var parentType = parentEditor.Values[0].GetType();
|
||||
|
||||
var method = parentType.GetMethod(typeReference.CheckMethod, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
if (method != null)
|
||||
{
|
||||
|
||||
@@ -268,8 +268,8 @@ bool Editor::CheckProjectUpgrade()
|
||||
// Check if last version was older
|
||||
else if (lastVersion.Major < FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor < FLAXENGINE_VERSION_MINOR))
|
||||
{
|
||||
LOG(Warning, "The project was opened with the older editor version last time");
|
||||
const auto result = MessageBox::Show(TEXT("The project was opened with the older editor version last time. Loading it may modify existing data so older editor version won't open it. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
|
||||
LOG(Warning, "The project was last opened with an older editor version");
|
||||
const auto result = MessageBox::Show(TEXT("The project was last opened with an older editor version.\nLoading it may modify existing data, which can result in older editor versions being unable to open it.\n\nDo you want to perform a backup before or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Question);
|
||||
if (result == DialogResult::Yes)
|
||||
{
|
||||
if (BackupProject())
|
||||
@@ -291,8 +291,8 @@ bool Editor::CheckProjectUpgrade()
|
||||
// Check if last version was newer
|
||||
else if (lastVersion.Major > FLAXENGINE_VERSION_MAJOR || (lastVersion.Major == FLAXENGINE_VERSION_MAJOR && lastVersion.Minor > FLAXENGINE_VERSION_MINOR))
|
||||
{
|
||||
LOG(Warning, "The project was opened with the newer editor version last time");
|
||||
const auto result = MessageBox::Show(TEXT("The project was opened with the newer editor version last time. Loading it may fail and corrupt existing data. Do you want to perform a backup before or cancel operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
|
||||
LOG(Warning, "The project was last opened with a newer editor version");
|
||||
const auto result = MessageBox::Show(TEXT("The project was last opened with a newer editor version.\nLoading it may fail and corrupt existing data.\n\nDo you want to perform a backup before loading or cancel the operation?"), TEXT("Project upgrade"), MessageBoxButtons::YesNoCancel, MessageBoxIcon::Warning);
|
||||
if (result == DialogResult::Yes)
|
||||
{
|
||||
if (BackupProject())
|
||||
|
||||
@@ -1390,6 +1390,7 @@ namespace FlaxEditor
|
||||
public void BuildAllMeshesSDF()
|
||||
{
|
||||
var models = new List<Model>();
|
||||
var forceRebuild = Input.GetKey(KeyboardKeys.F);
|
||||
Scene.ExecuteOnGraph(node =>
|
||||
{
|
||||
if (node is StaticModelNode staticModelNode && staticModelNode.Actor is StaticModel staticModel)
|
||||
@@ -1399,7 +1400,7 @@ namespace FlaxEditor
|
||||
model != null &&
|
||||
!models.Contains(model) &&
|
||||
!model.IsVirtual &&
|
||||
model.SDF.Texture == null)
|
||||
(forceRebuild || model.SDF.Texture == null))
|
||||
{
|
||||
models.Add(model);
|
||||
}
|
||||
@@ -1412,7 +1413,17 @@ namespace FlaxEditor
|
||||
{
|
||||
var model = models[i];
|
||||
Log($"[{i}/{models.Count}] Generating SDF for {model}");
|
||||
if (!model.GenerateSDF())
|
||||
float resolutionScale = 1.0f, backfacesThreshold = 0.6f;
|
||||
int lodIndex = 6;
|
||||
bool useGPU = true;
|
||||
var sdf = model.SDF;
|
||||
if (sdf.Texture != null)
|
||||
{
|
||||
// Preserve options set on this model
|
||||
resolutionScale = sdf.ResolutionScale;
|
||||
lodIndex = sdf.LOD;
|
||||
}
|
||||
if (!model.GenerateSDF(resolutionScale, lodIndex, true, backfacesThreshold, useGPU))
|
||||
model.Save();
|
||||
}
|
||||
});
|
||||
@@ -1587,7 +1598,7 @@ namespace FlaxEditor
|
||||
if (dockedTo != null && dockedTo.SelectedTab != gameWin && dockedTo.SelectedTab != null)
|
||||
result = dockedTo.SelectedTab.Size;
|
||||
else
|
||||
result = gameWin.Viewport.Size;
|
||||
result = gameWin.Viewport.ContentSize;
|
||||
|
||||
result *= root.DpiScale;
|
||||
result = Float2.Round(result);
|
||||
|
||||
@@ -129,11 +129,39 @@ namespace FlaxEditor.GUI.Input
|
||||
{
|
||||
base.Draw();
|
||||
|
||||
var style = Style.Current;
|
||||
var r = new Rectangle(0, 0, Width, Height);
|
||||
bool isTransparent = _value.A < 1;
|
||||
|
||||
Render2D.FillRectangle(r, _value);
|
||||
Render2D.DrawRectangle(r, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
var style = Style.Current;
|
||||
var fullRect = new Rectangle(0, 0, Width, Height);
|
||||
var colorRect = new Rectangle(0, 0, isTransparent ? Width * 0.7f : Width, Height);
|
||||
|
||||
if (isTransparent)
|
||||
{
|
||||
var alphaRect = new Rectangle(colorRect.Right, 0, Width - colorRect.Right, Height);
|
||||
|
||||
// Draw checkerboard pattern to part of the color value box
|
||||
Render2D.FillRectangle(alphaRect, Color.White);
|
||||
var smallRectSize = 7.9f;
|
||||
var numHor = Mathf.CeilToInt(alphaRect.Width / smallRectSize);
|
||||
var numVer = Mathf.CeilToInt(alphaRect.Height / smallRectSize);
|
||||
for (int i = 0; i < numHor; i++)
|
||||
{
|
||||
for (int j = 0; j < numVer; j++)
|
||||
{
|
||||
if ((i + j) % 2 == 0)
|
||||
{
|
||||
var rect = new Rectangle(alphaRect.X + smallRectSize * i, alphaRect.Y + smallRectSize * j, new Float2(smallRectSize));
|
||||
Render2D.PushClip(alphaRect);
|
||||
Render2D.FillRectangle(rect, Color.Gray);
|
||||
Render2D.PopClip();
|
||||
}
|
||||
}
|
||||
}
|
||||
Render2D.FillRectangle(alphaRect, _value);
|
||||
}
|
||||
|
||||
Render2D.FillRectangle(colorRect, _value with { A = 1 });
|
||||
Render2D.DrawRectangle(fullRect, IsMouseOver || IsNavFocused ? style.BackgroundSelected : Color.Black);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -447,8 +447,8 @@ namespace FlaxEditor.GUI.Tree
|
||||
// Select previous parent child
|
||||
var select = nodeParent.GetChild(myIndex - 1) as TreeNode;
|
||||
|
||||
// Select last child if is valid and expanded and has any children
|
||||
if (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
// Get bottom most child node
|
||||
while (select != null && select.IsExpanded && select.HasAnyVisibleChild)
|
||||
{
|
||||
select = select.GetChild(select.ChildrenCount - 1) as TreeNode;
|
||||
}
|
||||
|
||||
@@ -669,7 +669,7 @@ namespace FlaxEditor.Modules
|
||||
if (item != null)
|
||||
Editor.ContentEditing.Open(item);
|
||||
});
|
||||
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
|
||||
cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show());
|
||||
|
||||
// Scene
|
||||
MenuScene = MainMenu.AddButton("Scene");
|
||||
@@ -714,6 +714,7 @@ namespace FlaxEditor.Modules
|
||||
_menuToolsBuildCSGMesh = cm.AddButton("Build CSG mesh", inputOptions.BuildCSG, Editor.BuildCSG);
|
||||
_menuToolsBuildNavMesh = cm.AddButton("Build Nav Mesh", inputOptions.BuildNav, Editor.BuildNavMesh);
|
||||
_menuToolsBuildAllMeshesSDF = cm.AddButton("Build all meshes SDF", inputOptions.BuildSDF, Editor.BuildAllMeshesSDF);
|
||||
_menuToolsBuildAllMeshesSDF.LinkTooltip("Generates Sign Distance Field texture for all meshes used in loaded scenes. Use with 'F' key pressed to force rebuild SDF for meshes with existing one.");
|
||||
cm.AddSeparator();
|
||||
cm.AddButton("Game Cooker", Editor.Windows.GameCookerWin.FocusOrShow);
|
||||
_menuToolsCancelBuilding = cm.AddButton("Cancel building game", () => GameCooker.Cancel());
|
||||
|
||||
@@ -896,9 +896,11 @@ namespace FlaxEditor.Modules
|
||||
|
||||
if (type.IsAssignableTo(typeof(AssetEditorWindow)))
|
||||
{
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), typeof(AssetItem) });
|
||||
var assetItem = Editor.ContentDatabase.FindAsset(winData.AssetItemID);
|
||||
var assetType = assetItem.GetType();
|
||||
var ctor = type.GetConstructor(new Type[] { typeof(Editor), assetType });
|
||||
var win = (AssetEditorWindow)ctor.Invoke(new object[] { Editor.Instance, assetItem });
|
||||
|
||||
win.Show(winData.DockState, winData.DockState != DockState.Float ? winData.DockedTo : null, winData.SelectOnShow, winData.SplitterValue);
|
||||
if (winData.DockState == DockState.Float)
|
||||
{
|
||||
|
||||
@@ -650,6 +650,10 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Windows"), EditorOrder(4020)]
|
||||
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
||||
|
||||
[DefaultValue(typeof(InputBinding), "Control+Comma")]
|
||||
[EditorDisplay("Windows"), EditorOrder(4030)]
|
||||
public InputBinding EditorOptionsWindow = new InputBinding(KeyboardKeys.Comma, KeyboardKeys.Control);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Node Editors
|
||||
|
||||
@@ -406,6 +406,8 @@ namespace FlaxEngine.Utilities
|
||||
{
|
||||
if (type == ScriptType.Null)
|
||||
return null;
|
||||
if (type.BaseType == null)
|
||||
return type.Type;
|
||||
while (type.Type == null)
|
||||
type = type.BaseType;
|
||||
return type.Type;
|
||||
|
||||
@@ -2,11 +2,8 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Runtime.Serialization.Formatters.Binary;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
@@ -18,6 +15,7 @@ namespace FlaxEditor.Surface
|
||||
class AttributesEditor : ContextMenuBase
|
||||
{
|
||||
private CustomEditorPresenter _presenter;
|
||||
private Proxy _proxy;
|
||||
private byte[] _oldData;
|
||||
|
||||
private class Proxy
|
||||
@@ -72,11 +70,11 @@ namespace FlaxEditor.Surface
|
||||
/// Initializes a new instance of the <see cref="AttributesEditor"/> class.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The attributes list to edit.</param>
|
||||
/// <param name="attributeType">The allowed attribute types to use.</param>
|
||||
public AttributesEditor(Attribute[] attributes, IList<Type> attributeType)
|
||||
/// <param name="attributeTypes">The allowed attribute types to use.</param>
|
||||
public AttributesEditor(Attribute[] attributes, IList<Type> attributeTypes)
|
||||
{
|
||||
// Context menu dimensions
|
||||
const float width = 340.0f;
|
||||
const float width = 375.0f;
|
||||
const float height = 370.0f;
|
||||
Size = new Float2(width, height);
|
||||
|
||||
@@ -88,61 +86,68 @@ namespace FlaxEditor.Surface
|
||||
Parent = this
|
||||
};
|
||||
|
||||
// Buttons
|
||||
float buttonsWidth = (width - 16.0f) * 0.5f;
|
||||
// Ok and Cancel Buttons
|
||||
float buttonsWidth = (width - 12.0f) * 0.5f;
|
||||
float buttonsHeight = 20.0f;
|
||||
var cancelButton = new Button(4.0f, title.Bottom + 4.0f, buttonsWidth, buttonsHeight)
|
||||
var okButton = new Button(4.0f, Bottom - 4.0f - buttonsHeight, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "Ok",
|
||||
Parent = this
|
||||
};
|
||||
okButton.Clicked += OnOkButtonClicked;
|
||||
var cancelButton = new Button(okButton.Right + 4.0f, okButton.Y, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "Cancel",
|
||||
Parent = this
|
||||
};
|
||||
cancelButton.Clicked += Hide;
|
||||
var okButton = new Button(cancelButton.Right + 4.0f, cancelButton.Y, buttonsWidth, buttonsHeight)
|
||||
{
|
||||
Text = "OK",
|
||||
Parent = this
|
||||
};
|
||||
okButton.Clicked += OnOkButtonClicked;
|
||||
|
||||
// Actual panel
|
||||
// Actual panel used to display attributes
|
||||
var panel1 = new Panel(ScrollBars.Vertical)
|
||||
{
|
||||
Bounds = new Rectangle(0, okButton.Bottom + 4.0f, width, height - okButton.Bottom - 2.0f),
|
||||
Bounds = new Rectangle(0, title.Bottom + 4.0f, width, height - buttonsHeight - title.Height - 14.0f),
|
||||
Parent = this
|
||||
};
|
||||
var editor = new CustomEditorPresenter(null);
|
||||
editor.Panel.AnchorPreset = AnchorPresets.HorizontalStretchTop;
|
||||
editor.Panel.IsScrollable = true;
|
||||
editor.Panel.Parent = panel1;
|
||||
editor.Panel.Tag = attributeType;
|
||||
editor.Panel.Tag = attributeTypes;
|
||||
_presenter = editor;
|
||||
|
||||
// Cache 'previous' state to check if attributes were edited after operation
|
||||
_oldData = SurfaceMeta.GetAttributesData(attributes);
|
||||
|
||||
editor.Select(new Proxy
|
||||
_proxy = new Proxy
|
||||
{
|
||||
Value = attributes,
|
||||
});
|
||||
};
|
||||
editor.Select(_proxy);
|
||||
|
||||
_presenter.Modified += OnPresenterModified;
|
||||
OnPresenterModified();
|
||||
}
|
||||
|
||||
private void OnPresenterModified()
|
||||
{
|
||||
if (_proxy.Value.Length == 0)
|
||||
{
|
||||
var label = _presenter.Label("No attributes.\nPress the \"+\" button to add a new one and then select an attribute type using the \"Type\" dropdown.", TextAlignment.Center);
|
||||
label.Label.Wrapping = TextWrapping.WrapWords;
|
||||
label.Control.Height = 35f;
|
||||
label.Label.Margin = new Margin(10f);
|
||||
label.Label.TextColor = label.Label.TextColorHighlighted = Style.Current.ForegroundGrey;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnOkButtonClicked()
|
||||
{
|
||||
var newValue = ((Proxy)_presenter.Selection[0]).Value;
|
||||
for (int i = 0; i < newValue.Length; i++)
|
||||
{
|
||||
if (newValue[i] == null)
|
||||
{
|
||||
MessageBox.Show("One of the attributes is null. Please set it to the valid object.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
newValue = newValue.Where(v => v != null).ToArray();
|
||||
|
||||
var newData = SurfaceMeta.GetAttributesData(newValue);
|
||||
if (!_oldData.SequenceEqual(newData))
|
||||
{
|
||||
Edited?.Invoke(newValue);
|
||||
}
|
||||
|
||||
Hide();
|
||||
}
|
||||
@@ -183,7 +188,9 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
_presenter = null;
|
||||
_oldData = null;
|
||||
_proxy = null;
|
||||
Edited = null;
|
||||
_presenter.Modified -= OnPresenterModified;
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
@@ -214,22 +214,25 @@ namespace FlaxEditor.Surface
|
||||
if (!_isRenaming)
|
||||
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||
|
||||
// Close button
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Color button
|
||||
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if is resizing
|
||||
if (_isResizing)
|
||||
if (Surface.CanEdit)
|
||||
{
|
||||
// Draw overlay
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
// Close button
|
||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Resize button
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
// Color button
|
||||
Render2D.DrawSprite(style.Settings, _colorButtonRect, _colorButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
|
||||
// Check if is resizing
|
||||
if (_isResizing)
|
||||
{
|
||||
// Draw overlay
|
||||
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||
}
|
||||
|
||||
// Resize button
|
||||
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||
}
|
||||
|
||||
// Selection outline
|
||||
if (_isSelected)
|
||||
|
||||
@@ -431,27 +431,6 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public bool HasIndependentBoxes => Archetype.IndependentBoxes != null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this node has dependent boxes with assigned valid types. Otherwise any box has no dependent type assigned.
|
||||
/// </summary>
|
||||
public bool HasDependentBoxesSetup
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Archetype.DependentBoxes == null || Archetype.IndependentBoxes == null)
|
||||
return true;
|
||||
|
||||
for (int i = 0; i < Archetype.DependentBoxes.Length; i++)
|
||||
{
|
||||
var b = GetBox(Archetype.DependentBoxes[i]);
|
||||
if (b != null && b.CurrentType == b.DefaultType)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly List<SurfaceNode> UpdateStack = new List<SurfaceNode>();
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -400,7 +400,7 @@ namespace FlaxEditor.Surface
|
||||
return scriptType.GetGenericTypeDefinition() == typeof(Dictionary<,>);
|
||||
}
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static bool IsValidVisualScriptFunctionType(ScriptType scriptType)
|
||||
@@ -408,7 +408,7 @@ namespace FlaxEditor.Surface
|
||||
if (scriptType.IsGenericType || scriptType.IsStatic || !scriptType.IsPublic || scriptType.HasAttribute(typeof(HideInEditorAttribute), true))
|
||||
return false;
|
||||
var managedType = TypeUtils.GetType(scriptType);
|
||||
return !TypeUtils.IsDelegate(managedType);
|
||||
return managedType != null && !TypeUtils.IsDelegate(managedType);
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptTypeDescription(ScriptType type)
|
||||
|
||||
@@ -469,7 +469,8 @@ namespace FlaxEditor.Surface
|
||||
bool handled = base.OnMouseDown(location, button);
|
||||
if (!handled)
|
||||
CustomMouseDown?.Invoke(ref location, button, ref handled);
|
||||
if (handled)
|
||||
var root = Root;
|
||||
if (handled || root == null)
|
||||
{
|
||||
// Clear flags
|
||||
_isMovingSelection = false;
|
||||
@@ -523,11 +524,11 @@ namespace FlaxEditor.Surface
|
||||
if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation))
|
||||
{
|
||||
// Check if user is pressing control
|
||||
if (Root.GetKey(KeyboardKeys.Control))
|
||||
if (root.GetKey(KeyboardKeys.Control))
|
||||
{
|
||||
AddToSelection(controlUnderMouse);
|
||||
}
|
||||
else if (Root.GetKey(KeyboardKeys.Shift))
|
||||
else if (root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
RemoveFromSelection(controlUnderMouse);
|
||||
}
|
||||
@@ -539,7 +540,7 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
|
||||
// Start moving selected nodes
|
||||
if (!Root.GetKey(KeyboardKeys.Shift))
|
||||
if (!root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
StartMouseCapture();
|
||||
_movingSelectionViewPos = _rootControl.Location;
|
||||
@@ -559,7 +560,7 @@ namespace FlaxEditor.Surface
|
||||
// Start selecting or commenting
|
||||
StartMouseCapture();
|
||||
|
||||
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
|
||||
if (!root.GetKey(KeyboardKeys.Control) && !root.GetKey(KeyboardKeys.Shift))
|
||||
{
|
||||
ClearSelection();
|
||||
}
|
||||
|
||||
@@ -178,19 +178,31 @@ namespace FlaxEditor.Surface
|
||||
|
||||
// Update boxes types for nodes that dependant box types based on incoming connections
|
||||
{
|
||||
bool keepUpdating = false;
|
||||
int updateLimit = 100;
|
||||
bool keepUpdating = true;
|
||||
int updatesMin = 2, updatesMax = 100;
|
||||
do
|
||||
{
|
||||
keepUpdating = false;
|
||||
for (int i = 0; i < RootControl.Children.Count; i++)
|
||||
{
|
||||
if (RootControl.Children[i] is SurfaceNode node && !node.HasDependentBoxesSetup)
|
||||
if (RootControl.Children[i] is SurfaceNode node)
|
||||
{
|
||||
node.UpdateBoxesTypes();
|
||||
keepUpdating = true;
|
||||
var arch = node.Archetype;
|
||||
if (arch.DependentBoxes != null && arch.IndependentBoxes != null)
|
||||
{
|
||||
foreach (var boxId in arch.DependentBoxes)
|
||||
{
|
||||
var b = node.GetBox(boxId);
|
||||
if (b != null && b.CurrentType == b.DefaultType)
|
||||
{
|
||||
keepUpdating = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (keepUpdating && updateLimit-- > 0);
|
||||
} while ((keepUpdating && --updatesMax > 0) || --updatesMin > 0);
|
||||
}
|
||||
|
||||
Loaded?.Invoke(this);
|
||||
|
||||
@@ -74,11 +74,6 @@ struct TextureDataResult
|
||||
PixelFormat Format;
|
||||
Int2 Mip0Size;
|
||||
BytesContainer* Mip0DataPtr;
|
||||
|
||||
TextureDataResult()
|
||||
: Lock(FlaxStorage::LockData::Invalid)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
bool GetTextureDataForSampling(Texture* texture, TextureDataResult& data, bool hdr = false)
|
||||
|
||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Viewport
|
||||
private PrefabUIEditorRoot _uiRoot;
|
||||
private bool _showUI = false;
|
||||
|
||||
private int _defaultScaleActiveIndex = 0;
|
||||
private int _defaultScaleActiveIndex = -1;
|
||||
private int _customScaleActiveIndex = -1;
|
||||
private ContextMenuButton _uiModeButton;
|
||||
private ContextMenuChildMenu _uiViewOptions;
|
||||
|
||||
@@ -140,7 +140,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
|
||||
private string _cacheFolder;
|
||||
private Guid _assetId;
|
||||
private AssetItem _item;
|
||||
private Surface _surface;
|
||||
private Label _loadingLabel;
|
||||
private CancellationTokenSource _token;
|
||||
@@ -163,13 +163,13 @@ namespace FlaxEditor.Windows
|
||||
public AssetReferencesGraphWindow(Editor editor, AssetItem assetItem)
|
||||
: base(editor, false, ScrollBars.None)
|
||||
{
|
||||
Title = assetItem.ShortName + " References";
|
||||
_item = assetItem;
|
||||
Title = _item.ShortName + " References";
|
||||
|
||||
_tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder));
|
||||
_cacheFolder = Path.Combine(Globals.ProjectCacheFolder, "References");
|
||||
if (!Directory.Exists(_cacheFolder))
|
||||
Directory.CreateDirectory(_cacheFolder);
|
||||
_assetId = assetItem.ID;
|
||||
_surface = new Surface(this)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
@@ -194,6 +194,7 @@ namespace FlaxEditor.Windows
|
||||
_nodesAssets.Add(assetId);
|
||||
var node = new AssetNode((uint)_nodes.Count + 1, _surface.Context, GraphNodes[0], GraphGroups[0], assetId);
|
||||
_nodes.Add(node);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -392,8 +393,7 @@ namespace FlaxEditor.Windows
|
||||
_nodesAssets = new HashSet<Guid>();
|
||||
var searchLevel = 4; // TODO: make it as an option (somewhere in window UI)
|
||||
// TODO: add option to filter assets by type (eg. show only textures as leaf nodes)
|
||||
var assetNode = SpawnNode(_assetId);
|
||||
// TODO: add some outline or tint color to the main node
|
||||
var assetNode = SpawnNode(_item.ID);
|
||||
BuildGraph(assetNode, searchLevel, false);
|
||||
ArrangeGraph(assetNode, false);
|
||||
BuildGraph(assetNode, searchLevel, true);
|
||||
@@ -402,6 +402,10 @@ namespace FlaxEditor.Windows
|
||||
return;
|
||||
_progress = 100.0f;
|
||||
|
||||
var commentRect = assetNode.EditorBounds;
|
||||
commentRect.Expand(80f);
|
||||
_surface.Context.CreateComment(ref commentRect, _item.ShortName, Color.Green);
|
||||
|
||||
// Update UI
|
||||
FlaxEngine.Scripting.InvokeOnUpdate(() =>
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ using FlaxEditor.Surface;
|
||||
using FlaxEditor.Viewport.Previews;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
@@ -430,7 +429,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
for (var i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var p = parameters[i];
|
||||
if (p.IsOverride)
|
||||
if (p.IsOverride && p.IsPublic)
|
||||
{
|
||||
p.IsOverride = false;
|
||||
actions.Add(new EditParamOverrideAction
|
||||
|
||||
@@ -90,25 +90,15 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
var gpu = group.Checkbox("Bake on GPU", "If checked, SDF generation will be calculated using GPU on Compute Shader, otherwise CPU will use Job System. GPU generation is fast but result in artifacts in various meshes (eg. foliage).");
|
||||
gpu.CheckBox.Checked = sdfOptions.GPU;
|
||||
gpu.CheckBox.StateChanged += c => { Window._sdfOptions.GPU = c.Checked; };
|
||||
|
||||
var backfacesThresholdProp = group.AddPropertyItem("Backfaces Threshold", "Custom threshold (in range 0-1) for adjusting mesh internals detection based on the percentage of test rays hit triangle backfaces. Use lower value for more dense mesh.");
|
||||
var backfacesThreshold = backfacesThresholdProp.FloatValue();
|
||||
var backfacesThresholdLabel = backfacesThresholdProp.Labels.Last();
|
||||
backfacesThreshold.ValueBox.MinValue = 0.001f;
|
||||
backfacesThreshold.ValueBox.MaxValue = 1.0f;
|
||||
backfacesThreshold.ValueBox.Value = sdfOptions.BackfacesThreshold;
|
||||
backfacesThreshold.ValueBox.BoxValueChanged += b => { Window._sdfOptions.BackfacesThreshold = b.Value; };
|
||||
|
||||
// Toggle Backfaces Threshold visibility (CPU-only option)
|
||||
gpu.CheckBox.StateChanged += c =>
|
||||
{
|
||||
Window._sdfOptions.GPU = c.Checked;
|
||||
backfacesThresholdLabel.Visible = !c.Checked;
|
||||
backfacesThreshold.ValueBox.Visible = !c.Checked;
|
||||
};
|
||||
backfacesThresholdLabel.Visible = !gpu.CheckBox.Checked;
|
||||
backfacesThreshold.ValueBox.Visible = !gpu.CheckBox.Checked;
|
||||
|
||||
var lodIndex = group.IntegerValue("LOD Index", "Index of the model Level of Detail to use for SDF data building. By default uses the lowest quality LOD for fast building.");
|
||||
lodIndex.IntValue.MinValue = 0;
|
||||
lodIndex.IntValue.MaxValue = Asset.LODsCount - 1;
|
||||
|
||||
@@ -142,6 +142,7 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
Title = "Content";
|
||||
Icon = editor.Icons.Folder32;
|
||||
var style = Style.Current;
|
||||
|
||||
FlaxEditor.Utilities.Utils.SetupCommonInputActions(this);
|
||||
|
||||
@@ -164,6 +165,8 @@ namespace FlaxEditor.Windows
|
||||
_navigationBar = new NavigationBar
|
||||
{
|
||||
Parent = _toolStrip,
|
||||
ScrollbarTrackColor = style.Background,
|
||||
ScrollbarThumbColor = style.ForegroundGrey,
|
||||
};
|
||||
|
||||
// Split panel
|
||||
@@ -179,7 +182,7 @@ namespace FlaxEditor.Windows
|
||||
var headerPanel = new ContainerControl
|
||||
{
|
||||
AnchorPreset = AnchorPresets.HorizontalStretchTop,
|
||||
BackgroundColor = Style.Current.Background,
|
||||
BackgroundColor = style.Background,
|
||||
IsScrollable = false,
|
||||
Offsets = new Margin(0, 0, 0, 18 + 6),
|
||||
};
|
||||
|
||||
@@ -116,6 +116,11 @@ namespace FlaxEditor.Windows
|
||||
if (InputOptions.WindowShortcutsAvaliable)
|
||||
Editor.Windows.VisualScriptDebuggerWin.FocusOrShow();
|
||||
});
|
||||
InputActions.Add(options => options.EditorOptionsWindow, () =>
|
||||
{
|
||||
if (InputOptions.WindowShortcutsAvaliable)
|
||||
Editor.Windows.EditorOptionsWin.FocusOrShow();
|
||||
});
|
||||
|
||||
// Register
|
||||
Editor.Windows.OnWindowAdd(this);
|
||||
|
||||
@@ -10,17 +10,117 @@ using FlaxEditor.Modules;
|
||||
using FlaxEditor.Options;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
|
||||
namespace FlaxEditor.Windows
|
||||
{
|
||||
/// <summary>
|
||||
/// Render output control with content scaling support.
|
||||
/// </summary>
|
||||
public class ScaledRenderOutputControl : RenderOutputControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Custom scale.
|
||||
/// </summary>
|
||||
public float ContentScale = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Actual bounds size for content (incl. scale).
|
||||
/// </summary>
|
||||
public Float2 ContentSize => Size / ContentScale;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ScaledRenderOutputControl(SceneRenderTask task)
|
||||
: base(task)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw()
|
||||
{
|
||||
DrawSelf();
|
||||
|
||||
// Draw children with scale
|
||||
var scaling = new Float3(ContentScale, ContentScale, 1);
|
||||
Matrix3x3.Scaling(ref scaling, out Matrix3x3 scale);
|
||||
Render2D.PushTransform(scale);
|
||||
if (ClipChildren)
|
||||
{
|
||||
GetDesireClientArea(out var clientArea);
|
||||
Render2D.PushClip(ref clientArea);
|
||||
DrawChildren();
|
||||
Render2D.PopClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawChildren();
|
||||
}
|
||||
|
||||
Render2D.PopTransform();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void GetDesireClientArea(out Rectangle rect)
|
||||
{
|
||||
// Scale the area for the client controls
|
||||
rect = new Rectangle(Float2.Zero, Size / ContentScale);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsContent(ref Float2 locationParent, out Float2 location)
|
||||
{
|
||||
// Skip local PointFromParent but use base code
|
||||
location = base.PointFromParent(ref locationParent);
|
||||
return ContainsPoint(ref location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
|
||||
{
|
||||
location /= ContentScale;
|
||||
return base.IntersectsChildContent(child, location, out childSpaceLocation);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool ContainsPoint(ref Float2 location, bool precise = false)
|
||||
{
|
||||
if (precise) // Ignore as utility-only element
|
||||
return false;
|
||||
return base.ContainsPoint(ref location, precise);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RayCast(ref Float2 location, out Control hit)
|
||||
{
|
||||
var p = location / ContentScale;
|
||||
if (RayCastChildren(ref p, out hit))
|
||||
return true;
|
||||
return base.RayCast(ref location, out hit);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointToParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointToParent(ref location);
|
||||
result *= ContentScale;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Float2 PointFromParent(ref Float2 location)
|
||||
{
|
||||
var result = base.PointFromParent(ref location);
|
||||
result /= ContentScale;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Provides in-editor play mode simulation.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Windows.EditorWindow" />
|
||||
public class GameWindow : EditorWindow
|
||||
{
|
||||
private readonly RenderOutputControl _viewport;
|
||||
private readonly ScaledRenderOutputControl _viewport;
|
||||
private readonly GameRoot _guiRoot;
|
||||
private bool _showGUI = true, _editGUI = true;
|
||||
private bool _showDebugDraw = false;
|
||||
@@ -77,7 +177,7 @@ namespace FlaxEditor.Windows
|
||||
/// <summary>
|
||||
/// Gets the viewport.
|
||||
/// </summary>
|
||||
public RenderOutputControl Viewport => _viewport;
|
||||
public ScaledRenderOutputControl Viewport => _viewport;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether show game GUI in the view or keep it hidden.
|
||||
@@ -295,7 +395,7 @@ namespace FlaxEditor.Windows
|
||||
var task = MainRenderTask.Instance;
|
||||
|
||||
// Setup viewport
|
||||
_viewport = new RenderOutputControl(task)
|
||||
_viewport = new ScaledRenderOutputControl(task)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.StretchAll,
|
||||
Offsets = Margin.Zero,
|
||||
@@ -396,11 +496,8 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (v == null)
|
||||
return;
|
||||
|
||||
if (v.Size.Y <= 0 || v.Size.X <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(v.Label, "Free Aspect", StringComparison.Ordinal) && v.Size == new Int2(1, 1))
|
||||
{
|
||||
@@ -448,15 +545,7 @@ namespace FlaxEditor.Windows
|
||||
|
||||
private void ResizeViewport()
|
||||
{
|
||||
if (!_freeAspect)
|
||||
{
|
||||
_windowAspectRatio = Width / Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
_windowAspectRatio = 1;
|
||||
}
|
||||
|
||||
_windowAspectRatio = _freeAspect ? 1 : Width / Height;
|
||||
var scaleWidth = _viewportAspectRatio / _windowAspectRatio;
|
||||
var scaleHeight = _windowAspectRatio / _viewportAspectRatio;
|
||||
|
||||
@@ -468,6 +557,24 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
_viewport.Bounds = new Rectangle(Width * (1 - scaleWidth) / 2, 0, Width * scaleWidth, Height);
|
||||
}
|
||||
|
||||
if (_viewport.KeepAspectRatio)
|
||||
{
|
||||
var resolution = _viewport.CustomResolution.HasValue ? (Float2)_viewport.CustomResolution.Value : Size;
|
||||
if (scaleHeight < 1)
|
||||
{
|
||||
_viewport.ContentScale = _viewport.Width / resolution.X;
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewport.ContentScale = _viewport.Height / resolution.Y;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_viewport.ContentScale = 1;
|
||||
}
|
||||
|
||||
_viewport.SyncBackbufferSize();
|
||||
PerformLayout();
|
||||
}
|
||||
@@ -907,6 +1014,7 @@ namespace FlaxEditor.Windows
|
||||
return child.OnNavigate(direction, Float2.Zero, this, visited);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -957,7 +1065,7 @@ namespace FlaxEditor.Windows
|
||||
else
|
||||
_defaultScaleActiveIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
if (_customScaleActiveIndex != -1)
|
||||
{
|
||||
var options = Editor.UI.CustomViewportScaleOptions;
|
||||
|
||||
@@ -296,13 +296,15 @@ namespace FlaxEditor.Windows.Profiler
|
||||
var resources = _resources.Get(_memoryUsageChart.SelectedSampleIndex);
|
||||
if (resources == null || resources.Length == 0)
|
||||
return;
|
||||
var resourcesOrdered = resources.OrderByDescending(x => x.MemoryUsage);
|
||||
var resourcesOrdered = resources.OrderByDescending(x => x?.MemoryUsage ?? 0);
|
||||
|
||||
// Add rows
|
||||
var rowColor2 = Style.Current.Background * 1.4f;
|
||||
int rowIndex = 0;
|
||||
foreach (var e in resourcesOrdered)
|
||||
{
|
||||
if (e == null)
|
||||
continue;
|
||||
ClickableRow row;
|
||||
if (_tableRowsCache.Count != 0)
|
||||
{
|
||||
|
||||
@@ -319,22 +319,22 @@ namespace FlaxEditor.Windows
|
||||
{
|
||||
if (assetItem.IsOfType<SceneAsset>())
|
||||
return true;
|
||||
return assetItem.OnEditorDrag(this);
|
||||
return assetItem.OnEditorDrag(this) && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
private static bool ValidateDragActorType(ScriptType actorType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType);
|
||||
return Editor.Instance.CodeEditing.Actors.Get().Contains(actorType) && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
private static bool ValidateDragControlType(ScriptType controlType)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType);
|
||||
return Editor.Instance.CodeEditing.Controls.Get().Contains(controlType) && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
private static bool ValidateDragScriptItem(ScriptItem script)
|
||||
{
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null;
|
||||
return Editor.Instance.CodeEditing.Actors.Get(script) != ScriptType.Null && Level.IsAnySceneLoaded;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -490,6 +490,7 @@ namespace FlaxEditor.Windows
|
||||
if (result == DragDropEffect.None)
|
||||
{
|
||||
_isDropping = true;
|
||||
|
||||
// Drag assets
|
||||
if (_dragAssets != null && _dragAssets.HasValidDrag)
|
||||
{
|
||||
@@ -504,7 +505,7 @@ namespace FlaxEditor.Windows
|
||||
}
|
||||
var actor = item.OnEditorDrop(this);
|
||||
actor.Name = item.ShortName;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
var graphNode = Editor.Scene.GetActorNode(actor.ID);
|
||||
if (graphNode != null)
|
||||
graphNodes.Add(graphNode);
|
||||
@@ -527,7 +528,7 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
}
|
||||
actor.Name = item.Name;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
Editor.Scene.MarkSceneEdited(actor.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
@@ -549,7 +550,7 @@ namespace FlaxEditor.Windows
|
||||
Control = control,
|
||||
Name = item.Name,
|
||||
};
|
||||
Level.SpawnActor(uiControl);
|
||||
Editor.SceneEditing.Spawn(uiControl);
|
||||
Editor.Scene.MarkSceneEdited(uiControl.Scene);
|
||||
}
|
||||
result = DragDropEffect.Move;
|
||||
@@ -571,7 +572,7 @@ namespace FlaxEditor.Windows
|
||||
continue;
|
||||
}
|
||||
actor.Name = actorType.Name;
|
||||
Level.SpawnActor(actor);
|
||||
Editor.SceneEditing.Spawn(actor);
|
||||
var graphNode = Editor.Scene.GetActorNode(actor.ID);
|
||||
if (graphNode != null)
|
||||
graphNodes.Add(graphNode);
|
||||
|
||||
@@ -137,6 +137,7 @@ const Char* SplashScreenQuotes[] =
|
||||
TEXT("Good Luck Have Fun"),
|
||||
TEXT("GG Well Played"),
|
||||
TEXT("Now with documentation."),
|
||||
TEXT("We do this not because it is easy,\nbut because we thought it would be easy"),
|
||||
};
|
||||
|
||||
SplashScreen::~SplashScreen()
|
||||
|
||||
@@ -246,11 +246,19 @@ void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float
|
||||
const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f;
|
||||
#define ADD_OUTGOING_EVENT(type) context.Data->OutgoingEvents.Add({ k.Value.Instance, (AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime, AnimGraphInstanceData::OutgoingEvent::type })
|
||||
if ((k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration
|
||||
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration()) && Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
|
||||
&& (Math::FloorToInt(animPos) != 0 && Math::CeilToInt(animPrevPos) != Math::CeilToInt(anim->GetDuration())
|
||||
&& Math::FloorToInt(animPrevPos) != 0 && Math::CeilToInt(animPos) != Math::CeilToInt(anim->GetDuration())))
|
||||
// Handle the edge case of an event on 0 or on max animation duration during looping
|
||||
|| (loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == anim->GetDuration())
|
||||
|| (!loop && duration == 0.0f && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|
||||
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && k.Time == 0.0f)
|
||||
|| (loop && Math::FloorToInt(animPos) == 0 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (loop && Math::FloorToInt(animPrevPos) == 0 && Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration()))
|
||||
|| (Math::FloorToInt(animPos) == 1 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 1.0f)
|
||||
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 1 && k.Time == 1.0f)
|
||||
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|
||||
|| (Math::CeilToInt(animPos) == Math::CeilToInt(anim->GetDuration()) - 1 && Math::CeilToInt(animPrevPos) == Math::CeilToInt(anim->GetDuration()) && Math::NearEqual(k.Time, anim->GetDuration() - 1.0f))
|
||||
|| (Math::FloorToInt(animPos) == 0 && Math::FloorToInt(animPrevPos) == 0 && k.Time == 0.0f)
|
||||
)
|
||||
{
|
||||
int32 stateIndex = -1;
|
||||
@@ -2433,10 +2441,14 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
||||
{
|
||||
if (bucket.LoopsLeft == 0)
|
||||
{
|
||||
// End playing animation
|
||||
// End playing animation and reset bucket params
|
||||
value = tryGetValue(node->GetBox(1), Value::Null);
|
||||
bucket.Index = -1;
|
||||
slot.Animation = nullptr;
|
||||
bucket.TimePosition = 0.0f;
|
||||
bucket.BlendInPosition = 0.0f;
|
||||
bucket.BlendOutPosition = 0.0f;
|
||||
bucket.LoopsDone = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2545,9 +2557,15 @@ void AnimGraphExecutor::ProcessGroupFunction(Box* boxBase, Node* node, Value& va
|
||||
// Function Input
|
||||
case 1:
|
||||
{
|
||||
// Skip when graph is too small (eg. preview) and fallback with default value from the function graph
|
||||
if (context.GraphStack.Count() < 2)
|
||||
{
|
||||
value = tryGetValue(node->TryGetBox(1), Value::Zero);
|
||||
break;
|
||||
}
|
||||
|
||||
// Find the function call
|
||||
AnimGraphNode* functionCallNode = nullptr;
|
||||
ASSERT(context.GraphStack.Count() >= 2);
|
||||
Graph* graph;
|
||||
for (int32 i = context.CallStack.Count() - 1; i >= 0; i--)
|
||||
{
|
||||
|
||||
@@ -23,11 +23,11 @@ void AudioListener::Update()
|
||||
{
|
||||
// Update the velocity
|
||||
const Vector3 pos = GetPosition();
|
||||
const float dt = Time::Update.UnscaledDeltaTime.GetTotalSeconds();
|
||||
const float dt = Math::Max(Time::Update.UnscaledDeltaTime.GetTotalSeconds(), 0.00001f);
|
||||
const auto prevVelocity = _velocity;
|
||||
_velocity = (pos - _prevPos) / dt;
|
||||
_prevPos = pos;
|
||||
if (_velocity != prevVelocity)
|
||||
if (_velocity != prevVelocity && !_velocity.IsNanOrInfinity())
|
||||
{
|
||||
AudioBackend::Listener::VelocityChanged(_velocity);
|
||||
}
|
||||
|
||||
@@ -666,7 +666,7 @@ void Asset::onLoaded()
|
||||
{
|
||||
onLoaded_MainThread();
|
||||
}
|
||||
else if (OnLoaded.IsBinded())
|
||||
else if (OnLoaded.IsBinded() || _references.HasItems())
|
||||
{
|
||||
Function<void()> action;
|
||||
action.Bind<Asset, &Asset::onLoaded>(this);
|
||||
|
||||
@@ -218,10 +218,14 @@ Asset::LoadResult MaterialInstance::load()
|
||||
Guid baseMaterialId;
|
||||
headerStream.Read(baseMaterialId);
|
||||
auto baseMaterial = Content::LoadAsync<MaterialBase>(baseMaterialId);
|
||||
if (baseMaterial)
|
||||
baseMaterial->AddReference();
|
||||
|
||||
// Load parameters
|
||||
if (Params.Load(&headerStream))
|
||||
{
|
||||
if (baseMaterial)
|
||||
baseMaterial->RemoveReference();
|
||||
LOG(Warning, "Cannot load material parameters.");
|
||||
return LoadResult::CannotLoadData;
|
||||
}
|
||||
@@ -239,6 +243,8 @@ Asset::LoadResult MaterialInstance::load()
|
||||
ParamsChanged();
|
||||
}
|
||||
|
||||
if (baseMaterial)
|
||||
baseMaterial->RemoveReference();
|
||||
return LoadResult::Ok;
|
||||
}
|
||||
|
||||
|
||||
@@ -262,6 +262,7 @@ bool Model::GenerateSDF(float resolutionScale, int32 lodIndex, bool cacheData, f
|
||||
LOG(Warning, "Cannot generate SDF for virtual models on a main thread.");
|
||||
return true;
|
||||
}
|
||||
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
|
||||
lodIndex = Math::Clamp(lodIndex, HighestResidentLODIndex(), LODs.Count() - 1);
|
||||
|
||||
// Generate SDF
|
||||
|
||||
@@ -61,7 +61,7 @@ public:
|
||||
model->GetLODData(_lodIndex, data);
|
||||
if (data.IsInvalid())
|
||||
{
|
||||
LOG(Warning, "Missing data chunk");
|
||||
LOG(Warning, "Missing data chunk with LOD{} for model '{}'", _lodIndex, model->ToString());
|
||||
return true;
|
||||
}
|
||||
MemoryReadStream stream(data.Get(), data.Length());
|
||||
@@ -234,6 +234,7 @@ bool ModelBase::Save(bool withMeshDataFromGpu, const StringView& path)
|
||||
LOG(Error, "To save virtual model asset you need to specify 'withMeshDataFromGpu' (it has no other storage container to get data).");
|
||||
return true;
|
||||
}
|
||||
auto chunkLocks = Storage ? Storage->Lock() : FlaxStorage::LockData();
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
// Use a temporary chunks for data storage for virtual assets
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
/// <param name="id">The asset id.</param>
|
||||
/// <returns>Loaded asset of null.</returns>
|
||||
template<typename T>
|
||||
T* LoadAsync(const Guid& id)
|
||||
T* Load(const Guid& id)
|
||||
{
|
||||
for (auto& e : *this)
|
||||
{
|
||||
@@ -26,8 +26,10 @@ public:
|
||||
return (T*)e.Get();
|
||||
}
|
||||
auto asset = (T*)::LoadAsset(id, T::TypeInitializer);
|
||||
if (asset)
|
||||
if (asset && !asset->WaitForLoaded())
|
||||
Add(asset);
|
||||
else
|
||||
asset = nullptr;
|
||||
return asset;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/Internal/InternalCalls.h"
|
||||
#include "Engine/Scripting/Scripting.h"
|
||||
#if USE_EDITOR
|
||||
#include "Editor/Editor.h"
|
||||
@@ -346,17 +347,21 @@ int32 LoadingThread::Run()
|
||||
ContentLoadTask* task;
|
||||
ThisLoadThread = this;
|
||||
|
||||
MONO_THREAD_INFO_TYPE* monoThreadInfo = nullptr;
|
||||
while (Platform::AtomicRead(&_exitFlag) == 0)
|
||||
{
|
||||
if (LoadTasks.try_dequeue(task))
|
||||
{
|
||||
Run(task);
|
||||
MONO_THREAD_INFO_GET(monoThreadInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
MONO_ENTER_GC_SAFE_WITH_INFO(monoThreadInfo);
|
||||
LoadTasksMutex.Lock();
|
||||
LoadTasksSignal.Wait(LoadTasksMutex);
|
||||
LoadTasksMutex.Unlock();
|
||||
MONO_EXIT_GC_SAFE_WITH_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,6 +705,7 @@ Asset* Content::GetAsset(const StringView& outputPath)
|
||||
{
|
||||
if (outputPath.IsEmpty())
|
||||
return nullptr;
|
||||
PROFILE_CPU();
|
||||
ScopeLock lock(AssetsLocker);
|
||||
for (auto i = Assets.Begin(); i.IsNotEnd(); ++i)
|
||||
{
|
||||
|
||||
@@ -146,6 +146,10 @@ bool BinaryAssetFactoryBase::UpgradeAsset(const AssetInfo& info, FlaxStorage* st
|
||||
context.Input = context.Output;
|
||||
} while (upgrader->ShouldUpgrade(context.Input.SerializedVersion));
|
||||
|
||||
// Prevent other threads from loading the storage when it is upgrading
|
||||
// It works because CriticalSection allows recursion
|
||||
ScopeLock upgradeLock(storage->_loadLocker);
|
||||
|
||||
// Release storage internal data (should also close file handles)
|
||||
{
|
||||
// HACK: file is locked by some tasks: the current one that called asset data upgrade (LoadAssetTask)
|
||||
|
||||
@@ -87,6 +87,10 @@ public:
|
||||
/// </summary>
|
||||
double LastAccessTime = 0.0;
|
||||
|
||||
/// <summary>
|
||||
/// Flag set to indicate that chunk is during loading (atomic access to sync multiple reading threads).
|
||||
/// </summary>
|
||||
int64 IsLoading = 0;
|
||||
/// <summary>
|
||||
/// The chunk data.
|
||||
/// </summary>
|
||||
@@ -146,7 +150,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsLoaded() const
|
||||
{
|
||||
return Data.IsValid();
|
||||
return Data.IsValid() && Platform::AtomicRead(&IsLoading) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,7 +158,7 @@ public:
|
||||
/// </summary>
|
||||
FORCE_INLINE bool IsMissing() const
|
||||
{
|
||||
return Data.IsInvalid();
|
||||
return !IsLoaded();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "FlaxPackage.h"
|
||||
#include "ContentStorageManager.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/ScopeExit.h"
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
@@ -74,8 +75,6 @@ FlaxChunk* FlaxChunk::Clone() const
|
||||
|
||||
const int32 FlaxStorage::MagicCode = 1180124739;
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockData::Invalid(nullptr);
|
||||
|
||||
struct Header
|
||||
{
|
||||
int32 MagicCode;
|
||||
@@ -246,6 +245,7 @@ FlaxStorage::~FlaxStorage()
|
||||
ASSERT(IsDisposed());
|
||||
CHECK(_chunksLock == 0);
|
||||
CHECK(_refCount == 0);
|
||||
CHECK(_isUnloadingData == 0);
|
||||
ASSERT(_chunks.IsEmpty());
|
||||
|
||||
#if USE_EDITOR
|
||||
@@ -261,6 +261,22 @@ FlaxStorage::~FlaxStorage()
|
||||
#endif
|
||||
}
|
||||
|
||||
void FlaxStorage::LockChunks()
|
||||
{
|
||||
RETRY:
|
||||
Platform::InterlockedIncrement(&_chunksLock);
|
||||
if (Platform::AtomicRead(&_isUnloadingData) != 0)
|
||||
{
|
||||
// Someone else is closing file handles or freeing chunks so wait for it to finish and retry
|
||||
Platform::InterlockedDecrement(&_chunksLock);
|
||||
do
|
||||
{
|
||||
Platform::Sleep(1);
|
||||
} while (Platform::AtomicRead(&_isUnloadingData) != 0);
|
||||
goto RETRY;
|
||||
}
|
||||
}
|
||||
|
||||
FlaxStorage::LockData FlaxStorage::LockSafe()
|
||||
{
|
||||
auto lock = LockData(this);
|
||||
@@ -689,7 +705,6 @@ bool FlaxStorage::LoadAssetHeader(const Guid& id, AssetInitData& data)
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load header
|
||||
return LoadAssetHeader(e, data);
|
||||
}
|
||||
|
||||
@@ -699,7 +714,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
ASSERT(IsLoaded());
|
||||
ASSERT(chunk != nullptr && _chunks.Contains(chunk));
|
||||
|
||||
// Check if already loaded
|
||||
// Protect against loading the same chunk from multiple threads at once
|
||||
while (Platform::InterlockedCompareExchange(&chunk->IsLoading, 1, 0) != 0)
|
||||
Platform::Sleep(1);
|
||||
SCOPE_EXIT{ Platform::AtomicStore(&chunk->IsLoading, 0); };
|
||||
if (chunk->IsLoaded())
|
||||
return false;
|
||||
|
||||
@@ -776,12 +794,10 @@ bool FlaxStorage::LoadAssetChunk(FlaxChunk* chunk)
|
||||
// Raw data
|
||||
chunk->Data.Read(stream, size);
|
||||
}
|
||||
ASSERT(chunk->IsLoaded());
|
||||
chunk->RegisterUsage();
|
||||
}
|
||||
|
||||
UnlockChunks();
|
||||
|
||||
return failed;
|
||||
}
|
||||
|
||||
@@ -1420,10 +1436,12 @@ FileReadStream* FlaxStorage::OpenFile()
|
||||
|
||||
bool FlaxStorage::CloseFileHandles()
|
||||
{
|
||||
// Guard the whole process so if new thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
SCOPE_EXIT{ Platform::InterlockedDecrement(&_isUnloadingData); };
|
||||
|
||||
if (Platform::AtomicRead(&_chunksLock) == 0 && Platform::AtomicRead(&_files) == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false; // Early out when no files are opened
|
||||
PROFILE_CPU();
|
||||
PROFILE_MEM(ContentFiles);
|
||||
|
||||
@@ -1496,9 +1514,21 @@ void FlaxStorage::Tick(double time)
|
||||
{
|
||||
auto chunk = _chunks.Get()[i];
|
||||
const bool wasUsed = (time - chunk->LastAccessTime) < unusedDataChunksLifetime;
|
||||
if (!wasUsed && chunk->IsLoaded() && EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory))
|
||||
if (!wasUsed &&
|
||||
chunk->IsLoaded() &&
|
||||
EnumHasNoneFlags(chunk->Flags, FlaxChunkFlags::KeepInMemory) &&
|
||||
Platform::AtomicRead(&chunk->IsLoading) == 0)
|
||||
{
|
||||
// Guard the unloading so if other thread wants to lock the chunks will need to wait for this to end
|
||||
Platform::InterlockedIncrement(&_isUnloadingData);
|
||||
if (Platform::AtomicRead(&_chunksLock) != 0 || Platform::AtomicRead(&chunk->IsLoading) != 0)
|
||||
{
|
||||
// Someone started loading so skip ticking
|
||||
Platform::InterlockedDecrement(&_isUnloadingData);
|
||||
return;
|
||||
}
|
||||
chunk->Unload();
|
||||
Platform::InterlockedDecrement(&_isUnloadingData);
|
||||
}
|
||||
wasAnyUsed |= wasUsed;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ protected:
|
||||
int64 _refCount = 0;
|
||||
int64 _chunksLock = 0;
|
||||
int64 _files = 0;
|
||||
int64 _isUnloadingData = 0;
|
||||
double _lastRefLostTime;
|
||||
CriticalSection _loadLocker;
|
||||
|
||||
@@ -129,10 +130,7 @@ public:
|
||||
/// <summary>
|
||||
/// Locks the storage chunks data to prevent disposing them. Also ensures that file handles won't be closed while chunks are locked.
|
||||
/// </summary>
|
||||
FORCE_INLINE void LockChunks()
|
||||
{
|
||||
Platform::InterlockedIncrement(&_chunksLock);
|
||||
}
|
||||
void LockChunks();
|
||||
|
||||
/// <summary>
|
||||
/// Unlocks the storage chunks data.
|
||||
@@ -148,7 +146,6 @@ public:
|
||||
struct LockData
|
||||
{
|
||||
friend FlaxStorage;
|
||||
static LockData Invalid;
|
||||
|
||||
private:
|
||||
FlaxStorage* _storage;
|
||||
@@ -161,6 +158,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
LockData()
|
||||
: _storage(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
LockData(const LockData& other)
|
||||
: _storage(other._storage)
|
||||
{
|
||||
|
||||
@@ -658,7 +658,10 @@ public:
|
||||
--_count;
|
||||
T* data = _allocation.Get();
|
||||
if (index < _count)
|
||||
Memory::MoveAssignItems(data + index, data + (index + 1), _count - index);
|
||||
{
|
||||
for (int32 i = index; i < _count; i++)
|
||||
data[i] = MoveTemp(data[i + 1]);
|
||||
}
|
||||
Memory::DestructItems(data + _count, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -409,27 +409,36 @@ protected:
|
||||
else
|
||||
{
|
||||
// Rebuild entire table completely
|
||||
const int32 elementsCount = _elementsCount;
|
||||
const int32 oldSize = _size;
|
||||
AllocationData oldAllocation;
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, _size, _size);
|
||||
AllocationUtils::MoveToEmpty<BucketType, AllocationType>(oldAllocation, _allocation, oldSize, oldSize);
|
||||
_allocation.Allocate(_size);
|
||||
BucketType* data = _allocation.Get();
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
data[i]._state = HashSetBucketState::Empty;
|
||||
BucketType* oldData = oldAllocation.Get();
|
||||
FindPositionResult pos;
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
{
|
||||
BucketType& oldBucket = oldData[i];
|
||||
if (oldBucket.IsOccupied())
|
||||
{
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
if (pos.FreeSlotIndex == -1)
|
||||
{
|
||||
// Grow and retry to handle pathological cases (eg. heavy collisions)
|
||||
EnsureCapacity(_size + 1, true);
|
||||
FindPosition(oldBucket.GetKey(), pos);
|
||||
ASSERT(pos.FreeSlotIndex != -1);
|
||||
}
|
||||
BucketType& bucket = _allocation.Get()[pos.FreeSlotIndex];
|
||||
bucket = MoveTemp(oldBucket);
|
||||
}
|
||||
}
|
||||
for (int32 i = 0; i < _size; ++i)
|
||||
for (int32 i = 0; i < oldSize; ++i)
|
||||
oldData[i].Free();
|
||||
_elementsCount = elementsCount;
|
||||
}
|
||||
_deletedCount = 0;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -19,7 +20,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS \
|
||||
_Pragma("clang diagnostic push") \
|
||||
@@ -37,8 +38,9 @@
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
|
||||
#define DLLEXPORT __attribute__ ((__visibility__ ("default")))
|
||||
#define DLLEXPORT __attribute__((__visibility__("default")))
|
||||
#define DLLIMPORT
|
||||
#define USED __attribute__((used))
|
||||
#define THREADLOCAL __thread
|
||||
#define STDCALL __attribute__((stdcall))
|
||||
#define CDECL __attribute__((cdecl))
|
||||
@@ -52,7 +54,7 @@
|
||||
#define PACK_BEGIN()
|
||||
#define PACK_END() __attribute__((__packed__))
|
||||
#define ALIGN_BEGIN(_align)
|
||||
#define ALIGN_END(_align) __attribute__( (aligned(_align) ) )
|
||||
#define ALIGN_END(_align) __attribute__((aligned(_align)))
|
||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||
@@ -67,6 +69,7 @@
|
||||
|
||||
#define DLLEXPORT __declspec(dllexport)
|
||||
#define DLLIMPORT __declspec(dllimport)
|
||||
#define USED
|
||||
#define THREADLOCAL __declspec(thread)
|
||||
#define STDCALL __stdcall
|
||||
#define CDECL __cdecl
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user