Merge remote-tracking branch 'origin/master' into sdl_platform

This commit is contained in:
2025-06-04 19:30:30 +03:00
74 changed files with 490 additions and 312 deletions

View File

@@ -145,7 +145,7 @@ namespace FlaxEditor.Content.GUI
set
{
value = Mathf.Clamp(value, 0.3f, 3.0f);
if (!Mathf.NearEqual(value, _viewScale))
if (value != _viewScale)
{
_viewScale = value;
ViewScaleChanged?.Invoke();

View File

@@ -21,6 +21,7 @@ namespace FlaxEditor.CustomEditors.Editors
public sealed class LocalizedStringEditor : GenericEditor
{
private TextBoxElement _idElement, _valueElement;
private Button _viewStringButton;
/// <inheritdoc />
public override DisplayStyle Style => DisplayStyle.Inline;
@@ -70,6 +71,21 @@ namespace FlaxEditor.CustomEditors.Editors
};
addString.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
addString.ButtonClicked += OnAddStringClicked;
var viewString = new Button
{
Visible = false,
Width = 16.0f,
BackgroundColor = Color.White,
BackgroundColorHighlighted = Color.Gray,
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search12),
TooltipText = "Find localized text in Localized String Table asset for the current locale...",
Parent = valueElement.TextBox,
};
viewString.SetAnchorPreset(AnchorPresets.MiddleRight, false, true);
viewString.LocalX -= 16.0f;
viewString.ButtonClicked += OnViewStringClicked;
_viewStringButton = viewString;
}
/// <inheritdoc />
@@ -80,6 +96,7 @@ namespace FlaxEditor.CustomEditors.Editors
if (_valueElement != null)
{
_valueElement.TextBox.WatermarkText = Localization.GetString(_idElement.Text);
_viewStringButton.Visible = !string.IsNullOrEmpty(_valueElement.TextBox.WatermarkText);
}
}
@@ -92,14 +109,21 @@ namespace FlaxEditor.CustomEditors.Editors
_valueElement = null;
}
private void OnSelectStringClicked(Button button)
private bool GetSettings(out LocalizationSettings settings)
{
var settings = GameSettings.Load<LocalizationSettings>();
settings = GameSettings.Load<LocalizationSettings>();
if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0)
{
MessageBox.Show("No valid localization settings setup.");
return;
return true;
}
return false;
}
private void OnSelectStringClicked(Button button)
{
if (GetSettings(out var settings))
return;
Profiler.BeginEvent("LocalizedStringEditor.OnSelectStringClicked");
var allKeys = new HashSet<string>();
for (int i = 0; i < settings.LocalizedStringTables.Length; i++)
@@ -136,6 +160,7 @@ namespace FlaxEditor.CustomEditors.Editors
{
menu.Hide();
_idElement.TextBox.SetTextAsUser(after[0].Text);
_valueElement.TextBox.SetTextAsUser(string.Empty);
}
};
searchBox.TextChanged += delegate
@@ -158,12 +183,8 @@ namespace FlaxEditor.CustomEditors.Editors
private void OnAddStringClicked(Button button)
{
var settings = GameSettings.Load<LocalizationSettings>();
if (settings?.LocalizedStringTables == null || settings.LocalizedStringTables.Length == 0)
{
MessageBox.Show("No valid localization settings setup.");
if (GetSettings(out var settings))
return;
}
Profiler.BeginEvent("LocalizedStringEditor.OnAddStringClicked");
var allKeys = new HashSet<string>();
for (int i = 0; i < settings.LocalizedStringTables.Length; i++)
@@ -231,5 +252,30 @@ namespace FlaxEditor.CustomEditors.Editors
_idElement.TextBox.SetTextAsUser(newKey);
Profiler.EndEvent();
}
private void OnViewStringClicked(Button button)
{
if (GetSettings(out var settings))
return;
var id = _idElement.TextBox.Text;
var value = _valueElement.TextBox.WatermarkText;
for (int i = 0; i < settings.LocalizedStringTables.Length; i++)
{
var table = settings.LocalizedStringTables[i];
if (table && !table.WaitForLoaded())
{
var entries = table.Entries;
if (entries.TryGetValue(id, out var messages))
{
if (messages.Length != 0 && messages[0] == value)
{
Editor.Instance.ContentEditing.Open(table);
return;
}
}
}
}
MessageBox.Show("Unable to find localized string table.");
}
}
}

View File

@@ -35,7 +35,7 @@ namespace FlaxEditor.CustomEditors.Editors
}
_element = layout.TextBox(isMultiLine);
_defaultWatermarkColor = _element.TextBox.WatermarkTextColor;
_watermarkColor = _defaultWatermarkColor = _element.TextBox.WatermarkTextColor;
if (watermarkAttribute is WatermarkAttribute watermark)
{
_watermarkText = watermark.WatermarkText;

View File

@@ -296,6 +296,17 @@ namespace FlaxEditor.GUI.ContextMenu
}
}
private static void ForceDefocus(ContainerControl c)
{
foreach (var cc in c.Children)
{
if (cc.ContainsFocus)
cc.Defocus();
if (cc is ContainerControl ccc)
ForceDefocus(ccc);
}
}
/// <summary>
/// Hide popup menu and all child menus.
/// </summary>
@@ -310,6 +321,9 @@ namespace FlaxEditor.GUI.ContextMenu
// Close child
HideChild();
// Force defocus
ForceDefocus(this);
// Unlink from window
Parent = null;

View File

@@ -41,7 +41,7 @@ namespace FlaxEditor.GUI.Input
get => _min;
set
{
if (!Mathd.NearEqual(_min, value))
if (_min != value)
{
if (value > _max)
throw new ArgumentException();
@@ -58,7 +58,7 @@ namespace FlaxEditor.GUI.Input
get => _max;
set
{
if (!Mathd.NearEqual(_max, value))
if (_max != value)
{
if (value < _min)
throw new ArgumentException();

View File

@@ -38,7 +38,7 @@ namespace FlaxEditor.GUI.Input
get => _min;
set
{
if (!Mathf.NearEqual(_min, value))
if (_min != value)
{
if (value > _max)
throw new ArgumentException();
@@ -54,7 +54,7 @@ namespace FlaxEditor.GUI.Input
get => _max;
set
{
if (!Mathf.NearEqual(_max, value))
if (_max != value)
{
if (value < _min)
throw new ArgumentException();

View File

@@ -54,7 +54,7 @@ namespace FlaxEditor.GUI.Input
set
{
value = Mathf.Clamp(value, Minimum, Maximum);
if (!Mathf.NearEqual(value, _value))
if (value != _value)
{
_value = value;
@@ -311,7 +311,7 @@ namespace FlaxEditor.GUI.Input
get => _min;
set
{
if (!Mathf.NearEqual(_min, value))
if (_min != value)
{
if (value > _max)
throw new ArgumentException();
@@ -330,7 +330,7 @@ namespace FlaxEditor.GUI.Input
get => _max;
set
{
if (!Mathf.NearEqual(_max, value))
if (_max != value)
{
if (value < _min)
throw new ArgumentException();

View File

@@ -35,7 +35,8 @@ namespace FlaxEditor.SceneGraph
return false;
}
BoundingSphere sphere = new BoundingSphere(Transform.Translation, 7.0f);
var center = _actor.Transform.Translation;
ViewportIconsRenderer.GetBounds(ref center, ref ray.Ray.Position, out var sphere);
return CollisionsHelper.RayIntersectsSphere(ref ray.Ray, ref sphere, out distance);
}
}

View File

@@ -534,7 +534,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Tangent Vector",
Description = "World space tangent vector",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(160, 40),
Size = new Float2(160, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, "Tangent", typeof(Float3), 0),
@@ -546,7 +546,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Bitangent Vector",
Description = "World space bitangent vector",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(160, 40),
Size = new Float2(160, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, "Bitangent", typeof(Float3), 0),
@@ -558,7 +558,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Camera Position",
Description = "World space camera location",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(160, 40),
Size = new Float2(160, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, "XYZ", typeof(Float3), 0),
@@ -570,7 +570,7 @@ namespace FlaxEditor.Surface.Archetypes
Title = "Per Instance Random",
Description = "Per object instance random value (normalized to range 0-1)",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(200, 40),
Size = new Float2(200, 30),
Elements = new[]
{
NodeElementArchetype.Factory.Output(0, "", typeof(float), 0),

View File

@@ -488,11 +488,9 @@ namespace FlaxEditor.Surface
// Check if user is pressing control
if (Root.GetKey(KeyboardKeys.Control))
{
// Add to selection
if (!controlUnderMouse.IsSelected)
{
AddToSelection(controlUnderMouse);
}
// Add/remove from selection
controlUnderMouse.IsSelected = !controlUnderMouse.IsSelected;
SelectionChanged?.Invoke();
}
// Check if node isn't selected
else if (!controlUnderMouse.IsSelected)

View File

@@ -23,8 +23,6 @@
#include "Engine/Level/Actors/SpotLight.h"
#include "Engine/Video/VideoPlayer.h"
#define ICON_RADIUS 7.0f
enum class IconTypes
{
PointLight,
@@ -66,6 +64,16 @@ public:
};
ViewportIconsRendererService ViewportIconsRendererServiceInstance;
float ViewportIconsRenderer::Scale = 1.0f;
void ViewportIconsRenderer::GetBounds(const Vector3& position, const Vector3& viewPosition, BoundingSphere& bounds)
{
constexpr Real minSize = 7.0;
constexpr Real maxSize = 30.0;
Real scale = Math::Square(Vector3::Distance(position, viewPosition) / 1000.0f);
Real radius = minSize + Math::Min<Real>(scale, 1.0f) * (maxSize - minSize);
bounds = BoundingSphere(position, radius * Scale);
}
void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor)
{
@@ -133,7 +141,8 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
AssetReference<Texture> texture;
for (Actor* icon : icons)
{
BoundingSphere sphere(icon->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
BoundingSphere sphere;
ViewportIconsRenderer::GetBounds(icon->GetPosition() - renderContext.View.Origin, renderContext.View.Position, sphere);
if (!frustum.Intersects(sphere))
continue;
IconTypes iconType;
@@ -173,7 +182,7 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Scene
if (draw.Buffer)
{
// Create world matrix
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
Matrix::Scaling((float)sphere.Radius * 2.0f, m2);
Matrix::RotationY(PI, world);
Matrix::Multiply(m2, world, m1);
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);
@@ -193,14 +202,15 @@ void ViewportIconsRendererService::DrawIcons(RenderContext& renderContext, Actor
auto& view = renderContext.View;
const BoundingFrustum frustum = view.Frustum;
Matrix m1, m2, world;
BoundingSphere sphere(actor->GetPosition() - renderContext.View.Origin, ICON_RADIUS);
BoundingSphere sphere;
ViewportIconsRenderer::GetBounds(actor->GetPosition() - renderContext.View.Origin, renderContext.View.Position, sphere);
IconTypes iconType;
AssetReference<Texture> texture;
if (frustum.Intersects(sphere) && ActorTypeToIconType.TryGet(actor->GetTypeHandle(), iconType))
{
// Create world matrix
Matrix::Scaling(ICON_RADIUS * 2.0f, m2);
Matrix::Scaling((float)sphere.Radius * 2.0f, m2);
Matrix::RotationY(PI, world);
Matrix::Multiply(m2, world, m1);
Matrix::Billboard(sphere.Center, view.Position, Vector3::Up, view.Direction, m2);

View File

@@ -17,6 +17,19 @@ API_CLASS(Static, Namespace="FlaxEditor") class FLAXENGINE_API ViewportIconsRend
DECLARE_SCRIPTING_TYPE_NO_SPAWN(ViewportIconsRenderer);
public:
/// <summary>
/// Global scale of the icons.
/// </summary>
API_FIELD() static float Scale;
/// <summary>
/// Draws the icons for the actors in the given scene (or actor tree).
/// </summary>
/// <param name="position">The icon position.</param>
/// <param name="viewPosition">The viewer position.</param>
/// <param name="bounds">The computed bounds for the icon.</param>
API_FUNCTION() static void GetBounds(API_PARAM(Ref) const Vector3& position, API_PARAM(Ref) const Vector3& viewPosition, API_PARAM(Out) BoundingSphere& bounds);
/// <summary>
/// Draws the icons for the actors in the given scene (or actor tree).
/// </summary>

View File

@@ -1008,6 +1008,18 @@ namespace FlaxEditor.Viewport
ViewWidgetButtonMenu.VisibleChanged += control => resolutionValue.Value = ResolutionScale;
}
// Icons Scale
{
var icons = ViewWidgetButtonMenu.AddButton("Icons");
icons.CloseMenuOnClick = false;
var iconsValue = new FloatValueBox(ViewportIconsRenderer.Scale, xLocationForExtras, 2, 70.0f, 0.01f, 100.0f, 0.001f)
{
Parent = icons
};
iconsValue.ValueChanged += () => ViewportIconsRenderer.Scale = iconsValue.Value;
ViewWidgetButtonMenu.VisibleChanged += control => iconsValue.Value = ViewportIconsRenderer.Scale;
}
#endregion View mode widget
}

View File

@@ -76,29 +76,29 @@ namespace FlaxEditor.Windows.Assets
// Transparency
[EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")]
[EditorOrder(200), DefaultValue(MaterialTransparentLightingMode.Surface), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Transparent material lighting mode.")]
public MaterialTransparentLightingMode TransparentLightingMode;
[EditorOrder(205), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
[EditorOrder(205), DefaultValue(true), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
public bool EnableReflections;
[VisibleIf(nameof(EnableReflections))]
[EditorOrder(210), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")]
[EditorOrder(210), DefaultValue(false), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")]
public bool EnableScreenSpaceReflections;
[EditorOrder(210), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
[EditorOrder(210), DefaultValue(true), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
public bool EnableFog;
[EditorOrder(220), DefaultValue(true), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")]
[EditorOrder(220), DefaultValue(true), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables distortion effect when rendering.")]
public bool EnableDistortion;
[EditorOrder(224), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")]
[EditorOrder(224), DefaultValue(false), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables sampling Global Illumination in material (eg. light probes or volumetric lightmap).")]
public bool EnableGlobalIllumination;
[EditorOrder(225), DefaultValue(false), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")]
[EditorOrder(225), DefaultValue(false), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Enables refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.")]
public bool PixelNormalOffsetRefraction;
[EditorOrder(230), DefaultValue(0.12f), VisibleIf(nameof(IsStandard)), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
[EditorOrder(230), DefaultValue(0.12f), VisibleIf(nameof(IsForward)), EditorDisplay("Transparency"), Tooltip("Controls opacity values clipping point."), Limit(0.0f, 1.0f, 0.01f)]
public float OpacityThreshold;
// Tessellation
@@ -146,6 +146,7 @@ namespace FlaxEditor.Windows.Assets
private bool IsDecal => Domain == MaterialDomain.Decal;
private bool IsGUI => Domain == MaterialDomain.GUI;
private bool IsStandard => Domain == MaterialDomain.Surface || Domain == MaterialDomain.Terrain || Domain == MaterialDomain.Particle || Domain == MaterialDomain.Deformable;
private bool IsForward => Domain == MaterialDomain.Particle || ((Domain == MaterialDomain.Deformable || Domain == MaterialDomain.Surface) && BlendMode != MaterialBlendMode.Opaque);
private bool IsStandardOrGUI => IsStandard || IsGUI;
/// <summary>

View File

@@ -254,7 +254,8 @@ namespace FlaxEditor.Windows.Assets
if (lodIndex >= countLODs - loadedLODs)
{
var mesh = lod.GetMesh(0);
vertexLayout = mesh.VertexLayout;
if (mesh != null)
vertexLayout = mesh.VertexLayout;
if (vertexLayout != null && vertexLayout.Elements.Length != 0)
break;
vertexLayout = null;