diff --git a/Content/Editor/HSWheel.flax b/Content/Editor/HSWheel.flax
new file mode 100644
index 000000000..e7eddccad
--- /dev/null
+++ b/Content/Editor/HSWheel.flax
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cfbfaffd30ae4cc0f220f42382be54a96b3df9461b68eab603fd93075a9db8fc
+size 15454
diff --git a/Source/Editor/EditorAssets.cs b/Source/Editor/EditorAssets.cs
index d9b3d7f18..447dc3375 100644
--- a/Source/Editor/EditorAssets.cs
+++ b/Source/Editor/EditorAssets.cs
@@ -69,6 +69,11 @@ namespace FlaxEditor
///
public static string WindowIcon = "Editor/EditorIcon";
+ ///
+ /// The material used for the HS color wheel.
+ ///
+ public static string HSWheelMaterial = "Editor/HSWheel";
+
///
/// The window icons font.
///
diff --git a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
index d028b7c7c..5d4d6f477 100644
--- a/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
+++ b/Source/Editor/GUI/Dialogs/ColorPickerDialog.cs
@@ -1,10 +1,11 @@
// Copyright (c) Wojciech Figat. All rights reserved.
+using System.Collections.Generic;
using FlaxEditor.GUI.Input;
+using FlaxEditor.GUI.Tabs;
using FlaxEngine;
using FlaxEngine.GUI;
using FlaxEngine.Json;
-using System.Collections.Generic;
namespace FlaxEditor.GUI.Dialogs
{
@@ -25,15 +26,16 @@ namespace FlaxEditor.GUI.Dialogs
///
public class ColorPickerDialog : Dialog, IColorPickerDialog
{
- private const float ButtonsWidth = 60.0f;
private const float PickerMargin = 6.0f;
- private const float EyedropperMargin = 8.0f;
- private const float RGBAMargin = 12.0f;
- private const float HSVMargin = 0.0f;
private const float ChannelsMargin = 4.0f;
private const float ChannelTextWidth = 12.0f;
private const float SavedColorButtonWidth = 20.0f;
private const float SavedColorButtonHeight = 20.0f;
+ private const float TabHeight = 20;
+ private const float ValueBoxesWidth = 100.0f;
+ private const float HSVRGBTextWidth = 15.0f;
+ private const float ColorPreviewHeight = 50.0f;
+ private const int SavedColorsAmount = 10;
private Color _initialValue;
private Color _value;
@@ -46,16 +48,19 @@ namespace FlaxEditor.GUI.Dialogs
private ColorValueBox.ColorPickerClosedEvent _onClosed;
private ColorSelectorWithSliders _cSelector;
+ private Tabs.Tabs _hsvRGBTabs;
+ private Tab _RGBTab;
+ private Panel _rgbPanel;
private FloatValueBox _cRed;
private FloatValueBox _cGreen;
private FloatValueBox _cBlue;
- private FloatValueBox _cAlpha;
+ private Tab _hsvTab;
+ private Panel _hsvPanel;
private FloatValueBox _cHue;
private FloatValueBox _cSaturation;
private FloatValueBox _cValue;
private TextBox _cHex;
- private Button _cCancel;
- private Button _cOK;
+ private FloatValueBox _cAlpha;
private Button _cEyedropper;
private Button _cLinearSRGB;
@@ -127,97 +132,110 @@ namespace FlaxEditor.GUI.Dialogs
_savedColors = JsonSerializer.Deserialize>(savedColors);
// Selector
- _cSelector = new ColorSelectorWithSliders(180, 18)
+ _cSelector = new ColorSelectorWithSliders(180, 21)
{
Location = new Float2(PickerMargin, PickerMargin),
Parent = this
};
_cSelector.ColorChanged += x => SelectedColor = x;
- // Red
- _cRed = new FloatValueBox(0, _cSelector.Right + PickerMargin + RGBAMargin + ChannelTextWidth, PickerMargin, 100, 0, float.MaxValue, 0.001f)
+ _hsvRGBTabs = new Tabs.Tabs
{
- Parent = this
+ Location = new Float2(_cSelector.Right + 30.0f, PickerMargin),
+ TabsTextHorizontalAlignment = TextAlignment.Center,
+ Width = ValueBoxesWidth + HSVRGBTextWidth * 2.0f + ChannelsMargin * 2.0f,
+ Height = (FloatValueBox.DefaultHeight + ChannelsMargin) * 4 + ChannelsMargin,
+ Parent = this,
+ };
+ _hsvRGBTabs.TabsSize = new Float2(_hsvRGBTabs.Width * 0.5f, TabHeight);
+ _hsvRGBTabs.SelectedTabChanged += SelectedTabChanged;
+
+ // RGB Tab
+ _RGBTab = _hsvRGBTabs.AddTab(new Tab("RGB"));
+ _rgbPanel = new Panel(ScrollBars.Vertical)
+ {
+ AnchorPreset = AnchorPresets.StretchAll,
+ Offsets = Margin.Zero,
+ Parent = _RGBTab,
+ };
+
+ // HSV Tab
+ _hsvTab = _hsvRGBTabs.AddTab(new Tab("HSV"));
+ _hsvPanel = new Panel(ScrollBars.Vertical)
+ {
+ AnchorPreset = AnchorPresets.StretchAll,
+ Offsets = Margin.Zero,
+ Parent = _hsvTab,
+ };
+
+ // Red
+ _cRed = new FloatValueBox(0, HSVRGBTextWidth + ChannelsMargin, PickerMargin, ValueBoxesWidth, 0, float.MaxValue, 0.001f)
+ {
+ Parent = _rgbPanel,
};
_cRed.ValueChanged += OnRGBAChanged;
// Green
_cGreen = new FloatValueBox(0, _cRed.X, _cRed.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f)
{
- Parent = this
+ Parent = _rgbPanel,
};
_cGreen.ValueChanged += OnRGBAChanged;
// Blue
_cBlue = new FloatValueBox(0, _cRed.X, _cGreen.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f)
{
- Parent = this
+ Parent = _rgbPanel,
};
_cBlue.ValueChanged += OnRGBAChanged;
- // Alpha
- _cAlpha = new FloatValueBox(0, _cRed.X, _cBlue.Bottom + ChannelsMargin, _cRed.Width, 0, float.MaxValue, 0.001f)
- {
- Parent = this
- };
- _cAlpha.ValueChanged += OnRGBAChanged;
-
// Hue
- _cHue = new FloatValueBox(0, PickerMargin + HSVMargin + ChannelTextWidth, _cSelector.Bottom + PickerMargin, 100, 0, 360)
+ _cHue = new FloatValueBox(0, HSVRGBTextWidth + ChannelsMargin, PickerMargin, ValueBoxesWidth)
{
- Parent = this
+ Parent = _hsvPanel,
+ Category = Utils.ValueCategory.Angle,
};
_cHue.ValueChanged += OnHSVChanged;
// Saturation
_cSaturation = new FloatValueBox(0, _cHue.X, _cHue.Bottom + ChannelsMargin, _cHue.Width, 0, 100.0f, 0.1f)
{
- Parent = this
+ Parent = _hsvPanel,
};
_cSaturation.ValueChanged += OnHSVChanged;
// Value
_cValue = new FloatValueBox(0, _cHue.X, _cSaturation.Bottom + ChannelsMargin, _cHue.Width, 0, float.MaxValue, 0.1f)
{
- Parent = this
+ Parent = _hsvPanel,
};
_cValue.ValueChanged += OnHSVChanged;
- // Set valid dialog size based on UI content
- _dialogSize = Size = new Float2(_cRed.Right + PickerMargin, 300);
+ // Alpha
+ _cAlpha = new FloatValueBox(0, _hsvRGBTabs.Left + HSVRGBTextWidth + ChannelsMargin, _hsvRGBTabs.Bottom + ChannelsMargin * 4.0f, ValueBoxesWidth, 0, float.MaxValue, 0.001f)
+ {
+ Parent = this,
+ };
+ _cAlpha.ValueChanged += OnRGBAChanged;
// Hex
- const float hexTextBoxWidth = 80;
- _cHex = new TextBox(false, Width - hexTextBoxWidth - PickerMargin, _cSelector.Bottom + PickerMargin, hexTextBoxWidth)
+ _cHex = new TextBox(false, _hsvRGBTabs.Left + HSVRGBTextWidth + ChannelsMargin, _cAlpha.Bottom + ChannelsMargin * 2.0f, ValueBoxesWidth)
{
- Parent = this
+ Parent = this,
};
_cHex.EditEnd += OnHexChanged;
- // Cancel
- _cCancel = new Button(Width - ButtonsWidth - PickerMargin, Height - Button.DefaultHeight - PickerMargin, ButtonsWidth)
- {
- Text = "Cancel",
- Parent = this
- };
- _cCancel.Clicked += OnCancel;
-
- // OK
- _cOK = new Button(_cCancel.Left - ButtonsWidth - PickerMargin, _cCancel.Y, ButtonsWidth)
- {
- Text = "Ok",
- Parent = this
- };
- _cOK.Clicked += OnSubmit;
+ // Set valid dialog size based on UI content
+ _dialogSize = Size = new Float2(_hsvRGBTabs.Right + PickerMargin, _cHex.Bottom + 40.0f + ColorPreviewHeight + PickerMargin);
// Create saved color buttons
- CreateAllSaveButtons();
+ CreateAllSavedColorsButtons();
// Eyedropper button
var style = Style.Current;
- _cEyedropper = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin)
+ _cEyedropper = new Button(_cSelector.BottomLeft.X, _cSelector.BottomLeft.Y - 25.0f, 25.0f, 25.0f)
{
- TooltipText = "Eyedropper tool to pick a color directly from the screen",
+ TooltipText = "Eyedropper tool to pick a color directly from the screen.",
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.Search32),
BackgroundColor = style.Foreground,
BackgroundColorHighlighted = style.Foreground.RGBMultiplied(0.9f),
@@ -226,14 +244,11 @@ namespace FlaxEditor.GUI.Dialogs
Parent = this,
};
_cEyedropper.Clicked += OnEyedropStart;
- _cEyedropper.Height = (_cValue.Bottom - _cEyedropper.Y) * 0.5f;
- _cEyedropper.Width = _cEyedropper.Height;
- _cEyedropper.X -= _cEyedropper.Width;
// Linear/sRGB toggle button
- _cLinearSRGB = new Button(_cOK.X - EyedropperMargin, _cHex.Bottom + PickerMargin)
+ _cLinearSRGB = new Button(_cSelector.X, _cHex.Bottom + PickerMargin)
{
- TooltipText = "Toggles between color preview in Linear and sRGB",
+ TooltipText = "Toggles between color preview in Linear and sRGB.",
BackgroundBrush = new SpriteBrush(Editor.Instance.Icons.SplineAligned64),
BackgroundColor = _cEyedropper.BackgroundColor,
BackgroundColorHighlighted = _cEyedropper.BackgroundColorHighlighted,
@@ -261,12 +276,10 @@ namespace FlaxEditor.GUI.Dialogs
foreach (var color in _savedColors)
{
if (color == _value)
- {
return;
- }
}
- // Set color of button to current value;
+ // Set color of button to current value
button.BackgroundColor = _value;
button.BackgroundColorHighlighted = _value;
button.BackgroundColorSelected = _value.RGBMultiplied(0.8f);
@@ -278,10 +291,10 @@ namespace FlaxEditor.GUI.Dialogs
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List));
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
- // create new + button
- if (_savedColorButtons.Count < 8)
+ // Create new + button
+ if (_savedColorButtons.Count < SavedColorsAmount)
{
- var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
+ var savedColorButton = new Button(PickerMargin * (_savedColorButtons.Count + 1) + SavedColorButtonWidth * _savedColorButtons.Count, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight)
{
Text = "+",
Parent = this,
@@ -298,11 +311,32 @@ namespace FlaxEditor.GUI.Dialogs
}
}
+ private void SelectedTabChanged(Tabs.Tabs tabs)
+ {
+ if (_rgbPanel == null || _hsvPanel == null)
+ return;
+
+ switch (tabs.SelectedTabIndex)
+ {
+ // RGB
+ case 0:
+ _rgbPanel.Visible = true;
+ _hsvPanel.Visible = false;
+ break;
+ // HSV
+ case 1:
+ _rgbPanel.Visible = false;
+ _hsvPanel.Visible = true;
+ break;
+ }
+ }
+
private void OnColorPicked(Color32 colorPicked)
{
if (_activeEyedropper)
{
_activeEyedropper = false;
+ _cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.Foreground;
if (colorPicked != Color.Transparent)
{
Color color = colorPicked;
@@ -317,6 +351,7 @@ namespace FlaxEditor.GUI.Dialogs
private void OnEyedropStart()
{
_activeEyedropper = true;
+ _cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.BackgroundHighlighted;
Platform.PickScreenColor();
Platform.PickScreenColorDone += OnColorPicked;
}
@@ -334,6 +369,7 @@ namespace FlaxEditor.GUI.Dialogs
if (_disableEvents)
return;
+ _cHue.Value = Mathf.Wrap(_cHue.Value, 0f, 360f);
SelectedColor = Color.FromHSV(_cHue.Value, _cSaturation.Value / 100.0f, _cValue.Value / 100.0f, _cAlpha.Value);
}
@@ -374,64 +410,76 @@ namespace FlaxEditor.GUI.Dialogs
base.Draw();
- // RGBA
- var rgbaR = new Rectangle(_cRed.Left - ChannelTextWidth, _cRed.Y, 10000, _cRed.Height);
- Render2D.DrawText(style.FontMedium, "R", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
- rgbaR.Location.Y = _cGreen.Y;
- Render2D.DrawText(style.FontMedium, "G", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
- rgbaR.Location.Y = _cBlue.Y;
- Render2D.DrawText(style.FontMedium, "B", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
- rgbaR.Location.Y = _cAlpha.Y;
- Render2D.DrawText(style.FontMedium, "A", rgbaR, textColor, TextAlignment.Near, TextAlignment.Center);
+ switch (_hsvRGBTabs.SelectedTabIndex)
+ {
+ // RGB
+ case 0:
+ var rgbRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _hsvRGBTabs.Top + TabHeight + PickerMargin, 10000, _cRed.Height);
+ Render2D.DrawText(style.FontMedium, "R", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ rgbRect.Location.Y += _cRed.Height + ChannelsMargin;
+ Render2D.DrawText(style.FontMedium, "G", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ rgbRect.Location.Y += _cRed.Height + ChannelsMargin;
+ Render2D.DrawText(style.FontMedium, "B", rgbRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ break;
+ // HSV
+ case 1:
+ // Left
+ var hsvLeftRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _hsvRGBTabs.Top + TabHeight + PickerMargin, 10000, _cHue.Height);
+ Render2D.DrawText(style.FontMedium, "H", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ hsvLeftRect.Location.Y += _cHue.Height + ChannelsMargin;
+ Render2D.DrawText(style.FontMedium, "S", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ hsvLeftRect.Location.Y += _cHue.Height + ChannelsMargin;
+ Render2D.DrawText(style.FontMedium, "V", hsvLeftRect, textColor, TextAlignment.Near, TextAlignment.Center);
- // HSV left
- var hsvHl = new Rectangle(_cHue.Left - ChannelTextWidth, _cHue.Y, 10000, _cHue.Height);
- Render2D.DrawText(style.FontMedium, "H", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center);
- hsvHl.Location.Y = _cSaturation.Y;
- Render2D.DrawText(style.FontMedium, "S", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center);
- hsvHl.Location.Y = _cValue.Y;
- Render2D.DrawText(style.FontMedium, "V", hsvHl, textColor, TextAlignment.Near, TextAlignment.Center);
+ // Right
+ var hsvRightRect = new Rectangle(_hsvRGBTabs.Right - HSVRGBTextWidth, _hsvRGBTabs.Top + TabHeight + PickerMargin, ChannelTextWidth, _cHue.Height);
+ Render2D.DrawText(style.FontMedium, "°", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ hsvRightRect.Location.Y += _cHue.Height + ChannelsMargin;
+ Render2D.DrawText(style.FontMedium, "%", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ hsvRightRect.Location.Y += _cHue.Height + ChannelsMargin;
+ Render2D.DrawText(style.FontMedium, "%", hsvRightRect, textColor, TextAlignment.Near, TextAlignment.Center);
+ break;
+ }
- // HSV right
- var hsvHr = new Rectangle(_cHue.Right + 2, _cHue.Y, 10000, _cHue.Height);
- Render2D.DrawText(style.FontMedium, "°", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center);
- hsvHr.Location.Y = _cSaturation.Y;
- Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center);
- hsvHr.Location.Y = _cValue.Y;
- Render2D.DrawText(style.FontMedium, "%", hsvHr, textColor, TextAlignment.Near, TextAlignment.Center);
+ // A
+ var alphaHexRect = new Rectangle(_hsvRGBTabs.Left + PickerMargin, _cAlpha.Top, ChannelTextWidth, _cAlpha.Height);
+ Render2D.DrawText(style.FontMedium, "A", alphaHexRect, textColor, TextAlignment.Near, TextAlignment.Center);
// Hex
- var hex = new Rectangle(_cHex.Left - 26, _cHex.Y, 10000, _cHex.Height);
- Render2D.DrawText(style.FontMedium, "Hex", hex, textColor, TextAlignment.Near, TextAlignment.Center);
+ alphaHexRect.Y += _cAlpha.Height + ChannelsMargin * 2.0f;
+ alphaHexRect.X -= 5.0f; // "Hex" is two characters wider than the other labels so we need to adjust for that
+ Render2D.DrawText(style.FontMedium, "Hex", alphaHexRect, textColor, TextAlignment.Far, TextAlignment.Center);
// Color difference
- var newRect = new Rectangle(_cOK.X - 3, _cHex.Bottom + PickerMargin, 130, 0);
- newRect.Size.Y = 50;
- Render2D.FillRectangle(newRect, Color.White);
- var smallRectSize = 10;
- var numHor = Mathf.FloorToInt(newRect.Width / smallRectSize);
- var numVer = Mathf.FloorToInt(newRect.Height / smallRectSize);
+ var differenceRect = new Rectangle(_hsvRGBTabs.Left, _cHex.Bottom + 40.0f, _hsvRGBTabs.Width, ColorPreviewHeight);
+
// Draw checkerboard for background of color to help with transparency
+ Render2D.FillRectangle(differenceRect, Color.White);
+ var smallRectSize = 10;
+ var numHor = Mathf.CeilToInt(differenceRect.Width / smallRectSize);
+ var numVer = Mathf.CeilToInt(differenceRect.Height / smallRectSize);
+ Render2D.PushClip(differenceRect);
for (int i = 0; i < numHor; i++)
{
for (int j = 0; j < numVer; j++)
{
if ((i + j) % 2 == 0)
{
- var rect = new Rectangle(newRect.X + smallRectSize * i, newRect.Y + smallRectSize * j, new Float2(smallRectSize));
+ var rect = new Rectangle(differenceRect.X + smallRectSize * i, differenceRect.Y + smallRectSize * j, new Float2(smallRectSize));
Render2D.FillRectangle(rect, Color.Gray);
}
}
}
- Render2D.FillRectangle(newRect, _linear ? _value.ToSRgb() : _value);
+ Render2D.PopClip();
+ Render2D.FillRectangle(differenceRect, _linear ? _value.ToSRgb() : _value);
}
///
protected override void OnShow()
{
- // Auto cancel on lost focus
+ // Apply changes on lost focus
#if !PLATFORM_LINUX
- ((WindowRootControl)Root).Window.LostFocus += OnWindowLostFocus;
+ ((WindowRootControl)Root).Window.LostFocus += OnSubmit;
#endif
base.OnShow();
@@ -444,6 +492,7 @@ namespace FlaxEditor.GUI.Dialogs
{
// Cancel eye dropping
_activeEyedropper = false;
+ _cEyedropper.BackgroundColor = _cEyedropper.BackgroundColorHighlighted = Style.Current.Foreground;
Platform.PickScreenColorDone -= OnColorPicked;
return true;
}
@@ -455,9 +504,7 @@ namespace FlaxEditor.GUI.Dialogs
public override bool OnMouseUp(Float2 location, MouseButton button)
{
if (base.OnMouseUp(location, button))
- {
return true;
- }
var child = GetChildAtRecursive(location);
if (button == MouseButton.Right && child is Button b && b.Tag is Color c)
@@ -519,20 +566,20 @@ namespace FlaxEditor.GUI.Dialogs
}
_savedColorButtons.Clear();
- CreateAllSaveButtons();
+ CreateAllSavedColorsButtons();
// Save new colors
var savedColors = JsonSerializer.Serialize(_savedColors, typeof(List));
Editor.Instance.ProjectCache.SetCustomData("ColorPickerSavedColors", savedColors);
}
- private void CreateAllSaveButtons()
+ private void CreateAllSavedColorsButtons()
{
// Create saved color buttons
for (int i = 0; i < _savedColors.Count; i++)
{
var savedColor = _savedColors[i];
- var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
+ var savedColorButton = new Button(PickerMargin * (i + 1) + SavedColorButtonWidth * i, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight)
{
Parent = this,
Tag = savedColor,
@@ -543,9 +590,9 @@ namespace FlaxEditor.GUI.Dialogs
savedColorButton.ButtonClicked += OnSavedColorButtonClicked;
_savedColorButtons.Add(savedColorButton);
}
- if (_savedColors.Count < 8)
+ if (_savedColors.Count < SavedColorsAmount)
{
- var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, Height - SavedColorButtonHeight - PickerMargin, SavedColorButtonWidth, SavedColorButtonHeight)
+ var savedColorButton = new Button(PickerMargin * (_savedColors.Count + 1) + SavedColorButtonWidth * _savedColors.Count, _cHex.Bottom + 40.0f + ColorPreviewHeight * 0.5f, SavedColorButtonWidth, SavedColorButtonHeight)
{
Text = "+",
Parent = this,
@@ -557,19 +604,6 @@ namespace FlaxEditor.GUI.Dialogs
}
}
- private void OnWindowLostFocus()
- {
- // Auto apply color on defocus
- var autoAcceptColorPickerChange = Editor.Instance.Options.Options.Interface.AutoAcceptColorPickerChange;
- if (_useDynamicEditing && _initialValue != _value && _canPassLastChangeEvent && autoAcceptColorPickerChange)
- {
- _canPassLastChangeEvent = false;
- _onChanged?.Invoke(_value, false);
- }
-
- OnCancel();
- }
-
///
public override void OnSubmit()
{
@@ -577,6 +611,9 @@ namespace FlaxEditor.GUI.Dialogs
return;
_disableEvents = true;
+ // Ensure the cursor is restored
+ Cursor = CursorType.Default;
+
// Send color event if modified
if (_value != _initialValue)
{
@@ -593,6 +630,9 @@ namespace FlaxEditor.GUI.Dialogs
return;
_disableEvents = true;
+ // Ensure the cursor is restored
+ Cursor = CursorType.Default;
+
// Restore color if modified
if (_useDynamicEditing && _initialValue != _value && _canPassLastChangeEvent)
{
diff --git a/Source/Editor/GUI/Dialogs/ColorSelector.cs b/Source/Editor/GUI/Dialogs/ColorSelector.cs
index 3bd76a79a..5854be931 100644
--- a/Source/Editor/GUI/Dialogs/ColorSelector.cs
+++ b/Source/Editor/GUI/Dialogs/ColorSelector.cs
@@ -12,6 +12,13 @@ namespace FlaxEditor.GUI.Dialogs
///
public class ColorSelector : ContainerControl
{
+ private const String GrayedOutParamName = "GrayedOut";
+
+ ///
+ /// Offset value applied to mouse cursor position when the user lets go of wheel or sliders.
+ ///
+ protected const float MouseCursorOffset = 6.0f;
+
///
/// The color.
///
@@ -22,9 +29,22 @@ namespace FlaxEditor.GUI.Dialogs
///
protected Rectangle _wheelRect;
- private readonly SpriteHandle _colorWheelSprite;
+ private readonly MaterialBase _hsWheelMaterial;
private bool _isMouseDownWheel;
+ private Rectangle wheelDragRect
+ {
+ get
+ {
+ var hsv = _color.ToHSV();
+ float hAngle = hsv.X * Mathf.DegreesToRadians;
+ float hRadius = hsv.Y * _wheelRect.Width * 0.5f;
+ var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle));
+ float wheelBoxSize = IsSliding ? 9.0f : 5.0f;
+ return new Rectangle(hsPos - (wheelBoxSize * 0.5f) + _wheelRect.Center, new Float2(wheelBoxSize));
+ }
+ }
+
///
/// Occurs when selected color gets changed.
///
@@ -78,7 +98,8 @@ namespace FlaxEditor.GUI.Dialogs
{
AutoFocus = true;
- _colorWheelSprite = Editor.Instance.Icons.ColorWheel128;
+ _hsWheelMaterial = FlaxEngine.Content.LoadAsyncInternal(EditorAssets.HSWheelMaterial);
+ _hsWheelMaterial = _hsWheelMaterial.CreateVirtualInstance();
_wheelRect = new Rectangle(0, 0, wheelSize, wheelSize);
}
@@ -165,17 +186,19 @@ namespace FlaxEditor.GUI.Dialogs
{
base.Draw();
- var hsv = _color.ToHSV();
bool enabled = EnabledInHierarchy;
+ _hsWheelMaterial.SetParameterValue(GrayedOutParamName, enabled ? 1.0f : 0.5f);
+ Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray);
+
// Wheel
float boxExpand = (2.0f * 4.0f / 128.0f) * _wheelRect.Width;
- Render2D.DrawSprite(_colorWheelSprite, _wheelRect.MakeExpanded(boxExpand), enabled ? Color.White : Color.Gray);
- float hAngle = hsv.X * Mathf.DegreesToRadians;
- float hRadius = hsv.Y * _wheelRect.Width * 0.5f;
- var hsPos = new Float2(hRadius * Mathf.Cos(hAngle), -hRadius * Mathf.Sin(hAngle));
- const float wheelBoxSize = 4.0f;
- Render2D.DrawRectangle(new Rectangle(hsPos - (wheelBoxSize * 0.5f) + _wheelRect.Center, new Float2(wheelBoxSize)), _isMouseDownWheel ? Color.Gray : Color.Black);
+ Render2D.DrawMaterial(_hsWheelMaterial, _wheelRect, enabled ? Color.White : Color.Gray);
+ Float3 hsv = _color.ToHSV();
+ Color hsColor = Color.FromHSV(new Float3(hsv.X, hsv.Y, 1));
+ Rectangle wheelRect = wheelDragRect;
+ Render2D.FillRectangle(wheelRect, hsColor);
+ Render2D.DrawRectangle(wheelRect, Color.Black, _isMouseDownWheel ? 2.0f : 1.0f);
}
///
@@ -194,6 +217,15 @@ namespace FlaxEditor.GUI.Dialogs
base.OnMouseMove(location);
}
+ ///
+ public override void OnMouseMoveRelative(Float2 motion)
+ {
+ var location = PointFromScreen(FlaxEngine.Input.MouseScreenPosition);
+ UpdateMouse(ref location);
+
+ base.OnMouseMoveRelative(motion);
+ }
+
///
public override bool OnMouseDown(Float2 location, MouseButton button)
{
@@ -202,6 +234,7 @@ namespace FlaxEditor.GUI.Dialogs
if (!_isMouseDownWheel)
{
_isMouseDownWheel = true;
+ Cursor = CursorType.Hidden;
StartMouseCapture();
SlidingStart?.Invoke();
}
@@ -218,6 +251,10 @@ namespace FlaxEditor.GUI.Dialogs
if (button == MouseButton.Left && _isMouseDownWheel)
{
EndMouseCapture();
+ // Make the cursor appear where the user expects it to be (position of selection rectangle)
+ Rectangle dragRect = wheelDragRect;
+ Root.MousePosition = dragRect.Center + MouseCursorOffset;
+ Cursor = CursorType.Default;
EndSliding();
return true;
}
@@ -246,10 +283,10 @@ namespace FlaxEditor.GUI.Dialogs
///
public class ColorSelectorWithSliders : ColorSelector
{
- private Rectangle _slider1Rect;
- private Rectangle _slider2Rect;
- private bool _isMouseDownSlider1;
- private bool _isMouseDownSlider2;
+ private Rectangle _valueSliderRect;
+ private Rectangle _alphaSliderRect;
+ private bool _isMouseDownValueSlider;
+ private bool _isMouseDownAlphaSlider;
///
/// Initializes a new instance of the class.
@@ -260,26 +297,26 @@ namespace FlaxEditor.GUI.Dialogs
: base(wheelSize)
{
// Setup dimensions
- const float slidersMargin = 8.0f;
- _slider1Rect = new Rectangle(wheelSize + slidersMargin, 0, slidersThickness, wheelSize);
- _slider2Rect = new Rectangle(_slider1Rect.Right + slidersMargin, _slider1Rect.Y, slidersThickness, _slider1Rect.Height);
- Size = new Float2(_slider2Rect.Right, wheelSize);
+ const float slidersMargin = 10.0f;
+ _valueSliderRect = new Rectangle(wheelSize + slidersMargin, 0, slidersThickness, wheelSize);
+ _alphaSliderRect = new Rectangle(_valueSliderRect.Right + slidersMargin * 1.5f, _valueSliderRect.Y, slidersThickness, _valueSliderRect.Height);
+ Size = new Float2(_alphaSliderRect.Right, wheelSize);
}
///
protected override void UpdateMouse(ref Float2 location)
{
- if (_isMouseDownSlider1)
+ if (_isMouseDownValueSlider)
{
var hsv = _color.ToHSV();
- hsv.Z = 1.0f - Mathf.Saturate((location.Y - _slider1Rect.Y) / _slider1Rect.Height);
+ hsv.Z = 1.0f - Mathf.Saturate((location.Y - _valueSliderRect.Y) / _valueSliderRect.Height);
Color = Color.FromHSV(hsv, _color.A);
}
- else if (_isMouseDownSlider2)
+ else if (_isMouseDownAlphaSlider)
{
var color = _color;
- color.A = 1.0f - Mathf.Saturate((location.Y - _slider2Rect.Y) / _slider2Rect.Height);
+ color.A = 1.0f - Mathf.Saturate((location.Y - _alphaSliderRect.Y) / _alphaSliderRect.Height);
Color = color;
}
@@ -296,56 +333,69 @@ namespace FlaxEditor.GUI.Dialogs
// Cache data
var style = Style.Current;
+ var features = Render2D.Features;
+ Render2D.Features = features & ~Render2D.RenderingFeatures.VertexSnapping;
var hsv = _color.ToHSV();
var hs = hsv;
hs.Z = 1.0f;
Color hsC = Color.FromHSV(hs);
- const float slidersOffset = 3.0f;
- const float slidersThickness = 4.0f;
- // Value
- float valueY = _slider2Rect.Height * (1 - hsv.Z);
- var valueR = new Rectangle(_slider1Rect.X - slidersOffset, _slider1Rect.Y + valueY - slidersThickness / 2, _slider1Rect.Width + slidersOffset * 2, slidersThickness);
- Render2D.FillRectangle(_slider1Rect, hsC, hsC, Color.Black, Color.Black);
- Render2D.DrawRectangle(_slider1Rect, _isMouseDownSlider1 ? style.BackgroundSelected : Color.Black);
- Render2D.DrawRectangle(valueR, _isMouseDownSlider1 ? Color.White : Color.Gray);
+ // Value slider
+ float valueKnobExpand = _isMouseDownValueSlider ? 10.0f : 4.0f;
+ float valueY = _valueSliderRect.Height * (1 - hsv.Z);
+ float valueKnobWidth = _valueSliderRect.Width + valueKnobExpand;
+ float valueKnobHeight = _isMouseDownValueSlider ? 7.0f : 4.0f;
+ float valueKnobX = _valueSliderRect.X - valueKnobExpand * 0.5f;
+ float valueKnobY = _valueSliderRect.Y + valueY - valueKnobHeight * 0.5f;
+ Rectangle valueKnobRect = new Rectangle(valueKnobX, valueKnobY, valueKnobWidth, valueKnobHeight);
+ Render2D.FillRectangle(_valueSliderRect, hsC, hsC, Color.Black, Color.Black);
+ // Draw one black and one white border to make the knob visible at any saturation level
+ Render2D.DrawRectangle(valueKnobRect, Color.White, _isMouseDownValueSlider ? 3.0f : 2.0f);
+ Render2D.DrawRectangle(valueKnobRect, Color.Black, _isMouseDownValueSlider ? 2.0f : 1.0f);
- // Draw checkerboard pattern to part of the alpha slider background
- var alphaRect = _slider2Rect;
- Render2D.FillRectangle(alphaRect, Color.White);
- var smallRectSize = alphaRect.Width * 0.5f;
- var numHor = Mathf.CeilToInt(alphaRect.Width / smallRectSize);
- var numVer = Mathf.CeilToInt(alphaRect.Height / smallRectSize);
+ // Draw checkerboard pattern as background of alpha slider
+ Render2D.FillRectangle(_alphaSliderRect, Color.White);
+ var smallRectSize = _alphaSliderRect.Width / 2.0f;
+ var numHor = Mathf.CeilToInt(_alphaSliderRect.Width / smallRectSize);
+ var numVer = Mathf.CeilToInt(_alphaSliderRect.Height / smallRectSize);
+ Render2D.PushClip(_alphaSliderRect);
for (int i = 0; i < numHor; i++)
{
for (int j = 0; j < numVer; j++)
{
if ((i + j) % 2 == 0)
{
- var rect = new Rectangle(alphaRect.X + smallRectSize * i, alphaRect.Y + smallRectSize * j, new Float2(smallRectSize));
- Render2D.PushClip(alphaRect);
+ var rect = new Rectangle(_alphaSliderRect.X + smallRectSize * i, _alphaSliderRect.Y + smallRectSize * j, new Float2(smallRectSize));
Render2D.FillRectangle(rect, Color.Gray);
- Render2D.PopClip();
}
}
}
+ Render2D.PopClip();
- // Alpha
- float alphaY = _slider2Rect.Height * (1 - _color.A);
- var alphaR = new Rectangle(_slider2Rect.X - slidersOffset, _slider2Rect.Y + alphaY - slidersThickness / 2, _slider2Rect.Width + slidersOffset * 2, slidersThickness);
+ // Alpha slider
+ float alphaKnobExpand = _isMouseDownAlphaSlider ? 10.0f : 4.0f;
+ float alphaY = _alphaSliderRect.Height * (1 - _color.A);
+ float alphaKnobWidth = _alphaSliderRect.Width + alphaKnobExpand;
+ float alphaKnobHeight = _isMouseDownAlphaSlider ? 7.0f : 4.0f;
+ float alphaKnobX = _alphaSliderRect.X - alphaKnobExpand * 0.5f;
+ float alphaKnobY = _alphaSliderRect.Y + alphaY - alphaKnobExpand * 0.5f;
+ Rectangle alphaKnobRect = new Rectangle(alphaKnobX, alphaKnobY, alphaKnobWidth, alphaKnobHeight);
var color = _color;
- color.A = 1; // Keep slider 2 fill rect from changing color alpha while selecting.
- Render2D.FillRectangle(_slider2Rect, color, color, Color.Transparent, Color.Transparent);
- Render2D.DrawRectangle(_slider2Rect, _isMouseDownSlider2 ? style.BackgroundSelected : Color.Black);
- Render2D.DrawRectangle(alphaR, _isMouseDownSlider2 ? Color.White : Color.Gray);
+ color.A = 1; // Prevent alpha slider fill from becoming transparent
+ Render2D.FillRectangle(_alphaSliderRect, color, color, Color.Transparent, Color.Transparent);
+ // Draw one black and one white border to make the knob visible at any saturation level
+ Render2D.DrawRectangle(alphaKnobRect, Color.White, _isMouseDownAlphaSlider ? 3.0f : 2.0f);
+ Render2D.DrawRectangle(alphaKnobRect, Color.Black, _isMouseDownAlphaSlider ? 2.0f : 1.0f);
+
+ Render2D.Features = features;
}
///
public override void OnLostFocus()
{
// Clear flags
- _isMouseDownSlider1 = false;
- _isMouseDownSlider2 = false;
+ _isMouseDownValueSlider = false;
+ _isMouseDownAlphaSlider = false;
base.OnLostFocus();
}
@@ -353,15 +403,17 @@ namespace FlaxEditor.GUI.Dialogs
///
public override bool OnMouseDown(Float2 location, MouseButton button)
{
- if (button == MouseButton.Left && _slider1Rect.Contains(location))
+ if (button == MouseButton.Left && _valueSliderRect.Contains(location))
{
- _isMouseDownSlider1 = true;
+ _isMouseDownValueSlider = true;
+ Cursor = CursorType.Hidden;
StartMouseCapture();
UpdateMouse(ref location);
}
- if (button == MouseButton.Left && _slider2Rect.Contains(location))
+ if (button == MouseButton.Left && _alphaSliderRect.Contains(location))
{
- _isMouseDownSlider2 = true;
+ _isMouseDownAlphaSlider = true;
+ Cursor = CursorType.Hidden;
StartMouseCapture();
UpdateMouse(ref location);
}
@@ -372,10 +424,16 @@ namespace FlaxEditor.GUI.Dialogs
///
public override bool OnMouseUp(Float2 location, MouseButton button)
{
- if (button == MouseButton.Left && (_isMouseDownSlider1 || _isMouseDownSlider2))
+ if (button == MouseButton.Left && (_isMouseDownValueSlider || _isMouseDownAlphaSlider))
{
- _isMouseDownSlider1 = false;
- _isMouseDownSlider2 = false;
+ // Make the cursor appear where the user expects it to be (center of slider horizontally and slider knob position vertically)
+ float sliderCenter = _isMouseDownValueSlider ? _valueSliderRect.Center.X : _alphaSliderRect.Center.X;
+ // Calculate y position based on the slider knob to avoid incrementing value by a small amount when the user repeatedly clicks the slider (f.e. when moving in small steps)
+ float mouseSliderPosition = _isMouseDownValueSlider ? _valueSliderRect.Y + _valueSliderRect.Height * (1 - _color.ToHSV().Z) + MouseCursorOffset : _alphaSliderRect.Y + _alphaSliderRect.Height * (1 - _color.A) + MouseCursorOffset;
+ Root.MousePosition = new Float2(sliderCenter + MouseCursorOffset, Mathf.Clamp(mouseSliderPosition, _valueSliderRect.Top, _valueSliderRect.Bottom));
+ Cursor = CursorType.Default;
+ _isMouseDownValueSlider = false;
+ _isMouseDownAlphaSlider = false;
EndMouseCapture();
return true;
}
@@ -387,8 +445,8 @@ namespace FlaxEditor.GUI.Dialogs
public override void OnEndMouseCapture()
{
// Clear flags
- _isMouseDownSlider1 = false;
- _isMouseDownSlider2 = false;
+ _isMouseDownValueSlider = false;
+ _isMouseDownAlphaSlider = false;
base.OnEndMouseCapture();
}
diff --git a/Source/Editor/GUI/Timeline/Timeline.cs b/Source/Editor/GUI/Timeline/Timeline.cs
index 15e7eb953..1fb19e9ec 100644
--- a/Source/Editor/GUI/Timeline/Timeline.cs
+++ b/Source/Editor/GUI/Timeline/Timeline.cs
@@ -2148,10 +2148,9 @@ namespace FlaxEditor.GUI.Timeline
///
public void ShowWholeTimeline()
{
- var viewWidth = Width;
- var timelineWidth = Duration * UnitsPerSecond * Zoom + 8 * StartOffset;
- _backgroundArea.ViewOffset = Float2.Zero;
- Zoom = viewWidth / timelineWidth;
+ const float padding = 40f;
+ Zoom = (_backgroundArea.Width - padding * 2f) / (Duration * UnitsPerSecond);
+ _backgroundArea.ViewOffset = new Float2(-_leftEdge.X + padding, _backgroundArea.ViewOffset.Y);
}
///
diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs
index 1ad754598..75acd170d 100644
--- a/Source/Editor/Options/InterfaceOptions.cs
+++ b/Source/Editor/Options/InterfaceOptions.cs
@@ -253,13 +253,6 @@ namespace FlaxEditor.Options
[EditorDisplay("Interface"), EditorOrder(280), Tooltip("Editor content window orientation.")]
public FlaxEngine.GUI.Orientation ContentWindowOrientation { get; set; } = FlaxEngine.GUI.Orientation.Horizontal;
- ///
- /// If checked, color pickers will always modify the color unless 'Cancel' if pressed, otherwise color won't change unless 'Ok' is pressed.
- ///
- [DefaultValue(true)]
- [EditorDisplay("Interface"), EditorOrder(290)]
- public bool AutoAcceptColorPickerChange { get; set; } = true;
-
///
/// Gets or sets the formatting option for numeric values in the editor.
///
diff --git a/Source/Editor/SceneGraph/Actors/ClothNode.cs b/Source/Editor/SceneGraph/Actors/ClothNode.cs
new file mode 100644
index 000000000..b5caa8258
--- /dev/null
+++ b/Source/Editor/SceneGraph/Actors/ClothNode.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Wojciech Figat. All rights reserved.
+
+using FlaxEngine;
+
+namespace FlaxEditor.SceneGraph.Actors
+{
+ ///
+ /// Scene tree node for actor type.
+ ///
+ [HideInEditor]
+ public sealed class ClothNode : ActorNode
+ {
+ ///
+ public ClothNode(Actor actor)
+ : base(actor)
+ {
+ }
+
+ ///
+ public override void PostSpawn()
+ {
+ base.PostSpawn();
+
+ // Snap to the parent
+ if (!(ParentNode is SceneNode))
+ Actor.LocalTransform = Transform.Identity;
+ }
+ }
+}
diff --git a/Source/Editor/SceneGraph/SceneGraphFactory.cs b/Source/Editor/SceneGraph/SceneGraphFactory.cs
index c4aecef7e..bd465aad2 100644
--- a/Source/Editor/SceneGraph/SceneGraphFactory.cs
+++ b/Source/Editor/SceneGraph/SceneGraphFactory.cs
@@ -51,6 +51,7 @@ namespace FlaxEditor.SceneGraph
CustomNodesTypes.Add(typeof(AudioSource), typeof(AudioSourceNode));
CustomNodesTypes.Add(typeof(BoneSocket), typeof(BoneSocketNode));
CustomNodesTypes.Add(typeof(Decal), typeof(DecalNode));
+ CustomNodesTypes.Add(typeof(Cloth), typeof(ClothNode));
CustomNodesTypes.Add(typeof(BoxCollider), typeof(BoxColliderNode));
CustomNodesTypes.Add(typeof(SphereCollider), typeof(ColliderNode));
CustomNodesTypes.Add(typeof(CapsuleCollider), typeof(ColliderNode));
diff --git a/Source/Editor/Surface/Archetypes/Parameters.cs b/Source/Editor/Surface/Archetypes/Parameters.cs
index 3383e7662..905f793c9 100644
--- a/Source/Editor/Surface/Archetypes/Parameters.cs
+++ b/Source/Editor/Surface/Archetypes/Parameters.cs
@@ -22,15 +22,97 @@ namespace FlaxEditor.Surface.Archetypes
[HideInEditor]
public static class Parameters
{
+ ///
+ /// Surface node type for parameters group Get/Set nodes.
+ ///
+ ///
+ public abstract class SurfaceNodeParamsBase : SurfaceNode
+ {
+ ///
+ /// The combobox for picking parameter.
+ ///
+ protected ComboBoxElement _combobox;
+
+ ///
+ /// Fag used to block layout updated when updating node.
+ ///
+ protected bool _isUpdateLocked;
+
+ ///
+ protected SurfaceNodeParamsBase(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
+ : base(id, context, nodeArch, groupArch)
+ {
+ }
+
+ ///
+ /// Gets the selected parameter.
+ ///
+ /// Surface parameter object or null if nothing selected or cannot find it.
+ protected SurfaceParameter GetSelected()
+ {
+ if (Surface != null)
+ return Surface.GetParameter(_combobox.SelectedItem);
+ return Context.GetParameter((Guid)Values[0]);
+ }
+
+ ///
+ /// Updates the combo box.
+ ///
+ protected void UpdateCombo()
+ {
+ if (_isUpdateLocked)
+ return;
+ _isUpdateLocked = true;
+ if (_combobox == null)
+ {
+ _combobox = GetChild();
+ _combobox.SelectedIndexChanged += OnSelectedChanged;
+ }
+ string toSelect = null;
+ Guid loadedSelected = (Guid)Values[0];
+ _combobox.ClearItems();
+ var parameters = Surface.Parameters;
+ for (int i = 0; i < parameters.Count; i++)
+ {
+ var param = parameters[i];
+ if (!param.IsPublic && !Surface.CanShowPrivateParameters)
+ continue;
+ _combobox.AddItem(param.Name);
+ if (param.ID == loadedSelected)
+ toSelect = param.Name;
+ }
+ _combobox.SelectedItem = toSelect;
+ _isUpdateLocked = false;
+ }
+
+ private void OnSelectedChanged(ComboBox cb)
+ {
+ if (_isUpdateLocked)
+ return;
+ var selected = GetSelected();
+ var selectedID = selected?.ID ?? Guid.Empty;
+ if (selectedID != (Guid)Values[0])
+ Set(selected, ref selectedID);
+ }
+
+ ///
+ /// Sets the selected parameter.
+ ///
+ /// Parameter.
+ /// Parameter identifier.
+ protected virtual void Set(SurfaceParameter selected, ref Guid selectedID)
+ {
+ SetValue(0, selectedID);
+ }
+ }
+
///
/// Surface node type for parameters group Get node.
///
///
- public class SurfaceNodeParamsGet : SurfaceNode, IParametersDependantNode
+ public class SurfaceNodeParamsGet : SurfaceNodeParamsBase, IParametersDependantNode
{
- private ComboBoxElement _combobox;
private readonly List _dynamicChildren = new List();
- private bool _isUpdateLocked;
private ScriptType _layoutType;
private NodeElementArchetype[] _layoutElements;
@@ -306,49 +388,6 @@ namespace FlaxEditor.Surface.Archetypes
UpdateTitle();
}
- private void UpdateCombo()
- {
- if (_isUpdateLocked)
- return;
- _isUpdateLocked = true;
- if (_combobox == null)
- {
- _combobox = (ComboBoxElement)_children[0];
- _combobox.SelectedIndexChanged += OnSelectedChanged;
- }
- int toSelect = -1;
- Guid loadedSelected = (Guid)Values[0];
- _combobox.ClearItems();
- for (int i = 0; i < Surface.Parameters.Count; i++)
- {
- var param = Surface.Parameters[i];
- _combobox.AddItem(param.Name);
- if (param.ID == loadedSelected)
- toSelect = i;
- }
- _combobox.SelectedIndex = toSelect;
- _isUpdateLocked = false;
- }
-
- private void OnSelectedChanged(ComboBox cb)
- {
- if (_isUpdateLocked)
- return;
- var selected = GetSelected();
- var selectedID = selected?.ID ?? Guid.Empty;
- SetValue(0, selectedID);
- }
-
- private SurfaceParameter GetSelected()
- {
- if (Surface != null)
- {
- var selectedIndex = _combobox.SelectedIndex;
- return selectedIndex >= 0 && selectedIndex < Surface.Parameters.Count ? Surface.Parameters[selectedIndex] : null;
- }
- return Context.GetParameter((Guid)Values[0]);
- }
-
private void ClearDynamicElements()
{
for (int i = 0; i < _dynamicChildren.Count; i++)
@@ -463,15 +502,19 @@ namespace FlaxEditor.Surface.Archetypes
else if (!_isUpdateLocked)
{
_isUpdateLocked = true;
- int toSelect = -1;
+ string toSelect = null;
Guid loadedSelected = (Guid)Values[0];
- for (int i = 0; i < Surface.Parameters.Count; i++)
+ var parameters = Surface.Parameters;
+ for (int i = 0; i < parameters.Count; i++)
{
- var param = Surface.Parameters[i];
+ var param = parameters[i];
if (param.ID == loadedSelected)
- toSelect = i;
+ {
+ toSelect = param.Name;
+ break;
+ }
}
- _combobox.SelectedIndex = toSelect;
+ _combobox.SelectedItem = toSelect;
_isUpdateLocked = false;
}
UpdateLayout();
@@ -817,66 +860,23 @@ namespace FlaxEditor.Surface.Archetypes
/// Surface node type for parameters group Set node.
///
///
- public class SurfaceNodeParamsSet : SurfaceNode, IParametersDependantNode
+ public class SurfaceNodeParamsSet : SurfaceNodeParamsBase, IParametersDependantNode
{
- private ComboBoxElement _combobox;
- private bool _isUpdateLocked;
-
///
public SurfaceNodeParamsSet(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
: base(id, context, nodeArch, groupArch)
{
}
- private void UpdateCombo()
+ ///
+ protected override void Set(SurfaceParameter selected, ref Guid selectedID)
{
- if (_isUpdateLocked)
- return;
- _isUpdateLocked = true;
- if (_combobox == null)
+ SetValues(new[]
{
- _combobox = GetChild();
- _combobox.SelectedIndexChanged += OnSelectedChanged;
- }
- int toSelect = -1;
- Guid loadedSelected = (Guid)Values[0];
- _combobox.ClearItems();
- for (int i = 0; i < Surface.Parameters.Count; i++)
- {
- var param = Surface.Parameters[i];
- _combobox.AddItem(param.Name);
- if (param.ID == loadedSelected)
- toSelect = i;
- }
- _combobox.SelectedIndex = toSelect;
- _isUpdateLocked = false;
- }
-
- private void OnSelectedChanged(ComboBox cb)
- {
- if (_isUpdateLocked)
- return;
- var selected = GetSelected();
- var selectedID = selected?.ID ?? Guid.Empty;
- if (selectedID != (Guid)Values[0])
- {
- SetValues(new[]
- {
- selectedID,
- selected != null ? TypeUtils.GetDefaultValue(selected.Type) : null,
- });
- UpdateUI();
- }
- }
-
- private SurfaceParameter GetSelected()
- {
- if (Surface != null)
- {
- var selectedIndex = _combobox.SelectedIndex;
- return selectedIndex >= 0 && selectedIndex < Surface.Parameters.Count ? Surface.Parameters[selectedIndex] : null;
- }
- return Context.GetParameter((Guid)Values[0]);
+ selectedID,
+ selected != null ? TypeUtils.GetDefaultValue(selected.Type) : null,
+ });
+ UpdateUI();
}
///
diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs
index e3bb94bcc..c4a8d31d3 100644
--- a/Source/Editor/Surface/VisjectSurface.cs
+++ b/Source/Editor/Surface/VisjectSurface.cs
@@ -583,6 +583,11 @@ namespace FlaxEditor.Surface
///
public virtual bool CanSetParameters => false;
+ ///
+ /// Gets a value indicating whether surface private parameters can be used, otherwise they will remain hidden.
+ ///
+ public virtual bool CanShowPrivateParameters => false;
+
///
/// True of the context menu should make use of a description panel drawn at the bottom of the menu
///
diff --git a/Source/Editor/Surface/VisjectSurfaceContext.cs b/Source/Editor/Surface/VisjectSurfaceContext.cs
index 0d10aa230..560a7c1d6 100644
--- a/Source/Editor/Surface/VisjectSurfaceContext.cs
+++ b/Source/Editor/Surface/VisjectSurfaceContext.cs
@@ -254,9 +254,10 @@ namespace FlaxEditor.Surface
public SurfaceParameter GetParameter(Guid id)
{
SurfaceParameter result = null;
- for (int i = 0; i < Parameters.Count; i++)
+ var parameters = Parameters;
+ for (int i = 0; i < parameters.Count; i++)
{
- var parameter = Parameters[i];
+ var parameter = parameters[i];
if (parameter.ID == id)
{
result = parameter;
@@ -274,9 +275,10 @@ namespace FlaxEditor.Surface
public SurfaceParameter GetParameter(string name)
{
SurfaceParameter result = null;
- for (int i = 0; i < Parameters.Count; i++)
+ var parameters = Parameters;
+ for (int i = 0; i < parameters.Count; i++)
{
- var parameter = Parameters[i];
+ var parameter = parameters[i];
if (parameter.Name == name)
{
result = parameter;
diff --git a/Source/Editor/Surface/VisualScriptSurface.cs b/Source/Editor/Surface/VisualScriptSurface.cs
index 2f70ee340..57aa29d67 100644
--- a/Source/Editor/Surface/VisualScriptSurface.cs
+++ b/Source/Editor/Surface/VisualScriptSurface.cs
@@ -187,6 +187,9 @@ namespace FlaxEditor.Surface
///
public override bool CanSetParameters => true;
+ ///
+ public override bool CanShowPrivateParameters => true;
+
///
public override bool UseContextMenuDescriptionPanel => true;
diff --git a/Source/Editor/Viewport/ViewportDraggingHelper.cs b/Source/Editor/Viewport/ViewportDraggingHelper.cs
index 21287fc94..3e6fce6ad 100644
--- a/Source/Editor/Viewport/ViewportDraggingHelper.cs
+++ b/Source/Editor/Viewport/ViewportDraggingHelper.cs
@@ -198,6 +198,11 @@ namespace FlaxEditor.Viewport
actor.Position = PostProcessSpawnedActorLocation(actor, ref hitLocation);
_owner.Spawn(actor);
_viewport.Focus();
+
+ // Scroll to the new actor in the hierarchy
+ var actorNode = Editor.Instance.Scene.GetActorNode(actor);
+ if (actorNode?.TreeNode.ParentTree.Parent is Panel treePanel)
+ FlaxEngine.Scripting.InvokeOnUpdate(() => treePanel.ScrollViewTo(actorNode.TreeNode));
}
private void Spawn(ScriptItem item, SceneGraphNode hit, ref Float2 location, ref Vector3 hitLocation, ref Vector3 hitNormal)
diff --git a/Source/Editor/Windows/Assets/AnimationWindow.cs b/Source/Editor/Windows/Assets/AnimationWindow.cs
index 268e18503..8dbcc5921 100644
--- a/Source/Editor/Windows/Assets/AnimationWindow.cs
+++ b/Source/Editor/Windows/Assets/AnimationWindow.cs
@@ -455,6 +455,7 @@ namespace FlaxEditor.Windows.Assets
_timeline.Enabled = true;
_timeline.SetNoTracksText(null);
ClearEditedFlag();
+ _timeline.ShowWholeTimeline();
}
}
diff --git a/Source/Engine/Content/Assets/Animation.cpp b/Source/Engine/Content/Assets/Animation.cpp
index 917db28ac..86fa41f89 100644
--- a/Source/Engine/Content/Assets/Animation.cpp
+++ b/Source/Engine/Content/Assets/Animation.cpp
@@ -691,11 +691,7 @@ Asset::LoadResult Animation::load()
continue;
}
#if USE_EDITOR
- if (!_registeredForScriptingReload)
- {
- _registeredForScriptingReload = true;
- Level::ScriptsReloadStart.Bind(this);
- }
+ _registerForScriptingReload = true;
#endif
}
}
@@ -733,6 +729,7 @@ void Animation::unload(bool isReloading)
{
ScopeWriteLock systemScope(Animations::SystemLocker);
#if USE_EDITOR
+ _registerForScriptingReload = false;
if (_registeredForScriptingReload)
{
_registeredForScriptingReload = false;
@@ -752,6 +749,22 @@ void Animation::unload(bool isReloading)
NestedAnims.Clear();
}
+#if USE_EDITOR
+
+void Animation::onLoaded_MainThread()
+{
+ if (_registerForScriptingReload && !_registeredForScriptingReload)
+ {
+ _registeredForScriptingReload = true;
+ Level::ScriptsReloadStart.Bind(this);
+ }
+ _registerForScriptingReload = false;
+
+ BinaryAsset::onLoaded_MainThread();
+}
+
+#endif
+
AssetChunksFlag Animation::getChunksToPreload() const
{
return GET_CHUNK_FLAG(0);
diff --git a/Source/Engine/Content/Assets/Animation.h b/Source/Engine/Content/Assets/Animation.h
index 4f4a773b4..c342cc64c 100644
--- a/Source/Engine/Content/Assets/Animation.h
+++ b/Source/Engine/Content/Assets/Animation.h
@@ -78,6 +78,7 @@ API_CLASS(NoSpawn) class FLAXENGINE_API Animation : public BinaryAsset
private:
#if USE_EDITOR
+ bool _registerForScriptingReload = false;
bool _registeredForScriptingReload = false;
void OnScriptsReloadStart();
#endif
@@ -163,5 +164,8 @@ protected:
// [BinaryAsset]
LoadResult load() override;
void unload(bool isReloading) override;
+#if USE_EDITOR
+ void onLoaded_MainThread() override;
+#endif
AssetChunksFlag getChunksToPreload() const override;
};
diff --git a/Source/Engine/Graphics/Models/MeshAccessor.cs b/Source/Engine/Graphics/Models/MeshAccessor.cs
index 29aa86c18..ffe509476 100644
--- a/Source/Engine/Graphics/Models/MeshAccessor.cs
+++ b/Source/Engine/Graphics/Models/MeshAccessor.cs
@@ -413,10 +413,16 @@ namespace FlaxEngine
return true;
for (int i = 0; i < buffersLocal.Length; i++)
{
- _data[(int)buffersLocal[i]] = meshBuffers[i];
- _layouts[(int)buffersLocal[i]] = meshLayouts[i];
+ int buffer = (int)buffersLocal[i];
+ _data[buffer] = meshBuffers[i];
+ _layouts[buffer] = meshLayouts[i];
+
+ // Get format if using a single item (eg. index buffer type)
+ var format = PixelFormat.Unknown;
+ if (meshLayouts[i] && meshLayouts[i].Elements.Length == 1)
+ format = meshLayouts[i].Elements[0].Format;
+ _formats[buffer] = format;
}
- _formats[(int)MeshBufferType.Index] = mesh.Use16BitIndexBuffer ? PixelFormat.R16_UInt : PixelFormat.R32_UInt;
return false;
}
diff --git a/Source/Engine/Graphics/Models/MeshAccessor.h b/Source/Engine/Graphics/Models/MeshAccessor.h
index 25fc01a1a..820a6db43 100644
--- a/Source/Engine/Graphics/Models/MeshAccessor.h
+++ b/Source/Engine/Graphics/Models/MeshAccessor.h
@@ -39,51 +39,61 @@ public:
FORCE_INLINE int32 GetInt(int32 index) const
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
return (int32)_sampler.Read(_data.Get() + index * _stride).X;
}
FORCE_INLINE float GetFloat(int32 index) const
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
return _sampler.Read(_data.Get() + index * _stride).X;
}
FORCE_INLINE Float2 GetFloat2(int32 index) const
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
return Float2(_sampler.Read(_data.Get() + index * _stride));
}
FORCE_INLINE Float3 GetFloat3(int32 index) const
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
return Float3(_sampler.Read(_data.Get() + index * _stride));
}
FORCE_INLINE Float4 GetFloat4(int32 index) const
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
return _sampler.Read(_data.Get() + index * _stride);
}
FORCE_INLINE void SetInt(int32 index, const int32 value)
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4((float)value));
}
FORCE_INLINE void SetFloat(int32 index, const float value)
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4(value));
}
FORCE_INLINE void SetFloat2(int32 index, const Float2& value)
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4(value));
}
FORCE_INLINE void SetFloat3(int32 index, const Float3& value)
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, Float4(value));
}
FORCE_INLINE void SetFloat4(int32 index, const Float4& value)
{
+ ASSERT_LOW_LAYER(index * _stride < _data.Length());
_sampler.Write(_data.Get() + index * _stride, value);
}
@@ -94,19 +104,33 @@ public:
void Set(Span src);
void Set(Span src);
void Set(Span src);
+ template
+ void Set(const Array& dst) const
+ {
+ Set(Span(dst.Get(), dst.Count()));
+ }
// Copies the contents of this stream into a destination data span.
void CopyTo(Span dst) const;
void CopyTo(Span dst) const;
void CopyTo(Span dst) const;
+ template
+ void CopyTo(Array& dst) const
+ {
+ dst.Resize(GetCount());
+ CopyTo(Span(dst.Get(), dst.Count()));
+ }
};
private:
BytesContainer _data[(int32)MeshBufferType::MAX];
PixelFormat _formats[(int32)MeshBufferType::MAX] = {};
GPUVertexLayout* _layouts[(int32)MeshBufferType::MAX] = {};
+ Array> _usedModels;
public:
+ ~MeshAccessor();
+
///
/// Loads the data from the mesh.
///
diff --git a/Source/Engine/Graphics/Models/MeshBase.cpp b/Source/Engine/Graphics/Models/MeshBase.cpp
index bbec523a2..81fffb3e7 100644
--- a/Source/Engine/Graphics/Models/MeshBase.cpp
+++ b/Source/Engine/Graphics/Models/MeshBase.cpp
@@ -188,6 +188,16 @@ void MeshAccessor::Stream::CopyTo(Span<::Color> dst) const
}
}
+MeshAccessor::~MeshAccessor()
+{
+ for (ModelBase* model : _usedModels)
+ {
+ if (model->Storage)
+ model->Storage->UnlockChunks();
+ model->RemoveReference();
+ }
+}
+
bool MeshAccessor::LoadMesh(const MeshBase* mesh, bool forceGpu, Span buffers)
{
CHECK_RETURN(mesh, true);
@@ -196,14 +206,28 @@ bool MeshAccessor::LoadMesh(const MeshBase* mesh, bool forceGpu, Span(allBuffers, ARRAY_COUNT(allBuffers));
Array> meshBuffers;
Array> meshLayouts;
+ if (ModelBase* model = mesh->GetModelBase())
+ {
+ // Maintain reference to mesh data (it's buffers might be referenced, not copied)
+ model->AddReference();
+ if (model->Storage)
+ model->Storage->LockChunks();
+ _usedModels.Add(model);
+ }
if (mesh->DownloadData(buffers, meshBuffers, meshLayouts, forceGpu))
return true;
for (int32 i = 0; i < buffers.Length(); i++)
{
- _data[(int32)buffers[i]] = MoveTemp(meshBuffers[i]);
- _layouts[(int32)buffers[i]] = meshLayouts[i];
+ const int32 buffer = (int32)buffers[i];
+ _data[buffer] = MoveTemp(meshBuffers[i]);
+ _layouts[buffer] = meshLayouts[i];
+
+ // Get format if using a single item (eg. index buffer type)
+ PixelFormat format = PixelFormat::Unknown;
+ if (meshLayouts[i] && meshLayouts[i]->GetElements().Count() == 1)
+ format = meshLayouts[i]->GetElements()[0].Format;
+ _formats[buffer] = format;
}
- _formats[(int32)MeshBufferType::Index] = mesh->Use16BitIndexBuffer() ? PixelFormat::R16_UInt : PixelFormat::R32_UInt;
return false;
}
@@ -714,6 +738,9 @@ bool MeshBase::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int3
_cachedVertexBufferCount = meshData.Vertices;
_cachedIndexBufferCount = (int32)meshData.Triangles * 3;
_cachedIndexBuffer.Copy((const byte*)meshData.IBData, _cachedIndexBufferCount * (int32)meshData.IBStride);
+ GPUVertexLayout::Elements ibLayout;
+ ibLayout.Add({ VertexElement::Types::Attribute, 0, 0, 0, meshData.IBStride == sizeof(uint16) ? PixelFormat::R16_UInt : PixelFormat::R32_UInt });
+ _cachedVertexLayouts[3] = GPUVertexLayout::Get(ibLayout);
for (int32 vb = 0; vb < meshData.VBData.Count(); vb++)
{
_cachedVertexBuffers[vb].Copy((const byte*)meshData.VBData[vb], (int32)(meshData.VBLayout[vb]->GetStride() * meshData.Vertices));
@@ -728,6 +755,8 @@ bool MeshBase::DownloadDataCPU(MeshBufferType type, BytesContainer& result, int3
case MeshBufferType::Index:
result.Link(_cachedIndexBuffer);
count = _cachedIndexBufferCount;
+ if (layout)
+ *layout = _cachedVertexLayouts[3];
break;
case MeshBufferType::Vertex0:
result.Link(_cachedVertexBuffers[0]);
diff --git a/Source/Engine/Graphics/Models/MeshBase.h b/Source/Engine/Graphics/Models/MeshBase.h
index 1c76df166..cb51f466b 100644
--- a/Source/Engine/Graphics/Models/MeshBase.h
+++ b/Source/Engine/Graphics/Models/MeshBase.h
@@ -50,7 +50,7 @@ protected:
GPUBuffer* _indexBuffer = nullptr;
mutable BytesContainer _cachedVertexBuffers[MODEL_MAX_VB];
- mutable GPUVertexLayout* _cachedVertexLayouts[MODEL_MAX_VB] = {};
+ mutable GPUVertexLayout* _cachedVertexLayouts[MODEL_MAX_VB + 1] = {};
mutable BytesContainer _cachedIndexBuffer;
mutable int32 _cachedIndexBufferCount = 0, _cachedVertexBufferCount = 0;
diff --git a/Source/Engine/Level/Actors/AnimatedModel.cpp b/Source/Engine/Level/Actors/AnimatedModel.cpp
index 11497e558..ddafd215d 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.cpp
+++ b/Source/Engine/Level/Actors/AnimatedModel.cpp
@@ -1397,6 +1397,16 @@ bool AnimatedModel::GetMeshData(const MeshReference& ref, MeshBufferType type, B
return mesh.DownloadDataCPU(type, result, count, layout);
}
+MeshBase* AnimatedModel::GetMesh(const MeshReference& ref) const
+{
+ const auto model = SkinnedModel.Get();
+ if (!model || model->WaitForLoaded())
+ return nullptr;
+ auto& lod = model->LODs[Math::Min(ref.LODIndex, model->LODs.Count() - 1)];
+ auto& mesh = lod.Meshes[Math::Min(ref.MeshIndex, lod.Meshes.Count() - 1)];
+ return &mesh;
+}
+
MeshDeformation* AnimatedModel::GetMeshDeformation() const
{
if (!_deformation)
diff --git a/Source/Engine/Level/Actors/AnimatedModel.h b/Source/Engine/Level/Actors/AnimatedModel.h
index a520d6723..f9182d115 100644
--- a/Source/Engine/Level/Actors/AnimatedModel.h
+++ b/Source/Engine/Level/Actors/AnimatedModel.h
@@ -479,6 +479,7 @@ public:
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout) const override;
+ MeshBase* GetMesh(const MeshReference& ref) const override;
void UpdateBounds() override;
MeshDeformation* GetMeshDeformation() const override;
void OnDeleteObject() override;
diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.cpp b/Source/Engine/Level/Actors/ModelInstanceActor.cpp
index 527775bfc..b8dc3af1a 100644
--- a/Source/Engine/Level/Actors/ModelInstanceActor.cpp
+++ b/Source/Engine/Level/Actors/ModelInstanceActor.cpp
@@ -14,6 +14,12 @@ String ModelInstanceActor::MeshReference::ToString() const
return String::Format(TEXT("Actor={},LOD={},Mesh={}"), Actor ? Actor->GetNamePath() : String::Empty, LODIndex, MeshIndex);
}
+MeshBase* ModelInstanceActor::MeshReference::Get() const
+{
+ auto actor = Actor.Get();
+ return actor ? actor->GetMesh(*this) : nullptr;
+}
+
void ModelInstanceActor::SetEntries(const Array& value)
{
WaitForModelLoad();
diff --git a/Source/Engine/Level/Actors/ModelInstanceActor.h b/Source/Engine/Level/Actors/ModelInstanceActor.h
index d5e2498fb..d553d132a 100644
--- a/Source/Engine/Level/Actors/ModelInstanceActor.h
+++ b/Source/Engine/Level/Actors/ModelInstanceActor.h
@@ -29,6 +29,7 @@ API_CLASS(Abstract) class FLAXENGINE_API ModelInstanceActor : public Actor
API_FIELD() int32 MeshIndex = 0;
String ToString() const;
+ MeshBase* Get() const;
};
protected:
@@ -113,6 +114,7 @@ public:
///
/// Extracts mesh buffer data from CPU. Might be cached internally (eg. by Model/SkinnedModel).
+ /// [Deprecated in 1.12]
///
/// Mesh reference.
/// Buffer type
@@ -120,11 +122,22 @@ public:
/// The amount of items inside the result buffer.
/// The result layout of the result buffer (for vertex buffers). Optional, pass null to ignore it.
/// True if failed, otherwise false.
+ DEPRECATED("Use GetMesh to resolve mesh reference and access mesh data with MeshAccessor.")
virtual bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout = nullptr) const
{
return true;
}
+ ///
+ /// Resolves a given mesh reference.
+ ///
+ /// Mesh reference.
+ /// Mesh or null if invalid ref.
+ virtual MeshBase* GetMesh(const MeshReference& ref) const
+ {
+ return nullptr;
+ }
+
///
/// Gets the mesh deformation utility for this model instance (optional).
///
diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp
index 19e4e3f34..fad068bf0 100644
--- a/Source/Engine/Level/Actors/StaticModel.cpp
+++ b/Source/Engine/Level/Actors/StaticModel.cpp
@@ -665,6 +665,16 @@ bool StaticModel::GetMeshData(const MeshReference& ref, MeshBufferType type, Byt
return mesh.DownloadDataCPU(type, result, count, layout);
}
+MeshBase* StaticModel::GetMesh(const MeshReference& ref) const
+{
+ const auto model = Model.Get();
+ if (!model || model->WaitForLoaded())
+ return nullptr;
+ auto& lod = model->LODs[Math::Min(ref.LODIndex, model->LODs.Count() - 1)];
+ auto& mesh = lod.Meshes[Math::Min(ref.MeshIndex, lod.Meshes.Count() - 1)];
+ return &mesh;
+}
+
MeshDeformation* StaticModel::GetMeshDeformation() const
{
if (!_deformation)
diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h
index 4b575a0ed..063638e14 100644
--- a/Source/Engine/Level/Actors/StaticModel.h
+++ b/Source/Engine/Level/Actors/StaticModel.h
@@ -181,6 +181,7 @@ public:
bool IntersectsEntry(int32 entryIndex, const Ray& ray, Real& distance, Vector3& normal) override;
bool IntersectsEntry(const Ray& ray, Real& distance, Vector3& normal, int32& entryIndex) override;
bool GetMeshData(const MeshReference& ref, MeshBufferType type, BytesContainer& result, int32& count, GPUVertexLayout** layout) const override;
+ MeshBase* GetMesh(const MeshReference& ref) const override;
MeshDeformation* GetMeshDeformation() const override;
void UpdateBounds() override;
diff --git a/Source/Engine/Physics/Actors/Cloth.cpp b/Source/Engine/Physics/Actors/Cloth.cpp
index 1f833fa0e..3e0eb42d4 100644
--- a/Source/Engine/Physics/Actors/Cloth.cpp
+++ b/Source/Engine/Physics/Actors/Cloth.cpp
@@ -133,7 +133,7 @@ Array Cloth::GetParticles() const
if (_cloth)
{
PROFILE_CPU();
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
PhysicsBackend::LockClothParticles(_cloth);
const Span particles = PhysicsBackend::GetClothParticles(_cloth);
result.Resize(particles.Length());
@@ -150,7 +150,7 @@ Array Cloth::GetParticles() const
void Cloth::SetParticles(Span value)
{
PROFILE_CPU();
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
#if USE_CLOTH_SANITY_CHECKS
{
// Sanity check
@@ -180,7 +180,7 @@ Span Cloth::GetPaint() const
void Cloth::SetPaint(Span value)
{
PROFILE_CPU();
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
#if USE_CLOTH_SANITY_CHECKS
{
// Sanity check
@@ -208,8 +208,12 @@ void Cloth::SetPaint(Span value)
if (_cloth)
{
// Update cloth particles
+ MeshAccessor accessor;
+ MeshBufferType bufferTypes[2] = { MeshBufferType::Index, MeshBufferType::Vertex0 };
+ if (accessor.LoadMesh(GetMesh().Get(), false, ToSpan(bufferTypes, 2)))
+ return;
Array invMasses;
- CalculateInvMasses(invMasses);
+ CalculateInvMasses(accessor, invMasses);
PhysicsBackend::LockClothParticles(_cloth);
PhysicsBackend::SetClothParticles(_cloth, Span(), Span(), ToSpan(invMasses));
PhysicsBackend::SetClothPaint(_cloth, value);
@@ -227,18 +231,20 @@ bool Cloth::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal)
if (_cloth)
{
// Precise per-triangle intersection
- const ModelInstanceActor::MeshReference mesh = GetMesh();
- if (mesh.Actor == nullptr)
+ const ModelInstanceActor::MeshReference meshRef = GetMesh();
+ if (meshRef.Actor == nullptr)
return false;
- BytesContainer indicesData;
- int32 indicesCount;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
+ MeshAccessor accessor;
+ MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
+ if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return false;
+ auto indices = accessor.Index();
+ auto indicesData = indices.GetData();
PhysicsBackend::LockClothParticles(_cloth);
const Span particles = PhysicsBackend::GetClothParticles(_cloth);
const Transform transform = GetTransform();
- const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
- const int32 trianglesCount = indicesCount / 3;
+ const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
+ const int32 trianglesCount = indices.GetCount() / 3;
bool result = false;
distance = MAX_Real;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
@@ -306,7 +312,7 @@ void Cloth::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
{
Actor::Deserialize(stream, modifier);
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
DESERIALIZE_MEMBER(Mesh, _mesh);
_mesh.Actor = nullptr; // Don't store this reference
DESERIALIZE_MEMBER(Force, _forceSettings);
@@ -341,18 +347,20 @@ void Cloth::DrawPhysicsDebug(RenderView& view)
if (_cloth)
{
PROFILE_CPU();
- const ModelInstanceActor::MeshReference mesh = GetMesh();
- if (mesh.Actor == nullptr)
+ const ModelInstanceActor::MeshReference meshRef = GetMesh();
+ if (meshRef.Actor == nullptr)
return;
- BytesContainer indicesData;
- int32 indicesCount;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
+ MeshAccessor accessor;
+ MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
+ if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return;
+ auto indices = accessor.Index();
+ auto indicesData = indices.GetData();
PhysicsBackend::LockClothParticles(_cloth);
const Span particles = PhysicsBackend::GetClothParticles(_cloth);
const Transform transform = GetTransform();
- const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
- const int32 trianglesCount = indicesCount / 3;
+ const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
+ const int32 trianglesCount = indices.GetCount() / 3;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
@@ -390,18 +398,20 @@ void Cloth::OnDebugDrawSelected()
if (_cloth)
{
DEBUG_DRAW_WIRE_BOX(_box, Color::Violet.RGBMultiplied(0.8f), 0, true);
- const ModelInstanceActor::MeshReference mesh = GetMesh();
- if (mesh.Actor == nullptr)
+ const ModelInstanceActor::MeshReference meshRef = GetMesh();
+ if (meshRef.Actor == nullptr)
return;
- BytesContainer indicesData;
- int32 indicesCount;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
+ MeshAccessor accessor;
+ MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
+ if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return;
+ auto indices = accessor.Index();
+ auto indicesData = indices.GetData();
PhysicsBackend::LockClothParticles(_cloth);
const Span particles = PhysicsBackend::GetClothParticles(_cloth);
const Transform transform = GetTransform();
- const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
- const int32 trianglesCount = indicesCount / 3;
+ const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
+ const int32 trianglesCount = indices.GetCount() / 3;
for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
{
const int32 index = triangleIndex * 3;
@@ -542,7 +552,7 @@ bool Cloth::CreateCloth()
{
#if WITH_CLOTH
PROFILE_CPU();
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
// Skip if all vertices are fixed so cloth sim doesn't make sense
if (_paint.HasItems())
@@ -556,26 +566,36 @@ bool Cloth::CreateCloth()
// Get mesh data
// TODO: consider making it via async task so physics can wait on the cloth setup from mesh data just before next fixed update which gives more time when loading scene
- const ModelInstanceActor::MeshReference mesh = GetMesh();
- if (mesh.Actor == nullptr)
+ const ModelInstanceActor::MeshReference meshRef = GetMesh();
+ if (meshRef.Actor == nullptr)
return false;
PhysicsClothDesc desc;
desc.Actor = this;
- BytesContainer data;
- int32 count;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Vertex0, data, count))
+ MeshAccessor accessor;
+ MeshBufferType bufferTypes[2] = { MeshBufferType::Index, MeshBufferType::Vertex0 };
+ if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 2)))
return true;
- // TODO: use MeshAccessor vertex data layout descriptor instead hardcoded position data at the beginning of VB0
- desc.VerticesData = data.Get();
- desc.VerticesCount = count;
- desc.VerticesStride = data.Length() / count;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, data, count))
- return true;
- desc.IndicesData = data.Get();
- desc.IndicesCount = count;
- desc.IndicesStride = data.Length() / count;
+ auto position = accessor.Position();
+ Array tempPositions;
+ if (position.GetFormat() == PixelFormat::R32G32B32_Float)
+ {
+ desc.VerticesData = position.GetData().Get();
+ desc.VerticesCount = position.GetCount();
+ desc.VerticesStride = position.GetStride();
+ }
+ else
+ {
+ position.CopyTo(tempPositions);
+ desc.VerticesData = tempPositions.Get();
+ desc.VerticesCount = tempPositions.Count();
+ desc.VerticesStride = sizeof(Float3);
+ }
+ auto indices = accessor.Index();
+ desc.IndicesData = indices.GetData().Get();
+ desc.IndicesCount = indices.GetCount();
+ desc.IndicesStride = indices.GetStride();
Array invMasses;
- CalculateInvMasses(invMasses);
+ CalculateInvMasses(accessor, invMasses);
desc.InvMassesData = invMasses.Count() == desc.VerticesCount ? invMasses.Get() : nullptr;
desc.InvMassesStride = sizeof(float);
desc.MaxDistancesData = _paint.Count() == desc.VerticesCount ? _paint.Get() : nullptr;
@@ -595,13 +615,13 @@ bool Cloth::CreateCloth()
PhysicsBackend::ClearClothInertia(_cloth);
// Add cloth mesh deformer
- if (auto* deformation = mesh.Actor->GetMeshDeformation())
+ if (auto* deformation = meshRef.Actor->GetMeshDeformation())
{
Function deformer;
deformer.Bind(this);
- deformation->AddDeformer(mesh.LODIndex, mesh.MeshIndex, MeshBufferType::Vertex0, deformer);
+ deformation->AddDeformer(meshRef.LODIndex, meshRef.MeshIndex, MeshBufferType::Vertex0, deformer);
if (_simulationSettings.ComputeNormals)
- deformation->AddDeformer(mesh.LODIndex, mesh.MeshIndex, MeshBufferType::Vertex1, deformer);
+ deformation->AddDeformer(meshRef.LODIndex, meshRef.MeshIndex, MeshBufferType::Vertex1, deformer);
_meshDeformation = deformation;
}
@@ -631,39 +651,32 @@ void Cloth::DestroyCloth()
#endif
}
-void Cloth::CalculateInvMasses(Array& invMasses)
+void Cloth::CalculateInvMasses(MeshAccessor& accessor, Array& invMasses)
{
// Use per-particle max distance to evaluate which particles are immovable
#if WITH_CLOTH
if (_paint.IsEmpty())
return;
PROFILE_CPU();
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
// Get mesh data
- const ModelInstanceActor::MeshReference mesh = GetMesh();
- if (mesh.Actor == nullptr)
- return;
- BytesContainer verticesData;
- int32 verticesCount;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Vertex0, verticesData, verticesCount))
- return;
- BytesContainer indicesData;
- int32 indicesCount;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Index, indicesData, indicesCount))
- return;
+ auto positions = accessor.Position();
+ auto indices = accessor.Index();
+ CHECK(positions.IsValid() && indices.IsValid());
+ int32 verticesCount = positions.GetCount();
if (_paint.Count() != verticesCount)
{
// Fix incorrect paint data
- LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), mesh.ToString(), verticesCount);
+ LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), GetMesh().ToString(), verticesCount);
int32 countBefore = _paint.Count();
_paint.Resize(verticesCount);
for (int32 i = countBefore; i < verticesCount; i++)
_paint.Get()[i] = 0.0f;
}
- const int32 verticesStride = verticesData.Length() / verticesCount;
- const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
- const int32 trianglesCount = indicesCount / 3;
+ const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
+ const int32 trianglesCount = indices.GetCount() / 3;
+ auto indicesData = indices.GetData();
// Sum triangle area for each influenced particle
invMasses.Resize(verticesCount);
@@ -676,12 +689,9 @@ void Cloth::CalculateInvMasses(Array& invMasses)
const int32 i0 = indicesData.Get()[index];
const int32 i1 = indicesData.Get()[index + 1];
const int32 i2 = indicesData.Get()[index + 2];
- // TODO: use MeshAccessor vertex data layout descriptor instead hardcoded position data at the beginning of VB0
-#define GET_POS(i) *(Float3*)((byte*)verticesData.Get() + i * verticesStride)
- const Float3 v0(GET_POS(i0));
- const Float3 v1(GET_POS(i1));
- const Float3 v2(GET_POS(i2));
-#undef GET_POS
+ const Float3 v0(positions.GetFloat3(i0));
+ const Float3 v1(positions.GetFloat3(i1));
+ const Float3 v2(positions.GetFloat3(i2));
const float area = Float3::TriangleArea(v0, v1, v2);
invMasses.Get()[i0] += area;
invMasses.Get()[i1] += area;
@@ -696,12 +706,9 @@ void Cloth::CalculateInvMasses(Array& invMasses)
const int32 i0 = indicesData.Get()[index];
const int32 i1 = indicesData.Get()[index + 1];
const int32 i2 = indicesData.Get()[index + 2];
- // TODO: use MeshAccessor vertex data layout descriptor instead hardcoded position data at the beginning of VB0
-#define GET_POS(i) *(Float3*)((byte*)verticesData.Get() + i * verticesStride)
- const Float3 v0(GET_POS(i0));
- const Float3 v1(GET_POS(i1));
- const Float3 v2(GET_POS(i2));
-#undef GET_POS
+ const Float3 v0(positions.GetFloat3(i0));
+ const Float3 v1(positions.GetFloat3(i1));
+ const Float3 v2(positions.GetFloat3(i2));
const float area = Float3::TriangleArea(v0, v1, v2);
invMasses.Get()[i0] += area;
invMasses.Get()[i1] += area;
@@ -787,25 +794,22 @@ bool Cloth::OnPreUpdate()
{
if (animatedModel->GraphInstance.NodesPose.IsEmpty() || _paint.IsEmpty())
return false;
- const ModelInstanceActor::MeshReference mesh = GetMesh();
- if (mesh.Actor == nullptr)
- return false;
- BytesContainer verticesData;
- int32 verticesCount;
- GPUVertexLayout* layout;
- if (mesh.Actor->GetMeshData(mesh, MeshBufferType::Vertex0, verticesData, verticesCount, &layout))
+ const ModelInstanceActor::MeshReference meshRef = GetMesh();
+ if (meshRef.Actor == nullptr)
return false;
MeshAccessor accessor;
- if (accessor.LoadBuffer(MeshBufferType::Vertex0, verticesData, layout))
+ MeshBufferType bufferTypes[1] = { MeshBufferType::Vertex0 };
+ if (accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
return false;
auto positionStream = accessor.Position();
auto blendIndicesStream = accessor.BlendIndices();
auto blendWeightsStream = accessor.BlendWeights();
if (!positionStream.IsValid() || !blendIndicesStream.IsValid() || !blendWeightsStream.IsValid())
return false;
+ const int32 verticesCount = positionStream.GetCount();
if (verticesCount != _paint.Count())
{
- LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), mesh.ToString(), verticesCount);
+ LOG(Warning, "Incorrect cloth '{}' paint size {} for mesh '{}' that has {} vertices", GetNamePath(), _paint.Count(), meshRef.ToString(), verticesCount);
return false;
}
PROFILE_CPU_NAMED("Skinned Pose");
@@ -925,7 +929,7 @@ void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformat
return;
#if WITH_CLOTH
PROFILE_CPU_NAMED("Cloth");
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
PhysicsBackend::LockClothParticles(_cloth);
const Span particles = PhysicsBackend::GetClothParticles(_cloth);
auto vbCount = (uint32)mesh->GetVertexCount();
@@ -934,49 +938,53 @@ void Cloth::RunClothDeformer(const MeshBase* mesh, MeshDeformationData& deformat
// Calculate normals
Array normals;
const ModelInstanceActor::MeshReference meshRef = GetMesh();
- BytesContainer indicesData;
- int32 indicesCount;
- if ((_simulationSettings.ComputeNormals || deformation.Type == MeshBufferType::Vertex1) &&
- meshRef.Actor && !meshRef.Actor->GetMeshData(meshRef, MeshBufferType::Index, indicesData, indicesCount))
+ if ((_simulationSettings.ComputeNormals || deformation.Type == MeshBufferType::Vertex1) && meshRef.Actor)
{
- PROFILE_CPU_NAMED("Normals");
- // TODO: optimize memory allocs (eg. use shared allocator)
- normals.Resize(vbCount);
- Platform::MemoryClear(normals.Get(), vbCount * sizeof(Float3));
- const bool indices16bit = indicesData.Length() / indicesCount == sizeof(uint16);
- const int32 trianglesCount = indicesCount / 3;
- if (indices16bit)
+ MeshAccessor accessor;
+ MeshBufferType bufferTypes[1] = { MeshBufferType::Index };
+ if (!accessor.LoadMesh(meshRef.Get(), false, ToSpan(bufferTypes, 1)))
{
- for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
+ PROFILE_CPU_NAMED("Normals");
+ auto indices = accessor.Index();
+ auto indicesData = indices.GetData();
+ // TODO: optimize memory allocs (eg. use shared allocator)
+ normals.Resize(vbCount);
+ Platform::MemoryClear(normals.Get(), vbCount * sizeof(Float3));
+ const bool indices16bit = indices.GetFormat() == PixelFormat::R16_UInt;
+ const int32 trianglesCount = indices.GetCount() / 3;
+ if (indices16bit)
{
- const int32 index = triangleIndex * 3;
- const int32 i0 = indicesData.Get()[index];
- const int32 i1 = indicesData.Get()[index + 1];
- const int32 i2 = indicesData.Get()[index + 2];
- const Float3 v0(particles.Get()[i0]);
- const Float3 v1(particles.Get()[i1]);
- const Float3 v2(particles.Get()[i2]);
- const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
- normals.Get()[i0] += normal;
- normals.Get()[i1] += normal;
- normals.Get()[i2] += normal;
+ for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
+ {
+ const int32 index = triangleIndex * 3;
+ const int32 i0 = indicesData.Get()[index];
+ const int32 i1 = indicesData.Get()[index + 1];
+ const int32 i2 = indicesData.Get()[index + 2];
+ const Float3 v0(particles.Get()[i0]);
+ const Float3 v1(particles.Get()[i1]);
+ const Float3 v2(particles.Get()[i2]);
+ const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
+ normals.Get()[i0] += normal;
+ normals.Get()[i1] += normal;
+ normals.Get()[i2] += normal;
+ }
}
- }
- else
- {
- for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
+ else
{
- const int32 index = triangleIndex * 3;
- const int32 i0 = indicesData.Get()[index];
- const int32 i1 = indicesData.Get()[index + 1];
- const int32 i2 = indicesData.Get()[index + 2];
- const Float3 v0(particles.Get()[i0]);
- const Float3 v1(particles.Get()[i1]);
- const Float3 v2(particles.Get()[i2]);
- const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
- normals.Get()[i0] += normal;
- normals.Get()[i1] += normal;
- normals.Get()[i2] += normal;
+ for (int32 triangleIndex = 0; triangleIndex < trianglesCount; triangleIndex++)
+ {
+ const int32 index = triangleIndex * 3;
+ const int32 i0 = indicesData.Get()[index];
+ const int32 i1 = indicesData.Get()[index + 1];
+ const int32 i2 = indicesData.Get()[index + 2];
+ const Float3 v0(particles.Get()[i0]);
+ const Float3 v1(particles.Get()[i1]);
+ const Float3 v2(particles.Get()[i2]);
+ const Float3 normal = Float3::Cross(v1 - v0, v2 - v0);
+ normals.Get()[i0] += normal;
+ normals.Get()[i1] += normal;
+ normals.Get()[i2] += normal;
+ }
}
}
}
diff --git a/Source/Engine/Physics/Actors/Cloth.h b/Source/Engine/Physics/Actors/Cloth.h
index f83f1c499..8d6f13370 100644
--- a/Source/Engine/Physics/Actors/Cloth.h
+++ b/Source/Engine/Physics/Actors/Cloth.h
@@ -371,6 +371,6 @@ private:
ImplementPhysicsDebug;
bool CreateCloth();
void DestroyCloth();
- void CalculateInvMasses(Array& invMasses);
+ void CalculateInvMasses(class MeshAccessor& accessor, Array& invMasses);
void RunClothDeformer(const MeshBase* mesh, struct MeshDeformationData& deformation);
};
diff --git a/Source/Engine/Physics/Colliders/SphereCollider.cpp b/Source/Engine/Physics/Colliders/SphereCollider.cpp
index 0576ae6cf..eda3884e4 100644
--- a/Source/Engine/Physics/Colliders/SphereCollider.cpp
+++ b/Source/Engine/Physics/Colliders/SphereCollider.cpp
@@ -26,7 +26,7 @@ void SphereCollider::SetRadius(const float value)
void SphereCollider::DrawPhysicsDebug(RenderView& view)
{
- const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius);
+ const BoundingSphere sphere(_sphere.Center - view.Origin, Math::Abs(_sphere.Radius));
if (!view.CullingFrustum.Intersects(sphere))
return;
if (view.Mode == ViewMode::PhysicsColliders && !GetIsTrigger())
diff --git a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp
index 379c54930..7e1140bbc 100644
--- a/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp
+++ b/Source/Engine/Physics/PhysX/PhysicsBackendPhysX.cpp
@@ -1201,6 +1201,7 @@ void ScenePhysX::UpdateVehicles(float dt)
void ScenePhysX::PreSimulateCloth(int32 i)
{
PROFILE_CPU();
+ PROFILE_MEM(PhysicsCloth);
auto clothPhysX = ClothsList[i];
auto& clothSettings = Cloths[clothPhysX];
@@ -1403,6 +1404,7 @@ void ScenePhysX::UpdateCloths(float dt)
if (!clothSolver || ClothsList.IsEmpty())
return;
PROFILE_CPU_NAMED("Physics.Cloth");
+ PROFILE_MEM(PhysicsCloth);
{
PROFILE_CPU_NAMED("Pre");
@@ -4029,7 +4031,7 @@ void PhysicsBackend::RemoveVehicle(void* scene, WheeledVehicle* actor)
void* PhysicsBackend::CreateCloth(const PhysicsClothDesc& desc)
{
PROFILE_CPU();
- PROFILE_MEM(Physics);
+ PROFILE_MEM(PhysicsCloth);
#if USE_CLOTH_SANITY_CHECKS
{
// Sanity check
diff --git a/Source/Engine/Profiler/ProfilerMemory.cpp b/Source/Engine/Profiler/ProfilerMemory.cpp
index 3f9bb95d3..d56d524fd 100644
--- a/Source/Engine/Profiler/ProfilerMemory.cpp
+++ b/Source/Engine/Profiler/ProfilerMemory.cpp
@@ -263,6 +263,7 @@ void InitProfilerMemory(const Char* cmdLine, int32 stage)
INIT_PARENT(Level, LevelTerrain);
INIT_PARENT(Navigation, NavigationMesh);
INIT_PARENT(Navigation, NavigationBuilding);
+ INIT_PARENT(Physics, PhysicsCloth);
INIT_PARENT(Scripting, ScriptingVisual);
INIT_PARENT(Scripting, ScriptingCSharp);
INIT_PARENT(ScriptingCSharp, ScriptingCSharpGCCommitted);
diff --git a/Source/Engine/Profiler/ProfilerMemory.h b/Source/Engine/Profiler/ProfilerMemory.h
index 0f38d0802..f3612c308 100644
--- a/Source/Engine/Profiler/ProfilerMemory.h
+++ b/Source/Engine/Profiler/ProfilerMemory.h
@@ -120,6 +120,8 @@ public:
// Total physics memory.
Physics,
+ // Cloth simulation and particles data.
+ PhysicsCloth,
// Total scripting memory allocated by game.
Scripting,
diff --git a/Source/Engine/Terrain/TerrainPatch.cpp b/Source/Engine/Terrain/TerrainPatch.cpp
index 95e9ffd05..61bbf7e3c 100644
--- a/Source/Engine/Terrain/TerrainPatch.cpp
+++ b/Source/Engine/Terrain/TerrainPatch.cpp
@@ -2241,11 +2241,11 @@ void TerrainPatch::DestroyCollision()
_physicsHeightField = nullptr;
#if TERRAIN_USE_PHYSICS_DEBUG
_debugLinesDirty = true;
- SAFE_DELETE(_debugLines);
+ SAFE_DELETE_GPU_RESOURCE(_debugLines);
#endif
#if USE_EDITOR
_collisionTriangles.Resize(0);
- SAFE_DELETE(_collisionTrianglesBuffer);
+ SAFE_DELETE_GPU_RESOURCE(_collisionTrianglesBuffer);
_collisionTrianglesBufferDirty = true;
#endif
_collisionVertices.Resize(0);
diff --git a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp
index 297a6f868..7dd33c12a 100644
--- a/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp
+++ b/Source/Engine/Tools/ModelTool/MeshAccelerationStructure.cpp
@@ -349,7 +349,7 @@ void MeshAccelerationStructure::Add(Model* model, int32 lodIndex)
meshData.IndexBuffer.Copy(indexStream.GetData());
meshData.VertexBuffer.Allocate(meshData.Vertices * sizeof(Float3));
positionStream.CopyTo(ToSpan(meshData.VertexBuffer.Get(), meshData.Vertices));
- meshData.Use16BitIndexBuffer = mesh.Use16BitIndexBuffer();
+ meshData.Use16BitIndexBuffer = indexStream.GetFormat() == PixelFormat::R16_UInt;
meshData.Bounds = mesh.GetBox();
}
}
diff --git a/Source/Engine/UI/GUI/Common/Dropdown.cs b/Source/Engine/UI/GUI/Common/Dropdown.cs
index 6488bb14a..b91d70dc9 100644
--- a/Source/Engine/UI/GUI/Common/Dropdown.cs
+++ b/Source/Engine/UI/GUI/Common/Dropdown.cs
@@ -595,7 +595,7 @@ namespace FlaxEngine.GUI
Size = new Float2(size.X - margin, size.Y),
Font = Font,
TextColor = TextColor * 0.9f,
- TextColorHighlighted = TextColorHighlighted.Brightness < 0.05f ? Color.Lerp(TextColorHighlighted, Color.White, 0.3f) : TextColorHighlighted,
+ TextColorHighlighted = BackgroundColorSelected.Brightness < 0.05f ? Color.Lerp(TextColorHighlighted, Color.White, 0.3f) : TextColorHighlighted,
HorizontalAlignment = HorizontalAlignment,
VerticalAlignment = VerticalAlignment,
Text = _items[i],
diff --git a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs
index 8d581d986..750a16aab 100644
--- a/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs
+++ b/Source/Tools/Flax.Build/Platforms/Windows/WindowsToolchainBase.cs
@@ -4,7 +4,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Runtime.InteropServices;
using System.Text;
using System.Xml;
using Flax.Build.Graph;
@@ -26,6 +25,11 @@ namespace Flax.Build.Platforms
///
protected readonly string _vcToolPath;
+ ///
+ /// The VC tools version.
+ ///
+ protected readonly string _vcToolVersion;
+
///
/// The compiler path.
///
@@ -147,6 +151,21 @@ namespace Flax.Build.Platforms
_libToolPath = Path.Combine(_vcToolPath, "lib.exe");
_xdcmakePath = Path.Combine(_vcToolPath, "xdcmake.exe");
+ // Find 'MSVC\XX.YY.ZZ\bin' to get version
+ _vcToolVersion = string.Empty;
+ var pathParts = _vcToolPath.Split('\\');
+ if (pathParts.Length >= 3)
+ {
+ for (int i = 3; i < pathParts.Length; i++)
+ {
+ if (pathParts[i] == "bin" && pathParts[i - 2] == "MSVC")
+ {
+ _vcToolVersion = pathParts[i - 1];
+ break;
+ }
+ }
+ }
+
// Add Visual C++ toolset include and library paths
var vcToolChainDir = toolsets[Toolset];
SystemIncludePaths.Add(Path.Combine(vcToolChainDir, "include"));
@@ -370,7 +389,7 @@ namespace Flax.Build.Platforms
public override void LogInfo()
{
var sdkPath = WindowsPlatformBase.GetSDKs()[SDK];
- Log.Info(string.Format("Using Windows Toolset {0} ({1})", Toolset, sdkPath));
+ Log.Info(string.Format("Using Windows Toolset {0}, {2} ({1})", Toolset, sdkPath, _vcToolVersion));
Log.Info(string.Format("Using Windows SDK {0} ({1})", WindowsPlatformBase.GetSDKVersion(SDK), _vcToolPath));
}
@@ -673,8 +692,9 @@ namespace Flax.Build.Platforms
var pchSourceFile = Path.Combine(options.IntermediateFolder, Path.ChangeExtension(pchFilName, "cpp"));
var contents = Bindings.BindingsGenerator.GetStringBuilder();
contents.AppendLine("// This code was auto-generated. Do not modify it.");
- // TODO: write compiler version to properly rebuild pch on Visual Studio updates
contents.Append("// Compiler: ").AppendLine(_compilerPath);
+ contents.Append("// Toolchain: ").AppendLine(_vcToolVersion);
+ contents.Append("// CppVersion: ").AppendLine(compileEnvironment.CppVersion.ToString());
contents.Append("#include \"").Append(pchSource).AppendLine("\"");
Utilities.WriteFileIfChanged(pchSourceFile, contents.ToString());
Bindings.BindingsGenerator.PutStringBuilder(contents);