Merge remote-tracking branch 'upstream/master'
This commit is contained in:
@@ -12,13 +12,14 @@ namespace FlaxEngine.Tools
|
||||
{
|
||||
partial struct Options
|
||||
{
|
||||
private bool ShowGeometry => Type == ModelTool.ModelType.Model || Type == ModelTool.ModelType.SkinnedModel;
|
||||
private bool ShowModel => Type == ModelTool.ModelType.Model;
|
||||
private bool ShowSkinnedModel => Type == ModelTool.ModelType.SkinnedModel;
|
||||
private bool ShowAnimation => Type == ModelTool.ModelType.Animation;
|
||||
private bool ShowGeometry => Type == ModelType.Model || Type == ModelType.SkinnedModel || Type == ModelType.Prefab;
|
||||
private bool ShowModel => Type == ModelType.Model || Type == ModelType.Prefab;
|
||||
private bool ShowSkinnedModel => Type == ModelType.SkinnedModel || Type == ModelType.Prefab;
|
||||
private bool ShowAnimation => Type == ModelType.Animation || Type == ModelType.Prefab;
|
||||
private bool ShowSmoothingNormalsAngle => ShowGeometry && CalculateNormals;
|
||||
private bool ShowSmoothingTangentsAngle => ShowGeometry && CalculateTangents;
|
||||
private bool ShowFramesRange => ShowAnimation && Duration == ModelTool.AnimationDuration.Custom;
|
||||
private bool ShowFramesRange => ShowAnimation && Duration == AnimationDuration.Custom;
|
||||
private bool ShowSplitting => Type != ModelType.Prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Graphics/GPUContext.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Graphics/RenderTools.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/Content/Factories/BinaryAssetFactory.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/Content/Upgraders/TextureAssetUpgrader.h"
|
||||
@@ -92,15 +93,12 @@ SpriteHandle PreviewsCache::FindSlot(const Guid& id)
|
||||
{
|
||||
if (WaitForLoaded())
|
||||
return SpriteHandle::Invalid;
|
||||
|
||||
// Find entry
|
||||
int32 index;
|
||||
if (_assets.Find(id, index))
|
||||
{
|
||||
const String spriteName = StringUtils::ToString(index);
|
||||
return FindSprite(spriteName);
|
||||
}
|
||||
|
||||
return SpriteHandle::Invalid;
|
||||
}
|
||||
|
||||
@@ -114,6 +112,17 @@ Asset::LoadResult PreviewsCache::load()
|
||||
return LoadResult::Failed;
|
||||
_assets.Set(previewsMetaChunk->Get<Guid>(), ASSETS_ICONS_PER_ATLAS);
|
||||
|
||||
// Verify if cached assets still exist (don't store thumbnails for removed files)
|
||||
AssetInfo assetInfo;
|
||||
for (Guid& id : _assets)
|
||||
{
|
||||
if (id.IsValid() && Content::GetAsset(id) == nullptr && !Content::GetAssetInfo(id, assetInfo))
|
||||
{
|
||||
// Free slot (no matter the texture contents)
|
||||
id = Guid::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
// Setup atlas sprites array
|
||||
Sprite sprite;
|
||||
sprite.Area.Size = static_cast<float>(ASSET_ICON_SIZE) / ASSETS_ICONS_ATLAS_SIZE;
|
||||
@@ -162,7 +171,7 @@ SpriteHandle PreviewsCache::OccupySlot(GPUTexture* source, const Guid& id)
|
||||
if (WaitForLoaded())
|
||||
return SpriteHandle::Invalid;
|
||||
|
||||
// Find free slot and for that asset
|
||||
// Find this asset slot or use the first empty
|
||||
int32 index = _assets.Find(id);
|
||||
if (index == INVALID_INDEX)
|
||||
index = _assets.Find(Guid::Empty);
|
||||
@@ -201,14 +210,12 @@ bool PreviewsCache::ReleaseSlot(const Guid& id)
|
||||
{
|
||||
bool result = false;
|
||||
ScopeLock lock(Locker);
|
||||
|
||||
int32 index = _assets.Find(id);
|
||||
if (index != INVALID_INDEX)
|
||||
{
|
||||
_assets[index] = Guid::Empty;
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,10 @@ namespace FlaxEditor.Content
|
||||
{
|
||||
if (_preview == null)
|
||||
{
|
||||
_preview = new ModelPreview(false);
|
||||
_preview = new ModelPreview(false)
|
||||
{
|
||||
ScaleToFit = false,
|
||||
};
|
||||
InitAssetPreview(_preview);
|
||||
}
|
||||
|
||||
@@ -91,6 +94,7 @@ namespace FlaxEditor.Content
|
||||
_preview.Model = (Model)request.Asset;
|
||||
_preview.Parent = guiRoot;
|
||||
_preview.SyncBackbufferSize();
|
||||
_preview.ViewportCamera.SetArcBallView(_preview.Model.GetBox());
|
||||
|
||||
_preview.Task.OnDraw();
|
||||
}
|
||||
|
||||
@@ -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); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,11 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
/// The finalized state.
|
||||
/// </summary>
|
||||
Disposed,
|
||||
|
||||
/// <summary>
|
||||
/// The request has failed (eg. asset cannot be loaded).
|
||||
/// </summary>
|
||||
Failed,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -78,6 +83,14 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
Proxy = proxy;
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
if (State == States.Prepared && (!Asset || Asset.LastLoadFailed))
|
||||
{
|
||||
State = States.Failed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares this request.
|
||||
/// </summary>
|
||||
@@ -85,11 +98,8 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
{
|
||||
if (State != States.Created)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
// Prepare
|
||||
Asset = FlaxEngine.Content.LoadAsync(Item.Path);
|
||||
Proxy.OnThumbnailDrawPrepare(this);
|
||||
|
||||
State = States.Prepared;
|
||||
}
|
||||
|
||||
@@ -101,9 +111,7 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
{
|
||||
if (State != States.Prepared)
|
||||
throw new InvalidOperationException();
|
||||
|
||||
Item.Thumbnail = icon;
|
||||
|
||||
State = States.Rendered;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,15 +21,11 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
/// </summary>
|
||||
public const float MinimumRequiredResourcesQuality = 0.8f;
|
||||
|
||||
// TODO: free atlas slots for deleted assets
|
||||
|
||||
private readonly List<PreviewsCache> _cache = new List<PreviewsCache>(4);
|
||||
private readonly string _cacheFolder;
|
||||
|
||||
private DateTime _lastFlushTime;
|
||||
|
||||
private readonly List<ThumbnailRequest> _requests = new List<ThumbnailRequest>(128);
|
||||
private readonly PreviewRoot _guiRoot = new PreviewRoot();
|
||||
private DateTime _lastFlushTime;
|
||||
private RenderTask _task;
|
||||
private GPUTexture _output;
|
||||
|
||||
@@ -88,7 +84,6 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
}
|
||||
}
|
||||
|
||||
// Add request
|
||||
AddRequest(assetItem, proxy);
|
||||
}
|
||||
}
|
||||
@@ -118,15 +113,15 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
for (int i = 0; i < _cache.Count; i++)
|
||||
{
|
||||
if (_cache[i].ReleaseSlot(assetItem.ID))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool HasMinimumQuality(TextureBase asset)
|
||||
{
|
||||
if (asset.HasStreamingError)
|
||||
return true; // Don't block thumbnails queue when texture fails to stream in (eg. unsupported format)
|
||||
var mipLevels = asset.MipLevels;
|
||||
var minMipLevels = Mathf.Min(mipLevels, 7);
|
||||
return asset.IsLoaded && asset.ResidentMipLevels >= Mathf.Max(minMipLevels, (int)(mipLevels * MinimumRequiredResourcesQuality));
|
||||
@@ -198,13 +193,7 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
/// <inheritdoc />
|
||||
void IContentItemOwner.OnItemDeleted(ContentItem item)
|
||||
{
|
||||
if (item is AssetItem assetItem)
|
||||
{
|
||||
lock (_requests)
|
||||
{
|
||||
RemoveRequest(assetItem);
|
||||
}
|
||||
}
|
||||
DeletePreview(item);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -494,10 +483,7 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
{
|
||||
// Wait some frames before start generating previews (late init feature)
|
||||
if (Time.TimeSinceStartup < 1.0f || HasAllAtlasesLoaded() == false)
|
||||
{
|
||||
// Back
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_requests)
|
||||
{
|
||||
@@ -515,6 +501,7 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
var request = _requests[i];
|
||||
try
|
||||
{
|
||||
request.Update();
|
||||
if (request.IsReady)
|
||||
{
|
||||
isAnyReady = true;
|
||||
@@ -523,6 +510,10 @@ namespace FlaxEditor.Content.Thumbnails
|
||||
{
|
||||
request.Prepare();
|
||||
}
|
||||
else if (request.State == ThumbnailRequest.States.Failed)
|
||||
{
|
||||
_requests.RemoveAt(i--);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -169,6 +169,30 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
permissions += String::Format(TEXT("\n <uses-permission android:name=\"{0}\" />"), e.Item);
|
||||
}
|
||||
|
||||
// Setup default Android screen orientation
|
||||
auto defaultOrienation = platformSettings->DefaultOrientation;
|
||||
String orientation = String("fullSensor");
|
||||
switch (defaultOrienation)
|
||||
{
|
||||
case AndroidPlatformSettings::ScreenOrientation::Portrait:
|
||||
orientation = String("portrait");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::PortraitReverse:
|
||||
orientation = String("reversePortrait");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::LandscapeRight:
|
||||
orientation = String("landscape");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::LandscapeLeft:
|
||||
orientation = String("reverseLandscape");
|
||||
break;
|
||||
case AndroidPlatformSettings::ScreenOrientation::AutoRotation:
|
||||
orientation = String("fullSensor");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Setup Android application attributes
|
||||
String attributes;
|
||||
if (data.Configuration != BuildConfiguration::Release)
|
||||
@@ -223,6 +247,7 @@ bool AndroidPlatformTools::OnPostProcess(CookingData& data)
|
||||
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${PackageName}"), packageName);
|
||||
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${ProjectVersion}"), projectVersion);
|
||||
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${AndroidPermissions}"), permissions);
|
||||
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${DefaultOrientation}"), orientation);
|
||||
EditorUtilities::ReplaceInFile(manifestPath, TEXT("${AndroidAttributes}"), attributes);
|
||||
const String stringsPath = data.OriginalOutputPath / TEXT("app/src/main/res/values/strings.xml");
|
||||
EditorUtilities::ReplaceInFile(stringsPath, TEXT("${ProjectName}"), gameSettings->ProductName);
|
||||
|
||||
@@ -1270,7 +1270,7 @@ bool CookAssetsStep::Perform(CookingData& data)
|
||||
{
|
||||
Array<CookingData::AssetTypeStatistics> assetTypes;
|
||||
data.Stats.AssetStats.GetValues(assetTypes);
|
||||
Sorting::QuickSort(assetTypes.Get(), assetTypes.Count());
|
||||
Sorting::QuickSort(assetTypes);
|
||||
|
||||
LOG(Info, "");
|
||||
LOG(Info, "Top assets types stats:");
|
||||
|
||||
@@ -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.Get(), versions.Count());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,8 @@ using FlaxEditor.CustomEditors.GUI;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using FlaxEngine.Utilities;
|
||||
using Newtonsoft.Json;
|
||||
using JsonSerializer = FlaxEngine.Json.JsonSerializer;
|
||||
|
||||
namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
@@ -386,22 +385,22 @@ namespace FlaxEditor.CustomEditors
|
||||
LinkedLabel = label;
|
||||
}
|
||||
|
||||
private void RevertDiffToDefault(CustomEditor editor)
|
||||
{
|
||||
if (editor.ChildrenEditors.Count == 0)
|
||||
{
|
||||
// Skip if no change detected
|
||||
if (!editor.Values.IsDefaultValueModified)
|
||||
return;
|
||||
/// <summary>
|
||||
/// If true, the value reverting to default/reference will be handled via iteration over children editors, instead of for a whole object at once.
|
||||
/// </summary>
|
||||
public virtual bool RevertValueWithChildren => ChildrenEditors.Count != 0;
|
||||
|
||||
editor.SetValueToDefault();
|
||||
private void RevertDiffToDefault()
|
||||
{
|
||||
if (RevertValueWithChildren)
|
||||
{
|
||||
foreach (var child in ChildrenEditors)
|
||||
child.RevertDiffToDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
||||
{
|
||||
RevertDiffToDefault(editor.ChildrenEditors[i]);
|
||||
}
|
||||
if (Values.IsDefaultValueModified)
|
||||
SetValueToDefault();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -414,11 +413,6 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (!Values.IsDefaultValueModified)
|
||||
return false;
|
||||
|
||||
// Skip array items (show diff only on a bottom level properties and fields)
|
||||
if (ParentEditor is Editors.ArrayEditor)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -430,7 +424,7 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (!Values.HasDefaultValue)
|
||||
return;
|
||||
RevertDiffToDefault(this);
|
||||
RevertDiffToDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -468,22 +462,17 @@ namespace FlaxEditor.CustomEditors
|
||||
}
|
||||
}
|
||||
|
||||
private void RevertDiffToReference(CustomEditor editor)
|
||||
private void RevertDiffToReference()
|
||||
{
|
||||
if (editor.ChildrenEditors.Count == 0)
|
||||
if (RevertValueWithChildren)
|
||||
{
|
||||
// Skip if no change detected
|
||||
if (!editor.Values.IsReferenceValueModified)
|
||||
return;
|
||||
|
||||
editor.SetValueToReference();
|
||||
foreach (var child in ChildrenEditors)
|
||||
child.RevertDiffToReference();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < editor.ChildrenEditors.Count; i++)
|
||||
{
|
||||
RevertDiffToReference(editor.ChildrenEditors[i]);
|
||||
}
|
||||
if (Values.IsReferenceValueModified)
|
||||
SetValueToReference();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -496,11 +485,6 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (!Values.IsReferenceValueModified)
|
||||
return false;
|
||||
|
||||
// Skip array items (show diff only on a bottom level properties and fields)
|
||||
if (ParentEditor is Editors.ArrayEditor)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -512,7 +496,7 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (!Values.HasReferenceValue)
|
||||
return;
|
||||
RevertDiffToReference(this);
|
||||
RevertDiffToReference();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -657,7 +641,7 @@ namespace FlaxEditor.CustomEditors
|
||||
// Default
|
||||
try
|
||||
{
|
||||
obj = JsonConvert.DeserializeObject(text, TypeUtils.GetType(Values.Type), JsonSerializer.Settings);
|
||||
obj = Newtonsoft.Json.JsonConvert.DeserializeObject(text, TypeUtils.GetType(Values.Type), JsonSerializer.Settings);
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -762,7 +746,7 @@ namespace FlaxEditor.CustomEditors
|
||||
/// </summary>
|
||||
public void SetValueToDefault()
|
||||
{
|
||||
SetValue(Values.DefaultValue);
|
||||
SetValueCloned(Values.DefaultValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -799,7 +783,19 @@ namespace FlaxEditor.CustomEditors
|
||||
return;
|
||||
}
|
||||
|
||||
SetValue(Values.ReferenceValue);
|
||||
SetValueCloned(Values.ReferenceValue);
|
||||
}
|
||||
|
||||
private void SetValueCloned(object value)
|
||||
{
|
||||
// For objects (eg. arrays) we need to clone them to prevent editing default/reference value within editor
|
||||
if (value != null && !value.GetType().IsValueType)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(value);
|
||||
value = JsonSerializer.Deserialize(json, value.GetType());
|
||||
}
|
||||
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -811,7 +807,6 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (_isSetBlocked)
|
||||
return;
|
||||
|
||||
if (OnDirty(this, value, token))
|
||||
{
|
||||
_hasValueDirty = true;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors.Editors;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Tools;
|
||||
|
||||
namespace FlaxEditor.CustomEditors.Dedicated;
|
||||
|
||||
/// <summary>
|
||||
/// The missing script editor.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(ModelPrefab)), DefaultEditor]
|
||||
public class ModelPrefabEditor : GenericEditor
|
||||
{
|
||||
private Guid _prefabId;
|
||||
private Button _reimportButton;
|
||||
private string _importPath;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
var modelPrefab = Values[0] as ModelPrefab;
|
||||
if (modelPrefab == null)
|
||||
return;
|
||||
_prefabId = modelPrefab.PrefabID;
|
||||
while (true)
|
||||
{
|
||||
if (_prefabId == Guid.Empty)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var prefab = FlaxEngine.Content.Load<Prefab>(_prefabId);
|
||||
if (prefab)
|
||||
{
|
||||
var prefabObjectId = modelPrefab.PrefabObjectID;
|
||||
var prefabObject = prefab.GetDefaultInstance(ref prefabObjectId);
|
||||
if (prefabObject.PrefabID == _prefabId)
|
||||
break;
|
||||
_prefabId = prefabObject.PrefabID;
|
||||
}
|
||||
}
|
||||
|
||||
var button = layout.Button("Reimport", "Reimports the source asset as prefab.");
|
||||
_reimportButton = button.Button;
|
||||
_reimportButton.Clicked += OnReimport;
|
||||
}
|
||||
|
||||
private void OnReimport()
|
||||
{
|
||||
var prefab = FlaxEngine.Content.Load<Prefab>(_prefabId);
|
||||
var modelPrefab = (ModelPrefab)Values[0];
|
||||
var importPath = modelPrefab.ImportPath;
|
||||
var editor = Editor.Instance;
|
||||
if (editor.ContentImporting.GetReimportPath("Model Prefab", ref importPath))
|
||||
return;
|
||||
var folder = editor.ContentDatabase.Find(Path.GetDirectoryName(prefab.Path)) as ContentFolder;
|
||||
if (folder == null)
|
||||
return;
|
||||
var importOptions = modelPrefab.ImportOptions;
|
||||
importOptions.Type = ModelTool.ModelType.Prefab;
|
||||
_importPath = importPath;
|
||||
_reimportButton.Enabled = false;
|
||||
editor.ContentImporting.ImportFileEnd += OnImportFileEnd;
|
||||
editor.ContentImporting.Import(importPath, folder, true, importOptions);
|
||||
}
|
||||
|
||||
private void OnImportFileEnd(IFileEntryAction entry, bool failed)
|
||||
{
|
||||
if (entry.SourceUrl == _importPath)
|
||||
{
|
||||
// Restore button
|
||||
_importPath = null;
|
||||
_reimportButton.Enabled = true;
|
||||
Editor.Instance.ContentImporting.ImportFileEnd -= OnImportFileEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,6 +246,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
||||
var multiAction = new MultiUndoAction(actions);
|
||||
multiAction.Do();
|
||||
var presenter = ScriptsEditor.Presenter;
|
||||
ScriptsEditor.ParentEditor?.RebuildLayout();
|
||||
if (presenter != null)
|
||||
{
|
||||
presenter.Undo.AddAction(multiAction);
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
protected bool NotNullItems;
|
||||
|
||||
private IntegerValueElement _size;
|
||||
private PropertyNameLabel _sizeLabel;
|
||||
private Color _background;
|
||||
private int _elementsCount;
|
||||
private bool _readOnly;
|
||||
@@ -109,6 +110,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool RevertValueWithChildren => false; // Always revert value for a whole collection
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
@@ -174,7 +178,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
else
|
||||
{
|
||||
_size = dragArea.IntegerValue("Size");
|
||||
var sizeProperty = dragArea.AddPropertyItem("Size");
|
||||
_sizeLabel = sizeProperty.Labels.Last();
|
||||
_size = sizeProperty.IntegerValue();
|
||||
_size.IntValue.MinValue = 0;
|
||||
_size.IntValue.MaxValue = ushort.MaxValue;
|
||||
_size.IntValue.Value = size;
|
||||
@@ -274,6 +280,15 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Deinitialize()
|
||||
{
|
||||
_size = null;
|
||||
_sizeLabel = null;
|
||||
|
||||
base.Deinitialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the parent layout if its collection.
|
||||
/// </summary>
|
||||
@@ -296,7 +311,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
{
|
||||
if (IsSetBlocked)
|
||||
return;
|
||||
|
||||
Resize(_size.IntValue.Value);
|
||||
}
|
||||
|
||||
@@ -311,11 +325,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
return;
|
||||
|
||||
var cloned = CloneValues();
|
||||
|
||||
var tmp = cloned[dstIndex];
|
||||
cloned[dstIndex] = cloned[srcIndex];
|
||||
cloned[srcIndex] = tmp;
|
||||
|
||||
SetValue(cloned);
|
||||
}
|
||||
|
||||
@@ -371,6 +383,17 @@ namespace FlaxEditor.CustomEditors.Editors
|
||||
if (HasDifferentValues || HasDifferentTypes)
|
||||
return;
|
||||
|
||||
// Update reference/default value indicator
|
||||
if (_sizeLabel != null)
|
||||
{
|
||||
var color = Color.Transparent;
|
||||
if (Values.HasReferenceValue && Values.ReferenceValue is IList referenceValue && referenceValue.Count != Count)
|
||||
color = FlaxEngine.GUI.Style.Current.BackgroundSelected;
|
||||
else if (Values.HasDefaultValue && Values.DefaultValue is IList defaultValue && defaultValue.Count != Count)
|
||||
color = Color.Yellow * 0.8f;
|
||||
_sizeLabel.HighlightStripColor = color;
|
||||
}
|
||||
|
||||
// Check if collection has been resized (by UI or from external source)
|
||||
if (Count != _elementsCount)
|
||||
{
|
||||
|
||||
@@ -73,7 +73,6 @@ namespace FlaxEditor.CustomEditors
|
||||
{
|
||||
if (instanceValues == null || instanceValues.Count != Count)
|
||||
throw new ArgumentException();
|
||||
|
||||
for (int i = 0; i < Count; i++)
|
||||
{
|
||||
var v = instanceValues[i];
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -201,7 +201,21 @@ namespace FlaxEditor.Gizmo
|
||||
ActorNode prefabRoot = GetPrefabRootInParent(actorNode);
|
||||
if (prefabRoot != null && actorNode != prefabRoot)
|
||||
{
|
||||
hit = WalkUpAndFindActorNodeBeforeSelection(actorNode, prefabRoot);
|
||||
bool isPrefabInSelection = false;
|
||||
foreach (var e in sceneEditing.Selection)
|
||||
{
|
||||
if (e is ActorNode ae && GetPrefabRootInParent(ae) == prefabRoot)
|
||||
{
|
||||
isPrefabInSelection = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip selecting prefab root if we already had object from that prefab selected
|
||||
if (!isPrefabInSelection)
|
||||
{
|
||||
hit = WalkUpAndFindActorNodeBeforeSelection(actorNode, prefabRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
@@ -769,7 +642,7 @@ bool ManagedEditor::TryRestoreImportOptions(ModelTool::Options& options, String
|
||||
|
||||
// Get options from model
|
||||
FileSystem::NormalizePath(assetPath);
|
||||
return ImportModelFile::TryGetImportOptions(assetPath, options);
|
||||
return ImportModel::TryGetImportOptions(assetPath, options);
|
||||
}
|
||||
|
||||
bool ManagedEditor::Import(const String& inputPath, const String& outputPath, const AudioTool::Options& options)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -126,29 +126,35 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
if (item != null && !item.GetImportPath(out string importPath))
|
||||
{
|
||||
// Check if input file is missing
|
||||
if (!System.IO.File.Exists(importPath))
|
||||
{
|
||||
Editor.LogWarning(string.Format("Cannot reimport asset \'{0}\'. File \'{1}\' does not exist.", item.Path, importPath));
|
||||
if (skipSettingsDialog)
|
||||
return;
|
||||
|
||||
// Ask user to select new file location
|
||||
var title = string.Format("Please find missing \'{0}\' file for asset \'{1}\'", importPath, item.ShortName);
|
||||
if (FileSystem.ShowOpenFileDialog(Editor.Windows.MainWindow, null, "All files (*.*)\0*.*\0", false, title, out var files))
|
||||
return;
|
||||
if (files != null && files.Length > 0)
|
||||
importPath = files[0];
|
||||
|
||||
// Validate file path again
|
||||
if (!System.IO.File.Exists(importPath))
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetReimportPath(item.ShortName, ref importPath, skipSettingsDialog))
|
||||
return;
|
||||
Import(importPath, item.Path, true, skipSettingsDialog, settings);
|
||||
}
|
||||
}
|
||||
|
||||
internal bool GetReimportPath(string contextName, ref string importPath, bool skipSettingsDialog = false)
|
||||
{
|
||||
// Check if input file is missing
|
||||
if (!System.IO.File.Exists(importPath))
|
||||
{
|
||||
Editor.LogWarning(string.Format("Cannot reimport asset \'{0}\'. File \'{1}\' does not exist.", contextName, importPath));
|
||||
if (skipSettingsDialog)
|
||||
return true;
|
||||
|
||||
// Ask user to select new file location
|
||||
var title = string.Format("Please find missing \'{0}\' file for asset \'{1}\'", importPath, contextName);
|
||||
if (FileSystem.ShowOpenFileDialog(Editor.Windows.MainWindow, null, "All files (*.*)\0*.*\0", false, title, out var files))
|
||||
return true;
|
||||
if (files != null && files.Length > 0)
|
||||
importPath = files[0];
|
||||
|
||||
// Validate file path again
|
||||
if (!System.IO.File.Exists(importPath))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Imports the specified files.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,9 +7,37 @@ using Real = System.Single;
|
||||
#endif
|
||||
|
||||
using FlaxEngine;
|
||||
using FlaxEditor.CustomEditors.Dedicated;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.Scripting;
|
||||
|
||||
namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
/// <summary>
|
||||
/// Dedicated custom editor for BoxCollider objects.
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(BoxCollider)), DefaultEditor]
|
||||
public class BoxColliderEditor : ActorEditor
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Initialize(LayoutElementsContainer layout)
|
||||
{
|
||||
base.Initialize(layout);
|
||||
|
||||
layout.Space(20f);
|
||||
layout.Button("Resize to Fit", Editor.Instance.CodeDocs.GetTooltip(new ScriptMemberInfo(typeof(BoxCollider).GetMethod("AutoResize")))).Button.Clicked += OnResizeClicked;
|
||||
}
|
||||
|
||||
private void OnResizeClicked()
|
||||
{
|
||||
foreach (var value in Values)
|
||||
{
|
||||
if (value is BoxCollider collider)
|
||||
collider.AutoResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Scene tree node for <see cref="BoxCollider"/> actor type.
|
||||
/// </summary>
|
||||
@@ -37,5 +65,18 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
|
||||
return base.RayCastSelf(ref ray, out distance, out normal);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void PostSpawn()
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
if (Actor.HasPrefabLink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
((BoxCollider)Actor).AutoResize();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,6 +334,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
if (Actor.HasPrefabLink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup for an initial spline
|
||||
var spline = (Spline)Actor;
|
||||
spline.AddSplineLocalPoint(Vector3.Zero, false);
|
||||
|
||||
@@ -61,10 +61,15 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
if (Actor.HasPrefabLink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup for default values
|
||||
var text = (SpriteRender)Actor;
|
||||
text.Material = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.DefaultSpriteMaterial);
|
||||
text.Image = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.FlaxIconTexture);
|
||||
var sprite = (SpriteRender)Actor;
|
||||
sprite.Material = FlaxEngine.Content.LoadAsyncInternal<MaterialBase>(EditorAssets.DefaultSpriteMaterial);
|
||||
sprite.Image = FlaxEngine.Content.LoadAsyncInternal<Texture>(EditorAssets.FlaxIconTexture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
if (Actor.HasPrefabLink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup for default values
|
||||
var text = (TextRender)Actor;
|
||||
text.Text = "My Text";
|
||||
|
||||
@@ -29,15 +29,33 @@ namespace FlaxEditor.SceneGraph.Actors
|
||||
{
|
||||
base.PostSpawn();
|
||||
|
||||
if (Actor.HasPrefabLink)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Rotate to match the space (GUI uses upper left corner as a root)
|
||||
Actor.LocalOrientation = Quaternion.Euler(0, -180, -180);
|
||||
var uiControl = new UIControl
|
||||
bool canSpawn = true;
|
||||
foreach (var uiControl in Actor.GetChildren<UIControl>())
|
||||
{
|
||||
Name = "Canvas Scalar",
|
||||
Transform = Actor.Transform,
|
||||
Control = new CanvasScaler()
|
||||
};
|
||||
Root.Spawn(uiControl, Actor);
|
||||
if (uiControl.Get<CanvasScaler>() == null)
|
||||
continue;
|
||||
canSpawn = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (canSpawn)
|
||||
{
|
||||
var uiControl = new UIControl
|
||||
{
|
||||
Name = "Canvas Scalar",
|
||||
Transform = Actor.Transform,
|
||||
Control = new CanvasScaler()
|
||||
};
|
||||
Root.Spawn(uiControl, Actor);
|
||||
}
|
||||
_treeNode.Expand();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -85,12 +85,20 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
{
|
||||
if (Parent is ActorTreeNode parent)
|
||||
{
|
||||
for (int i = 0; i < parent.ChildrenCount; i++)
|
||||
var anyChanged = false;
|
||||
var children = parent.Children;
|
||||
for (int i = 0; i < children.Count; i++)
|
||||
{
|
||||
if (parent.Children[i] is ActorTreeNode child && child.Actor)
|
||||
child._orderInParent = child.Actor.OrderInParent;
|
||||
if (children[i] is ActorTreeNode child && child.Actor)
|
||||
{
|
||||
var orderInParent = child.Actor.OrderInParent;
|
||||
anyChanged |= child._orderInParent != orderInParent;
|
||||
if (anyChanged)
|
||||
child._orderInParent = orderInParent;
|
||||
}
|
||||
}
|
||||
parent.SortChildren();
|
||||
if (anyChanged)
|
||||
parent.SortChildren();
|
||||
}
|
||||
else if (Actor)
|
||||
{
|
||||
@@ -690,6 +698,10 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
if (thisHasScene != otherHasScene)
|
||||
return false;
|
||||
|
||||
// Reject dragging actors between prefab windows (different roots)
|
||||
if (!thisHasScene && ActorNode.Root != actorNode.Root)
|
||||
return false;
|
||||
|
||||
// Reject dragging parents and itself
|
||||
return actorNode.Actor != null && actorNode != ActorNode && actorNode.Find(Actor) == null;
|
||||
}
|
||||
|
||||
@@ -1335,6 +1335,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Surface?.AddBatchedUndoAction(action);
|
||||
action.Do();
|
||||
Surface?.OnNodesConnected(this, other);
|
||||
Surface?.MarkAsEdited();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1911,6 +1912,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
var action = new StateMachineStateBase.AddRemoveTransitionAction(this);
|
||||
SourceState.Surface?.AddBatchedUndoAction(action);
|
||||
SourceState.Surface?.MarkAsEdited();
|
||||
action.Do();
|
||||
}
|
||||
|
||||
|
||||
@@ -493,7 +493,15 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
TypeID = 10,
|
||||
Title = "Blend Additive",
|
||||
Description = "Blend animation poses (with additive mode)",
|
||||
Description =
|
||||
"Blend animation poses (with additive mode)" +
|
||||
"\n" +
|
||||
"\nNote: " +
|
||||
"\nOrder of nodes matters, because Additive animation is appplayed on top of curent frame." +
|
||||
"\n" +
|
||||
"\nTip for blender users:" +
|
||||
"\nInside NLA the the order is bottom (first node in flax) to the top (last node in flax)" +
|
||||
"\nu need to place it in this order to get correct resoults",
|
||||
Flags = NodeFlags.AnimGraph,
|
||||
Size = new Float2(170, 80),
|
||||
DefaultValues = new object[]
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -533,6 +533,7 @@ namespace FlaxEditor.Surface
|
||||
UpdateSelectionRectangle();
|
||||
}
|
||||
}
|
||||
bool showPrimaryMenu = false;
|
||||
if (_rightMouseDown && button == MouseButton.Right)
|
||||
{
|
||||
_rightMouseDown = false;
|
||||
@@ -546,8 +547,7 @@ namespace FlaxEditor.Surface
|
||||
_cmStartPos = location;
|
||||
if (controlUnderMouse == null)
|
||||
{
|
||||
// Show primary context menu
|
||||
ShowPrimaryMenu(_cmStartPos);
|
||||
showPrimaryMenu = true;
|
||||
}
|
||||
}
|
||||
_mouseMoveAmount = 0;
|
||||
@@ -573,8 +573,13 @@ namespace FlaxEditor.Surface
|
||||
return true;
|
||||
}
|
||||
|
||||
// If none of the child controls handled this show the primary context menu
|
||||
if (showPrimaryMenu)
|
||||
{
|
||||
ShowPrimaryMenu(_cmStartPos);
|
||||
}
|
||||
// Letting go of a connection or right clicking while creating a connection
|
||||
if (!_isMovingSelection && _connectionInstigator != null && !IsPrimaryMenuOpened)
|
||||
else if (!_isMovingSelection && _connectionInstigator != null && !IsPrimaryMenuOpened)
|
||||
{
|
||||
_cmStartPos = location;
|
||||
Cursor = CursorType.Default;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1374,8 +1374,8 @@ namespace FlaxEditor.Viewport
|
||||
ivp.Invert();
|
||||
|
||||
// Create near and far points
|
||||
var nearPoint = new Vector3(mousePosition, 0.0f);
|
||||
var farPoint = new Vector3(mousePosition, 1.0f);
|
||||
var nearPoint = new Vector3(mousePosition, _nearPlane);
|
||||
var farPoint = new Vector3(mousePosition, _farPlane);
|
||||
viewport.Unproject(ref nearPoint, ref ivp, out nearPoint);
|
||||
viewport.Unproject(ref farPoint, ref ivp, out farPoint);
|
||||
|
||||
|
||||
@@ -368,7 +368,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
actor.Layer = parentActor.Layer;
|
||||
|
||||
// Rename actor to identify it easily
|
||||
actor.Name = Utilities.Utils.IncrementNameNumber(actor.GetType().Name, x => parentActor.GetChild(x) == null);
|
||||
actor.Name = Utilities.Utils.IncrementNameNumber(actor.Name, x => parentActor.GetChild(x) == null);
|
||||
}
|
||||
|
||||
// Spawn it
|
||||
@@ -428,11 +428,9 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
private void Update(ActorNode actorNode)
|
||||
{
|
||||
if (actorNode.Actor)
|
||||
{
|
||||
actorNode.TreeNode.UpdateText();
|
||||
actorNode.TreeNode.OnOrderInParentChanged();
|
||||
}
|
||||
actorNode.TreeNode.UpdateText();
|
||||
if (actorNode.TreeNode.IsCollapsed)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < actorNode.ChildNodes.Count; i++)
|
||||
{
|
||||
|
||||
@@ -440,6 +440,7 @@ namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
try
|
||||
{
|
||||
FlaxEngine.Profiler.BeginEvent("PrefabWindow.Update");
|
||||
if (Graph.Main != null)
|
||||
{
|
||||
// Due to fact that actors in prefab editor are only created but not added to gameplay
|
||||
@@ -468,6 +469,10 @@ namespace FlaxEditor.Windows.Assets
|
||||
Graph.Root.TreeNode.ExpandAll(true);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
FlaxEngine.Profiler.EndEvent();
|
||||
}
|
||||
|
||||
// Auto fit
|
||||
if (_focusCamera && _viewport.Task.FrameCount > 1)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -381,6 +381,7 @@ namespace FlaxEditor.Windows
|
||||
Arguments = $"clone {gitPath} \"{clonePath}\"",
|
||||
ShellExecute = false,
|
||||
LogOutput = true,
|
||||
WaitForEnd = true
|
||||
};
|
||||
Platform.CreateProcess(ref settings);
|
||||
}
|
||||
@@ -402,6 +403,7 @@ namespace FlaxEditor.Windows
|
||||
Arguments = "submodule update --init",
|
||||
ShellExecute = false,
|
||||
LogOutput = true,
|
||||
WaitForEnd = true
|
||||
};
|
||||
Platform.CreateProcess(ref settings);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user