Merge remote-tracking branch 'origin/master' into 1.9
# Conflicts: # Source/Editor/Modules/ContentDatabaseModule.cs # Source/Editor/Surface/SurfaceUtils.cs # Source/Editor/Windows/Assets/MaterialInstanceWindow.cs # Source/Engine/Foliage/Foliage.cpp # Source/Engine/Graphics/Models/MeshBase.h # Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp
This commit is contained in:
5
Content/Editor/Scripting/CSharpEmptyTemplate.cs
Normal file
5
Content/Editor/Scripting/CSharpEmptyTemplate.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
%copyright%using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using FlaxEngine;
|
||||||
|
|
||||||
|
namespace %namespace%;
|
||||||
@@ -13,10 +13,10 @@ API_CLASS() class %module%%class% : public ISerializable
|
|||||||
API_AUTO_SERIALIZATION();
|
API_AUTO_SERIALIZATION();
|
||||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(%class%);
|
DECLARE_SCRIPTING_TYPE_NO_SPAWN(%class%);
|
||||||
public:
|
public:
|
||||||
// Custom float value.
|
// Custom float value.
|
||||||
API_FIELD(Attributes = "Range(0, 20), EditorOrder(0), EditorDisplay(\"Data\")")
|
API_FIELD(Attributes = "Range(0, 20), EditorOrder(0), EditorDisplay(\"Data\")")
|
||||||
float FloatValue = 20.0f;
|
float FloatValue = 20.0f;
|
||||||
// Custom vector data.
|
// Custom vector data.
|
||||||
API_FIELD(Attributes = "EditorOrder(1), EditorDisplay(\"Data\")")
|
API_FIELD(Attributes = "EditorOrder(1), EditorDisplay(\"Data\")")
|
||||||
Vector3 Vector3Value = Vector3(0.1f);
|
Vector3 Vector3Value = Vector3(0.1f);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,6 +7,6 @@ META_CB_END
|
|||||||
META_PS(true, FEATURE_LEVEL_ES2)
|
META_PS(true, FEATURE_LEVEL_ES2)
|
||||||
float4 PS_Fullscreen(Quad_VS2PS input) : SV_Target
|
float4 PS_Fullscreen(Quad_VS2PS input) : SV_Target
|
||||||
{
|
{
|
||||||
// Solid color fill from the constant buffer passed from code
|
// Solid color fill from the constant buffer passed from code
|
||||||
return Color;
|
return Color;
|
||||||
}
|
}
|
||||||
|
|||||||
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
BIN
Content/Shaders/ColorGrading.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Editor/LightmapUVsDensity.flax
(Stored with Git LFS)
BIN
Content/Shaders/Editor/LightmapUVsDensity.flax
(Stored with Git LFS)
Binary file not shown.
173
Source/Editor/Content/Create/PrefabCreateEntry.cs
Normal file
173
Source/Editor/Content/Create/PrefabCreateEntry.cs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
using Object = FlaxEngine.Object;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Content.Create
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Prefab asset creating handler. Allows to specify base actor to use as the root.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.Content.Create.CreateFileEntry" />
|
||||||
|
public class PrefabCreateEntry : CreateFileEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The create options.
|
||||||
|
/// </summary>
|
||||||
|
public class Options
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The root actor.
|
||||||
|
/// </summary>
|
||||||
|
[TypeReference(typeof(FlaxEngine.Actor), nameof(IsValid))]
|
||||||
|
[Tooltip("The actor type of the root of the new Prefab.")]
|
||||||
|
public Type RootActorType = typeof(EmptyActor);
|
||||||
|
|
||||||
|
private static bool IsValid(Type type)
|
||||||
|
{
|
||||||
|
return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Options _options = new Options();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override object Settings => _options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="PrefabCreateEntry"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resultUrl">The result file url.</param>
|
||||||
|
public PrefabCreateEntry(string resultUrl)
|
||||||
|
: base("Settings", resultUrl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Create()
|
||||||
|
{
|
||||||
|
var actorType = new ScriptType(_options.RootActorType ?? typeof(EmptyActor));
|
||||||
|
Actor actor;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
actor = actorType.CreateInstance() as Actor;
|
||||||
|
Object.Destroy(actor, 20.0f);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to create prefab with root actor type: " + actorType.Name);
|
||||||
|
Editor.LogWarning(ex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return PrefabManager.CreatePrefab(actor, ResultUrl, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Widget asset creating handler. Allows to specify base UIControl to use as the root.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.Content.Create.CreateFileEntry" />
|
||||||
|
public class WidgetCreateEntry : CreateFileEntry
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The create options.
|
||||||
|
/// </summary>
|
||||||
|
public class Options
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Which mode is used to initialize this widget.
|
||||||
|
/// </summary>
|
||||||
|
public enum WidgetMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the widget with a UICanvas.
|
||||||
|
/// </summary>
|
||||||
|
Canvas,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize the widget with a UIControl.
|
||||||
|
/// </summary>
|
||||||
|
Control
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The mode used to initialize the widget.
|
||||||
|
/// </summary>
|
||||||
|
[Tooltip("Whether to initialize the widget with a canvas or a control.")]
|
||||||
|
public WidgetMode WidgetInitializationMode = WidgetMode.Control;
|
||||||
|
|
||||||
|
bool ShowRoot => WidgetInitializationMode == WidgetMode.Control;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The root control.
|
||||||
|
/// </summary>
|
||||||
|
[TypeReference(typeof(Control), nameof(IsValid))]
|
||||||
|
[Tooltip("The control type of the root of the new Widget's root control."), VisibleIf(nameof(ShowRoot))]
|
||||||
|
public Type RootControlType = typeof(Button);
|
||||||
|
|
||||||
|
private static bool IsValid(Type type)
|
||||||
|
{
|
||||||
|
return (type.IsPublic || type.IsNestedPublic) && !type.IsAbstract && !type.IsGenericType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly Options _options = new Options();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override object Settings => _options;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WidgetCreateEntry"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resultUrl">The result file url.</param>
|
||||||
|
public WidgetCreateEntry(string resultUrl)
|
||||||
|
: base("Settings", resultUrl)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool Create()
|
||||||
|
{
|
||||||
|
Actor actor = null;
|
||||||
|
|
||||||
|
if (_options.WidgetInitializationMode == Options.WidgetMode.Control)
|
||||||
|
{
|
||||||
|
var controlType = new ScriptType(_options.RootControlType ?? typeof(Control));
|
||||||
|
Control control;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
control = controlType.CreateInstance() as Control;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to create widget with root control type: " + controlType.Name);
|
||||||
|
Editor.LogWarning(ex);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
actor = new UIControl
|
||||||
|
{
|
||||||
|
Control = control,
|
||||||
|
Name = controlType.Name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else if (_options.WidgetInitializationMode == Options.WidgetMode.Canvas)
|
||||||
|
{
|
||||||
|
actor = new UICanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actor == null)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to create widget. Final actor was null.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Object.Destroy(actor, 20.0f);
|
||||||
|
|
||||||
|
return PrefabManager.CreatePrefab(actor, ResultUrl, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ using FlaxEngine;
|
|||||||
namespace FlaxEditor.Content
|
namespace FlaxEditor.Content
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Content item that contains C# script file with source code.
|
/// Content item that contains C# file with source code.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.ScriptItem" />
|
/// <seealso cref="FlaxEditor.Content.ScriptItem" />
|
||||||
public class CSharpScriptItem : ScriptItem
|
public class CSharpScriptItem : ScriptItem
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace FlaxEditor.Content
|
|||||||
/// <seealso cref="FlaxEditor.Content.JsonAssetItem" />
|
/// <seealso cref="FlaxEditor.Content.JsonAssetItem" />
|
||||||
public sealed class PrefabItem : JsonAssetItem
|
public sealed class PrefabItem : JsonAssetItem
|
||||||
{
|
{
|
||||||
|
private string _cachedTypeDescription = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="PrefabItem"/> class.
|
/// Initializes a new instance of the <see cref="PrefabItem"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -42,6 +44,26 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override SpriteHandle DefaultThumbnail => SpriteHandle.Invalid;
|
public override SpriteHandle DefaultThumbnail => SpriteHandle.Invalid;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string TypeDescription
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_cachedTypeDescription == null)
|
||||||
|
{
|
||||||
|
_cachedTypeDescription = "Prefab";
|
||||||
|
var prefab = FlaxEngine.Content.Load<Prefab>(ID);
|
||||||
|
if (prefab)
|
||||||
|
{
|
||||||
|
Actor root = prefab.GetDefaultInstance();
|
||||||
|
if (root is UIControl or UICanvas)
|
||||||
|
_cachedTypeDescription = "Widget";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _cachedTypeDescription;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool IsOfType(Type type)
|
public override bool IsOfType(Type type)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,19 +9,15 @@ using FlaxEngine;
|
|||||||
namespace FlaxEditor.Content
|
namespace FlaxEditor.Content
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Context proxy object for C# script files.
|
/// Proxy object for C# files
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
/// /// <seealso cref="FlaxEditor.Content.ScriptProxy" />
|
||||||
[ContentContextMenu("New/C# Script")]
|
public abstract class CSharpProxy : ScriptProxy
|
||||||
public class CSharpScriptProxy : ScriptProxy
|
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The script files extension filter.
|
/// The script files extension filter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly string ExtensionFiler = "*.cs";
|
public static readonly string ExtensionFilter = "*.cs";
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override string Name => "C# Script";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool IsProxyFor(ContentItem item)
|
public override bool IsProxyFor(ContentItem item)
|
||||||
@@ -29,6 +25,12 @@ namespace FlaxEditor.Content
|
|||||||
return item is CSharpScriptItem;
|
return item is CSharpScriptItem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path for the C# template.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path to the template</param>
|
||||||
|
protected abstract void GetTemplatePath(out string path);
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ContentItem ConstructItem(string path)
|
public override ContentItem ConstructItem(string path)
|
||||||
{
|
{
|
||||||
@@ -39,7 +41,7 @@ namespace FlaxEditor.Content
|
|||||||
public override void Create(string outputPath, object arg)
|
public override void Create(string outputPath, object arg)
|
||||||
{
|
{
|
||||||
// Load template
|
// Load template
|
||||||
var templatePath = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cs");
|
GetTemplatePath(out var templatePath);
|
||||||
var scriptTemplate = File.ReadAllText(templatePath);
|
var scriptTemplate = File.ReadAllText(templatePath);
|
||||||
|
|
||||||
// Find the module that this script is being added (based on the path)
|
// Find the module that this script is being added (based on the path)
|
||||||
@@ -69,4 +71,38 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Color AccentColor => Color.FromRGB(0x1c9c2b);
|
public override Color AccentColor => Color.FromRGB(0x1c9c2b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Context proxy object for C# script files.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||||
|
[ContentContextMenu("New/C#/C# Script")]
|
||||||
|
public class CSharpScriptProxy : CSharpProxy
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Name => "C# Script";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void GetTemplatePath(out string path)
|
||||||
|
{
|
||||||
|
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/ScriptTemplate.cs");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Context proxy object for empty C# files.
|
||||||
|
/// </summary>
|
||||||
|
/// <seealso cref="FlaxEditor.Content.CSharpProxy" />
|
||||||
|
[ContentContextMenu("New/C#/C# Empty File")]
|
||||||
|
public class CSharpEmptyProxy : CSharpProxy
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string Name => "C# Empty File";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void GetTemplatePath(out string path)
|
||||||
|
{
|
||||||
|
path = StringUtils.CombinePaths(Globals.EngineContentFolder, "Editor/Scripting/CSharpEmptyTemplate.cs");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Context proxy object for C++ files.
|
/// Context proxy object for C++ files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
/// <seealso cref="FlaxEditor.Content.ScriptProxy" />
|
||||||
public abstract class CppProxy : ScriptProxy
|
public abstract class CppProxy : ScriptProxy
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -74,7 +74,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Context proxy object for C++ script files.
|
/// Context proxy object for C++ script files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
/// <seealso cref="FlaxEditor.Content.CppProxy" />
|
||||||
[ContentContextMenu("New/C++/C++ Script")]
|
[ContentContextMenu("New/C++/C++ Script")]
|
||||||
public class CppScriptProxy : CppProxy
|
public class CppScriptProxy : CppProxy
|
||||||
{
|
{
|
||||||
@@ -104,7 +104,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Context proxy object for C++ Json Asset files.
|
/// Context proxy object for C++ Json Asset files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
/// <seealso cref="FlaxEditor.Content.CppProxy" />
|
||||||
[ContentContextMenu("New/C++/C++ Function Library")]
|
[ContentContextMenu("New/C++/C++ Function Library")]
|
||||||
public class CppStaticClassProxy : CppProxy
|
public class CppStaticClassProxy : CppProxy
|
||||||
{
|
{
|
||||||
@@ -122,7 +122,7 @@ namespace FlaxEditor.Content
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Context proxy object for C++ Json Asset files.
|
/// Context proxy object for C++ Json Asset files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <seealso cref="FlaxEditor.Content.CSharpScriptProxy" />
|
/// <seealso cref="FlaxEditor.Content.CppProxy" />
|
||||||
[ContentContextMenu("New/C++/C++ Json Asset")]
|
[ContentContextMenu("New/C++/C++ Json Asset")]
|
||||||
public class CppAssetProxy : CppProxy
|
public class CppAssetProxy : CppProxy
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using FlaxEditor.Content.Create;
|
using FlaxEditor.Content.Create;
|
||||||
using FlaxEditor.Content.Thumbnails;
|
using FlaxEditor.Content.Thumbnails;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
|
using FlaxEditor.GUI.Timeline;
|
||||||
|
using FlaxEditor.GUI.Timeline.Tracks;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEditor.Windows.Assets;
|
using FlaxEditor.Windows.Assets;
|
||||||
@@ -48,6 +51,63 @@ namespace FlaxEditor.Content
|
|||||||
Editor.Instance.ContentImporting.Create(new ParticleEmitterCreateEntry(outputPath));
|
Editor.Instance.ContentImporting.Create(new ParticleEmitterCreateEntry(outputPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||||
|
{
|
||||||
|
base.OnContentWindowContextMenu(menu, item);
|
||||||
|
|
||||||
|
if (item is BinaryAssetItem binaryAssetItem)
|
||||||
|
{
|
||||||
|
var button = menu.AddButton("Create Particle System", CreateParticleSystemClicked);
|
||||||
|
button.Tag = binaryAssetItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateParticleSystemClicked(ContextMenuButton obj)
|
||||||
|
{
|
||||||
|
var binaryAssetItem = (BinaryAssetItem)obj.Tag;
|
||||||
|
CreateParticleSystem(binaryAssetItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the particle system from the given particle emitter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="emitterItem">The particle emitter item to use as a base for the particle system.</param>
|
||||||
|
public static void CreateParticleSystem(BinaryAssetItem emitterItem)
|
||||||
|
{
|
||||||
|
var particleSystemName = emitterItem.ShortName + " Particle System";
|
||||||
|
var particleSystemProxy = Editor.Instance.ContentDatabase.GetProxy<ParticleSystem>();
|
||||||
|
Editor.Instance.Windows.ContentWin.NewItem(particleSystemProxy, null, item => OnParticleSystemCreated(item, emitterItem), particleSystemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnParticleSystemCreated(ContentItem item, BinaryAssetItem particleItem)
|
||||||
|
{
|
||||||
|
var assetItem = (AssetItem)item;
|
||||||
|
var particleSystem = FlaxEngine.Content.LoadAsync<ParticleSystem>(assetItem.ID);
|
||||||
|
if (particleSystem == null || particleSystem.WaitForLoaded())
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to load created particle system.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleEmitter emitter = FlaxEngine.Content.LoadAsync<ParticleEmitter>(particleItem.ID);
|
||||||
|
if (emitter == null || emitter.WaitForLoaded())
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to load base particle emitter.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParticleSystemPreview tempPreview = new ParticleSystemPreview(false);
|
||||||
|
ParticleSystemTimeline timeline = new ParticleSystemTimeline(tempPreview);
|
||||||
|
timeline.Load(particleSystem);
|
||||||
|
|
||||||
|
var track = (ParticleEmitterTrack)timeline.NewTrack(ParticleEmitterTrack.GetArchetype());
|
||||||
|
track.Asset = emitter;
|
||||||
|
track.TrackMedia.DurationFrames = timeline.DurationFrames;
|
||||||
|
track.Rename(particleItem.ShortName);
|
||||||
|
timeline.AddTrack(track);
|
||||||
|
timeline.Save(particleSystem);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using FlaxEditor.Content.Create;
|
||||||
using FlaxEditor.Content.Thumbnails;
|
using FlaxEditor.Content.Thumbnails;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEditor.Windows.Assets;
|
using FlaxEditor.Windows.Assets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using Object = FlaxEngine.Object;
|
|
||||||
|
|
||||||
namespace FlaxEditor.Content
|
namespace FlaxEditor.Content
|
||||||
{
|
{
|
||||||
@@ -90,14 +89,8 @@ namespace FlaxEditor.Content
|
|||||||
var transform = Transform.Identity;
|
var transform = Transform.Identity;
|
||||||
if (!(arg is Actor actor))
|
if (!(arg is Actor actor))
|
||||||
{
|
{
|
||||||
// Create default prefab root object
|
Editor.Instance.ContentImporting.Create(new PrefabCreateEntry(outputPath));
|
||||||
actor = new EmptyActor
|
return;
|
||||||
{
|
|
||||||
Name = "Root"
|
|
||||||
};
|
|
||||||
|
|
||||||
// Cleanup it after usage
|
|
||||||
Object.Destroy(actor, 20.0f);
|
|
||||||
}
|
}
|
||||||
else if (actor.HasScene)
|
else if (actor.HasScene)
|
||||||
{
|
{
|
||||||
@@ -251,18 +244,8 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Create(string outputPath, object arg)
|
public override void Create(string outputPath, object arg)
|
||||||
{
|
{
|
||||||
// Create prefab with UI Control
|
Editor.Instance.ContentImporting.Create(new WidgetCreateEntry(outputPath));
|
||||||
var actor = new UIControl
|
return;
|
||||||
{
|
|
||||||
Name = Path.GetFileNameWithoutExtension(outputPath),
|
|
||||||
StaticFlags = StaticFlags.None,
|
|
||||||
};
|
|
||||||
actor.Control = new Button
|
|
||||||
{
|
|
||||||
Text = "Button",
|
|
||||||
};
|
|
||||||
PrefabManager.CreatePrefab(actor, outputPath, false);
|
|
||||||
Object.Destroy(actor, 20.0f);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using FlaxEditor.Content.Thumbnails;
|
using FlaxEditor.Content.Thumbnails;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
using FlaxEditor.Windows;
|
using FlaxEditor.Windows;
|
||||||
using FlaxEditor.Windows.Assets;
|
using FlaxEditor.Windows.Assets;
|
||||||
@@ -39,6 +40,59 @@ namespace FlaxEditor.Content
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override Type AssetType => typeof(SkinnedModel);
|
public override Type AssetType => typeof(SkinnedModel);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnContentWindowContextMenu(ContextMenu menu, ContentItem item)
|
||||||
|
{
|
||||||
|
base.OnContentWindowContextMenu(menu, item);
|
||||||
|
|
||||||
|
if (item is BinaryAssetItem binaryAssetItem)
|
||||||
|
{
|
||||||
|
var button = menu.AddButton("Create Animation Graph", CreateAnimationGraphClicked);
|
||||||
|
button.Tag = binaryAssetItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateAnimationGraphClicked(ContextMenuButton obj)
|
||||||
|
{
|
||||||
|
var binaryAssetItem = (BinaryAssetItem)obj.Tag;
|
||||||
|
CreateAnimationGraph(binaryAssetItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the animation graph from the given particle emitter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="skinnedModelItem">The skinned model item to use as the base model for the animation graph.</param>
|
||||||
|
public static void CreateAnimationGraph(BinaryAssetItem skinnedModelItem)
|
||||||
|
{
|
||||||
|
var animationGraphName = skinnedModelItem.ShortName + " Graph";
|
||||||
|
var animationGraphProxy = Editor.Instance.ContentDatabase.GetProxy<AnimationGraph>();
|
||||||
|
Editor.Instance.Windows.ContentWin.NewItem(animationGraphProxy, null, item => OnAnimationGraphCreated(item, skinnedModelItem), animationGraphName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnAnimationGraphCreated(ContentItem item, BinaryAssetItem skinnedModelItem)
|
||||||
|
{
|
||||||
|
var skinnedModel = FlaxEngine.Content.Load<SkinnedModel>(skinnedModelItem.ID);
|
||||||
|
if (skinnedModel == null)
|
||||||
|
{
|
||||||
|
Editor.LogError("Failed to load base skinned model.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hack the animation graph window to modify the base model of the animation graph.
|
||||||
|
// TODO: implement it without window logic (load AnimGraphSurface and set AnimationGraphWindow.BaseModelId to model)
|
||||||
|
AnimationGraphWindow win = new AnimationGraphWindow(Editor.Instance, item as AssetItem);
|
||||||
|
win.Show();
|
||||||
|
|
||||||
|
// Ensure the window knows the asset is loaded so we can save it later.
|
||||||
|
win.Asset.WaitForLoaded();
|
||||||
|
win.Update(0); // Call Update() to refresh the loaded flag.
|
||||||
|
|
||||||
|
win.SetBaseModel(skinnedModel);
|
||||||
|
win.Surface.MarkAsEdited();
|
||||||
|
win.Save();
|
||||||
|
win.Close();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
public override void OnThumbnailDrawPrepare(ThumbnailRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -289,6 +289,17 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove any leftover files copied from .NET SDK that are not needed by the engine runtime
|
||||||
|
{
|
||||||
|
Array<String> files;
|
||||||
|
FileSystem::DirectoryGetFiles(files, dstDotnet, TEXT("*.exe"));
|
||||||
|
for (const String& file : files)
|
||||||
|
{
|
||||||
|
LOG(Info, "Removing '{}'", FileSystem::ConvertAbsolutePathToRelative(dstDotnet, file));
|
||||||
|
FileSystem::DeleteFile(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Optimize deployed C# class library (remove DLLs unused by scripts)
|
// Optimize deployed C# class library (remove DLLs unused by scripts)
|
||||||
if (aotMode == DotNetAOTModes::None && buildSettings.SkipUnusedDotnetLibsPackaging)
|
if (aotMode == DotNetAOTModes::None && buildSettings.SkipUnusedDotnetLibsPackaging)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -261,7 +261,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
if (!file.Contains("GameProjectTarget"))
|
if (!file.Contains("GameProjectTarget"))
|
||||||
continue; // Skip
|
continue; // Skip
|
||||||
|
|
||||||
if (file.Contains("Modules.Add(\"Game\")"))
|
if (file.Contains("Modules.Add(\"Game\")") || file.Contains("Modules.Add(nameof(Game))"))
|
||||||
{
|
{
|
||||||
// Assume Game represents the main game module
|
// Assume Game represents the main game module
|
||||||
moduleName = "Game";
|
moduleName = "Game";
|
||||||
|
|||||||
@@ -123,10 +123,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
_linkButton.Clicked += ToggleLink;
|
_linkButton.Clicked += ToggleLink;
|
||||||
ToggleEnabled();
|
ToggleEnabled();
|
||||||
SetLinkStyle();
|
SetLinkStyle();
|
||||||
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value);
|
|
||||||
_linkButton.LocalX += textSize.X + 10;
|
|
||||||
if (LinkedLabel != null)
|
if (LinkedLabel != null)
|
||||||
{
|
{
|
||||||
|
var textSize = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText(LinkedLabel.Text.Value);
|
||||||
|
_linkButton.LocalX += textSize.X + 10;
|
||||||
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
LinkedLabel.SetupContextMenu += (label, menu, editor) =>
|
||||||
{
|
{
|
||||||
menu.AddSeparator();
|
menu.AddSeparator();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
@@ -39,20 +40,27 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
public override void Initialize(LayoutElementsContainer layout)
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
{
|
{
|
||||||
Window = layout.Control.RootWindow.Window;
|
Window = layout.Control.RootWindow.Window;
|
||||||
|
var panelElement = layout.CustomContainer<Panel>();
|
||||||
|
var panel = panelElement.ContainerControl as Panel;
|
||||||
|
|
||||||
var panel = layout.CustomContainer<UniformGridPanel>();
|
var button = panelElement.Button("Listen", "Press to listen for input events");
|
||||||
panel.CustomControl.SlotsHorizontally = 2;
|
|
||||||
panel.CustomControl.SlotsVertically = 1;
|
|
||||||
|
|
||||||
var button = panel.Button("Listen", "Press to listen for input events");
|
|
||||||
_button = button.Button;
|
_button = button.Button;
|
||||||
|
_button.Width = FlaxEngine.GUI.Style.Current.FontMedium.MeasureText("Listening...").X + 8;
|
||||||
|
_button.Height = ComboBox.DefaultHeight;
|
||||||
_button.Clicked += OnButtonClicked;
|
_button.Clicked += OnButtonClicked;
|
||||||
ResetButton();
|
ResetButton();
|
||||||
|
|
||||||
var padding = panel.CustomControl.SlotPadding;
|
panel.Height = ComboBox.DefaultHeight;
|
||||||
panel.CustomControl.Height = ComboBox.DefaultHeight + padding.Height;
|
|
||||||
|
|
||||||
base.Initialize(panel);
|
base.Initialize(panelElement);
|
||||||
|
|
||||||
|
if (panelElement.Children.Find(x => x is EnumElement) is EnumElement comboBoxElement)
|
||||||
|
{
|
||||||
|
var comboBox = comboBoxElement.ComboBox;
|
||||||
|
comboBox.AnchorPreset = AnchorPresets.StretchAll;
|
||||||
|
comboBox.Offsets = new Margin(0, _button.Width + 2, 0, 0);
|
||||||
|
comboBox.LocalX += _button.Width + 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -2,14 +2,18 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.CustomEditors.GUI;
|
using FlaxEditor.CustomEditors.GUI;
|
||||||
using FlaxEditor.GUI.Input;
|
using FlaxEditor.GUI.Input;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Drag;
|
using FlaxEditor.GUI.Drag;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
|
using FlaxEditor.Windows;
|
||||||
|
using FlaxEditor.Windows.Assets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
@@ -53,7 +57,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
Index = index;
|
Index = index;
|
||||||
|
|
||||||
SetupContextMenu += OnSetupContextMenu;
|
SetupContextMenu += OnSetupContextMenu;
|
||||||
_arrangeButtonRect = new Rectangle(2, 3, 12, 12);
|
_arrangeButtonRect = new Rectangle(2, 4, 12, 12);
|
||||||
|
|
||||||
// Extend margin of the label to support a dragging handle.
|
// Extend margin of the label to support a dragging handle.
|
||||||
Margin m = Margin;
|
Margin m = Margin;
|
||||||
@@ -75,7 +79,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
b = menu.AddButton("Move down", OnMoveDownClicked);
|
b = menu.AddButton("Move down", OnMoveDownClicked);
|
||||||
b.Enabled = Index + 1 < Editor.Count && !Editor._readOnly;
|
b.Enabled = Index + 1 < Editor.Count && !Editor._readOnly;
|
||||||
|
|
||||||
b = menu.AddButton("Remove", OnRemoveClicked);
|
b = menu.AddButton("Remove", OnRemoveClicked);
|
||||||
b.Enabled = !Editor._readOnly;
|
b.Enabled = !Editor._readOnly;
|
||||||
}
|
}
|
||||||
@@ -88,13 +92,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
_arrangeButtonInUse = false;
|
_arrangeButtonInUse = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
base.Draw();
|
base.Draw();
|
||||||
var style = FlaxEngine.GUI.Style.Current;
|
|
||||||
|
|
||||||
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
var mousePosition = PointFromScreen(Input.MouseScreenPosition);
|
var mousePosition = PointFromScreen(Input.MouseScreenPosition);
|
||||||
var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
|
var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
|
||||||
Render2D.DrawSprite(FlaxEditor.Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
|
Render2D.DrawSprite(FlaxEditor.Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
|
||||||
@@ -104,6 +107,14 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnSizeChanged()
|
||||||
|
{
|
||||||
|
base.OnSizeChanged();
|
||||||
|
|
||||||
|
_arrangeButtonRect.Y = (Height - _arrangeButtonRect.Height) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
private bool ArrangeAreaCheck(out int index, out Rectangle rect)
|
private bool ArrangeAreaCheck(out int index, out Rectangle rect)
|
||||||
{
|
{
|
||||||
var child = Editor.ChildrenEditors[0];
|
var child = Editor.ChildrenEditors[0];
|
||||||
@@ -228,7 +239,38 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
ArrowImageClosed = new SpriteBrush(icons.ArrowRight12);
|
ArrowImageClosed = new SpriteBrush(icons.ArrowRight12);
|
||||||
ArrowImageOpened = new SpriteBrush(icons.ArrowDown12);
|
ArrowImageOpened = new SpriteBrush(icons.ArrowDown12);
|
||||||
HeaderText = $"Element {index}";
|
HeaderText = $"Element {index}";
|
||||||
IsClosed = false;
|
|
||||||
|
string saveName = string.Empty;
|
||||||
|
if (editor.Presenter?.Owner is PropertiesWindow propertiesWindow)
|
||||||
|
{
|
||||||
|
var selection = FlaxEditor.Editor.Instance.SceneEditing.Selection[0];
|
||||||
|
if (selection != null)
|
||||||
|
{
|
||||||
|
saveName += $"{selection.ID},";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (editor.Presenter?.Owner is PrefabWindow prefabWindow)
|
||||||
|
{
|
||||||
|
var selection = prefabWindow.Selection[0];
|
||||||
|
if (selection != null)
|
||||||
|
{
|
||||||
|
saveName += $"{selection.ID},";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (editor.ParentEditor?.Layout.ContainerControl is DropPanel pdp)
|
||||||
|
{
|
||||||
|
saveName += $"{pdp.HeaderText},";
|
||||||
|
}
|
||||||
|
if (editor.Layout.ContainerControl is DropPanel mainGroup)
|
||||||
|
{
|
||||||
|
saveName += $"{mainGroup.HeaderText}";
|
||||||
|
IsClosed = FlaxEditor.Editor.Instance.ProjectCache.IsGroupToggled($"{saveName}:{index}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IsClosed = false;
|
||||||
|
}
|
||||||
|
|
||||||
Editor = editor;
|
Editor = editor;
|
||||||
Index = index;
|
Index = index;
|
||||||
Offsets = new Margin(7, 7, 0, 0);
|
Offsets = new Margin(7, 7, 0, 0);
|
||||||
@@ -239,6 +281,37 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
HeaderTextMargin = new Margin(18, 0, 0, 0);
|
HeaderTextMargin = new Margin(18, 0, 0, 0);
|
||||||
_arrangeButtonRect = new Rectangle(16, 3, 12, 12);
|
_arrangeButtonRect = new Rectangle(16, 3, 12, 12);
|
||||||
}
|
}
|
||||||
|
IsClosedChanged += OnIsClosedChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIsClosedChanged(DropPanel panel)
|
||||||
|
{
|
||||||
|
string saveName = string.Empty;
|
||||||
|
if (Editor.Presenter?.Owner is PropertiesWindow pw)
|
||||||
|
{
|
||||||
|
var selection = FlaxEditor.Editor.Instance.SceneEditing.Selection[0];
|
||||||
|
if (selection != null)
|
||||||
|
{
|
||||||
|
saveName += $"{selection.ID},";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Editor.Presenter?.Owner is PrefabWindow prefabWindow)
|
||||||
|
{
|
||||||
|
var selection = prefabWindow.Selection[0];
|
||||||
|
if (selection != null)
|
||||||
|
{
|
||||||
|
saveName += $"{selection.ID},";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Editor.ParentEditor?.Layout.ContainerControl is DropPanel pdp)
|
||||||
|
{
|
||||||
|
saveName += $"{pdp.HeaderText},";
|
||||||
|
}
|
||||||
|
if (Editor.Layout.ContainerControl is DropPanel mainGroup)
|
||||||
|
{
|
||||||
|
saveName += $"{mainGroup.HeaderText}";
|
||||||
|
FlaxEditor.Editor.Instance.ProjectCache.SetGroupToggle($"{saveName}:{Index}", panel.IsClosed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ArrangeAreaCheck(out int index, out Rectangle rect)
|
private bool ArrangeAreaCheck(out int index, out Rectangle rect)
|
||||||
@@ -278,10 +351,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
base.Draw();
|
base.Draw();
|
||||||
|
|
||||||
if (_canReorder)
|
if (_canReorder)
|
||||||
{
|
{
|
||||||
var style = FlaxEngine.GUI.Style.Current;
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
|
|
||||||
var mousePosition = PointFromScreen(Input.MouseScreenPosition);
|
var mousePosition = PointFromScreen(Input.MouseScreenPosition);
|
||||||
var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
|
var dragBarColor = _arrangeButtonRect.Contains(mousePosition) ? style.Foreground : style.ForegroundGrey;
|
||||||
Render2D.DrawSprite(FlaxEditor.Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
|
Render2D.DrawSprite(FlaxEditor.Editor.Instance.Icons.DragBar12, _arrangeButtonRect, _arrangeButtonInUse ? Color.Orange : dragBarColor);
|
||||||
@@ -377,6 +450,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
private bool _canResize;
|
private bool _canResize;
|
||||||
private bool _canReorderItems;
|
private bool _canReorderItems;
|
||||||
private CollectionAttribute.DisplayType _displayType;
|
private CollectionAttribute.DisplayType _displayType;
|
||||||
|
private List<CollectionDropPanel> _cachedDropPanels = new List<CollectionDropPanel>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the length of the collection.
|
/// Gets the length of the collection.
|
||||||
@@ -519,6 +593,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
(elementType.GetProperties().Length == 1 && elementType.GetFields().Length == 0) ||
|
(elementType.GetProperties().Length == 1 && elementType.GetFields().Length == 0) ||
|
||||||
elementType.Equals(new ScriptType(typeof(JsonAsset))) ||
|
elementType.Equals(new ScriptType(typeof(JsonAsset))) ||
|
||||||
elementType.Equals(new ScriptType(typeof(SettingsBase)));
|
elementType.Equals(new ScriptType(typeof(SettingsBase)));
|
||||||
|
if (_cachedDropPanels == null)
|
||||||
|
_cachedDropPanels = new List<CollectionDropPanel>();
|
||||||
|
else
|
||||||
|
_cachedDropPanels.Clear();
|
||||||
for (int i = 0; i < size; i++)
|
for (int i = 0; i < size; i++)
|
||||||
{
|
{
|
||||||
// Apply spacing
|
// Apply spacing
|
||||||
@@ -542,6 +620,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single))
|
else if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single))
|
||||||
{
|
{
|
||||||
var cdp = panel.CustomContainer<CollectionDropPanel>();
|
var cdp = panel.CustomContainer<CollectionDropPanel>();
|
||||||
|
_cachedDropPanels.Add(cdp.CustomControl);
|
||||||
cdp.CustomControl.Setup(this, i, _canReorderItems);
|
cdp.CustomControl.Setup(this, i, _canReorderItems);
|
||||||
var itemLayout = cdp.VerticalPanel();
|
var itemLayout = cdp.VerticalPanel();
|
||||||
cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
|
cdp.CustomControl.LinkedEditor = itemLayout.Object(new ListValueContainer(elementType, i, Values, attributes), overrideEditor);
|
||||||
@@ -549,6 +628,12 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
GenericEditor.OnReadOnlyProperty(itemLayout);
|
GenericEditor.OnReadOnlyProperty(itemLayout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_displayType == CollectionAttribute.DisplayType.Header || (_displayType == CollectionAttribute.DisplayType.Default && !single))
|
||||||
|
{
|
||||||
|
if (Layout is GroupElement g)
|
||||||
|
g.SetupContextMenu += OnSetupContextMenu;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_elementsCount = size;
|
_elementsCount = size;
|
||||||
|
|
||||||
@@ -583,6 +668,28 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSetupContextMenu(ContextMenu menu, DropPanel panel)
|
||||||
|
{
|
||||||
|
if (menu.Items.Any(x => x is ContextMenuButton b && b.Text.Equals("Open All", StringComparison.Ordinal)))
|
||||||
|
return;
|
||||||
|
|
||||||
|
menu.AddSeparator();
|
||||||
|
menu.AddButton("Open All", () =>
|
||||||
|
{
|
||||||
|
foreach (var cachedPanel in _cachedDropPanels)
|
||||||
|
{
|
||||||
|
cachedPanel.IsClosed = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
menu.AddButton("Close All", () =>
|
||||||
|
{
|
||||||
|
foreach (var cachedPanel in _cachedDropPanels)
|
||||||
|
{
|
||||||
|
cachedPanel.IsClosed = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void Deinitialize()
|
protected override void Deinitialize()
|
||||||
{
|
{
|
||||||
@@ -663,7 +770,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
cloned[i] = tmp;
|
cloned[i] = tmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SetValue(cloned);
|
SetValue(cloned);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -208,7 +208,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Draw info
|
// Draw info
|
||||||
|
Render2D.PushClip(nameRect);
|
||||||
Render2D.DrawText(style.FontMedium, Type != null ? $"None ({Utilities.Utils.GetPropertyNameUI(Type.ToString())})" : "-", nameRect, isEnabled ? style.ForegroundGrey : style.ForegroundGrey.AlphaMultiplied(0.75f), TextAlignment.Near, TextAlignment.Center);
|
Render2D.DrawText(style.FontMedium, Type != null ? $"None ({Utilities.Utils.GetPropertyNameUI(Type.ToString())})" : "-", nameRect, isEnabled ? style.ForegroundGrey : style.ForegroundGrey.AlphaMultiplied(0.75f), TextAlignment.Near, TextAlignment.Center);
|
||||||
|
Render2D.PopClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw picker button
|
// Draw picker button
|
||||||
|
|||||||
@@ -569,19 +569,19 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
internal static void OnReadOnlyProperty(LayoutElementsContainer itemLayout, int labelIndex = -1)
|
internal static void OnReadOnlyProperty(LayoutElementsContainer itemLayout, int labelIndex = -1)
|
||||||
{
|
{
|
||||||
PropertiesListElement list = null;
|
PropertiesList list = null;
|
||||||
int firstChildControlIndex = 0;
|
int firstChildControlIndex = 0;
|
||||||
bool disableSingle = true;
|
bool disableSingle = true;
|
||||||
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
var control = itemLayout.Children[itemLayout.Children.Count - 1];
|
||||||
if (control is GroupElement group && group.Children.Count > 0)
|
if (control is GroupElement group && group.Children.Count > 0)
|
||||||
{
|
{
|
||||||
list = group.Children[0] as PropertiesListElement;
|
list = (group.Children[0] as PropertiesListElement)?.Properties;
|
||||||
disableSingle = false; // Disable all nested editors
|
disableSingle = false; // Disable all nested editors
|
||||||
}
|
}
|
||||||
else if (control is PropertiesListElement list1 && labelIndex != -1)
|
else if (control is PropertiesListElement list1 && labelIndex != -1)
|
||||||
{
|
{
|
||||||
list = list1;
|
list = list1.Labels[labelIndex].FirstChildControlContainer ?? list1.Properties;
|
||||||
firstChildControlIndex = list.Labels[labelIndex].FirstChildControlIndex;
|
firstChildControlIndex = list1.Labels[labelIndex].FirstChildControlIndex;
|
||||||
}
|
}
|
||||||
else if (control?.Control != null)
|
else if (control?.Control != null)
|
||||||
{
|
{
|
||||||
@@ -591,10 +591,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
if (list != null)
|
if (list != null)
|
||||||
{
|
{
|
||||||
// Disable controls added to the editor
|
// Disable controls added to the editor
|
||||||
var count = list.Properties.Children.Count;
|
var count = list.Children.Count;
|
||||||
for (int j = firstChildControlIndex; j < count; j++)
|
for (int j = firstChildControlIndex; j < count; j++)
|
||||||
{
|
{
|
||||||
var child = list.Properties.Children[j];
|
var child = list.Children[j];
|
||||||
if (disableSingle && child is PropertyNameLabel)
|
if (disableSingle && child is PropertyNameLabel)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (_element == null)
|
|
||||||
{
|
{
|
||||||
// Use int value editor
|
// Use int value editor
|
||||||
var element = layout.IntegerValue();
|
var element = layout.IntegerValue();
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
if (layout.Children.Count == 0)
|
if (layout.Children.Count == 0)
|
||||||
return;
|
return;
|
||||||
var propList = layout.Children[layout.Children.Count - 1] as PropertiesListElement;
|
var propList = layout.Children[layout.Children.Count - 1] as PropertiesListElement;
|
||||||
if (propList == null || propList.Children.Count != 2)
|
if (propList == null || propList.Children.Count < 2)
|
||||||
return;
|
return;
|
||||||
var idElement = propList.Children[0] as TextBoxElement;
|
var idElement = propList.Children[propList.Children.Count - 2] as TextBoxElement;
|
||||||
var valueElement = propList.Children[1] as TextBoxElement;
|
var valueElement = propList.Children[propList.Children.Count - 1] as TextBoxElement;
|
||||||
if (idElement == null || valueElement == null)
|
if (idElement == null || valueElement == null)
|
||||||
return;
|
return;
|
||||||
_idElement = idElement;
|
_idElement = idElement;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
@@ -24,6 +26,11 @@ namespace FlaxEditor.CustomEditors.Elements
|
|||||||
EnableContainmentLines = true,
|
EnableContainmentLines = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event is fired if the group can setup a context menu and the context menu is being setup.
|
||||||
|
/// </summary>
|
||||||
|
public Action<ContextMenu, DropPanel> SetupContextMenu;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override ContainerControl ContainerControl => Panel;
|
public override ContainerControl ContainerControl => Panel;
|
||||||
|
|
||||||
|
|||||||
@@ -53,13 +53,7 @@ namespace FlaxEditor.CustomEditors.Elements
|
|||||||
|
|
||||||
internal void OnAddProperty(string name, string tooltip)
|
internal void OnAddProperty(string name, string tooltip)
|
||||||
{
|
{
|
||||||
var label = new PropertyNameLabel(name)
|
OnAddProperty(new PropertyNameLabel(name), tooltip);
|
||||||
{
|
|
||||||
Parent = Properties,
|
|
||||||
TooltipText = tooltip,
|
|
||||||
FirstChildControlIndex = Properties.Children.Count
|
|
||||||
};
|
|
||||||
Labels.Add(label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnAddProperty(PropertyNameLabel label, string tooltip)
|
internal void OnAddProperty(PropertyNameLabel label, string tooltip)
|
||||||
@@ -88,6 +82,7 @@ namespace FlaxEditor.CustomEditors.Elements
|
|||||||
public override void ClearLayout()
|
public override void ClearLayout()
|
||||||
{
|
{
|
||||||
base.ClearLayout();
|
base.ClearLayout();
|
||||||
|
|
||||||
Labels.Clear();
|
Labels.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ namespace FlaxEditor.CustomEditors.GUI
|
|||||||
// Update child controls enabled state
|
// Update child controls enabled state
|
||||||
if (FirstChildControlIndex >= 0 && Parent is PropertiesList propertiesList)
|
if (FirstChildControlIndex >= 0 && Parent is PropertiesList propertiesList)
|
||||||
{
|
{
|
||||||
|
if (FirstChildControlContainer != null)
|
||||||
|
propertiesList = FirstChildControlContainer;
|
||||||
var controls = propertiesList.Children;
|
var controls = propertiesList.Children;
|
||||||
var labels = propertiesList.Element.Labels;
|
var labels = propertiesList.Element.Labels;
|
||||||
var thisIndex = labels.IndexOf(this);
|
var thisIndex = labels.IndexOf(this);
|
||||||
|
|||||||
@@ -245,16 +245,25 @@ namespace FlaxEditor.CustomEditors.GUI
|
|||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
var label = _element.Labels[i];
|
var label = _element.Labels[i];
|
||||||
|
var container = label.FirstChildControlContainer ?? this;
|
||||||
|
|
||||||
if (label.FirstChildControlIndex < 0)
|
if (label.FirstChildControlIndex < 0)
|
||||||
yStarts[i] = 0;
|
yStarts[i] = 0;
|
||||||
else if (_children.Count <= label.FirstChildControlIndex)
|
else if (container.ChildrenCount <= label.FirstChildControlIndex)
|
||||||
yStarts[i] = y;
|
yStarts[i] = y;
|
||||||
|
else if (label.FirstChildControlContainer != null)
|
||||||
|
{
|
||||||
|
var firstChild = label.FirstChildControlContainer.Children[label.FirstChildControlIndex];
|
||||||
|
yStarts[i] = firstChild.PointToParent(this, Float2.Zero).Y;
|
||||||
|
if (i == count - 1)
|
||||||
|
yStarts[i + 1] = firstChild.Parent.Bottom;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
yStarts[i] = _children[label.FirstChildControlIndex].Top;
|
var firstChild = _children[label.FirstChildControlIndex];
|
||||||
|
yStarts[i] = firstChild.Top;
|
||||||
if (i == count - 1)
|
if (i == count - 1)
|
||||||
yStarts[i + 1] = _children[label.FirstChildControlIndex].Bottom;
|
yStarts[i + 1] = firstChild.Bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ namespace FlaxEditor.CustomEditors.GUI
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal int FirstChildControlIndex;
|
internal int FirstChildControlIndex;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper value used by the <see cref="PropertiesList"/> to draw property names in a proper area.
|
||||||
|
/// </summary>
|
||||||
|
internal PropertiesList FirstChildControlContainer;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The linked custom editor (shows the label property).
|
/// The linked custom editor (shows the label property).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -154,8 +159,16 @@ namespace FlaxEditor.CustomEditors.GUI
|
|||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
SetupContextMenu = null;
|
SetupContextMenu = null;
|
||||||
|
LinkedEditor = null;
|
||||||
|
FirstChildControlContainer = null;
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Text.ToString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,11 +74,11 @@ namespace FlaxEditor.CustomEditors
|
|||||||
{
|
{
|
||||||
var element = Group(title, useTransparentHeader);
|
var element = Group(title, useTransparentHeader);
|
||||||
element.Panel.Tag = linkedEditor;
|
element.Panel.Tag = linkedEditor;
|
||||||
element.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
|
element.Panel.MouseButtonRightClicked += (panel, location) => OnGroupPanelMouseButtonRightClicked(element, panel, location);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Float2 location)
|
private void OnGroupPanelMouseButtonRightClicked(GroupElement element, DropPanel groupPanel, Float2 location)
|
||||||
{
|
{
|
||||||
var linkedEditor = (CustomEditor)groupPanel.Tag;
|
var linkedEditor = (CustomEditor)groupPanel.Tag;
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
@@ -91,6 +91,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
menu.AddButton("Copy", linkedEditor.Copy);
|
menu.AddButton("Copy", linkedEditor.Copy);
|
||||||
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
var paste = menu.AddButton("Paste", linkedEditor.Paste);
|
||||||
paste.Enabled = linkedEditor.CanPaste;
|
paste.Enabled = linkedEditor.CanPaste;
|
||||||
|
element.SetupContextMenu?.Invoke(menu, groupPanel);
|
||||||
|
|
||||||
menu.Show(groupPanel, location);
|
menu.Show(groupPanel, location);
|
||||||
}
|
}
|
||||||
@@ -666,7 +667,20 @@ namespace FlaxEditor.CustomEditors
|
|||||||
}
|
}
|
||||||
|
|
||||||
var property = AddPropertyItem(name, tooltip);
|
var property = AddPropertyItem(name, tooltip);
|
||||||
return property.Object(values, editor);
|
int start = property.Properties.Children.Count;
|
||||||
|
var result = property.Object(values, editor);
|
||||||
|
|
||||||
|
// Special case when properties list is nested into another properties list (eg. array of structures or LocalizedString editor)
|
||||||
|
if (this is PropertiesListElement thisPropertiesList &&
|
||||||
|
editor.ParentEditor != null &&
|
||||||
|
editor.ParentEditor.LinkedLabel != null &&
|
||||||
|
editor.ParentEditor.LinkedLabel.FirstChildControlContainer == null)
|
||||||
|
{
|
||||||
|
editor.ParentEditor.LinkedLabel.FirstChildControlIndex = start;
|
||||||
|
editor.ParentEditor.LinkedLabel.FirstChildControlContainer = property.Properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ public class Editor : EditorModule
|
|||||||
options.ScriptingAPI.SystemReferences.Add("System.Xml");
|
options.ScriptingAPI.SystemReferences.Add("System.Xml");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
options.ScriptingAPI.SystemReferences.Add("System.Xml.ReaderWriter");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
options.ScriptingAPI.SystemReferences.Add("System.Text.RegularExpressions");
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.ComponentModel.TypeConverter");
|
|
||||||
options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile");
|
options.ScriptingAPI.SystemReferences.Add("System.IO.Compression.ZipFile");
|
||||||
|
|
||||||
// Enable optimizations for Editor, disable this for debugging the editor
|
// Enable optimizations for Editor, disable this for debugging the editor
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ bool Editor::CheckProjectUpgrade()
|
|||||||
" base.Init();\n"
|
" base.Init();\n"
|
||||||
"\n"
|
"\n"
|
||||||
" // Reference the modules for game\n"
|
" // Reference the modules for game\n"
|
||||||
" Modules.Add(\"{0}\");\n"
|
" Modules.Add(nameof({0}));\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"}}\n"
|
"}}\n"
|
||||||
), codeName), Encoding::UTF8);
|
), codeName), Encoding::UTF8);
|
||||||
@@ -242,7 +242,7 @@ bool Editor::CheckProjectUpgrade()
|
|||||||
" base.Init();\n"
|
" base.Init();\n"
|
||||||
"\n"
|
"\n"
|
||||||
" // Reference the modules for editor\n"
|
" // Reference the modules for editor\n"
|
||||||
" Modules.Add(\"{0}\");\n"
|
" Modules.Add(nameof({0}));\n"
|
||||||
"{1}"
|
"{1}"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"}}\n"
|
"}}\n"
|
||||||
@@ -476,7 +476,7 @@ int32 Editor::LoadProduct()
|
|||||||
" base.Init();\n"
|
" base.Init();\n"
|
||||||
"\n"
|
"\n"
|
||||||
" // Reference the modules for game\n"
|
" // Reference the modules for game\n"
|
||||||
" Modules.Add(\"Game\");\n"
|
" Modules.Add(nameof(Game));\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n"), Encoding::UTF8);
|
"}\n"), Encoding::UTF8);
|
||||||
failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
|
failed |= File::WriteAllText(projectPath / TEXT("Source/GameEditorTarget.Build.cs"),TEXT(
|
||||||
@@ -490,7 +490,7 @@ int32 Editor::LoadProduct()
|
|||||||
" base.Init();\n"
|
" base.Init();\n"
|
||||||
"\n"
|
"\n"
|
||||||
" // Reference the modules for editor\n"
|
" // Reference the modules for editor\n"
|
||||||
" Modules.Add(\"Game\");\n"
|
" Modules.Add(nameof(Game));\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n"), Encoding::UTF8);
|
"}\n"), Encoding::UTF8);
|
||||||
failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
|
failed |= File::WriteAllText(projectPath / TEXT("Source/Game/Game.Build.cs"),TEXT(
|
||||||
|
|||||||
@@ -337,14 +337,12 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the separator.
|
/// Adds the separator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Created context menu item control.</returns>
|
public void AddSeparator()
|
||||||
public ContextMenuSeparator AddSeparator()
|
|
||||||
{
|
{
|
||||||
var item = new ContextMenuSeparator(this)
|
var item = new ContextMenuSeparator(this)
|
||||||
{
|
{
|
||||||
Parent = _panel
|
Parent = _panel
|
||||||
};
|
};
|
||||||
return item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using System.Reflection;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.CustomEditors.Elements;
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
|
||||||
@@ -277,6 +278,14 @@ namespace FlaxEditor.GUI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnLayoutMenuButton(ContextMenuButton button, int index, bool construct = false)
|
||||||
|
{
|
||||||
|
base.OnLayoutMenuButton(button, index, construct);
|
||||||
|
if (IsFlags)
|
||||||
|
button.CloseMenuOnClick = false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnItemClicked(int index)
|
protected override void OnItemClicked(int index)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ namespace FlaxEditor.GUI.Input
|
|||||||
var style = Style.Current;
|
var style = Style.Current;
|
||||||
|
|
||||||
// Draw sliding UI
|
// Draw sliding UI
|
||||||
Render2D.DrawSprite(style.Scalar, SlideRect, style.Foreground);
|
Render2D.DrawSprite(style.Scalar, SlideRect, EnabledInHierarchy ? style.Foreground : style.ForegroundDisabled);
|
||||||
|
|
||||||
// Check if is sliding
|
// Check if is sliding
|
||||||
if (_isSliding)
|
if (_isSliding)
|
||||||
|
|||||||
@@ -130,9 +130,9 @@ namespace FlaxEditor.GUI.Timeline
|
|||||||
public override void OnPlay()
|
public override void OnPlay()
|
||||||
{
|
{
|
||||||
var time = CurrentTime;
|
var time = CurrentTime;
|
||||||
_preview.Play();
|
|
||||||
if (_preview != null)
|
if (_preview != null)
|
||||||
{
|
{
|
||||||
|
_preview.Play();
|
||||||
Editor.Internal_SetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor), time);
|
Editor.Internal_SetAnimationTime(Object.GetUnmanagedPtr(_preview.PreviewActor), time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
|||||||
|
|
||||||
private void Set(byte[] data)
|
private void Set(byte[] data)
|
||||||
{
|
{
|
||||||
|
if (_timeline == null)
|
||||||
|
return;
|
||||||
var track = _timeline.FindTrack(_name);
|
var track = _timeline.FindTrack(_name);
|
||||||
using (var memory = new MemoryStream(data))
|
using (var memory = new MemoryStream(data))
|
||||||
using (var stream = new BinaryReader(memory))
|
using (var stream = new BinaryReader(memory))
|
||||||
@@ -42,11 +44,8 @@ namespace FlaxEditor.GUI.Timeline.Undo
|
|||||||
track.Flags = (TrackFlags)stream.ReadByte();
|
track.Flags = (TrackFlags)stream.ReadByte();
|
||||||
track.Archetype.Load(Timeline.FormatVersion, track, stream);
|
track.Archetype.Load(Timeline.FormatVersion, track, stream);
|
||||||
}
|
}
|
||||||
if (_timeline != null)
|
_timeline.ArrangeTracks();
|
||||||
{
|
_timeline.MarkAsEdited();
|
||||||
_timeline.ArrangeTracks();
|
|
||||||
_timeline.MarkAsEdited();
|
|
||||||
}
|
|
||||||
track.OnUndo();
|
track.OnUndo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -272,11 +272,13 @@ DEFINE_INTERNAL_CALL(MString*) EditorInternal_GetShaderAssetSourceCode(BinaryAss
|
|||||||
obj->GetChunkData(SHADER_FILE_CHUNK_SOURCE, data);
|
obj->GetChunkData(SHADER_FILE_CHUNK_SOURCE, data);
|
||||||
auto source = data.Get<char>();
|
auto source = data.Get<char>();
|
||||||
auto sourceLength = data.Length();
|
auto sourceLength = data.Length();
|
||||||
|
if (sourceLength <= 0)
|
||||||
|
return MCore::String::GetEmpty();
|
||||||
Encryption::DecryptBytes(data.Get(), data.Length());
|
Encryption::DecryptBytes(data.Get(), data.Length());
|
||||||
source[sourceLength - 1] = 0;
|
source[sourceLength - 1] = 0;
|
||||||
|
|
||||||
// Get source and encrypt it back
|
// Get source and encrypt it back
|
||||||
const StringAnsiView srcData((const char*)data.Get(), data.Length());
|
const StringAnsiView srcData(source, sourceLength);
|
||||||
const auto str = MUtils::ToString(srcData);
|
const auto str = MUtils::ToString(srcData);
|
||||||
Encryption::EncryptBytes(data.Get(), data.Length());
|
Encryption::EncryptBytes(data.Get(), data.Length());
|
||||||
|
|
||||||
|
|||||||
@@ -686,6 +686,10 @@ namespace FlaxEditor.Modules
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Try to remove module if build.cs file is being deleted
|
||||||
|
if (item.Path.Contains(".Build.cs", StringComparison.Ordinal) && item.ItemType == ContentItemType.Script)
|
||||||
|
Editor.Instance.CodeEditing.RemoveModule(item.Path);
|
||||||
|
|
||||||
// Check if it's an asset
|
// Check if it's an asset
|
||||||
if (item.IsAsset)
|
if (item.IsAsset)
|
||||||
{
|
{
|
||||||
@@ -1077,6 +1081,7 @@ namespace FlaxEditor.Modules
|
|||||||
Proxy.Add(new ParticleSystemProxy());
|
Proxy.Add(new ParticleSystemProxy());
|
||||||
Proxy.Add(new SceneAnimationProxy());
|
Proxy.Add(new SceneAnimationProxy());
|
||||||
Proxy.Add(new CSharpScriptProxy());
|
Proxy.Add(new CSharpScriptProxy());
|
||||||
|
Proxy.Add(new CSharpEmptyProxy());
|
||||||
Proxy.Add(new CppAssetProxy());
|
Proxy.Add(new CppAssetProxy());
|
||||||
Proxy.Add(new CppStaticClassProxy());
|
Proxy.Add(new CppStaticClassProxy());
|
||||||
Proxy.Add(new CppScriptProxy());
|
Proxy.Add(new CppScriptProxy());
|
||||||
|
|||||||
@@ -145,6 +145,10 @@ namespace FlaxEditor.Modules
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save any modified scenes to prevent loosing local changes
|
||||||
|
if (Editor.Scene.IsEdited())
|
||||||
|
Level.SaveAllScenes();
|
||||||
|
|
||||||
// Load scenes after entering the play mode
|
// Load scenes after entering the play mode
|
||||||
_scenesToReload = new Guid[Level.ScenesCount];
|
_scenesToReload = new Guid[Level.ScenesCount];
|
||||||
for (int i = 0; i < _scenesToReload.Length; i++)
|
for (int i = 0; i < _scenesToReload.Length; i++)
|
||||||
|
|||||||
@@ -382,7 +382,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
|||||||
|
|
||||||
// Get editor target and target files and add module
|
// Get editor target and target files and add module
|
||||||
var files = Directory.GetFiles(path);
|
var files = Directory.GetFiles(path);
|
||||||
var targetModuleText = $"Modules.Add(\"{moduleName}\");\n ";
|
var targetModuleText = $"Modules.Add(nameof({moduleName}));\n ";
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase))
|
if (!file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase))
|
||||||
@@ -404,6 +404,93 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RemoveModule(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Read text, figure out if it is an editor module or game module
|
||||||
|
var editorModule = false;
|
||||||
|
var moduleTextIndex = -1;
|
||||||
|
var fileText = File.ReadAllText(path);
|
||||||
|
if (fileText.Contains("GameModule", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
moduleTextIndex = fileText.IndexOf("GameModule", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
else if (fileText.Contains("ThirdPartyModule", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
moduleTextIndex = fileText.IndexOf("ThirdPartyModule", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
else if (fileText.Contains("DepsModule", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
moduleTextIndex = fileText.IndexOf("DepsModule", StringComparison.Ordinal);
|
||||||
|
}
|
||||||
|
else if (fileText.Contains("GameEditorModule", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
moduleTextIndex = fileText.IndexOf("GameEditorModule", StringComparison.Ordinal);
|
||||||
|
editorModule = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If it does not contain a module, then this could be target file and not a module file
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get module name
|
||||||
|
var classTextIndex = fileText.IndexOf("class ", StringComparison.Ordinal);
|
||||||
|
var className = fileText.Substring(classTextIndex, moduleTextIndex - classTextIndex).Replace("class ", "").Replace(":", "").Trim();
|
||||||
|
Editor.Log($"Removing Module: {className}");
|
||||||
|
|
||||||
|
// Find target files
|
||||||
|
// Assume Target files are in the source directory that is up 2 levels
|
||||||
|
var sourceDirectoryInfo = Directory.GetParent(path)?.Parent;
|
||||||
|
if (sourceDirectoryInfo != null)
|
||||||
|
{
|
||||||
|
var sourceFiles = Directory.GetFiles(sourceDirectoryInfo.FullName);
|
||||||
|
// Search target files for module name and remove it
|
||||||
|
foreach (var file in sourceFiles)
|
||||||
|
{
|
||||||
|
string fileName = Path.GetFileName(file);
|
||||||
|
if (file.Contains(".Build.cs", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var targetText = File.ReadAllText(file);
|
||||||
|
|
||||||
|
// Skip game project if it is suppose to be an editor module
|
||||||
|
if (editorModule && targetText.Contains("GameProjectTarget", StringComparison.Ordinal))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var newText = targetText;
|
||||||
|
bool removedModuleText = false;
|
||||||
|
if (targetText.Contains($"Modules.Add(\"{className}\")", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
newText = newText.Replace($"Modules.Add(\"{className}\");\n", "", StringComparison.Ordinal).Replace($"Modules.Add(\"{className}\");", "", StringComparison.Ordinal);
|
||||||
|
removedModuleText = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetText.Contains($"Modules.Add(nameof({className}))", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
newText = newText.Replace($"Modules.Add(nameof({className}));\n", "", StringComparison.Ordinal).Replace($"Modules.Add(nameof({className}));", "", StringComparison.Ordinal);
|
||||||
|
removedModuleText = true;
|
||||||
|
}
|
||||||
|
if (removedModuleText)
|
||||||
|
{
|
||||||
|
File.WriteAllText(file, newText);
|
||||||
|
Editor.Log($"Removed Module: {className} from {file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Remove Generated module files
|
||||||
|
else if (fileName.Equals($"{className}.csproj", StringComparison.Ordinal) ||
|
||||||
|
fileName.Equals($"{className}.Gen.cs", StringComparison.Ordinal) ||
|
||||||
|
fileName.Equals($"{className}.Gen.cpp", StringComparison.Ordinal) ||
|
||||||
|
fileName.Equals($"{className}.Gen.h", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
File.Delete(file);
|
||||||
|
Editor.Log($"Deleted generated modules file for module: {className}. File path {file}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnUpdate()
|
public override void OnUpdate()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -376,11 +376,25 @@ namespace FlaxEditor.Options
|
|||||||
public float ConnectionCurvature { get; set; } = 1.0f;
|
public float ConnectionCurvature { get; set; } = 1.0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the visject connection curvature.
|
/// Gets or sets a value that indicates wether the context menu description panel is shown or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DefaultValue(true)]
|
[DefaultValue(true)]
|
||||||
[EditorDisplay("Visject"), EditorOrder(550), Tooltip("Shows/hides the description panel in the visual scripting context menu.")]
|
[EditorDisplay("Visject"), EditorOrder(550), Tooltip("Shows/hides the description panel in visual scripting context menu.")]
|
||||||
public bool VisualScriptingDescriptionPanel { get; set; } = true;
|
public bool NodeDescriptionPanel { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the surface grid snapping option.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(false)]
|
||||||
|
[EditorDisplay("Visject", "Grid Snapping"), EditorOrder(551), Tooltip("Toggles grid snapping when moving nodes.")]
|
||||||
|
public bool SurfaceGridSnapping { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the surface grid snapping option.
|
||||||
|
/// </summary>
|
||||||
|
[DefaultValue(20.0f)]
|
||||||
|
[EditorDisplay("Visject", "Grid Snapping Size"), EditorOrder(551), Tooltip("Defines the size of the grid for nodes snapping."), VisibleIf(nameof(SurfaceGridSnapping))]
|
||||||
|
public float SurfaceGridSnappingSize { get; set; } = 20.0f;
|
||||||
|
|
||||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||||
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -671,6 +672,18 @@ namespace FlaxEditor.Scripting
|
|||||||
/// <param name="value">The new member value.</param>
|
/// <param name="value">The new member value.</param>
|
||||||
public void SetValue(object obj, object value)
|
public void SetValue(object obj, object value)
|
||||||
{
|
{
|
||||||
|
// Perform automatic conversion if type supports it
|
||||||
|
var type = ValueType.Type;
|
||||||
|
var valueType = value?.GetType();
|
||||||
|
if (valueType != null && type != null && valueType != type)
|
||||||
|
{
|
||||||
|
var converter = TypeDescriptor.GetConverter(type);
|
||||||
|
if (converter.CanConvertTo(type))
|
||||||
|
value = converter.ConvertTo(value, type);
|
||||||
|
else if (converter.CanConvertFrom(valueType))
|
||||||
|
value = converter.ConvertFrom(null, null, value);
|
||||||
|
}
|
||||||
|
|
||||||
if (_managed is PropertyInfo propertyInfo)
|
if (_managed is PropertyInfo propertyInfo)
|
||||||
propertyInfo.SetValue(obj, value);
|
propertyInfo.SetValue(obj, value);
|
||||||
else if (_managed is FieldInfo fieldInfo)
|
else if (_managed is FieldInfo fieldInfo)
|
||||||
|
|||||||
@@ -57,8 +57,9 @@ namespace FlaxEditor.States
|
|||||||
{
|
{
|
||||||
// Generate project files when Cache is missing or was cleared previously
|
// Generate project files when Cache is missing or was cleared previously
|
||||||
var projectFolderPath = Editor.GameProject?.ProjectFolderPath;
|
var projectFolderPath = Editor.GameProject?.ProjectFolderPath;
|
||||||
if (!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Intermediate")) ||
|
if (!string.IsNullOrEmpty(projectFolderPath) &&
|
||||||
!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Projects")))
|
(!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Intermediate")) ||
|
||||||
|
!Directory.Exists(Path.Combine(projectFolderPath, "Cache", "Projects"))))
|
||||||
{
|
{
|
||||||
var customArgs = Editor.CodeEditing.SelectedEditor?.GenerateProjectCustomArgs;
|
var customArgs = Editor.CodeEditing.SelectedEditor?.GenerateProjectCustomArgs;
|
||||||
ScriptsBuilder.GenerateProject(customArgs);
|
ScriptsBuilder.GenerateProject(customArgs);
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
private readonly bool _is2D;
|
private readonly bool _is2D;
|
||||||
private Float2 _rangeX, _rangeY;
|
private Float2 _rangeX, _rangeY;
|
||||||
private Float2 _debugPos = Float2.Minimum;
|
private Float2 _debugPos = Float2.Minimum;
|
||||||
|
private float _debugScale = 1.0f;
|
||||||
private readonly List<BlendPoint> _blendPoints = new List<BlendPoint>();
|
private readonly List<BlendPoint> _blendPoints = new List<BlendPoint>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -445,6 +446,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
// Debug current playback position
|
// Debug current playback position
|
||||||
if (((AnimGraphSurface)_node.Surface).TryGetTraceEvent(_node, out var traceEvent))
|
if (((AnimGraphSurface)_node.Surface).TryGetTraceEvent(_node, out var traceEvent))
|
||||||
{
|
{
|
||||||
|
var prev = _debugPos;
|
||||||
if (_is2D)
|
if (_is2D)
|
||||||
{
|
{
|
||||||
unsafe
|
unsafe
|
||||||
@@ -456,10 +458,17 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
_debugPos = new Float2(traceEvent.Value, 0.0f);
|
_debugPos = new Float2(traceEvent.Value, 0.0f);
|
||||||
|
|
||||||
|
// Scale debug pointer when it moves to make it more visible when investigating blending
|
||||||
|
const float debugMaxSize = 2.0f;
|
||||||
|
float debugScale = Mathf.Saturate(Float2.Distance(ref _debugPos, ref prev) / new Float2(_rangeX.Absolute.ValuesSum, _rangeY.Absolute.ValuesSum).Length * 100.0f) * debugMaxSize + 1.0f;
|
||||||
|
float debugBlendSpeed = _debugScale <= debugScale ? 4.0f : 1.0f;
|
||||||
|
_debugScale = Mathf.Lerp(_debugScale, debugScale, deltaTime * debugBlendSpeed);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_debugPos = Float2.Minimum;
|
_debugPos = Float2.Minimum;
|
||||||
|
_debugScale = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
base.Update(deltaTime);
|
base.Update(deltaTime);
|
||||||
@@ -606,7 +615,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
{
|
{
|
||||||
// Draw dot with outline
|
// Draw dot with outline
|
||||||
var icon = Editor.Instance.Icons.VisjectBoxOpen32;
|
var icon = Editor.Instance.Icons.VisjectBoxOpen32;
|
||||||
var size = BlendPoint.DefaultSize;
|
var size = BlendPoint.DefaultSize * _debugScale;
|
||||||
var debugPos = BlendSpacePosToBlendPointPos(_debugPos);
|
var debugPos = BlendSpacePosToBlendPointPos(_debugPos);
|
||||||
var debugRect = new Rectangle(debugPos + new Float2(size * -0.5f) + size * 0.5f, new Float2(size));
|
var debugRect = new Rectangle(debugPos + new Float2(size * -0.5f) + size * 0.5f, new Float2(size));
|
||||||
var outline = Color.Black; // Shadow
|
var outline = Color.Black; // Shadow
|
||||||
|
|||||||
@@ -809,7 +809,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
if (!_useDescriptionPanel)
|
if (!_useDescriptionPanel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (archetype == null || !Editor.Instance.Options.Options.Interface.VisualScriptingDescriptionPanel)
|
if (archetype == null || !Editor.Instance.Options.Options.Interface.NodeDescriptionPanel)
|
||||||
{
|
{
|
||||||
HideDescriptionPanel();
|
HideDescriptionPanel();
|
||||||
return;
|
return;
|
||||||
@@ -875,7 +875,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
|||||||
AddInputOutputElement(archetype, output.Type, true, $"{output.Name} ({output.Type.Name})");
|
AddInputOutputElement(archetype, output.Type, true, $"{output.Name} ({output.Type.Name})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (archetype.Elements != null) // Skip if no Elements (ex: Comment node)
|
||||||
{
|
{
|
||||||
foreach (var element in archetype.Elements)
|
foreach (var element in archetype.Elements)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace FlaxEditor.Surface
|
|||||||
public class MaterialSurface : VisjectSurface
|
public class MaterialSurface : VisjectSurface
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public MaterialSurface(IVisjectSurfaceOwner owner, Action onSave, FlaxEditor.Undo undo)
|
public MaterialSurface(IVisjectSurfaceOwner owner, Action onSave = null, FlaxEditor.Undo undo = null)
|
||||||
: base(owner, onSave, undo)
|
: base(owner, onSave, undo)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using FlaxEditor.GUI;
|
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.GUI.Input;
|
using FlaxEditor.GUI.Input;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
@@ -86,7 +85,10 @@ namespace FlaxEditor.Surface
|
|||||||
// Read node data
|
// Read node data
|
||||||
Title = TitleValue;
|
Title = TitleValue;
|
||||||
Color = ColorValue;
|
Color = ColorValue;
|
||||||
Size = SizeValue;
|
var size = SizeValue;
|
||||||
|
if (Surface.GridSnappingEnabled)
|
||||||
|
size = Surface.SnapToGrid(size, true);
|
||||||
|
Size = size;
|
||||||
|
|
||||||
// Order
|
// Order
|
||||||
// Backwards compatibility - When opening with an older version send the old comments to the back
|
// Backwards compatibility - When opening with an older version send the old comments to the back
|
||||||
@@ -299,7 +301,10 @@ namespace FlaxEditor.Surface
|
|||||||
if (_isResizing)
|
if (_isResizing)
|
||||||
{
|
{
|
||||||
// Update size
|
// Update size
|
||||||
Size = Float2.Max(location, new Float2(140.0f, _headerRect.Bottom));
|
var size = Float2.Max(location, new Float2(140.0f, _headerRect.Bottom));
|
||||||
|
if (Surface.GridSnappingEnabled)
|
||||||
|
size = Surface.SnapToGrid(size, true);
|
||||||
|
Size = size;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -131,6 +131,15 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void OnSurfaceLoaded(SurfaceNodeActions action)
|
public virtual void OnSurfaceLoaded(SurfaceNodeActions action)
|
||||||
{
|
{
|
||||||
|
// Snap bounds (with ceil) when using grid snapping
|
||||||
|
if (Surface.GridSnappingEnabled)
|
||||||
|
{
|
||||||
|
var bounds = Bounds;
|
||||||
|
bounds.Location = Surface.SnapToGrid(bounds.Location, false);
|
||||||
|
bounds.Size = Surface.SnapToGrid(bounds.Size, true);
|
||||||
|
Bounds = bounds;
|
||||||
|
}
|
||||||
|
|
||||||
UpdateRectangles();
|
UpdateRectangles();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -167,6 +167,15 @@ namespace FlaxEditor.Surface
|
|||||||
if (Surface == null)
|
if (Surface == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Snap bounds (with ceil) when using grid snapping
|
||||||
|
if (Surface.GridSnappingEnabled)
|
||||||
|
{
|
||||||
|
var size = Surface.SnapToGrid(new Float2(width, height), true);
|
||||||
|
width = size.X;
|
||||||
|
height = size.Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrange output boxes on the right edge
|
||||||
for (int i = 0; i < Elements.Count; i++)
|
for (int i = 0; i < Elements.Count; i++)
|
||||||
{
|
{
|
||||||
if (Elements[i] is OutputBox box)
|
if (Elements[i] is OutputBox box)
|
||||||
@@ -175,6 +184,7 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resize
|
||||||
Size = CalculateNodeSize(width, height);
|
Size = CalculateNodeSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,10 +986,12 @@ namespace FlaxEditor.Surface
|
|||||||
else
|
else
|
||||||
Array.Copy(values, Values, values.Length);
|
Array.Copy(values, Values, values.Length);
|
||||||
OnValuesChanged();
|
OnValuesChanged();
|
||||||
Surface.MarkAsEdited(graphEdited);
|
|
||||||
|
|
||||||
if (Surface != null)
|
if (Surface != null)
|
||||||
|
{
|
||||||
|
Surface.MarkAsEdited(graphEdited);
|
||||||
Surface.AddBatchedUndoAction(new EditNodeValuesAction(this, before, graphEdited));
|
Surface.AddBatchedUndoAction(new EditNodeValuesAction(this, before, graphEdited));
|
||||||
|
}
|
||||||
|
|
||||||
_isDuringValuesEditing = false;
|
_isDuringValuesEditing = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
@@ -10,9 +9,9 @@ using FlaxEditor.CustomEditors;
|
|||||||
using FlaxEditor.CustomEditors.Elements;
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Utilities;
|
|
||||||
using FlaxEngine.Utilities;
|
using FlaxEngine.Utilities;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEditor.GUI;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface
|
namespace FlaxEditor.Surface
|
||||||
{
|
{
|
||||||
@@ -73,9 +72,8 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
// By name
|
// By name
|
||||||
if (Editor.Instance.Options.Options.General.ScriptMembersOrder == GeneralOptions.MembersOrder.Alphabetical)
|
if (Editor.Instance.Options.Options.General.ScriptMembersOrder == GeneralOptions.MembersOrder.Alphabetical)
|
||||||
{
|
|
||||||
return string.Compare(x.DisplayName, y.DisplayName, StringComparison.InvariantCulture);
|
return string.Compare(x.DisplayName, y.DisplayName, StringComparison.InvariantCulture);
|
||||||
}
|
|
||||||
// Keep same order
|
// Keep same order
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -111,6 +109,79 @@ namespace FlaxEditor.Surface
|
|||||||
return CustomEditors.Editors.GenericEditor.OnGroup(layout, "Parameters");
|
return CustomEditors.Editors.GenericEditor.OnGroup(layout, "Parameters");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class DummyMaterialSurfaceOwner : IVisjectSurfaceOwner
|
||||||
|
{
|
||||||
|
public Asset SurfaceAsset => null;
|
||||||
|
public string SurfaceName => null;
|
||||||
|
public FlaxEditor.Undo Undo => null;
|
||||||
|
public byte[] SurfaceData { get; set; }
|
||||||
|
public VisjectSurfaceContext ParentContext => null;
|
||||||
|
|
||||||
|
public void OnContextCreated(VisjectSurfaceContext context)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSurfaceEditedChanged()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSurfaceGraphEdited()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSurfaceClose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FindGraphParameters(Material material, List<SurfaceParameter> surfaceParameters)
|
||||||
|
{
|
||||||
|
if (material == null || material.WaitForLoaded())
|
||||||
|
return;
|
||||||
|
var surfaceData = material.LoadSurface(false);
|
||||||
|
if (surfaceData != null && surfaceData.Length > 0)
|
||||||
|
{
|
||||||
|
var surfaceOwner = new DummyMaterialSurfaceOwner { SurfaceData = surfaceData };
|
||||||
|
var surface = new MaterialSurface(surfaceOwner);
|
||||||
|
if (!surface.Load())
|
||||||
|
{
|
||||||
|
surfaceParameters.AddRange(surface.Parameters);
|
||||||
|
|
||||||
|
// Search for any nested parameters (eg. via Sample Layer)
|
||||||
|
foreach (var node in surface.Nodes)
|
||||||
|
{
|
||||||
|
if (node.GroupArchetype.GroupID == 8 && node.Archetype.TypeID == 1) // Sample Layer
|
||||||
|
{
|
||||||
|
if (node.Values != null && node.Values.Length > 0 && node.Values[0] is Guid layerId)
|
||||||
|
{
|
||||||
|
var layer = FlaxEngine.Content.Load<MaterialBase>(layerId);
|
||||||
|
if (layer)
|
||||||
|
{
|
||||||
|
FindGraphParameters(layer, surfaceParameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FindGraphParameters(MaterialBase materialBase, List<SurfaceParameter> surfaceParameters)
|
||||||
|
{
|
||||||
|
while (materialBase != null && !materialBase.WaitForLoaded())
|
||||||
|
{
|
||||||
|
if (materialBase is MaterialInstance materialInstance)
|
||||||
|
{
|
||||||
|
materialBase = materialInstance.BaseMaterial;
|
||||||
|
}
|
||||||
|
else if (materialBase is Material material)
|
||||||
|
{
|
||||||
|
FindGraphParameters(material, surfaceParameters);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static GraphParameterData[] InitGraphParameters(IEnumerable<MaterialParameter> parameters, Material material)
|
internal static GraphParameterData[] InitGraphParameters(IEnumerable<MaterialParameter> parameters, Material material)
|
||||||
{
|
{
|
||||||
int count = parameters.Count();
|
int count = parameters.Count();
|
||||||
@@ -118,128 +189,11 @@ namespace FlaxEditor.Surface
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
// Load material surface parameters meta to use it for material instance parameters editing
|
// Load material surface parameters meta to use it for material instance parameters editing
|
||||||
SurfaceParameter[] surfaceParameters = null;
|
var surfaceParameters = new List<SurfaceParameter>();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Profiler.BeginEvent("Init Material Parameters UI Data");
|
Profiler.BeginEvent("Init Material Parameters UI Data");
|
||||||
|
FindGraphParameters(material, surfaceParameters);
|
||||||
if (material != null && !material.WaitForLoaded())
|
|
||||||
{
|
|
||||||
var surfaceData = material.LoadSurface(false);
|
|
||||||
if (surfaceData != null && surfaceData.Length > 0)
|
|
||||||
{
|
|
||||||
using (var memoryStream = new MemoryStream(surfaceData))
|
|
||||||
using (var stream = new BinaryReader(memoryStream))
|
|
||||||
{
|
|
||||||
// IMPORTANT! This must match C++ Graph format
|
|
||||||
|
|
||||||
// Magic Code
|
|
||||||
int tmp = stream.ReadInt32();
|
|
||||||
if (tmp != 1963542358)
|
|
||||||
{
|
|
||||||
// Error
|
|
||||||
throw new Exception("Invalid Graph format version");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Version
|
|
||||||
var version = stream.ReadUInt32();
|
|
||||||
var guidBytes = new byte[16];
|
|
||||||
if (version < 7000)
|
|
||||||
{
|
|
||||||
// Time saved (not used anymore to prevent binary diffs after saving unmodified surface)
|
|
||||||
stream.ReadInt64();
|
|
||||||
|
|
||||||
// Nodes count
|
|
||||||
int nodesCount = stream.ReadInt32();
|
|
||||||
|
|
||||||
// Parameters count
|
|
||||||
int parametersCount = stream.ReadInt32();
|
|
||||||
|
|
||||||
// For each node
|
|
||||||
for (int j = 0; j < nodesCount; j++)
|
|
||||||
{
|
|
||||||
// ID
|
|
||||||
stream.ReadUInt32();
|
|
||||||
|
|
||||||
// Type
|
|
||||||
stream.ReadUInt16();
|
|
||||||
stream.ReadUInt16();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each param
|
|
||||||
surfaceParameters = new SurfaceParameter[parametersCount];
|
|
||||||
for (int j = 0; j < parametersCount; j++)
|
|
||||||
{
|
|
||||||
// Create param
|
|
||||||
var param = new SurfaceParameter();
|
|
||||||
surfaceParameters[j] = param;
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
param.Type = new ScriptType(VisjectSurfaceContext.GetGraphParameterValueType((VisjectSurfaceContext.GraphParamType_Deprecated)stream.ReadByte()));
|
|
||||||
stream.Read(guidBytes, 0, 16);
|
|
||||||
param.ID = new Guid(guidBytes);
|
|
||||||
param.Name = stream.ReadStr(97);
|
|
||||||
param.IsPublic = stream.ReadByte() != 0;
|
|
||||||
var isStatic = stream.ReadByte() != 0;
|
|
||||||
var isUIVisible = stream.ReadByte() != 0;
|
|
||||||
var isUIEditable = stream.ReadByte() != 0;
|
|
||||||
|
|
||||||
// References [Deprecated]
|
|
||||||
int refsCount = stream.ReadInt32();
|
|
||||||
for (int k = 0; k < refsCount; k++)
|
|
||||||
stream.ReadUInt32();
|
|
||||||
|
|
||||||
// Value
|
|
||||||
stream.ReadCommonValue(ref param.Value);
|
|
||||||
|
|
||||||
// Meta
|
|
||||||
param.Meta.Load(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (version == 7000)
|
|
||||||
{
|
|
||||||
// Nodes count
|
|
||||||
int nodesCount = stream.ReadInt32();
|
|
||||||
|
|
||||||
// Parameters count
|
|
||||||
int parametersCount = stream.ReadInt32();
|
|
||||||
|
|
||||||
// For each node
|
|
||||||
for (int j = 0; j < nodesCount; j++)
|
|
||||||
{
|
|
||||||
// ID
|
|
||||||
stream.ReadUInt32();
|
|
||||||
|
|
||||||
// Type
|
|
||||||
stream.ReadUInt16();
|
|
||||||
stream.ReadUInt16();
|
|
||||||
}
|
|
||||||
|
|
||||||
// For each param
|
|
||||||
surfaceParameters = new SurfaceParameter[parametersCount];
|
|
||||||
for (int j = 0; j < parametersCount; j++)
|
|
||||||
{
|
|
||||||
// Create param
|
|
||||||
var param = new SurfaceParameter();
|
|
||||||
surfaceParameters[j] = param;
|
|
||||||
|
|
||||||
// Properties
|
|
||||||
param.Type = stream.ReadVariantScriptType();
|
|
||||||
stream.Read(guidBytes, 0, 16);
|
|
||||||
param.ID = new Guid(guidBytes);
|
|
||||||
param.Name = stream.ReadStr(97);
|
|
||||||
param.IsPublic = stream.ReadByte() != 0;
|
|
||||||
|
|
||||||
// Value
|
|
||||||
param.Value = stream.ReadVariant();
|
|
||||||
|
|
||||||
// Meta
|
|
||||||
param.Meta.Load(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -253,7 +207,26 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
foreach (var parameter in parameters)
|
foreach (var parameter in parameters)
|
||||||
{
|
{
|
||||||
var surfaceParameter = surfaceParameters?.FirstOrDefault(x => x.ID == parameter.ParameterID);
|
var parameterId = parameter.ParameterID;
|
||||||
|
var surfaceParameter = surfaceParameters.FirstOrDefault(x => x.ID == parameterId);
|
||||||
|
if (surfaceParameter == null)
|
||||||
|
{
|
||||||
|
// Permutate original parameter ID to reflect logic in MaterialGenerator::prepareLayer used for nested layers
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
var raw = parameterId;
|
||||||
|
var interop = *(FlaxEngine.Json.JsonSerializer.GuidInterop*)&raw;
|
||||||
|
interop.A -= (uint)(i * 17 + 13);
|
||||||
|
parameterId = *(Guid*)&interop;
|
||||||
|
}
|
||||||
|
surfaceParameter = surfaceParameters.FirstOrDefault(x => x.ID == parameterId);
|
||||||
|
}
|
||||||
|
if (surfaceParameter != null)
|
||||||
|
{
|
||||||
|
// Reorder so it won't be picked by other parameter that uses the same ID (eg. params from duplicated materials used as layers in other material)
|
||||||
|
surfaceParameters.Remove(surfaceParameter);
|
||||||
|
surfaceParameters.Add(surfaceParameter);
|
||||||
|
}
|
||||||
var attributes = surfaceParameter?.Meta.GetAttributes() ?? FlaxEngine.Utils.GetEmptyArray<Attribute>();
|
var attributes = surfaceParameter?.Meta.GetAttributes() ?? FlaxEngine.Utils.GetEmptyArray<Attribute>();
|
||||||
data[i] = new GraphParameterData(null, parameter.Name, parameter.IsPublic, ToType(parameter.ParameterType), attributes, parameter);
|
data[i] = new GraphParameterData(null, parameter.Name, parameter.IsPublic, ToType(parameter.ParameterType), attributes, parameter);
|
||||||
i++;
|
i++;
|
||||||
@@ -587,5 +560,33 @@ namespace FlaxEditor.Surface
|
|||||||
return true;
|
return true;
|
||||||
return AreScriptTypesEqualInner(left, right) || AreScriptTypesEqualInner(right, left);
|
return AreScriptTypesEqualInner(left, right) || AreScriptTypesEqualInner(right, left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static void PerformCommonSetup(Windows.Assets.AssetEditorWindow window, ToolStrip toolStrip, VisjectSurface surface,
|
||||||
|
out ToolStripButton saveButton, out ToolStripButton undoButton, out ToolStripButton redoButton)
|
||||||
|
{
|
||||||
|
var editor = window.Editor;
|
||||||
|
var interfaceOptions = editor.Options.Options.Interface;
|
||||||
|
var inputOptions = editor.Options.Options.Input;
|
||||||
|
var undo = surface.Undo;
|
||||||
|
|
||||||
|
// Toolstrip
|
||||||
|
saveButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Save64, window.Save).LinkTooltip("Save");
|
||||||
|
toolStrip.AddSeparator();
|
||||||
|
undoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Undo64, undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||||
|
redoButton = (ToolStripButton)toolStrip.AddButton(editor.Icons.Redo64, undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||||
|
toolStrip.AddSeparator();
|
||||||
|
toolStrip.AddButton(editor.Icons.Search64, editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
||||||
|
toolStrip.AddButton(editor.Icons.CenterView64, surface.ShowWholeGraph).LinkTooltip("Show whole graph");
|
||||||
|
var gridSnapButton = toolStrip.AddButton(editor.Icons.Grid32, surface.ToggleGridSnapping);
|
||||||
|
gridSnapButton.LinkTooltip("Toggle grid snapping for nodes.");
|
||||||
|
gridSnapButton.AutoCheck = true;
|
||||||
|
gridSnapButton.Checked = surface.GridSnappingEnabled = interfaceOptions.SurfaceGridSnapping;
|
||||||
|
surface.GridSnappingSize = interfaceOptions.SurfaceGridSnappingSize;
|
||||||
|
|
||||||
|
// Setup input actions
|
||||||
|
window.InputActions.Add(options => options.Undo, undo.PerformUndo);
|
||||||
|
window.InputActions.Add(options => options.Redo, undo.PerformRedo);
|
||||||
|
window.InputActions.Add(options => options.Search, editor.ContentFinding.ShowSearch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ namespace FlaxEditor.Surface
|
|||||||
_cmCopyButton = menu.AddButton("Copy", Copy);
|
_cmCopyButton = menu.AddButton("Copy", Copy);
|
||||||
menu.AddButton("Paste", Paste).Enabled = CanEdit && CanPaste();
|
menu.AddButton("Paste", Paste).Enabled = CanEdit && CanPaste();
|
||||||
_cmDuplicateButton = menu.AddButton("Duplicate", Duplicate);
|
_cmDuplicateButton = menu.AddButton("Duplicate", Duplicate);
|
||||||
_cmDuplicateButton.Enabled = CanEdit;
|
_cmDuplicateButton.Enabled = CanEdit && selection.Any(node => (node.Archetype.Flags & NodeFlags.NoSpawnViaPaste) == 0);
|
||||||
var canRemove = CanEdit && selection.All(node => (node.Archetype.Flags & NodeFlags.NoRemove) == 0);
|
var canRemove = CanEdit && selection.All(node => (node.Archetype.Flags & NodeFlags.NoRemove) == 0);
|
||||||
menu.AddButton("Cut", Cut).Enabled = canRemove;
|
menu.AddButton("Cut", Cut).Enabled = canRemove;
|
||||||
menu.AddButton("Delete", Delete).Enabled = canRemove;
|
menu.AddButton("Delete", Delete).Enabled = canRemove;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.Options;
|
using FlaxEditor.Options;
|
||||||
@@ -24,6 +25,7 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
private string _currentInputText = string.Empty;
|
private string _currentInputText = string.Empty;
|
||||||
private Float2 _movingNodesDelta;
|
private Float2 _movingNodesDelta;
|
||||||
|
private Float2 _gridRoundingDelta;
|
||||||
private HashSet<SurfaceNode> _movingNodes;
|
private HashSet<SurfaceNode> _movingNodes;
|
||||||
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
|
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
|
||||||
|
|
||||||
@@ -189,6 +191,22 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snaps a coordinate point to the grid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The point to be rounded.</param>
|
||||||
|
/// <param name="ceil">Round to ceiling instead?</param>
|
||||||
|
/// <returns>Rounded coordinate.</returns>
|
||||||
|
public Float2 SnapToGrid(Float2 point, bool ceil = false)
|
||||||
|
{
|
||||||
|
float gridSize = GridSnappingSize;
|
||||||
|
Float2 snapped = point.Absolute / gridSize;
|
||||||
|
snapped = ceil ? Float2.Ceil(snapped) : Float2.Floor(snapped);
|
||||||
|
snapped.X = (float)Math.CopySign(snapped.X * gridSize, point.X);
|
||||||
|
snapped.Y = (float)Math.CopySign(snapped.Y * gridSize, point.Y);
|
||||||
|
return snapped;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnMouseEnter(Float2 location)
|
public override void OnMouseEnter(Float2 location)
|
||||||
{
|
{
|
||||||
@@ -256,18 +274,39 @@ namespace FlaxEditor.Surface
|
|||||||
// Moving
|
// Moving
|
||||||
else if (_isMovingSelection)
|
else if (_isMovingSelection)
|
||||||
{
|
{
|
||||||
|
var gridSnap = GridSnappingEnabled;
|
||||||
|
if (!gridSnap)
|
||||||
|
_gridRoundingDelta = Float2.Zero; // Reset in case user toggled option between frames.
|
||||||
|
|
||||||
// Calculate delta (apply view offset)
|
// Calculate delta (apply view offset)
|
||||||
var viewDelta = _rootControl.Location - _movingSelectionViewPos;
|
var viewDelta = _rootControl.Location - _movingSelectionViewPos;
|
||||||
_movingSelectionViewPos = _rootControl.Location;
|
_movingSelectionViewPos = _rootControl.Location;
|
||||||
var delta = location - _leftMouseDownPos - viewDelta;
|
var delta = location - _leftMouseDownPos - viewDelta + _gridRoundingDelta;
|
||||||
if (delta.LengthSquared > 0.01f)
|
var deltaLengthSquared = delta.LengthSquared;
|
||||||
|
|
||||||
|
delta /= _targetScale;
|
||||||
|
if ((!gridSnap || Mathf.Abs(delta.X) >= GridSnappingSize || (Mathf.Abs(delta.Y) >= GridSnappingSize))
|
||||||
|
&& deltaLengthSquared > 0.01f)
|
||||||
{
|
{
|
||||||
// Move selected nodes
|
if (gridSnap)
|
||||||
delta /= _targetScale;
|
{
|
||||||
|
Float2 unroundedDelta = delta;
|
||||||
|
delta = SnapToGrid(unroundedDelta);
|
||||||
|
_gridRoundingDelta = (unroundedDelta - delta) * _targetScale; // Standardize unit of the rounding delta, in case user zooms between node movements.
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var node in _movingNodes)
|
foreach (var node in _movingNodes)
|
||||||
|
{
|
||||||
|
if (gridSnap)
|
||||||
|
{
|
||||||
|
Float2 unroundedLocation = node.Location;
|
||||||
|
node.Location = SnapToGrid(unroundedLocation);
|
||||||
|
}
|
||||||
node.Location += delta;
|
node.Location += delta;
|
||||||
|
}
|
||||||
|
|
||||||
_leftMouseDownPos = location;
|
_leftMouseDownPos = location;
|
||||||
_movingNodesDelta += delta;
|
_movingNodesDelta += delta; // TODO: Figure out how to handle undo for differing values of _gridRoundingDelta between selected nodes. For now it will be a small error in undo.
|
||||||
if (_movingNodes.Count > 0)
|
if (_movingNodes.Count > 0)
|
||||||
{
|
{
|
||||||
Cursor = CursorType.SizeAll;
|
Cursor = CursorType.SizeAll;
|
||||||
|
|||||||
@@ -62,7 +62,8 @@ namespace FlaxEditor.Surface
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The method calls the <see cref="ISurfaceContext.SurfaceData"/> setter to assign the result bytes. Sets null value if failed.
|
/// The method calls the <see cref="ISurfaceContext.SurfaceData"/> setter to assign the result bytes. Sets null value if failed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public virtual void Save()
|
/// <returns>True if failed, otherwise false.</returns>
|
||||||
|
public virtual bool Save()
|
||||||
{
|
{
|
||||||
var wasEdited = IsEdited;
|
var wasEdited = IsEdited;
|
||||||
|
|
||||||
@@ -71,19 +72,16 @@ namespace FlaxEditor.Surface
|
|||||||
_context.CachedSurfaceMeta.Scale = ViewScale;
|
_context.CachedSurfaceMeta.Scale = ViewScale;
|
||||||
|
|
||||||
// Save context (and every modified child context)
|
// Save context (and every modified child context)
|
||||||
bool failed = RootContext.Save();
|
if (RootContext.Save())
|
||||||
|
return true;
|
||||||
if (failed)
|
|
||||||
{
|
|
||||||
// Error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear flag
|
// Clear flag
|
||||||
if (wasEdited)
|
if (wasEdited)
|
||||||
{
|
{
|
||||||
Owner.OnSurfaceEditedChanged();
|
Owner.OnSurfaceEditedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,29 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected SurfaceRootControl _rootControl;
|
protected SurfaceRootControl _rootControl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is grid snapping enabled for this surface?
|
||||||
|
/// </summary>
|
||||||
|
public bool GridSnappingEnabled
|
||||||
|
{
|
||||||
|
get => _gridSnappingEnabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_gridSnappingEnabled = value;
|
||||||
|
_gridRoundingDelta = Float2.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The size of the snapping grid.
|
||||||
|
/// </summary>
|
||||||
|
public float GridSnappingSize = 20f;
|
||||||
|
|
||||||
private float _targetScale = 1.0f;
|
private float _targetScale = 1.0f;
|
||||||
private float _moveViewWithMouseDragSpeed = 1.0f;
|
private float _moveViewWithMouseDragSpeed = 1.0f;
|
||||||
private bool _canEdit = true;
|
private bool _canEdit = true;
|
||||||
private readonly bool _supportsDebugging;
|
private readonly bool _supportsDebugging;
|
||||||
private bool _isReleasing;
|
private bool _isReleasing, _gridSnappingEnabled;
|
||||||
private VisjectCM _activeVisjectCM;
|
private VisjectCM _activeVisjectCM;
|
||||||
private GroupArchetype _customNodesGroup;
|
private GroupArchetype _customNodesGroup;
|
||||||
private List<NodeArchetype> _customNodes;
|
private List<NodeArchetype> _customNodes;
|
||||||
@@ -632,6 +650,11 @@ namespace FlaxEditor.Surface
|
|||||||
SelectionChanged?.Invoke();
|
SelectionChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void ToggleGridSnapping()
|
||||||
|
{
|
||||||
|
GridSnappingEnabled = !GridSnappingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects all the nodes.
|
/// Selects all the nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -891,9 +891,21 @@ namespace FlaxEditor.Surface
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Tabs _tabs;
|
protected Tabs _tabs;
|
||||||
|
|
||||||
private readonly ToolStripButton _saveButton;
|
/// <summary>
|
||||||
private readonly ToolStripButton _undoButton;
|
/// Save button on a toolstrip.
|
||||||
private readonly ToolStripButton _redoButton;
|
/// </summary>
|
||||||
|
protected ToolStripButton _saveButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Undo button on a toolstrip.
|
||||||
|
/// </summary>
|
||||||
|
protected ToolStripButton _undoButton;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Redo button on a toolstrip.
|
||||||
|
/// </summary>
|
||||||
|
protected ToolStripButton _redoButton;
|
||||||
|
|
||||||
private bool _showWholeGraphOnLoad = true;
|
private bool _showWholeGraphOnLoad = true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -950,8 +962,6 @@ namespace FlaxEditor.Surface
|
|||||||
protected VisjectSurfaceWindow(Editor editor, AssetItem item, bool useTabs = false)
|
protected VisjectSurfaceWindow(Editor editor, AssetItem item, bool useTabs = false)
|
||||||
: base(editor, item)
|
: base(editor, item)
|
||||||
{
|
{
|
||||||
var inputOptions = Editor.Options.Options.Input;
|
|
||||||
|
|
||||||
// Undo
|
// Undo
|
||||||
_undo = new FlaxEditor.Undo();
|
_undo = new FlaxEditor.Undo();
|
||||||
_undo.UndoDone += OnUndoRedo;
|
_undo.UndoDone += OnUndoRedo;
|
||||||
@@ -998,20 +1008,6 @@ namespace FlaxEditor.Surface
|
|||||||
_propertiesEditor.Panel.Parent = _split2.Panel2;
|
_propertiesEditor.Panel.Parent = _split2.Panel2;
|
||||||
}
|
}
|
||||||
_propertiesEditor.Modified += OnPropertyEdited;
|
_propertiesEditor.Modified += OnPropertyEdited;
|
||||||
|
|
||||||
// Toolstrip
|
|
||||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
|
||||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
|
||||||
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
|
||||||
|
|
||||||
// Setup input actions
|
|
||||||
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
|
||||||
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
|
||||||
InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUndoRedo(IUndoAction action)
|
private void OnUndoRedo(IUndoAction action)
|
||||||
|
|||||||
@@ -1427,6 +1427,26 @@ namespace FlaxEditor.Viewport
|
|||||||
return new Ray(nearPoint + viewOrigin, Vector3.Normalize(farPoint - nearPoint));
|
return new Ray(nearPoint + viewOrigin, Vector3.Normalize(farPoint - nearPoint));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Projects the point from 3D world-space to viewport coordinates.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="worldSpaceLocation">The input world-space location (XYZ in world).</param>
|
||||||
|
/// <param name="viewportSpaceLocation">The output viewport window coordinates (XY in screen pixels).</param>
|
||||||
|
public void ProjectPoint(Vector3 worldSpaceLocation, out Float2 viewportSpaceLocation)
|
||||||
|
{
|
||||||
|
viewportSpaceLocation = Float2.Minimum;
|
||||||
|
var viewport = new FlaxEngine.Viewport(0, 0, Width, Height);
|
||||||
|
if (viewport.Width < Mathf.Epsilon || viewport.Height < Mathf.Epsilon)
|
||||||
|
return;
|
||||||
|
Vector3 viewOrigin = Task.View.Origin;
|
||||||
|
Float3 position = ViewPosition - viewOrigin;
|
||||||
|
CreateProjectionMatrix(out var p);
|
||||||
|
CreateViewMatrix(position, out var v);
|
||||||
|
Matrix.Multiply(ref v, ref p, out var vp);
|
||||||
|
viewport.Project(ref worldSpaceLocation, ref vp, out var projected);
|
||||||
|
viewportSpaceLocation = new Float2((float)projected.X, (float)projected.Y);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when mouse control begins.
|
/// Called when mouse control begins.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_surface.ContextChanged += OnSurfaceContextChanged;
|
_surface.ContextChanged += OnSurfaceContextChanged;
|
||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_showNodesButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Bone64, () => _preview.ShowNodes = !_preview.ShowNodes).LinkTooltip("Show animated model nodes debug view");
|
_showNodesButton = (ToolStripButton)_toolstrip.AddButton(editor.Icons.Bone64, () => _preview.ShowNodes = !_preview.ShowNodes).LinkTooltip("Show animated model nodes debug view");
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/anim-graph/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/animation/anim-graph/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
@@ -295,6 +296,15 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
base.SetParameter(index, value);
|
base.SetParameter(index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the base model of the animation graph this window is editing.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="baseModel">The new base model.</param>
|
||||||
|
public void SetBaseModel(SkinnedModel baseModel)
|
||||||
|
{
|
||||||
|
_properties.BaseModel = baseModel;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void UnlinkItem()
|
protected override void UnlinkItem()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -172,13 +172,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_knowledgePropertiesEditor.Panel.Parent = _split2.Panel2;
|
_knowledgePropertiesEditor.Panel.Parent = _split2.Panel2;
|
||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
|
||||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
|
||||||
_toolstrip.AddButton(editor.Icons.CenterView64, _surface.ShowWholeGraph).LinkTooltip("Show whole graph");
|
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/ai/behavior-trees/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/ai/behavior-trees/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
// Debug behavior picker
|
// Debug behavior picker
|
||||||
@@ -206,11 +201,6 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_behaviorPicker.CheckValid = OnBehaviorPickerCheckValid;
|
_behaviorPicker.CheckValid = OnBehaviorPickerCheckValid;
|
||||||
_behaviorPicker.ValueChanged += OnBehaviorPickerValueChanged;
|
_behaviorPicker.ValueChanged += OnBehaviorPickerValueChanged;
|
||||||
|
|
||||||
// Setup input actions
|
|
||||||
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
|
||||||
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
|
||||||
InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
|
|
||||||
|
|
||||||
SetCanEdit(!Editor.IsPlayMode);
|
SetCanEdit(!Editor.IsPlayMode);
|
||||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||||
}
|
}
|
||||||
@@ -430,8 +420,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
private bool SaveSurface()
|
private bool SaveSurface()
|
||||||
{
|
{
|
||||||
_surface.Save();
|
return _surface.Save();
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetCanEdit(bool canEdit)
|
private void SetCanEdit(bool canEdit)
|
||||||
|
|||||||
@@ -242,8 +242,11 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
private void OpenOptionsContextMenu()
|
private void OpenOptionsContextMenu()
|
||||||
{
|
{
|
||||||
if (_optionsCM != null && _optionsCM.ContainsFocus)
|
if (_optionsCM != null)
|
||||||
return;
|
{
|
||||||
|
_optionsCM.Hide();
|
||||||
|
_optionsCM.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
_optionsCM = new ContextMenu();
|
_optionsCM = new ContextMenu();
|
||||||
_optionsCM.AddButton("Copy type name", () => Clipboard.Text = Asset.DataTypeName);
|
_optionsCM.AddButton("Copy type name", () => Clipboard.Text = Asset.DataTypeName);
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ using FlaxEditor.CustomEditors;
|
|||||||
using FlaxEditor.CustomEditors.Editors;
|
using FlaxEditor.CustomEditors.Editors;
|
||||||
using FlaxEditor.CustomEditors.GUI;
|
using FlaxEditor.CustomEditors.GUI;
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Surface;
|
using FlaxEditor.Surface;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
using FlaxEngine.GUI;
|
using FlaxEngine.GUI;
|
||||||
|
using FlaxEngine.Utilities;
|
||||||
|
|
||||||
namespace FlaxEditor.Windows.Assets
|
namespace FlaxEditor.Windows.Assets
|
||||||
{
|
{
|
||||||
@@ -247,21 +249,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
if (parameters.Length == 0)
|
if (parameters.Length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Utility buttons
|
|
||||||
{
|
|
||||||
var buttons = layout.CustomContainer<UniformGridPanel>();
|
|
||||||
var gridControl = buttons.CustomControl;
|
|
||||||
gridControl.ClipChildren = false;
|
|
||||||
gridControl.Height = Button.DefaultHeight;
|
|
||||||
gridControl.SlotsHorizontally = 2;
|
|
||||||
gridControl.SlotsVertically = 1;
|
|
||||||
var rebuildButton = buttons.Button("Remove overrides", "Unchecks all overrides for parameters.").Button;
|
|
||||||
rebuildButton.Clicked += OnRemoveOverrides;
|
|
||||||
var removeButton = buttons.Button("Override all", "Checks all parameters overrides.").Button;
|
|
||||||
removeButton.Clicked += OnOverrideAll;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parametersGroup = SurfaceUtils.InitGraphParametersGroup(layout);
|
var parametersGroup = SurfaceUtils.InitGraphParametersGroup(layout);
|
||||||
|
var settingButton = parametersGroup.AddSettingsButton();
|
||||||
|
settingButton.Clicked += (image, button) => OnSettingsButtonClicked(image, button, proxy.Window);
|
||||||
var baseMaterial = materialInstance.BaseMaterial;
|
var baseMaterial = materialInstance.BaseMaterial;
|
||||||
var material = baseMaterial;
|
var material = baseMaterial;
|
||||||
if (material)
|
if (material)
|
||||||
@@ -323,6 +313,19 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
itemLayout.Property(label, valueContainer, null, e.Tooltip?.Text);
|
itemLayout.Property(label, valueContainer, null, e.Tooltip?.Text);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnSettingsButtonClicked(Image image, MouseButton mouseButton, MaterialInstanceWindow window)
|
||||||
|
{
|
||||||
|
if (mouseButton != MouseButton.Left)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var cm = new ContextMenu();
|
||||||
|
if (window != null)
|
||||||
|
cm.AddButton("Revert All Parameters", window.OnRevertAllParameters).TooltipText = "Reverts all the overridden parameters to the default values.";
|
||||||
|
cm.AddButton("Override All Parameters", OnOverrideAll).TooltipText = "Checks all parameters overrides.";
|
||||||
|
cm.AddButton("Remove Parameter Overrides", OnRemoveOverrides).TooltipText = "Unchecks all overrides for parameters.";
|
||||||
|
cm.Show(image, image.Size);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnRemoveOverrides()
|
private void OnRemoveOverrides()
|
||||||
{
|
{
|
||||||
@@ -389,8 +392,6 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
||||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(Editor.Icons.Rotate64, OnRevertAllParameters).LinkTooltip("Revert all the parameters to the default values");
|
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/instanced-materials/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/instanced-materials/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
|
|
||||||
// Split Panel
|
// Split Panel
|
||||||
|
|||||||
@@ -257,8 +257,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
|
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
|
||||||
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/graphics/materials/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -141,8 +141,9 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_toolstrip.AddSeparator();
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
|
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
|
||||||
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using FlaxEditor.Content;
|
|||||||
using FlaxEditor.Content.Import;
|
using FlaxEditor.Content.Import;
|
||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.CustomEditors.Editors;
|
using FlaxEditor.CustomEditors.Editors;
|
||||||
|
using FlaxEditor.CustomEditors.Elements;
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
using FlaxEditor.GUI.ContextMenu;
|
using FlaxEditor.GUI.ContextMenu;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
@@ -133,10 +134,21 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
group.Panel.Tag = i;
|
group.Panel.Tag = i;
|
||||||
group.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
|
group.Panel.MouseButtonRightClicked += OnGroupPanelMouseButtonRightClicked;
|
||||||
group.Object(new ListValueContainer(elementType, i, Values));
|
group.Object(new ListValueContainer(elementType, i, Values));
|
||||||
|
|
||||||
|
var stringNameElement = group.Editors[0].ChildrenEditors.Find(x => x is StringEditor).Layout.Children.Find(x => x is TextBoxElement) as TextBoxElement;
|
||||||
|
if (stringNameElement != null)
|
||||||
|
{
|
||||||
|
stringNameElement.TextBox.TextBoxEditEnd += (textbox) => OnNameChanged(group.Panel, (TextBox)textbox);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnNameChanged(DropPanel panel, TextBox textbox)
|
||||||
|
{
|
||||||
|
panel.HeaderText = textbox.Text;
|
||||||
|
}
|
||||||
|
|
||||||
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Float2 location)
|
private void OnGroupPanelMouseButtonRightClicked(DropPanel groupPanel, Float2 location)
|
||||||
{
|
{
|
||||||
var menu = new ContextMenu();
|
var menu = new ContextMenu();
|
||||||
|
|||||||
@@ -70,13 +70,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_undo.ActionDone += OnUndoRedo;
|
_undo.ActionDone += OnUndoRedo;
|
||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
|
||||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
|
||||||
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
|
||||||
|
|
||||||
// Panel
|
// Panel
|
||||||
_panel = new Panel(ScrollBars.None)
|
_panel = new Panel(ScrollBars.None)
|
||||||
@@ -85,11 +79,6 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
Offsets = new Margin(0, 0, _toolstrip.Bottom, 0),
|
Offsets = new Margin(0, 0, _toolstrip.Bottom, 0),
|
||||||
Parent = this
|
Parent = this
|
||||||
};
|
};
|
||||||
|
|
||||||
// Setup input actions
|
|
||||||
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
|
||||||
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
|
||||||
InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUndoRedo(IUndoAction action)
|
private void OnUndoRedo(IUndoAction action)
|
||||||
|
|||||||
@@ -597,13 +597,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_propertiesEditor.Select(_properties);
|
_propertiesEditor.Select(_properties);
|
||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
_saveButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Save64, Save).LinkTooltip("Save");
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_undoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Undo64, _undo.PerformUndo).LinkTooltip($"Undo ({inputOptions.Undo})");
|
|
||||||
_redoButton = (ToolStripButton)_toolstrip.AddButton(Editor.Icons.Redo64, _undo.PerformRedo).LinkTooltip($"Redo ({inputOptions.Redo})");
|
|
||||||
_toolstrip.AddSeparator();
|
|
||||||
_toolstrip.AddButton(Editor.Icons.Search64, Editor.ContentFinding.ShowSearch).LinkTooltip($"Open content search tool ({inputOptions.Search})");
|
|
||||||
_toolstrip.AddButton(editor.Icons.CenterView64, ShowWholeGraph).LinkTooltip("Show whole graph");
|
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/visual/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/scripting/visual/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
_debugToolstripControls = new[]
|
_debugToolstripControls = new[]
|
||||||
@@ -643,9 +637,6 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
debugObjectPickerContainer.Parent = _toolstrip;
|
debugObjectPickerContainer.Parent = _toolstrip;
|
||||||
|
|
||||||
// Setup input actions
|
// Setup input actions
|
||||||
InputActions.Add(options => options.Undo, _undo.PerformUndo);
|
|
||||||
InputActions.Add(options => options.Redo, _undo.PerformRedo);
|
|
||||||
InputActions.Add(options => options.Search, Editor.ContentFinding.ShowSearch);
|
|
||||||
InputActions.Add(options => options.DebuggerContinue, OnDebuggerContinue);
|
InputActions.Add(options => options.DebuggerContinue, OnDebuggerContinue);
|
||||||
InputActions.Add(options => options.DebuggerStepOver, OnDebuggerStepOver);
|
InputActions.Add(options => options.DebuggerStepOver, OnDebuggerStepOver);
|
||||||
InputActions.Add(options => options.DebuggerStepOut, OnDebuggerStepOut);
|
InputActions.Add(options => options.DebuggerStepOut, OnDebuggerStepOut);
|
||||||
@@ -1202,7 +1193,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
private bool SaveSurface()
|
private bool SaveSurface()
|
||||||
{
|
{
|
||||||
_surface.Save();
|
if (_surface.Save())
|
||||||
|
return true;
|
||||||
|
|
||||||
// Reselect actors to prevent issues after Visual Script properties were modified
|
// Reselect actors to prevent issues after Visual Script properties were modified
|
||||||
Editor.Windows.PropertiesWin.Presenter.BuildLayoutOnUpdate();
|
Editor.Windows.PropertiesWin.Presenter.BuildLayoutOnUpdate();
|
||||||
|
|||||||
@@ -1421,6 +1421,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
const auto cData = node->Values[4 + c * 2].AsFloat4();
|
const auto cData = node->Values[4 + c * 2].AsFloat4();
|
||||||
|
|
||||||
// Get triangle coords
|
// Get triangle coords
|
||||||
|
byte anims[3] = { a, b, c };
|
||||||
Float2 points[3] = {
|
Float2 points[3] = {
|
||||||
Float2(aData.X, aData.Y),
|
Float2(aData.X, aData.Y),
|
||||||
Float2(bData.X, bData.Y),
|
Float2(bData.X, bData.Y),
|
||||||
@@ -1534,18 +1535,11 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
bestPoint = closest;
|
bestPoint = closest;
|
||||||
hasBest = true;
|
hasBest = true;
|
||||||
|
|
||||||
float d = Float2::Distance(s[0], s[1]);
|
const float d = Float2::Distance(s[0], s[1]);
|
||||||
if (Math::IsZero(d))
|
bestWeight = d < ANIM_GRAPH_BLEND_THRESHOLD ? 0 : Float2::Distance(s[0], closest) / d;
|
||||||
{
|
|
||||||
bestWeight = 0;
|
bestAnims[0] = anims[j];
|
||||||
}
|
bestAnims[1] = anims[(j + 1) % 3];
|
||||||
else
|
|
||||||
{
|
|
||||||
bestWeight = Float2::Distance(s[0], closest) / d;
|
|
||||||
}
|
|
||||||
|
|
||||||
bestAnims[0] = j;
|
|
||||||
bestAnims[1] = (j + 1) % 3;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1471,7 +1471,8 @@ Asset::LoadResult VisualScript::load()
|
|||||||
for (int32 i = 0; i < count; i++)
|
for (int32 i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
const int32 oldIndex = _oldParamsLayout.Find(Graph.Parameters[i].Identifier);
|
const int32 oldIndex = _oldParamsLayout.Find(Graph.Parameters[i].Identifier);
|
||||||
instanceParams[i] = oldIndex != -1 ? valuesCache[oldIndex] : Graph.Parameters[i].Value;
|
const bool useOldValue = oldIndex != -1 && valuesCache[oldIndex] != _oldParamsValues[i];
|
||||||
|
instanceParams[i] = useOldValue ? valuesCache[oldIndex] : Graph.Parameters[i].Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1486,6 +1487,8 @@ Asset::LoadResult VisualScript::load()
|
|||||||
instanceParams[i] = Graph.Parameters[i].Value;
|
instanceParams[i] = Graph.Parameters[i].Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_oldParamsLayout.Clear();
|
||||||
|
_oldParamsValues.Clear();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -1499,15 +1502,18 @@ void VisualScript::unload(bool isReloading)
|
|||||||
{
|
{
|
||||||
// Cache existing instanced parameters IDs to restore values after asset reload (params order might be changed but the IDs are stable)
|
// Cache existing instanced parameters IDs to restore values after asset reload (params order might be changed but the IDs are stable)
|
||||||
_oldParamsLayout.Resize(Graph.Parameters.Count());
|
_oldParamsLayout.Resize(Graph.Parameters.Count());
|
||||||
|
_oldParamsValues.Resize(Graph.Parameters.Count());
|
||||||
for (int32 i = 0; i < Graph.Parameters.Count(); i++)
|
for (int32 i = 0; i < Graph.Parameters.Count(); i++)
|
||||||
{
|
{
|
||||||
auto& param = Graph.Parameters[i];
|
auto& param = Graph.Parameters[i];
|
||||||
_oldParamsLayout[i] = param.Identifier;
|
_oldParamsLayout[i] = param.Identifier;
|
||||||
|
_oldParamsValues[i] = param.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_oldParamsLayout.Clear();
|
_oldParamsLayout.Clear();
|
||||||
|
_oldParamsValues.Clear();
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
_instances.Clear();
|
_instances.Clear();
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ private:
|
|||||||
Array<Field, InlinedAllocation<32>> _fields;
|
Array<Field, InlinedAllocation<32>> _fields;
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
Array<Guid> _oldParamsLayout;
|
Array<Guid> _oldParamsLayout;
|
||||||
|
Array<Variant> _oldParamsValues;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
#if FLAX_EDITOR
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Globalization;
|
||||||
|
#endif
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace FlaxEngine
|
namespace FlaxEngine
|
||||||
@@ -11,6 +15,7 @@ namespace FlaxEngine
|
|||||||
/// <typeparam name="T">Type of the asset instance type.</typeparam>
|
/// <typeparam name="T">Type of the asset instance type.</typeparam>
|
||||||
#if FLAX_EDITOR
|
#if FLAX_EDITOR
|
||||||
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.AssetRefEditor))]
|
[CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.AssetRefEditor))]
|
||||||
|
[TypeConverter(typeof(TypeConverters.JsonAssetReferenceConverter))]
|
||||||
#endif
|
#endif
|
||||||
[Newtonsoft.Json.JsonConverter(typeof(Json.JsonAssetReferenceConverter))]
|
[Newtonsoft.Json.JsonConverter(typeof(Json.JsonAssetReferenceConverter))]
|
||||||
public struct JsonAssetReference<T> : IComparable, IComparable<JsonAssetReference<T>>, IEquatable<JsonAssetReference<T>>
|
public struct JsonAssetReference<T> : IComparable, IComparable<JsonAssetReference<T>>, IEquatable<JsonAssetReference<T>>
|
||||||
@@ -125,7 +130,7 @@ namespace FlaxEngine
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return Asset?.ToString();
|
return Asset?.ToString() ?? "null";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -141,3 +146,33 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if FLAX_EDITOR
|
||||||
|
namespace FlaxEngine.TypeConverters
|
||||||
|
{
|
||||||
|
internal class JsonAssetReferenceConverter : TypeConverter
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
|
||||||
|
{
|
||||||
|
if (value is string valueStr)
|
||||||
|
{
|
||||||
|
var result = Activator.CreateInstance(destinationType);
|
||||||
|
Json.JsonSerializer.ParseID(valueStr, out var id);
|
||||||
|
var asset = Content.LoadAsync<JsonAsset>(id);
|
||||||
|
destinationType.GetField("Asset").SetValue(result, asset);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return base.ConvertTo(context, culture, value, destinationType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
|
||||||
|
{
|
||||||
|
if (destinationType.Name.StartsWith("JsonAssetReference", StringComparison.Ordinal))
|
||||||
|
return true;
|
||||||
|
return base.CanConvertTo(context, destinationType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -639,7 +639,17 @@ CreateAssetResult ImportModel::CreateAnimation(CreateAssetContext& context, Mode
|
|||||||
|
|
||||||
// Save animation data
|
// Save animation data
|
||||||
MemoryWriteStream stream(8182);
|
MemoryWriteStream stream(8182);
|
||||||
const int32 animIndex = options && options->ObjectIndex != -1 ? options->ObjectIndex : 0; // Single animation per asset
|
int32 animIndex = options ? options->ObjectIndex : -1; // Single animation per asset
|
||||||
|
if (animIndex == -1)
|
||||||
|
{
|
||||||
|
// Pick the longest animation by default (eg. to skip ref pose anim if exported as the first one)
|
||||||
|
animIndex = 0;
|
||||||
|
for (int32 i = 1; i < modelData.Animations.Count(); i++)
|
||||||
|
{
|
||||||
|
if (modelData.Animations[i].GetLength() > modelData.Animations[animIndex].GetLength())
|
||||||
|
animIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (modelData.Pack2AnimationHeader(&stream, animIndex))
|
if (modelData.Pack2AnimationHeader(&stream, animIndex))
|
||||||
return CreateAssetResult::Error;
|
return CreateAssetResult::Error;
|
||||||
if (context.AllocateChunk(0))
|
if (context.AllocateChunk(0))
|
||||||
|
|||||||
@@ -865,6 +865,16 @@ namespace FlaxEngine
|
|||||||
return new Float2(Mathf.Ceil(v.X), Mathf.Ceil(v.Y));
|
return new Float2(Mathf.Ceil(v.X), Mathf.Ceil(v.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the vector with components containing the smallest integer smaller to or equal to the original value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="v">The value.</param>
|
||||||
|
/// <returns>The result.</returns>
|
||||||
|
public static Float2 Floor(Float2 v)
|
||||||
|
{
|
||||||
|
return new Float2(Mathf.Floor(v.X), Mathf.Floor(v.Y));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Breaks the components of the vector into an integral and a fractional part. Returns vector made of fractional parts.
|
/// Breaks the components of the vector into an integral and a fractional part. Returns vector made of fractional parts.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Matrix3x3.h"
|
#include "Matrix3x3.h"
|
||||||
#include "Math.h"
|
#include "Math.h"
|
||||||
#include "../Types/String.h"
|
#include "../Types/String.h"
|
||||||
|
#include "Engine/Core/Math/Transform.h"
|
||||||
|
|
||||||
Quaternion Quaternion::Zero(0, 0, 0, 0);
|
Quaternion Quaternion::Zero(0, 0, 0, 0);
|
||||||
Quaternion Quaternion::One(1, 1, 1, 1);
|
Quaternion Quaternion::One(1, 1, 1, 1);
|
||||||
@@ -537,3 +538,14 @@ void Quaternion::RotationYawPitchRoll(float yaw, float pitch, float roll, Quater
|
|||||||
result.Y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
|
result.Y = sinYawOver2 * cosPitchOver2 * cosRollOver2 - cosYawOver2 * sinPitchOver2 * sinRollOver2;
|
||||||
result.Z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
|
result.Z = cosYawOver2 * cosPitchOver2 * sinRollOver2 - sinYawOver2 * sinPitchOver2 * cosRollOver2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Quaternion Quaternion::GetRotationFromNormal(const Vector3& normal, const Transform& reference)
|
||||||
|
{
|
||||||
|
Float3 up = reference.GetUp();
|
||||||
|
const float dot = Vector3::Dot(normal, up);
|
||||||
|
if (Math::NearEqual(Math::Abs(dot), 1))
|
||||||
|
{
|
||||||
|
up = reference.GetRight();
|
||||||
|
}
|
||||||
|
return Quaternion::LookRotation(normal, up);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1483,6 +1483,45 @@ namespace FlaxEngine
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets rotation from a normal in relation to a transform.<br/>
|
||||||
|
/// This function is especially useful for axis aligned faces,
|
||||||
|
/// and with <seealso cref="Physics.RayCast(Vector3, Vector3, out RayCastHit, float, uint, bool)"/>.
|
||||||
|
///
|
||||||
|
/// <example><para><b>Example code:</b></para>
|
||||||
|
/// <code>
|
||||||
|
/// <see langword="public" /> <see langword="class" /> GetRotationFromNormalExample : <see cref="Script"/><br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||||
|
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||||
|
/// {<br/>
|
||||||
|
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||||
|
/// {<br/>
|
||||||
|
/// <see cref="Vector3"/> position = hit.Collider.Position;
|
||||||
|
/// <see cref="Transform"/> transform = hit.Collider.Transform;
|
||||||
|
/// <see cref="Vector3"/> point = hit.Point;
|
||||||
|
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||||
|
/// <see cref="Quaternion"/> rot = <see cref="Quaternion"/>.GetRotationFromNormal(normal,transform);
|
||||||
|
/// SomeObject.Position = point;
|
||||||
|
/// SomeObject.Orientation = rot;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="normal">The normal vector.</param>
|
||||||
|
/// <param name="reference">The reference transform.</param>
|
||||||
|
/// <returns>The rotation from the normal vector.</returns>
|
||||||
|
public static Quaternion GetRotationFromNormal(Vector3 normal, Transform reference)
|
||||||
|
{
|
||||||
|
Float3 up = reference.Up;
|
||||||
|
var dot = Vector3.Dot(normal, up);
|
||||||
|
if (Mathf.NearEqual(Math.Abs(dot), 1))
|
||||||
|
up = reference.Right;
|
||||||
|
return LookRotation(normal, up);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds two quaternions.
|
/// Adds two quaternions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -660,6 +660,14 @@ public:
|
|||||||
// @param roll The roll of rotation (in radians)
|
// @param roll The roll of rotation (in radians)
|
||||||
// @param result When the method completes, contains the newly created quaternion
|
// @param result When the method completes, contains the newly created quaternion
|
||||||
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result);
|
static void RotationYawPitchRoll(float yaw, float pitch, float roll, Quaternion& result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets rotation from a normal in relation to a transform. This function is especially useful for axis aligned faces, and with <seealso cref="Physics::RayCast"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="normal">The normal vector.</param>
|
||||||
|
/// <param name="reference">The reference transform.</param>
|
||||||
|
/// <returns>The rotation from the normal vector.</returns>
|
||||||
|
static Quaternion GetRotationFromNormal(const Vector3& normal, const Transform& reference);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -252,3 +252,9 @@ void Transform::Lerp(const Transform& t1, const Transform& t2, float amount, Tra
|
|||||||
Quaternion::Slerp(t1.Orientation, t2.Orientation, amount, result.Orientation);
|
Quaternion::Slerp(t1.Orientation, t2.Orientation, amount, result.Orientation);
|
||||||
Float3::Lerp(t1.Scale, t2.Scale, amount, result.Scale);
|
Float3::Lerp(t1.Scale, t2.Scale, amount, result.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Transform Transform::AlignRotationToNormalAndSnapToGrid(const Vector3& point, const Vector3& normal, const Vector3& normalOffset, const Transform& relativeTo, const Vector3& gridSize, const Float3& scale)
|
||||||
|
{
|
||||||
|
Quaternion rot = Quaternion::GetRotationFromNormal(normal, relativeTo);
|
||||||
|
return Transform(Vector3::SnapToGrid(point, gridSize, rot, relativeTo.Translation, normalOffset), rot, scale);
|
||||||
|
}
|
||||||
|
|||||||
@@ -478,6 +478,96 @@ namespace FlaxEngine
|
|||||||
Float3.Lerp(ref start.Scale, ref end.Scale, amount, out result.Scale);
|
Float3.Lerp(ref start.Scale, ref end.Scale, amount, out result.Scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines the functions:<br/>
|
||||||
|
/// <see cref="Vector3.SnapToGrid(FlaxEngine.Vector3,FlaxEngine.Vector3)"/>,<br/>
|
||||||
|
/// <see cref="Quaternion.GetRotationFromNormal"/>.
|
||||||
|
/// <example><para><b>Example code:</b></para>
|
||||||
|
/// <code>
|
||||||
|
/// <see langword="public" /> <see langword="class" /> AlignRotationToObjectAndSnapToGridExample : <see cref="Script"/><br/>
|
||||||
|
/// <see langword="public" /> <see cref="Vector3"/> Offset = new Vector3(0, 0, 50f);<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Vector3"/> GridSize = <see cref="Vector3.One"/> * 20.0f;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||||
|
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||||
|
/// {<br/>
|
||||||
|
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||||
|
/// {<br/>
|
||||||
|
/// <see cref="Transform"/> transform = hit.Collider.Transform;
|
||||||
|
/// <see cref="Vector3"/> point = hit.Point;
|
||||||
|
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||||
|
/// SomeObject.Transform = <see cref="Transform"/>.AlignRotationToNormalAndSnapToGrid
|
||||||
|
/// (
|
||||||
|
/// point,
|
||||||
|
/// normal,
|
||||||
|
/// Offset,
|
||||||
|
/// transform,
|
||||||
|
/// SomeObject.Scale,
|
||||||
|
/// GridSize,
|
||||||
|
/// Float3.One
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The position to snap.</param>
|
||||||
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
|
/// <param name="normalOffset">The local grid offset to apply after snapping.</param>
|
||||||
|
/// <param name="normal">The normal vector.</param>
|
||||||
|
/// <param name="relativeTo">The relative transform.</param>
|
||||||
|
/// <param name="scale">The scale to apply to the transform.</param>
|
||||||
|
/// <returns>The rotated and snapped transform.</returns>
|
||||||
|
public static Transform AlignRotationToNormalAndSnapToGrid(Vector3 point, Vector3 normal, Vector3 normalOffset, Transform relativeTo, Vector3 gridSize, Float3 scale)
|
||||||
|
{
|
||||||
|
Quaternion rot = Quaternion.GetRotationFromNormal(normal, relativeTo);
|
||||||
|
return new Transform(Vector3.SnapToGrid(point, gridSize, rot, relativeTo.Translation, normalOffset), rot, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines the functions:<br/>
|
||||||
|
/// <see cref="Vector3.SnapToGrid(FlaxEngine.Vector3,FlaxEngine.Vector3)"/>,<br/>
|
||||||
|
/// <see cref="Quaternion.GetRotationFromNormal"/>.
|
||||||
|
/// <example><para><b>Example code:</b></para>
|
||||||
|
/// <code>
|
||||||
|
/// <see langword="public" /> <see langword="class" /> AlignRotationToObjectAndSnapToGridExample : <see cref="Script"/><br/>
|
||||||
|
/// <see langword="public" /> <see cref="Vector3"/> Offset = new Vector3(0, 0, 50f);<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Vector3"/> GridSize = <see cref="Vector3.One"/> * 20.0f;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||||
|
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||||
|
/// {<br/>
|
||||||
|
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||||
|
/// {<br/>
|
||||||
|
/// <see cref="Transform"/> transform = hit.Collider.Transform;
|
||||||
|
/// <see cref="Vector3"/> point = hit.Point;
|
||||||
|
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||||
|
/// SomeObject.Transform = <see cref="Transform"/>.AlignRotationToNormalAndSnapToGrid
|
||||||
|
/// (
|
||||||
|
/// point,
|
||||||
|
/// normal,
|
||||||
|
/// Offset,
|
||||||
|
/// transform,
|
||||||
|
/// GridSize
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The position to snap.</param>
|
||||||
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
|
/// <param name="normalOffset">The local grid offset to apply after snapping.</param>
|
||||||
|
/// <param name="normal">The normal vector.</param>
|
||||||
|
/// <param name="relativeTo">The relative transform.</param>
|
||||||
|
/// <returns>The rotated and snapped transform with scale <see cref="Float3.One"/>.</returns>
|
||||||
|
public static Transform AlignRotationToNormalAndSnapToGrid(Vector3 point, Vector3 normal, Vector3 normalOffset, Transform relativeTo, Vector3 gridSize)
|
||||||
|
{
|
||||||
|
return AlignRotationToNormalAndSnapToGrid(point, normal, normalOffset, relativeTo, gridSize, Float3.One);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tests for equality between two objects.
|
/// Tests for equality between two objects.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -291,6 +291,20 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Combines the functions: <br/>
|
||||||
|
/// <see cref="SnapToGrid"/>,<br/>
|
||||||
|
/// <see cref="GetRotationFromNormal"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The position to snap.</param>
|
||||||
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
|
/// <param name="normalOffset">The local grid offset to apply after snapping.</param>
|
||||||
|
/// <param name="normal">The normal vector.</param>
|
||||||
|
/// <param name="relativeTo">The relative transform.</param>
|
||||||
|
/// <param name="scale">The scale to apply to the transform.</param>
|
||||||
|
/// <returns>The rotated and snapped transform.</returns>
|
||||||
|
static Transform AlignRotationToNormalAndSnapToGrid(const Vector3& point, const Vector3& normal, const Vector3& normalOffset, const Transform& relativeTo, const Vector3& gridSize, const Float3& scale = Float3::One);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FORCE_INLINE Transform operator*(const Transform& other) const
|
FORCE_INLINE Transform operator*(const Transform& other) const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -324,6 +324,20 @@ float Float3::Angle(const Float3& from, const Float3& to)
|
|||||||
return Math::Acos(dot);
|
return Math::Acos(dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Float3 Float3::SnapToGrid(const Float3& pos, const Float3& gridSize)
|
||||||
|
{
|
||||||
|
return Float3(Math::Ceil((pos.X - (gridSize.X * 0.5f)) / gridSize.X) * gridSize.X,
|
||||||
|
Math::Ceil((pos.Y - (gridSize.Y * 0.5f)) / gridSize.Y) * gridSize.Y,
|
||||||
|
Math::Ceil((pos.Z - (gridSize.Z * 0.5f)) / gridSize.Z) * gridSize.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Float3 Float3::SnapToGrid(const Float3& point, const Float3& gridSize, const Quaternion& gridOrientation, const Float3& gridOrigin, const Float3& offset)
|
||||||
|
{
|
||||||
|
return (gridOrientation * (gridOrientation.Conjugated() * SnapToGrid(point - gridOrigin, gridSize) + offset)) + gridOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
// Double
|
// Double
|
||||||
|
|
||||||
static_assert(sizeof(Double3) == 24, "Invalid Double3 type size.");
|
static_assert(sizeof(Double3) == 24, "Invalid Double3 type size.");
|
||||||
@@ -638,6 +652,20 @@ double Double3::Angle(const Double3& from, const Double3& to)
|
|||||||
return Math::Acos(dot);
|
return Math::Acos(dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Double3 Double3::SnapToGrid(const Double3& pos, const Double3& gridSize)
|
||||||
|
{
|
||||||
|
return Double3(Math::Ceil((pos.X - (gridSize.X * 0.5)) / gridSize.X) * gridSize.X,
|
||||||
|
Math::Ceil((pos.Y - (gridSize.Y * 0.5)) / gridSize.Y) * gridSize.Y,
|
||||||
|
Math::Ceil((pos.Z - (gridSize.Z * 0.5)) / gridSize.Z) * gridSize.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Double3 Double3::SnapToGrid(const Double3& point, const Double3& gridSize, const Quaternion& gridOrientation, const Double3& gridOrigin, const Double3& offset)
|
||||||
|
{
|
||||||
|
return (gridOrientation * (gridOrientation.Conjugated() * SnapToGrid(point - gridOrigin, gridSize) + offset)) + gridOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
// Int
|
// Int
|
||||||
|
|
||||||
static_assert(sizeof(Int3) == 12, "Invalid Int3 type size.");
|
static_assert(sizeof(Int3) == 12, "Invalid Int3 type size.");
|
||||||
@@ -852,3 +880,17 @@ int32 Int3::Angle(const Int3& from, const Int3& to)
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Int3 Int3::SnapToGrid(const Int3& pos, const Int3& gridSize)
|
||||||
|
{
|
||||||
|
return Int3(((pos.X - (gridSize.X / 2)) / gridSize.X) * gridSize.X,
|
||||||
|
((pos.Y - (gridSize.Y / 2)) / gridSize.Y) * gridSize.Y,
|
||||||
|
((pos.Z - (gridSize.Z / 2)) / gridSize.Z) * gridSize.Z);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
Int3 Int3::SnapToGrid(const Int3& point, const Int3& gridSize, const Quaternion& gridOrientation, const Int3& gridOrigin, const Int3& offset)
|
||||||
|
{
|
||||||
|
return (gridOrientation * (gridOrientation.Conjugated() * SnapToGrid(point - gridOrigin, gridSize) + offset)) + gridOrigin;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1672,7 +1672,7 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Snaps the input position into the grid.
|
/// Snaps the input position onto the grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pos">The position to snap.</param>
|
/// <param name="pos">The position to snap.</param>
|
||||||
/// <param name="gridSize">The size of the grid.</param>
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
@@ -1685,6 +1685,44 @@ namespace FlaxEngine
|
|||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snaps the <paramref name="point"/> onto the rotated grid.<br/>
|
||||||
|
/// For world aligned grid snapping use <b><see cref="SnapToGrid(FlaxEngine.Vector3,FlaxEngine.Vector3)"/></b> instead.
|
||||||
|
/// <example><para><b>Example code:</b></para>
|
||||||
|
/// <code>
|
||||||
|
/// <see langword="public" /> <see langword="class" /> SnapToGridExample : <see cref="Script"/><br/>
|
||||||
|
/// <see langword="public" /> <see cref="Vector3"/> GridSize = <see cref="Vector3.One"/> * 20.0f;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> RayOrigin;<br/>
|
||||||
|
/// <see langword="public" /> <see cref="Actor"/> SomeObject;<br/>
|
||||||
|
/// <see langword="public" /> <see langword="override" /> <see langword="void" /> <see cref="Script.OnFixedUpdate"/><br/>
|
||||||
|
/// {<br/>
|
||||||
|
/// <see langword="if" /> (<see cref="Physics"/>.RayCast(RayOrigin.Position, RayOrigin.Transform.Forward, out <see cref="RayCastHit"/> hit)
|
||||||
|
/// {<br/>
|
||||||
|
/// <see cref="Vector3"/> position = hit.Collider.Position;
|
||||||
|
/// <see cref="FlaxEngine.Transform"/> transform = hit.Collider.Transform;
|
||||||
|
/// <see cref="Vector3"/> point = hit.Point;
|
||||||
|
/// <see cref="Vector3"/> normal = hit.Normal;
|
||||||
|
/// //Get rotation from normal relative to collider transform
|
||||||
|
/// <see cref="Quaternion"/> rot = <see cref="Quaternion"/>.GetRotationFromNormal(normal, transform);
|
||||||
|
/// point = <see cref="Vector3"/>.SnapToGrid(point, GridSize, rot, position);
|
||||||
|
/// SomeObject.Position = point;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// </code>
|
||||||
|
/// </example>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The position to snap.</param>
|
||||||
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
|
/// <param name="gridOrientation">The rotation of the grid.</param>
|
||||||
|
/// <param name="gridOrigin">The center point of the grid.</param>
|
||||||
|
/// <param name="offset">The local position offset applied to the snapped position before grid rotation.</param>
|
||||||
|
/// <returns>The position snapped to the grid.</returns>
|
||||||
|
public static Vector3 SnapToGrid(Vector3 point, Vector3 gridSize, Quaternion gridOrientation, Vector3 gridOrigin, Vector3 offset)
|
||||||
|
{
|
||||||
|
return ((SnapToGrid(point - gridOrigin, gridSize) * gridOrientation.Conjugated() + offset) * gridOrientation) + gridOrigin;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds two vectors.
|
/// Adds two vectors.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -927,6 +927,25 @@ public:
|
|||||||
/// <param name="to">The second vector.</param>
|
/// <param name="to">The second vector.</param>
|
||||||
/// <returns>The angle (in radians).</returns>
|
/// <returns>The angle (in radians).</returns>
|
||||||
static FLAXENGINE_API T Angle(const Vector3Base& from, const Vector3Base& to);
|
static FLAXENGINE_API T Angle(const Vector3Base& from, const Vector3Base& to);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snaps the input position onto the grid.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pos">The position to snap.</param>
|
||||||
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
|
/// <returns>The position snapped to the grid.</returns>
|
||||||
|
static FLAXENGINE_API Vector3Base SnapToGrid(const Vector3Base& pos, const Vector3Base& gridSize);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Snaps the <paramref name="point"/> onto the rotated grid. For world aligned grid snapping use <b><see cref="SnapToGrid"/></b> instead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point">The position to snap.</param>
|
||||||
|
/// <param name="gridSize">The size of the grid.</param>
|
||||||
|
/// <param name="gridOrigin">The center point of the grid.</param>
|
||||||
|
/// <param name="gridOrientation">The rotation of the grid.</param>
|
||||||
|
/// <param name="offset">The local position offset applied to the snapped position before grid rotation.</param>
|
||||||
|
/// <returns>The position snapped to the grid.</returns>
|
||||||
|
static FLAXENGINE_API Vector3Base SnapToGrid(const Vector3Base& point, const Vector3Base& gridSize, const Quaternion& gridOrientation, const Vector3Base& gridOrigin = Zero, const Vector3Base& offset = Zero);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
|||||||
@@ -479,6 +479,10 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr
|
|||||||
batch.DrawCall.InstanceCount = 1;
|
batch.DrawCall.InstanceCount = 1;
|
||||||
auto& firstInstance = batch.Instances[0];
|
auto& firstInstance = batch.Instances[0];
|
||||||
firstInstance.Load(batch.DrawCall);
|
firstInstance.Load(batch.DrawCall);
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
|
||||||
|
batch.DrawCall.Surface.LODDitherFactor = type.ScaleInLightmap; // See LightmapUVsDensityMaterialShader
|
||||||
|
#endif
|
||||||
|
|
||||||
if (EnumHasAnyFlags(drawModes, DrawPass::Forward))
|
if (EnumHasAnyFlags(drawModes, DrawPass::Forward))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,11 +39,13 @@ public:
|
|||||||
|
|
||||||
FORCE_INLINE GPUPipelineState* Get(int index) const
|
FORCE_INLINE GPUPipelineState* Get(int index) const
|
||||||
{
|
{
|
||||||
|
ASSERT_LOW_LAYER(index >= 0 && index < Size);
|
||||||
return States[index];
|
return States[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
FORCE_INLINE GPUPipelineState*& operator[](int32 index)
|
FORCE_INLINE GPUPipelineState*& operator[](int32 index)
|
||||||
{
|
{
|
||||||
|
ASSERT_LOW_LAYER(index >= 0 && index < Size);
|
||||||
return States[index];
|
return States[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +131,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
FORCE_INLINE GPUShaderProgramCS* Get(const int index) const
|
FORCE_INLINE GPUShaderProgramCS* Get(const int index) const
|
||||||
{
|
{
|
||||||
|
ASSERT_LOW_LAYER(index >= 0 && index < Size);
|
||||||
return Shaders[index];
|
return Shaders[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -480,6 +480,8 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
|
|||||||
const ViewMode viewMode = renderContext.View.Mode;
|
const ViewMode viewMode = renderContext.View.Mode;
|
||||||
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
|
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
|
||||||
GBufferPass::AddIndexBufferToModelLOD(_indexBuffer, &((Model*)_model)->LODs[_lodIndex]);
|
GBufferPass::AddIndexBufferToModelLOD(_indexBuffer, &((Model*)_model)->LODs[_lodIndex]);
|
||||||
|
if (viewMode == ViewMode::LightmapUVsDensity)
|
||||||
|
drawCall.Surface.LODDitherFactor = info.LightmapScale; // See LightmapUVsDensityMaterialShader
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Push draw call to the render list
|
// Push draw call to the render list
|
||||||
@@ -541,6 +543,8 @@ void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& in
|
|||||||
const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode;
|
const ViewMode viewMode = renderContextBatch.GetMainContext().View.Mode;
|
||||||
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
|
if (viewMode == ViewMode::LightmapUVsDensity || viewMode == ViewMode::LODPreview)
|
||||||
GBufferPass::AddIndexBufferToModelLOD(_indexBuffer, &((Model*)_model)->LODs[_lodIndex]);
|
GBufferPass::AddIndexBufferToModelLOD(_indexBuffer, &((Model*)_model)->LODs[_lodIndex]);
|
||||||
|
if (viewMode == ViewMode::LightmapUVsDensity)
|
||||||
|
drawCall.Surface.LODDitherFactor = info.LightmapScale; // See LightmapUVsDensityMaterialShader
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Push draw call to the render lists
|
// Push draw call to the render lists
|
||||||
|
|||||||
@@ -241,5 +241,9 @@ public:
|
|||||||
/// The object sorting key.
|
/// The object sorting key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
int8 SortOrder;
|
int8 SortOrder;
|
||||||
|
|
||||||
|
#if USE_EDITOR
|
||||||
|
float LightmapScale = -1.0f;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -52,6 +52,11 @@ API_ENUM() enum class ToneMappingMode
|
|||||||
/// The ACES Filmic reference tonemapper (approximation).
|
/// The ACES Filmic reference tonemapper (approximation).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ACES = 2,
|
ACES = 2,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The AGX tonemapper.
|
||||||
|
/// </summary>
|
||||||
|
AGX = 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -213,13 +213,13 @@ protected:
|
|||||||
const int32 dstMips = dstTexture->MipLevels();
|
const int32 dstMips = dstTexture->MipLevels();
|
||||||
GPUTexture* srcTexture = _streamingTexture->GetTexture();
|
GPUTexture* srcTexture = _streamingTexture->GetTexture();
|
||||||
const int32 srcMips = srcTexture->MipLevels();
|
const int32 srcMips = srcTexture->MipLevels();
|
||||||
|
const int32 srcMissingMips = srcMips - srcTexture->ResidentMipLevels();
|
||||||
const int32 mipCount = Math::Min(dstMips, srcMips);
|
const int32 mipCount = Math::Min(dstMips, srcMips);
|
||||||
ASSERT(mipCount > 0);
|
for (int32 mipIndex = srcMissingMips; mipIndex < mipCount; mipIndex++)
|
||||||
for (int32 mipIndex = 0; mipIndex < mipCount; mipIndex++)
|
|
||||||
{
|
{
|
||||||
context->GPU->CopySubresource(dstTexture, dstMips - mipIndex - 1, srcTexture, srcMips - mipIndex - 1);
|
context->GPU->CopySubresource(dstTexture, dstMips - mipIndex - 1, srcTexture, srcMips - mipIndex - 1);
|
||||||
}
|
}
|
||||||
_uploadedMipCount = mipCount;
|
_uploadedMipCount = mipCount - srcMissingMips;
|
||||||
|
|
||||||
return Result::Ok;
|
return Result::Ok;
|
||||||
}
|
}
|
||||||
@@ -238,10 +238,10 @@ protected:
|
|||||||
|
|
||||||
void OnSync() override
|
void OnSync() override
|
||||||
{
|
{
|
||||||
|
_newTexture->SetResidentMipLevels(_uploadedMipCount);
|
||||||
Swap(_streamingTexture->_texture, _newTexture);
|
Swap(_streamingTexture->_texture, _newTexture);
|
||||||
_streamingTexture->GetTexture()->SetResidentMipLevels(_uploadedMipCount);
|
|
||||||
_streamingTexture->ResidencyChanged();
|
|
||||||
SAFE_DELETE_GPU_RESOURCE(_newTexture);
|
SAFE_DELETE_GPU_RESOURCE(_newTexture);
|
||||||
|
_streamingTexture->ResidencyChanged();
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
GPUTask::OnSync();
|
GPUTask::OnSync();
|
||||||
|
|||||||
@@ -27,32 +27,18 @@ struct AxisEvaluation
|
|||||||
|
|
||||||
struct ActionData
|
struct ActionData
|
||||||
{
|
{
|
||||||
bool Active;
|
bool Active = false;
|
||||||
uint64 FrameIndex;
|
uint64 FrameIndex = 0;
|
||||||
InputActionState State;
|
InputActionState State = InputActionState::Waiting;
|
||||||
|
|
||||||
ActionData()
|
|
||||||
{
|
|
||||||
Active = false;
|
|
||||||
FrameIndex = 0;
|
|
||||||
State = InputActionState::Waiting;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AxisData
|
struct AxisData
|
||||||
{
|
{
|
||||||
float Value;
|
float Value = 0.0f;
|
||||||
float ValueRaw;
|
float ValueRaw = 0.0f;
|
||||||
float PrevKeyValue;
|
float PrevValue = 0.0f;
|
||||||
uint64 FrameIndex;
|
float PrevKeyValue = 0.0f;
|
||||||
|
uint64 FrameIndex = 0;
|
||||||
AxisData()
|
|
||||||
{
|
|
||||||
Value = 0.0f;
|
|
||||||
ValueRaw = 0.0f;
|
|
||||||
PrevKeyValue = 0.0f;
|
|
||||||
FrameIndex = 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace InputImpl
|
namespace InputImpl
|
||||||
@@ -990,6 +976,7 @@ void InputService::Update()
|
|||||||
|
|
||||||
// Setup axis data
|
// Setup axis data
|
||||||
data.PrevKeyValue = e.PrevKeyValue;
|
data.PrevKeyValue = e.PrevKeyValue;
|
||||||
|
data.PrevValue = data.Value;
|
||||||
data.ValueRaw = e.RawValue;
|
data.ValueRaw = e.RawValue;
|
||||||
data.Value = e.Value;
|
data.Value = e.Value;
|
||||||
|
|
||||||
@@ -1025,7 +1012,7 @@ void InputService::Update()
|
|||||||
{
|
{
|
||||||
for (auto i = Axes.Begin(); i.IsNotEnd(); ++i)
|
for (auto i = Axes.Begin(); i.IsNotEnd(); ++i)
|
||||||
{
|
{
|
||||||
if (Math::NotNearEqual(i->Value.Value, i->Value.PrevKeyValue))
|
if (Math::NotNearEqual(i->Value.Value, i->Value.PrevValue))
|
||||||
{
|
{
|
||||||
Input::AxisValueChanged(i->Key);
|
Input::AxisValueChanged(i->Key);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,10 @@ Ray Camera::ConvertMouseToRay(const Float2& mousePosition, const Viewport& viewp
|
|||||||
viewport.Unproject(nearPoint, ivp, nearPoint);
|
viewport.Unproject(nearPoint, ivp, nearPoint);
|
||||||
viewport.Unproject(farPoint, ivp, farPoint);
|
viewport.Unproject(farPoint, ivp, farPoint);
|
||||||
|
|
||||||
return Ray(nearPoint, Vector3::Normalize(farPoint - nearPoint));
|
Vector3 dir = Vector3::Normalize(farPoint - nearPoint);
|
||||||
|
if (dir.IsZero())
|
||||||
|
return Ray::Identity;
|
||||||
|
return Ray(nearPoint, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
Viewport Camera::GetViewport() const
|
Viewport Camera::GetViewport() const
|
||||||
@@ -303,6 +306,8 @@ void Camera::GetMatrices(Matrix& view, Matrix& projection, const Viewport& viewp
|
|||||||
void Camera::OnPreviewModelLoaded()
|
void Camera::OnPreviewModelLoaded()
|
||||||
{
|
{
|
||||||
_previewModelBuffer.Setup(_previewModel.Get());
|
_previewModelBuffer.Setup(_previewModel.Get());
|
||||||
|
if (_previewModelBuffer.Count() > 0)
|
||||||
|
_previewModelBuffer.At(0).ReceiveDecals = false;
|
||||||
|
|
||||||
UpdateCache();
|
UpdateCache();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,35 +150,30 @@ float Spline::GetSplineLength() const
|
|||||||
{
|
{
|
||||||
float sum = 0.0f;
|
float sum = 0.0f;
|
||||||
constexpr int32 slices = 20;
|
constexpr int32 slices = 20;
|
||||||
constexpr float step = 1.0f / (float)slices;
|
constexpr float step = 1.0f / (float)(slices - 1);
|
||||||
Vector3 prevPoint = Vector3::Zero;
|
const Vector3 scale = _transform.Scale;
|
||||||
if (Curve.GetKeyframes().Count() != 0)
|
|
||||||
{
|
|
||||||
const auto& a = Curve[0];
|
|
||||||
prevPoint = a.Value.Translation * _transform.Scale;
|
|
||||||
}
|
|
||||||
for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++)
|
for (int32 i = 1; i < Curve.GetKeyframes().Count(); i++)
|
||||||
{
|
{
|
||||||
const auto& a = Curve[i - 1];
|
const auto& a = Curve[i - 1];
|
||||||
const auto& b = Curve[i];
|
const auto& b = Curve[i];
|
||||||
|
Vector3 prevPoint = a.Value.Translation * scale;
|
||||||
|
|
||||||
const float length = Math::Abs(b.Time - a.Time);
|
const float length = Math::Abs(b.Time - a.Time);
|
||||||
Vector3 leftTangent, rightTangent;
|
Vector3 leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||||
|
|
||||||
// TODO: implement sth more analytical than brute-force solution
|
for (int32 slice = 1; slice < slices; slice++)
|
||||||
for (int32 slice = 0; slice < slices; slice++)
|
|
||||||
{
|
{
|
||||||
const float t = (float)slice * step;
|
const float t = (float)slice * step;
|
||||||
Vector3 pos;
|
Vector3 pos;
|
||||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||||
pos *= _transform.Scale;
|
pos *= scale;
|
||||||
sum += (float)Vector3::DistanceSquared(pos, prevPoint);
|
sum += (float)Vector3::Distance(pos, prevPoint);
|
||||||
prevPoint = pos;
|
prevPoint = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Math::Sqrt(sum);
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Spline::GetSplineSegmentLength(int32 index) const
|
float Spline::GetSplineSegmentLength(int32 index) const
|
||||||
@@ -188,28 +183,28 @@ float Spline::GetSplineSegmentLength(int32 index) const
|
|||||||
CHECK_RETURN(index > 0 && index < GetSplinePointsCount(), 0.0f);
|
CHECK_RETURN(index > 0 && index < GetSplinePointsCount(), 0.0f);
|
||||||
float sum = 0.0f;
|
float sum = 0.0f;
|
||||||
constexpr int32 slices = 20;
|
constexpr int32 slices = 20;
|
||||||
constexpr float step = 1.0f / (float)slices;
|
constexpr float step = 1.0f / (float)(slices - 1);
|
||||||
const auto& a = Curve[index - 1];
|
const auto& a = Curve[index - 1];
|
||||||
const auto& b = Curve[index];
|
const auto& b = Curve[index];
|
||||||
Vector3 startPoint = a.Value.Translation * _transform.Scale;
|
const Vector3 scale = _transform.Scale;
|
||||||
|
Vector3 prevPoint = a.Value.Translation * scale;
|
||||||
{
|
{
|
||||||
const float length = Math::Abs(b.Time - a.Time);
|
const float length = Math::Abs(b.Time - a.Time);
|
||||||
Vector3 leftTangent, rightTangent;
|
Vector3 leftTangent, rightTangent;
|
||||||
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
AnimationUtils::GetTangent(a.Value.Translation, a.TangentOut.Translation, length, leftTangent);
|
||||||
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
AnimationUtils::GetTangent(b.Value.Translation, b.TangentIn.Translation, length, rightTangent);
|
||||||
|
|
||||||
// TODO: implement sth more analytical than brute-force solution
|
for (int32 slice = 1; slice < slices; slice++)
|
||||||
for (int32 slice = 0; slice < slices; slice++)
|
|
||||||
{
|
{
|
||||||
const float t = (float)slice * step;
|
const float t = (float)slice * step;
|
||||||
Vector3 pos;
|
Vector3 pos;
|
||||||
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
AnimationUtils::Bezier(a.Value.Translation, leftTangent, rightTangent, b.Value.Translation, t, pos);
|
||||||
pos *= _transform.Scale;
|
pos *= scale;
|
||||||
sum += (float)Vector3::DistanceSquared(pos, startPoint);
|
sum += (float)Vector3::Distance(pos, prevPoint);
|
||||||
startPoint = pos;
|
prevPoint = pos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Math::Sqrt(sum);
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
float Spline::GetSplineTime(int32 index) const
|
float Spline::GetSplineTime(int32 index) const
|
||||||
|
|||||||
@@ -357,6 +357,10 @@ void StaticModel::Draw(RenderContext& renderContext)
|
|||||||
draw.ForcedLOD = _forcedLod;
|
draw.ForcedLOD = _forcedLod;
|
||||||
draw.SortOrder = _sortOrder;
|
draw.SortOrder = _sortOrder;
|
||||||
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (HasStaticFlag(StaticFlags::Lightmap))
|
||||||
|
draw.LightmapScale = _scaleInLightmap;
|
||||||
|
#endif
|
||||||
|
|
||||||
Model->Draw(renderContext, draw);
|
Model->Draw(renderContext, draw);
|
||||||
|
|
||||||
@@ -391,6 +395,10 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
|
|||||||
draw.ForcedLOD = _forcedLod;
|
draw.ForcedLOD = _forcedLod;
|
||||||
draw.SortOrder = _sortOrder;
|
draw.SortOrder = _sortOrder;
|
||||||
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (HasStaticFlag(StaticFlags::Lightmap))
|
||||||
|
draw.LightmapScale = _scaleInLightmap;
|
||||||
|
#endif
|
||||||
|
|
||||||
Model->Draw(renderContextBatch, draw);
|
Model->Draw(renderContextBatch, draw);
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,7 @@ using System.Runtime.CompilerServices;
|
|||||||
|
|
||||||
namespace FlaxEngine
|
namespace FlaxEngine
|
||||||
{
|
{
|
||||||
#if FLAX_EDITOR
|
|
||||||
[TypeConverter(typeof(TypeConverters.TagConverter))]
|
[TypeConverter(typeof(TypeConverters.TagConverter))]
|
||||||
#endif
|
|
||||||
partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string>
|
partial struct Tag : IEquatable<Tag>, IEquatable<string>, IComparable, IComparable<Tag>, IComparable<string>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -254,7 +252,6 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if FLAX_EDITOR
|
|
||||||
namespace FlaxEngine.TypeConverters
|
namespace FlaxEngine.TypeConverters
|
||||||
{
|
{
|
||||||
internal class TagConverter : TypeConverter
|
internal class TagConverter : TypeConverter
|
||||||
@@ -291,4 +288,3 @@ namespace FlaxEngine.TypeConverters
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -1503,6 +1503,8 @@ NetworkStream* NetworkReplicator::BeginInvokeRPC()
|
|||||||
|
|
||||||
bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
bool NetworkReplicator::EndInvokeRPC(ScriptingObject* obj, const ScriptingTypeHandle& type, const StringAnsiView& name, NetworkStream* argsStream, Span<uint32> targetIds)
|
||||||
{
|
{
|
||||||
|
if (targetIds.IsValid() && targetIds.Length() == 0)
|
||||||
|
return true; // Target list is provided, but it's empty so nobody will get this RPC
|
||||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||||
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
const NetworkRpcInfo* info = NetworkRpcInfo::RPCsTable.TryGet(NetworkRpcName(type, name));
|
||||||
if (!info || !obj || NetworkManager::IsOffline())
|
if (!info || !obj || NetworkManager::IsOffline())
|
||||||
|
|||||||
@@ -66,12 +66,14 @@ bool GPUParticles::Init(ParticleEmitter* owner, MemoryReadStream& shaderCacheStr
|
|||||||
LOG(Warning, "Missing valid GPU particles constant buffer.");
|
LOG(Warning, "Missing valid GPU particles constant buffer.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (cb0->GetSize() < sizeof(GPUParticlesData))
|
const int32 cbSize = cb0->GetSize();
|
||||||
|
if (cbSize < sizeof(GPUParticlesData))
|
||||||
{
|
{
|
||||||
LOG(Warning, "Invalid size GPU particles constant buffer. required {0} bytes but got {1}", sizeof(GPUParticlesData), cb0->GetSize());
|
LOG(Warning, "Invalid size GPU particles constant buffer. required {0} bytes but got {1}", sizeof(GPUParticlesData), cbSize);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
_cbData.Resize(cb0->GetSize());
|
_cbData.Resize(cbSize);
|
||||||
|
Platform::MemoryClear(_cbData.Get(), cbSize);
|
||||||
|
|
||||||
// Load material parameters
|
// Load material parameters
|
||||||
if (_params.Load(materialParamsStream))
|
if (_params.Load(materialParamsStream))
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ private:
|
|||||||
bool _useVolumeTexture;
|
bool _useVolumeTexture;
|
||||||
PixelFormat _lutFormat;
|
PixelFormat _lutFormat;
|
||||||
AssetReference<Shader> _shader;
|
AssetReference<Shader> _shader;
|
||||||
GPUPipelineStatePermutationsPs<3> _psLut;
|
GPUPipelineStatePermutationsPs<4> _psLut;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#include "Engine/Renderer/DrawCall.h"
|
#include "Engine/Renderer/DrawCall.h"
|
||||||
#include "Engine/Foliage/Foliage.h"
|
#include "Engine/Foliage/Foliage.h"
|
||||||
#include "Engine/ShadowsOfMordor/Builder.Config.h"
|
#include "Engine/ShadowsOfMordor/Builder.Config.h"
|
||||||
#include "Engine/Level/Level.h"
|
|
||||||
#include "Engine/Level/Scene/Scene.h"
|
#include "Engine/Level/Scene/Scene.h"
|
||||||
#include "Engine/Level/Actors/StaticModel.h"
|
#include "Engine/Level/Actors/StaticModel.h"
|
||||||
|
|
||||||
@@ -70,40 +69,6 @@ DrawPass LightmapUVsDensityMaterialShader::GetDrawModes() const
|
|||||||
return DrawPass::GBuffer;
|
return DrawPass::GBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
Actor* FindActorByDrawCall(Actor* actor, const DrawCall& drawCall, float& scaleInLightmap)
|
|
||||||
{
|
|
||||||
// TODO: large-worlds
|
|
||||||
const auto asStaticModel = ScriptingObject::Cast<StaticModel>(actor);
|
|
||||||
if (asStaticModel && asStaticModel->GetPerInstanceRandom() == drawCall.PerInstanceRandom && asStaticModel->GetPosition() == drawCall.ObjectPosition)
|
|
||||||
{
|
|
||||||
scaleInLightmap = asStaticModel->GetScaleInLightmap();
|
|
||||||
return asStaticModel;
|
|
||||||
}
|
|
||||||
const auto asFoliage = ScriptingObject::Cast<Foliage>(actor);
|
|
||||||
if (asFoliage)
|
|
||||||
{
|
|
||||||
for (auto i = asFoliage->Instances.Begin(); i.IsNotEnd(); ++i)
|
|
||||||
{
|
|
||||||
auto& instance = *i;
|
|
||||||
if (instance.Random == drawCall.PerInstanceRandom && instance.Transform.Translation == drawCall.ObjectPosition)
|
|
||||||
{
|
|
||||||
scaleInLightmap = asFoliage->FoliageTypes[instance.Type].ScaleInLightmap;
|
|
||||||
return asFoliage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Actor* child : actor->Children)
|
|
||||||
{
|
|
||||||
const auto other = FindActorByDrawCall(child, drawCall, scaleInLightmap);
|
|
||||||
if (other)
|
|
||||||
return other;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
||||||
{
|
{
|
||||||
// Prepare
|
// Prepare
|
||||||
@@ -121,33 +86,6 @@ void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
|||||||
_ps->Init(psDesc);
|
_ps->Init(psDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the static model that produced this draw call
|
|
||||||
const Actor* drawCallActor = nullptr;
|
|
||||||
float scaleInLightmap = 1.0f;
|
|
||||||
if (params.RenderContext.Task)
|
|
||||||
{
|
|
||||||
// Skip this lookup as it's too slow
|
|
||||||
|
|
||||||
/*if (params.RenderContext.Task->ActorsSource & ActorsSources::CustomActors)
|
|
||||||
{
|
|
||||||
for (auto actor : params.RenderContext.Task->CustomActors)
|
|
||||||
{
|
|
||||||
drawCallActor = FindActorByDrawCall(actor, drawCall, scaleInLightmap);
|
|
||||||
if (drawCallActor)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!drawCallActor && params.RenderContext.Task->ActorsSource & ActorsSources::Scenes)
|
|
||||||
{
|
|
||||||
for (auto& scene : Level::Scenes)
|
|
||||||
{
|
|
||||||
drawCallActor = FindActorByDrawCall(scene, drawCall, scaleInLightmap);
|
|
||||||
if (drawCallActor)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bind constants
|
// Bind constants
|
||||||
if (cb && cb->GetSize())
|
if (cb && cb->GetSize())
|
||||||
{
|
{
|
||||||
@@ -166,19 +104,15 @@ void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
|
|||||||
data.LightmapSize = 1024.0f;
|
data.LightmapSize = 1024.0f;
|
||||||
data.LightmapArea = drawCall.Surface.LightmapUVsArea;
|
data.LightmapArea = drawCall.Surface.LightmapUVsArea;
|
||||||
const ModelLOD* drawCallModelLod;
|
const ModelLOD* drawCallModelLod;
|
||||||
if (GBufferPass::IndexBufferToModelLOD.TryGet(drawCall.Geometry.IndexBuffer, drawCallModelLod))
|
float scaleInLightmap = drawCall.Surface.LODDitherFactor; // Reuse field
|
||||||
|
if (scaleInLightmap < 0.0f)
|
||||||
|
data.LightmapSize = -1.0f; // Not using lightmap
|
||||||
|
else if (GBufferPass::IndexBufferToModelLOD.TryGet(drawCall.Geometry.IndexBuffer, drawCallModelLod))
|
||||||
{
|
{
|
||||||
// Calculate current lightmap slot size for the object (matches the ShadowsOfMordor calculations when baking the lighting)
|
// Calculate current lightmap slot size for the object (matches the ShadowsOfMordor calculations when baking the lighting)
|
||||||
float globalObjectsScale = 1.0f;
|
float globalObjectsScale = 1.0f;
|
||||||
int32 atlasSize = 1024;
|
int32 atlasSize = 1024;
|
||||||
int32 chartsPadding = 3;
|
int32 chartsPadding = 3;
|
||||||
const Scene* drawCallScene = drawCallActor ? drawCallActor->GetScene() : (Level::Scenes.Count() != 0 ? Level::Scenes[0] : nullptr);
|
|
||||||
if (drawCallScene)
|
|
||||||
{
|
|
||||||
globalObjectsScale = drawCallScene->Info.LightmapSettings.GlobalObjectsScale;
|
|
||||||
atlasSize = (int32)drawCallScene->Info.LightmapSettings.AtlasSize;
|
|
||||||
chartsPadding = drawCallScene->Info.LightmapSettings.ChartsPadding;
|
|
||||||
}
|
|
||||||
BoundingBox box = drawCallModelLod->GetBox(drawCall.World);
|
BoundingBox box = drawCallModelLod->GetBox(drawCall.World);
|
||||||
Float3 size = box.GetSize();
|
Float3 size = box.GetSize();
|
||||||
float dimensionsCoeff = size.AverageArithmetic();
|
float dimensionsCoeff = size.AverageArithmetic();
|
||||||
|
|||||||
@@ -479,7 +479,7 @@ namespace FlaxEngine.Json
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool CanConvert(Type objectType)
|
public override bool CanConvert(Type objectType)
|
||||||
{
|
{
|
||||||
return objectType.Name.StartsWith("JsonAssetReference");
|
return objectType.Name.StartsWith("JsonAssetReference", StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the list with physical materials used to define the terrain collider physical properties - each for terrain layer (layer index matches index in this array).
|
/// Gets the list with physical materials used to define the terrain collider physical properties - each for terrain layer (layer index matches index in this array).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_PROPERTY(Attributes="EditorOrder(520), EditorDisplay(\"Collision\"), Collection(MinCount = 8, MaxCount = 8)")
|
API_PROPERTY(Attributes="EditorOrder(520), EditorDisplay(\"Collision\"), Collection(MinCount=8, MaxCount=8)")
|
||||||
FORCE_INLINE const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& GetPhysicalMaterials() const
|
FORCE_INLINE const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& GetPhysicalMaterials() const
|
||||||
{
|
{
|
||||||
return _physicalMaterials;
|
return _physicalMaterials;
|
||||||
@@ -199,6 +199,27 @@ public:
|
|||||||
API_PROPERTY()
|
API_PROPERTY()
|
||||||
void SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& value);
|
void SetPhysicalMaterials(const Array<JsonAssetReference<PhysicalMaterial>, FixedAllocation<8>>& value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the physical material used to define the terrain collider physical properties.
|
||||||
|
/// [Deprecated on 16.02.2024, expires on 16.02.2026]
|
||||||
|
/// </summary>
|
||||||
|
API_PROPERTY(Attributes="HideInEditor, NoSerialize")
|
||||||
|
DEPRECATED("Use PhysicalMaterials instead.") FORCE_INLINE JsonAssetReference<PhysicalMaterial>& GetPhysicalMaterial()
|
||||||
|
{
|
||||||
|
return _physicalMaterials[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the physical materials used to define the terrain collider physical properties.
|
||||||
|
/// [Deprecated on 16.02.2024, expires on 16.02.2026]
|
||||||
|
/// </summary>
|
||||||
|
DEPRECATED("Use PhysicalMaterials instead.") API_PROPERTY()
|
||||||
|
void SetPhysicalMaterial(const JsonAssetReference<PhysicalMaterial>& value)
|
||||||
|
{
|
||||||
|
for (auto& e : _physicalMaterials)
|
||||||
|
e = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the terrain Level Of Detail count.
|
/// Gets the terrain Level Of Detail count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -124,6 +124,10 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
|
|||||||
}
|
}
|
||||||
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
|
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
|
||||||
drawCall.PerInstanceRandom = _perInstanceRandom;
|
drawCall.PerInstanceRandom = _perInstanceRandom;
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
|
||||||
|
drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader
|
||||||
|
#endif
|
||||||
|
|
||||||
// Add half-texel offset for heightmap sampling in vertex shader
|
// Add half-texel offset for heightmap sampling in vertex shader
|
||||||
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
|
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
|
||||||
@@ -181,6 +185,10 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
|
|||||||
}
|
}
|
||||||
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
|
drawCall.WorldDeterminantSign = RenderTools::GetWorldDeterminantSign(drawCall.World);
|
||||||
drawCall.PerInstanceRandom = _perInstanceRandom;
|
drawCall.PerInstanceRandom = _perInstanceRandom;
|
||||||
|
#if USE_EDITOR
|
||||||
|
if (renderContext.View.Mode == ViewMode::LightmapUVsDensity)
|
||||||
|
drawCall.Surface.LODDitherFactor = 1.0f; // See LightmapUVsDensityMaterialShader
|
||||||
|
#endif
|
||||||
|
|
||||||
// Add half-texel offset for heightmap sampling in vertex shader
|
// Add half-texel offset for heightmap sampling in vertex shader
|
||||||
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
|
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
|
||||||
|
|||||||
@@ -7,6 +7,12 @@
|
|||||||
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
#include "Engine/Scripting/ManagedCLR/MUtils.h"
|
||||||
#include <ThirdParty/catch2/catch.hpp>
|
#include <ThirdParty/catch2/catch.hpp>
|
||||||
|
|
||||||
|
Foo::Foo(const SpawnParams& params)
|
||||||
|
: ScriptingObject(params)
|
||||||
|
, FooInterface(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
TestNesting::TestNesting(const SpawnParams& params)
|
TestNesting::TestNesting(const SpawnParams& params)
|
||||||
: SerializableScriptingObject(params)
|
: SerializableScriptingObject(params)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -8,6 +8,21 @@
|
|||||||
#include "Engine/Scripting/ScriptingObject.h"
|
#include "Engine/Scripting/ScriptingObject.h"
|
||||||
#include "Engine/Scripting/SerializableScriptingObject.h"
|
#include "Engine/Scripting/SerializableScriptingObject.h"
|
||||||
|
|
||||||
|
// Test interface (name conflict with namespace)
|
||||||
|
API_INTERFACE(Namespace="Foo") class FLAXENGINE_API IFoo
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE_MINIMAL(IFoo);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test class (name conflict with namespace)
|
||||||
|
API_CLASS(Namespace="Foo") class FLAXENGINE_API Foo : public ScriptingObject
|
||||||
|
{
|
||||||
|
DECLARE_SCRIPTING_TYPE(Foo);
|
||||||
|
|
||||||
|
// Test field.
|
||||||
|
API_FIELD() IFoo* FooInterface;
|
||||||
|
};
|
||||||
|
|
||||||
// Test compilation with nested types.
|
// Test compilation with nested types.
|
||||||
API_CLASS() class TestNesting : public SerializableScriptingObject
|
API_CLASS() class TestNesting : public SerializableScriptingObject
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -192,15 +192,10 @@ void MaterialGenerator::prepareLayer(MaterialLayer* layer, bool allowVisiblePara
|
|||||||
|
|
||||||
// For all not root layers (sub-layers) we won't to change theirs ID in order to prevent duplicated ID)
|
// For all not root layers (sub-layers) we won't to change theirs ID in order to prevent duplicated ID)
|
||||||
m.SrcId = param->Identifier;
|
m.SrcId = param->Identifier;
|
||||||
if (isRooLayer)
|
m.DstId = param->Identifier;
|
||||||
|
if (!isRooLayer)
|
||||||
{
|
{
|
||||||
// Use the same ID (so we can edit it)
|
// Generate new ID (stable permutation based on the original ID)
|
||||||
m.DstId = param->Identifier;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Generate new ID
|
|
||||||
m.DstId = param->Identifier;
|
|
||||||
m.DstId.A += _parameters.Count() * 17 + 13;
|
m.DstId.A += _parameters.Count() * 17 + 13;
|
||||||
}
|
}
|
||||||
layer->ParamIdsMappings.Add(m);
|
layer->ParamIdsMappings.Add(m);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user