From c5e558172d0cb295b6b925a8cbe794d8029e5c60 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 22 Dec 2022 11:40:10 -0600 Subject: [PATCH 01/16] Added auto save reminder popup with an option to change the reminder time. --- Source/Editor/Editor.cs | 31 ++- Source/Editor/GUI/Popups/AutoSavePopup.cs | 224 ++++++++++++++++++++++ Source/Editor/Options/GeneralOptions.cs | 11 +- 3 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 Source/Editor/GUI/Popups/AutoSavePopup.cs diff --git a/Source/Editor/Editor.cs b/Source/Editor/Editor.cs index 94cbf4ce1..cac61704c 100644 --- a/Source/Editor/Editor.cs +++ b/Source/Editor/Editor.cs @@ -9,6 +9,7 @@ using FlaxEditor.Content; using FlaxEditor.Content.Import; using FlaxEditor.Content.Settings; using FlaxEditor.Content.Thumbnails; +using FlaxEditor.GUI; using FlaxEditor.Modules; using FlaxEditor.Modules.SourceCodeEditing; using FlaxEditor.Options; @@ -46,6 +47,8 @@ namespace FlaxEditor private bool _isAfterInit, _areModulesInited, _areModulesAfterInitEnd, _isHeadlessMode; private string _projectToOpen; private float _lastAutoSaveTimer; + private AutoSavePopup _autoSavePopup; + private bool _autoSaveNow; private Guid _startupSceneCmdLine; private const string ProjectDataLastScene = "LastScene"; @@ -491,7 +494,28 @@ namespace FlaxEditor var timeToNextSave = options.AutoSaveFrequency * 60.0f - timeSinceLastSave; var countDownDuration = 4.0f; - if (timeToNextSave <= 0.0f) + // Show auto save popup + if (timeToNextSave <= options.AutoSaveReminderTime && timeToNextSave >= 0) + { + if (_autoSavePopup == null) + { + _autoSavePopup = AutoSavePopup.Show(Instance.Windows.MainWindow.GUI, timeToNextSave); + _autoSavePopup.SaveNowButton.Clicked += () => _autoSaveNow = true; + _autoSavePopup.CancelSaveButton.Clicked += () => + { + Log("Auto save canceled"); + _autoSavePopup.HidePopup(); + _lastAutoSaveTimer = Time.UnscaledGameTime; // Reset timer + }; + } + else if (!_autoSavePopup.Visible && !_autoSavePopup.UserClosed) + _autoSavePopup.ShowPopup(); + + if (_autoSavePopup.Visible) + _autoSavePopup.UpdateTime(timeToNextSave); + } + + if (timeToNextSave <= 0.0f || _autoSaveNow) { Log("Auto save"); _lastAutoSaveTimer = Time.UnscaledGameTime; @@ -499,6 +523,11 @@ namespace FlaxEditor Scene.SaveScenes(); if (options.AutoSaveContent) SaveContent(); + + // Hide auto save popup and reset user closed + _autoSavePopup.HidePopup(); + _autoSavePopup.UserClosed = false; + _autoSaveNow = false; } else if (timeToNextSave < countDownDuration) { diff --git a/Source/Editor/GUI/Popups/AutoSavePopup.cs b/Source/Editor/GUI/Popups/AutoSavePopup.cs new file mode 100644 index 000000000..bb337059d --- /dev/null +++ b/Source/Editor/GUI/Popups/AutoSavePopup.cs @@ -0,0 +1,224 @@ +using FlaxEngine; +using FlaxEngine.GUI; + +namespace FlaxEditor.GUI +{ + /// + /// Popup menu for reminding of an upcoming auto save. + /// + public class AutoSavePopup : ContainerControl + { + /// + /// Gets or sets the value for if the user has manually closed this popup. + /// + public bool UserClosed { get; set; } + + /// + /// The save now button. Used to skip the last remaining auto save time. + /// + public Button SaveNowButton => _saveNowButton; + + /// + /// Button that should be used to cancel the auto save. + /// + public Button CancelSaveButton => _cancelSaveButton; + + private int _timeRemaining; + private Panel _backgroundPanel; + private Label _autoSaveLabel; + private Label _timeRemainingLabel; + private Label _timeLabel; + private Button _saveNowButton; + private Button _cancelSaveButton; + private Button _closeMenuButton; + private Color _defaultTextColor; + + /// + /// Initialize the AutoSavePopup. + /// + /// The initial time to set the time label to. + public AutoSavePopup(float initialTime) + { + UpdateTime(initialTime); + + _backgroundPanel = new Panel(ScrollBars.None) + { + Parent = this, + AnchorPreset = AnchorPresets.StretchAll, + }; + + _autoSaveLabel = new Label(0, 0, 25, 10) + { + Parent = _backgroundPanel, + Text = "Auto Save", + AutoWidth = true, + AutoHeight = true, + AnchorPreset = AnchorPresets.TopLeft, + }; + _autoSaveLabel.Font.Size = 16; + _autoSaveLabel.LocalX += 5; + _autoSaveLabel.LocalY += 5; + + _timeRemainingLabel = new Label(0, 0, 25, 10) + { + Parent = _backgroundPanel, + Text = "Time Remaining: ", + AutoWidth = true, + AutoHeight = true, + AnchorPreset = AnchorPresets.MiddleLeft, + }; + _timeRemainingLabel.Font.Size = 16; + _timeRemainingLabel.LocalX += 5; + _timeRemainingLabel.LocalY -= _timeRemainingLabel.Height / 2; + + _timeLabel = new Label(0, 0, 25, 10) + { + Parent = _backgroundPanel, + Text = _timeRemaining.ToString(), + HorizontalAlignment = TextAlignment.Far, + VerticalAlignment = TextAlignment.Center, + Width = 25, + AutoHeight = true, + AnchorPreset = AnchorPresets.MiddleRight, + }; + _timeLabel.Font.Size = 16; + _timeLabel.LocalX -= 5; + _timeLabel.LocalY -= _timeLabel.Height / 2; + + _saveNowButton = new Button + { + Parent = _backgroundPanel, + Height = 14, + Width = 60, + AnchorPreset = AnchorPresets.BottomLeft, + BackgroundColor = Color.Transparent, + BorderColor = Color.Transparent, + BackgroundColorHighlighted = Color.Transparent, + BackgroundColorSelected = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + Text = "Save Now", + TooltipText = "Saves now and restarts auto save timer." + }; + _saveNowButton.LocalY -= 5; + _saveNowButton.LocalX += 5; + + _cancelSaveButton = new Button + { + Parent = _backgroundPanel, + Height = 14, + Width = 70, + AnchorPreset = AnchorPresets.BottomRight, + BackgroundColor = Color.Transparent, + BorderColor = Color.Transparent, + BackgroundColorHighlighted = Color.Transparent, + BackgroundColorSelected = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + Text = "Cancel Save", + TooltipText = "Cancels this auto save." + }; + _cancelSaveButton.LocalY -= 5; + _cancelSaveButton.LocalX -= 5; + + _closeMenuButton = new Button + { + Parent = _backgroundPanel, + Height = 14, + Width = 14, + AnchorPreset = AnchorPresets.TopRight, + BackgroundBrush = new SpriteBrush(Style.Current.Cross), + BorderColor = Color.Transparent, + BackgroundColorHighlighted = Style.Current.ForegroundGrey, + BackgroundColorSelected = Color.Transparent, + BorderColorHighlighted = Color.Transparent, + BorderColorSelected = Color.Transparent, + TooltipText = "Close Popup." + }; + _closeMenuButton.LocalY += 5; + _closeMenuButton.LocalX -= 5; + _closeMenuButton.BackgroundColor = _closeMenuButton.TextColor; + _closeMenuButton.Clicked += () => + { + UserClosed = true; + HidePopup(); + }; + + _defaultTextColor = _saveNowButton.TextColor; + } + + /// + /// Updates the time label + /// + /// The time to change the label to. + public void UpdateTime(float time) + { + _timeRemaining = Mathf.CeilToInt(time); + if (_timeLabel != null) + _timeLabel.Text = _timeRemaining.ToString(); + } + + /// + public override void Update(float deltaTime) + { + if (IsMouseOver) + { + _saveNowButton.TextColor = _saveNowButton.IsMouseOver ? Style.Current.BackgroundHighlighted : _defaultTextColor; + _cancelSaveButton.TextColor = _cancelSaveButton.IsMouseOver ? Style.Current.BackgroundHighlighted : _defaultTextColor; + } + base.Update(deltaTime); + } + + /// + public override void Draw() + { + // Draw background + var style = Style.Current; + var bounds = new Rectangle(Float2.Zero, Size); + Render2D.FillRectangle(bounds, style.Background); + Render2D.DrawRectangle(bounds, Color.Lerp(style.BackgroundSelected, style.Background, 0.6f)); + + base.Draw(); + } + + /// + /// Creates and shows the AutoSavePopup + /// + /// The parent control. + /// The time to start at. + /// + public static AutoSavePopup Show(ContainerControl parentControl, float initialTime) + { + var popup = new AutoSavePopup(initialTime) + { + Parent = parentControl, + Height = 75, + Width = 250, + AnchorPreset = AnchorPresets.BottomRight, + }; + popup.LocalX -= 10; + popup.LocalY -= 30; + popup.ShowPopup(); + return popup; + } + + /// + /// Shows the popup by changing its visibility + /// + public void ShowPopup() + { + Visible = true; + UserClosed = false; + _saveNowButton.TextColor = _defaultTextColor; + _cancelSaveButton.TextColor = _defaultTextColor; + } + + /// + /// Hides the popup by changing its visibility + /// + public void HidePopup() + { + Visible = false; + if (_containsFocus) + Defocus(); + } + } +} diff --git a/Source/Editor/Options/GeneralOptions.cs b/Source/Editor/Options/GeneralOptions.cs index 5f05cae2c..d1f168444 100644 --- a/Source/Editor/Options/GeneralOptions.cs +++ b/Source/Editor/Options/GeneralOptions.cs @@ -189,19 +189,26 @@ namespace FlaxEditor.Options [DefaultValue(5), Limit(1)] [EditorDisplay("Auto Save", "Auto Save Frequency"), EditorOrder(801), Tooltip("The interval between auto saves (in minutes)")] public int AutoSaveFrequency { get; set; } = 5; + + /// + /// Gets or sets a value indicating the time before the auto save that the popup shows (in seconds). + /// + [DefaultValue(10), Limit(-1)] + [EditorDisplay("Auto Save", "Auto Save Reminder Time"), EditorOrder(802), Tooltip("The time before the auto save that the reminder popup shows (in seconds). Set to -1 to not show the reminder popup.")] + public int AutoSaveReminderTime { get; set; } = 10; /// /// Gets or sets a value indicating whether enable auto saves for scenes. /// [DefaultValue(true)] - [EditorDisplay("Auto Save", "Auto Save Scenes"), EditorOrder(802), Tooltip("Enables or disables auto saving opened scenes")] + [EditorDisplay("Auto Save", "Auto Save Scenes"), EditorOrder(803), Tooltip("Enables or disables auto saving opened scenes")] public bool AutoSaveScenes { get; set; } = true; /// /// Gets or sets a value indicating whether enable auto saves for content. /// [DefaultValue(true)] - [EditorDisplay("Auto Save", "Auto Save Content"), EditorOrder(803), Tooltip("Enables or disables auto saving content")] + [EditorDisplay("Auto Save", "Auto Save Content"), EditorOrder(804), Tooltip("Enables or disables auto saving content")] public bool AutoSaveContent { get; set; } = true; /// From 5022e05c25e18ac49d5586e44b824eeda27994f1 Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Fri, 23 Dec 2022 21:04:13 +1100 Subject: [PATCH 02/16] Added Stochastic Sampling material node --- Source/Editor/Surface/Archetypes/Textures.cs | 18 +++ .../MaterialGenerator.Textures.cpp | 109 ++++++++++++++++++ 2 files changed, 127 insertions(+) diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index 6e4e34f43..4d9f64085 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -405,6 +405,24 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Output(0, "Color", typeof(Float3), 3) } }, + new NodeArchetype + { + TypeID = 17, + Title = "Stochastic Sample Texture", + Description = "Projects a texture using world-space coordinates instead of UVs.", + Flags = NodeFlags.MaterialGraph, + Size = new Float2(240, 60), + DefaultValues = new object[] + { + new Float2(1.0f, 1.0f), + }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0), + NodeElementArchetype.Factory.Input(1, "UV", true, typeof(Float2), 1, 0), + NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 2), + } + }, }; } } diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index d6ae2719f..d339bb72e 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -669,6 +669,115 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) _writer.Write(*triplanarTexture); value = result; } + case 17: + { + auto textureBox = node->GetBox(0); + auto uvsBox = node->GetBox(1); + + Value uvs; + const bool useCustomUVs = uvsBox->HasConnection(); + if (useCustomUVs) + { + // Get custom UVs + uvs = eatBox(uvsBox->GetParent(), uvsBox->FirstConnection()); + } + else + { + // Use default UVs + uvs = getUVs; + } + + if (!textureBox->HasConnection()) + { + // No texture to sample + value = Value::Zero; + break; + } + + if (!CanUseSample(_treeType)) + { + // Must sample texture in pixel shader + value = Value::Zero; + break; + } + + const auto texture = eatBox(textureBox->GetParent(), textureBox->FirstConnection()); + const auto textureParam = findParam(texture.Value); + const bool isNormalMap = textureParam->Type == MaterialParameterType::NormalMap; + + auto result = writeLocal(Value::InitForZero(ValueType::Float4), node); + createGradients(node); + + auto proceduralSample = String::Format(TEXT( + " {{\n" + " float W1, W2, W3;\n" + " float2 vertex1, vertex2, vertex3;\n" + " float2 uv = {0} * 3.464; // 2 * sqrt (3);\n" + " float2 UV1, UV2, UV3;\n" + " const float2x2 gridToSkewedGrid = float2x2( 1.0, 0.0, -0.57735027, 1.15470054 );\n" + " float2 skewedCoord = mul( gridToSkewedGrid, uv );\n" + " int2 baseId = int2( floor( skewedCoord ) );\n" + " float3 temp = float3( frac( skewedCoord ), 0 );\n" + " temp.z = 1.0 - temp.x - temp.y;\n" + " if ( temp.z > 0.0 )\n" + " {{\n" + " W1 = temp.z;\n" + " W2 = temp.y;\n" + " W3 = temp.x;\n" + " vertex1 = baseId;\n" + " vertex2 = baseId + int2( 0, 1 );\n" + " vertex3 = baseId + int2( 1, 0 );\n" + " }}\n" + " else\n" + " {{\n" + " W1 = -temp.z;\n" + " W2 = 1.0 - temp.y;\n" + " W3 = 1.0 - temp.x;\n" + " vertex1 = baseId + int2( 1, 1 );\n" + " vertex2 = baseId + int2( 1, 0 );\n" + " vertex3 = baseId + int2( 0, 1 );\n" + " }}\n" + " UV1 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex1 ) ) * 43758.5453 );\n" + " UV2 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex2 ) ) * 43758.5453 );\n" + " UV3 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex3 ) ) * 43758.5453 );\n" + " float4 tex1 = {1}.SampleGrad(SamplerLinearWrap, UV1, {2}, {3});\n" + " float4 tex2 = {1}.SampleGrad(SamplerLinearWrap, UV2, {2}, {3});\n" + " float4 tex3 = {1}.SampleGrad(SamplerLinearWrap, UV3, {2}, {3});\n" + ), + uvs.Value, // {0} + texture.Value, // {1} + _ddx.Value, // {2} + _ddy.Value // {3} + ); + + // Decode normal map vector + if (isNormalMap) + { + proceduralSample += String::Format(TEXT( + " tex1.xy = tex1.xy * 2.0 - 1.0;\n" + " tex1.z = sqrt(saturate(1.0 - dot(tex1.xy, tex1.xy))); \n" + " tex2.xy = tex2.xy * 2.0 - 1.0;\n" + " tex2.z = sqrt(saturate(1.0 - dot(tex2.xy, tex2.xy))); \n" + " tex3.xy = tex3.xy * 2.0 - 1.0;\n" + " tex3.z = sqrt(saturate(1.0 - dot(tex3.xy, tex3.xy))); \n" + )); + } + + proceduralSample += String::Format(TEXT( + " tex1 *= W1;\n" + " tex2 *= W2;\n" + " tex3 *= W3;\n" + " {0} = tex1 + tex2 + tex3;\n" + " }}\n" + ), + result.Value // {0} + ); + + _writer.Write(*proceduralSample); + value = result; + + break; + } default: break; } From bba136c9fed1ed0c234faf4f4ce5be013286db62 Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Mon, 26 Dec 2022 20:35:22 +1100 Subject: [PATCH 03/16] Added comment to denote which node it is --- .../Tools/MaterialGenerator/MaterialGenerator.Textures.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index d339bb72e..aae3fac4f 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -669,6 +669,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) _writer.Write(*triplanarTexture); value = result; } + // Stochastic Texture Sample case 17: { auto textureBox = node->GetBox(0); From a2f52edd4780ae91d50397b07940c250ae7e53bd Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Mon, 26 Dec 2022 20:44:53 +1100 Subject: [PATCH 04/16] Updated description & comments --- Source/Editor/Surface/Archetypes/Textures.cs | 4 ++-- .../Tools/MaterialGenerator/MaterialGenerator.Textures.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index 4d9f64085..af8ddf608 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -408,8 +408,8 @@ namespace FlaxEditor.Surface.Archetypes new NodeArchetype { TypeID = 17, - Title = "Stochastic Sample Texture", - Description = "Projects a texture using world-space coordinates instead of UVs.", + Title = "Procedural Sample Texture", + Description = "Samples a texture to create a more natural look with less obvious tiling.", Flags = NodeFlags.MaterialGraph, Size = new Float2(240, 60), DefaultValues = new object[] diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index aae3fac4f..66024540c 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -669,7 +669,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) _writer.Write(*triplanarTexture); value = result; } - // Stochastic Texture Sample + // Procedural Texture Sample case 17: { auto textureBox = node->GetBox(0); From 9099197a885c9918ff31b649e62eefa7d1aa317c Mon Sep 17 00:00:00 2001 From: Olly Rybak Date: Tue, 27 Dec 2022 22:26:01 +1100 Subject: [PATCH 05/16] Added ability to change sample method --- Source/Editor/Surface/Archetypes/Textures.cs | 72 ++++++++++++++++++- .../MaterialGenerator.Textures.cpp | 40 +++++++++-- 2 files changed, 107 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index af8ddf608..60a43a6cb 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -86,6 +86,71 @@ namespace FlaxEditor.Surface.Archetypes } } + // TODO merge the above and below function into one? + internal class ProceduralSampleNode : SurfaceNode + { + private ComboBox _textureGroupPicker; + + public ProceduralSampleNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + } + + public override void OnValuesChanged() + { + base.OnValuesChanged(); + + UpdateUI(); + } + + public override void OnLoaded() + { + base.OnLoaded(); + + UpdateUI(); + } + + private void UpdateUI() + { + if ((int)Values[1] == (int)CommonSamplerType.TextureGroup) + { + if (_textureGroupPicker == null) + { + _textureGroupPicker = new ComboBox + { + Location = new Float2(FlaxEditor.Surface.Constants.NodeMarginX + 50, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize + FlaxEditor.Surface.Constants.LayoutOffsetY * 3), + Width = 100, + Parent = this, + }; + _textureGroupPicker.SelectedIndexChanged += OnSelectedTextureGroupChanged; + var groups = GameSettings.Load(); + if (groups?.TextureGroups != null) + { + for (int i = 0; i < groups.TextureGroups.Length; i++) + _textureGroupPicker.AddItem(groups.TextureGroups[i].Name); + } + } + else + { + _textureGroupPicker.Visible = true; + } + _textureGroupPicker.SelectedIndexChanged -= OnSelectedTextureGroupChanged; + _textureGroupPicker.SelectedIndex = (int)Values[2]; + _textureGroupPicker.SelectedIndexChanged += OnSelectedTextureGroupChanged; + } + else if (_textureGroupPicker != null) + { + _textureGroupPicker.Visible = false; + } + ResizeAuto(); + } + + private void OnSelectedTextureGroupChanged(ComboBox comboBox) + { + SetValue(2, _textureGroupPicker.SelectedIndex); + } + } + /// /// The nodes for that group. /// @@ -408,19 +473,24 @@ namespace FlaxEditor.Surface.Archetypes new NodeArchetype { TypeID = 17, + Create = (id, context, arch, groupArch) => new ProceduralSampleNode(id, context, arch, groupArch), Title = "Procedural Sample Texture", Description = "Samples a texture to create a more natural look with less obvious tiling.", Flags = NodeFlags.MaterialGraph, - Size = new Float2(240, 60), + Size = new Float2(240, 100), DefaultValues = new object[] { new Float2(1.0f, 1.0f), + 2, + 0, }, Elements = new[] { NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0), NodeElementArchetype.Factory.Input(1, "UV", true, typeof(Float2), 1, 0), NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 2), + NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 2, "Sampler"), + NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 2, 100, 1, typeof(CommonSamplerType)) } }, }; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index 66024540c..9c786a827 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -672,6 +672,20 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) // Procedural Texture Sample case 17: { + enum CommonSamplerType + { + LinearClamp = 0, + PointClamp = 1, + LinearWrap = 2, + PointWrap = 3, + TextureGroup = 4, + }; + const Char* SamplerNames[] + { + TEXT("SamplerLinearClamp"), + TEXT("SamplerLinearWrap"), + }; + auto textureBox = node->GetBox(0); auto uvsBox = node->GetBox(1); @@ -706,6 +720,23 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) const auto textureParam = findParam(texture.Value); const bool isNormalMap = textureParam->Type == MaterialParameterType::NormalMap; + const Char* samplerName; + const int32 samplerIndex = node->Values[1].AsInt; + if (samplerIndex == TextureGroup) + { + auto& textureGroupSampler = findOrAddTextureGroupSampler(node->Values[2].AsInt); + samplerName = *textureGroupSampler.ShaderName; + } + else if (samplerIndex >= 0 && samplerIndex < ARRAY_COUNT(SamplerNames)) + { + samplerName = SamplerNames[samplerIndex]; + } + else + { + OnError(node, box, TEXT("Invalid texture sampler.")); + return; + } + auto result = writeLocal(Value::InitForZero(ValueType::Float4), node); createGradients(node); @@ -741,14 +772,15 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) " UV1 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex1 ) ) * 43758.5453 );\n" " UV2 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex2 ) ) * 43758.5453 );\n" " UV3 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex3 ) ) * 43758.5453 );\n" - " float4 tex1 = {1}.SampleGrad(SamplerLinearWrap, UV1, {2}, {3});\n" - " float4 tex2 = {1}.SampleGrad(SamplerLinearWrap, UV2, {2}, {3});\n" - " float4 tex3 = {1}.SampleGrad(SamplerLinearWrap, UV3, {2}, {3});\n" + " float4 tex1 = {1}.SampleGrad({4}, UV1, {2}, {3});\n" + " float4 tex2 = {1}.SampleGrad({4}, UV2, {2}, {3});\n" + " float4 tex3 = {1}.SampleGrad({4}, UV3, {2}, {3});\n" ), uvs.Value, // {0} texture.Value, // {1} _ddx.Value, // {2} - _ddy.Value // {3} + _ddy.Value, // {3} + samplerName // {4} ); // Decode normal map vector From 8c78493a51b3ea428ea04159b6dce5eea9119c61 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Tue, 27 Dec 2022 10:08:08 -0600 Subject: [PATCH 06/16] Simplified auto save poprup and moved it to be on the status bar for better UX. --- Source/Editor/GUI/Popups/AutoSavePopup.cs | 105 ++++++---------------- 1 file changed, 25 insertions(+), 80 deletions(-) diff --git a/Source/Editor/GUI/Popups/AutoSavePopup.cs b/Source/Editor/GUI/Popups/AutoSavePopup.cs index bb337059d..28f9577d8 100644 --- a/Source/Editor/GUI/Popups/AutoSavePopup.cs +++ b/Source/Editor/GUI/Popups/AutoSavePopup.cs @@ -25,13 +25,11 @@ namespace FlaxEditor.GUI private int _timeRemaining; private Panel _backgroundPanel; - private Label _autoSaveLabel; - private Label _timeRemainingLabel; private Label _timeLabel; private Button _saveNowButton; private Button _cancelSaveButton; - private Button _closeMenuButton; private Color _defaultTextColor; + private bool _isMoved = false; /// /// Initialize the AutoSavePopup. @@ -45,69 +43,42 @@ namespace FlaxEditor.GUI { Parent = this, AnchorPreset = AnchorPresets.StretchAll, + BackgroundColor = Color.Transparent, }; - _autoSaveLabel = new Label(0, 0, 25, 10) - { - Parent = _backgroundPanel, - Text = "Auto Save", - AutoWidth = true, - AutoHeight = true, - AnchorPreset = AnchorPresets.TopLeft, - }; - _autoSaveLabel.Font.Size = 16; - _autoSaveLabel.LocalX += 5; - _autoSaveLabel.LocalY += 5; - - _timeRemainingLabel = new Label(0, 0, 25, 10) - { - Parent = _backgroundPanel, - Text = "Time Remaining: ", - AutoWidth = true, - AutoHeight = true, - AnchorPreset = AnchorPresets.MiddleLeft, - }; - _timeRemainingLabel.Font.Size = 16; - _timeRemainingLabel.LocalX += 5; - _timeRemainingLabel.LocalY -= _timeRemainingLabel.Height / 2; - _timeLabel = new Label(0, 0, 25, 10) { Parent = _backgroundPanel, Text = _timeRemaining.ToString(), - HorizontalAlignment = TextAlignment.Far, - VerticalAlignment = TextAlignment.Center, - Width = 25, - AutoHeight = true, - AnchorPreset = AnchorPresets.MiddleRight, + HorizontalAlignment = TextAlignment.Near, + VerticalAlignment = TextAlignment.Near, + AutoWidth = true, + Height = 14, + AnchorPreset = AnchorPresets.MiddleLeft, }; - _timeLabel.Font.Size = 16; - _timeLabel.LocalX -= 5; - _timeLabel.LocalY -= _timeLabel.Height / 2; _saveNowButton = new Button { Parent = _backgroundPanel, Height = 14, Width = 60, - AnchorPreset = AnchorPresets.BottomLeft, + AnchorPreset = AnchorPresets.MiddleRight, BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BackgroundColorHighlighted = Color.Transparent, BackgroundColorSelected = Color.Transparent, BorderColorHighlighted = Color.Transparent, Text = "Save Now", - TooltipText = "Saves now and restarts auto save timer." + TooltipText = "Saves now and restarts the auto save timer." }; - _saveNowButton.LocalY -= 5; - _saveNowButton.LocalX += 5; + _saveNowButton.LocalX -= 85; _cancelSaveButton = new Button { Parent = _backgroundPanel, Height = 14, Width = 70, - AnchorPreset = AnchorPresets.BottomRight, + AnchorPreset = AnchorPresets.MiddleRight, BackgroundColor = Color.Transparent, BorderColor = Color.Transparent, BackgroundColorHighlighted = Color.Transparent, @@ -116,32 +87,8 @@ namespace FlaxEditor.GUI Text = "Cancel Save", TooltipText = "Cancels this auto save." }; - _cancelSaveButton.LocalY -= 5; _cancelSaveButton.LocalX -= 5; - _closeMenuButton = new Button - { - Parent = _backgroundPanel, - Height = 14, - Width = 14, - AnchorPreset = AnchorPresets.TopRight, - BackgroundBrush = new SpriteBrush(Style.Current.Cross), - BorderColor = Color.Transparent, - BackgroundColorHighlighted = Style.Current.ForegroundGrey, - BackgroundColorSelected = Color.Transparent, - BorderColorHighlighted = Color.Transparent, - BorderColorSelected = Color.Transparent, - TooltipText = "Close Popup." - }; - _closeMenuButton.LocalY += 5; - _closeMenuButton.LocalX -= 5; - _closeMenuButton.BackgroundColor = _closeMenuButton.TextColor; - _closeMenuButton.Clicked += () => - { - UserClosed = true; - HidePopup(); - }; - _defaultTextColor = _saveNowButton.TextColor; } @@ -153,7 +100,19 @@ namespace FlaxEditor.GUI { _timeRemaining = Mathf.CeilToInt(time); if (_timeLabel != null) - _timeLabel.Text = _timeRemaining.ToString(); + _timeLabel.Text = "Auto Save in: " + _timeRemaining; + + // Move on text update if the progress bar is visible - removes this call from update + if (Editor.Instance.UI.ProgressVisible && !_isMoved) + { + _isMoved = true; + LocalX -= 250; + } + else if (!Editor.Instance.UI.ProgressVisible && _isMoved) + { + LocalX += 250; + _isMoved = false; + } } /// @@ -167,18 +126,6 @@ namespace FlaxEditor.GUI base.Update(deltaTime); } - /// - public override void Draw() - { - // Draw background - var style = Style.Current; - var bounds = new Rectangle(Float2.Zero, Size); - Render2D.FillRectangle(bounds, style.Background); - Render2D.DrawRectangle(bounds, Color.Lerp(style.BackgroundSelected, style.Background, 0.6f)); - - base.Draw(); - } - /// /// Creates and shows the AutoSavePopup /// @@ -190,12 +137,10 @@ namespace FlaxEditor.GUI var popup = new AutoSavePopup(initialTime) { Parent = parentControl, - Height = 75, + Height = Editor.Instance.UI.StatusBar.Height, Width = 250, AnchorPreset = AnchorPresets.BottomRight, }; - popup.LocalX -= 10; - popup.LocalY -= 30; popup.ShowPopup(); return popup; } From 607505a31653aaba31b15d1f0433f2c3ba6a452a Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 15:11:07 -0600 Subject: [PATCH 07/16] Always capitalize the first letter of a property name label --- Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs index b050e60c3..18bc1878d 100644 --- a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs +++ b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs @@ -51,6 +51,9 @@ namespace FlaxEditor.CustomEditors.GUI /// The name. public PropertyNameLabel(string name) { + // Format name with capital first letter + name = name[0].ToString().ToUpper() + name.Substring(1); + Text = name; HorizontalAlignment = TextAlignment.Near; VerticalAlignment = TextAlignment.Center; From 9d3b8eeeaff18ebc186e5ba7b3573196bad99aeb Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 15:21:48 -0600 Subject: [PATCH 08/16] changed to change it in property UI --- Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs | 3 --- Source/Editor/Utilities/Utils.cs | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs index 18bc1878d..b050e60c3 100644 --- a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs +++ b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs @@ -51,9 +51,6 @@ namespace FlaxEditor.CustomEditors.GUI /// The name. public PropertyNameLabel(string name) { - // Format name with capital first letter - name = name[0].ToString().ToUpper() + name.Substring(1); - Text = name; HorizontalAlignment = TextAlignment.Near; VerticalAlignment = TextAlignment.Center; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index ea7f44efc..c89d824e7 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -912,6 +912,12 @@ namespace FlaxEditor.Utilities { var c = name[i]; + if (i == 0) + { + sb.Append(char.ToUpper(c)); + continue; + } + // Space before word starting with uppercase letter if (char.IsUpper(c) && i > 0) { From 5f83918c06d0b3717eef6ce2903803fe02374dbe Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 15:25:12 -0600 Subject: [PATCH 09/16] small fix --- Source/Editor/Utilities/Utils.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index c89d824e7..e4c06fd9b 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -906,13 +906,16 @@ namespace FlaxEditor.Utilities if (name.StartsWith("g_") || name.StartsWith("m_")) startIndex = 2; + if (name.StartsWith("_")) + startIndex = 1; + // Filter text var lastChar = '\0'; for (int i = startIndex; i < length; i++) { var c = name[i]; - if (i == 0) + if (i == startIndex) { sb.Append(char.ToUpper(c)); continue; From f52e76fe309ef15b5b582e0c017f5ea7b622d91f Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 15:11:07 -0600 Subject: [PATCH 10/16] Always capitalize the first letter of a property name label --- Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs index b050e60c3..18bc1878d 100644 --- a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs +++ b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs @@ -51,6 +51,9 @@ namespace FlaxEditor.CustomEditors.GUI /// The name. public PropertyNameLabel(string name) { + // Format name with capital first letter + name = name[0].ToString().ToUpper() + name.Substring(1); + Text = name; HorizontalAlignment = TextAlignment.Near; VerticalAlignment = TextAlignment.Center; From cf908fcfeb30c3e7d283a82e72a498b5a7554c35 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 15:21:48 -0600 Subject: [PATCH 11/16] changed to change it in property UI --- Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs | 3 --- Source/Editor/Utilities/Utils.cs | 6 ++++++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs index 18bc1878d..b050e60c3 100644 --- a/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs +++ b/Source/Editor/CustomEditors/GUI/PropertyNameLabel.cs @@ -51,9 +51,6 @@ namespace FlaxEditor.CustomEditors.GUI /// The name. public PropertyNameLabel(string name) { - // Format name with capital first letter - name = name[0].ToString().ToUpper() + name.Substring(1); - Text = name; HorizontalAlignment = TextAlignment.Near; VerticalAlignment = TextAlignment.Center; diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index ea7f44efc..c89d824e7 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -912,6 +912,12 @@ namespace FlaxEditor.Utilities { var c = name[i]; + if (i == 0) + { + sb.Append(char.ToUpper(c)); + continue; + } + // Space before word starting with uppercase letter if (char.IsUpper(c) && i > 0) { From 7c3d5d739a2a9cf4092733914e4c61e8dd407908 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 15:25:12 -0600 Subject: [PATCH 12/16] small fix --- Source/Editor/Utilities/Utils.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Utilities/Utils.cs b/Source/Editor/Utilities/Utils.cs index c89d824e7..e4c06fd9b 100644 --- a/Source/Editor/Utilities/Utils.cs +++ b/Source/Editor/Utilities/Utils.cs @@ -906,13 +906,16 @@ namespace FlaxEditor.Utilities if (name.StartsWith("g_") || name.StartsWith("m_")) startIndex = 2; + if (name.StartsWith("_")) + startIndex = 1; + // Filter text var lastChar = '\0'; for (int i = startIndex; i < length; i++) { var c = name[i]; - if (i == 0) + if (i == startIndex) { sb.Append(char.ToUpper(c)); continue; From 80baa1381460baad30708d97ab4294025b8e4223 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Thu, 29 Dec 2022 21:14:51 -0600 Subject: [PATCH 13/16] Fix tests to match captial first letter --- Source/Tools/FlaxEngine.Tests/TestPropertyNameUI.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Source/Tools/FlaxEngine.Tests/TestPropertyNameUI.cs b/Source/Tools/FlaxEngine.Tests/TestPropertyNameUI.cs index 0b7644190..14adb0c40 100644 --- a/Source/Tools/FlaxEngine.Tests/TestPropertyNameUI.cs +++ b/Source/Tools/FlaxEngine.Tests/TestPropertyNameUI.cs @@ -11,10 +11,10 @@ namespace FlaxEditor.Tests [Test] public void TestFormatting() { - Assert.AreEqual("property", Utils.GetPropertyNameUI("property")); - Assert.AreEqual("property", Utils.GetPropertyNameUI("_property")); - Assert.AreEqual("property", Utils.GetPropertyNameUI("m_property")); - Assert.AreEqual("property", Utils.GetPropertyNameUI("g_property")); + Assert.AreEqual("Property", Utils.GetPropertyNameUI("property")); + Assert.AreEqual("Property", Utils.GetPropertyNameUI("_property")); + Assert.AreEqual("Property", Utils.GetPropertyNameUI("m_property")); + Assert.AreEqual("Property", Utils.GetPropertyNameUI("g_property")); Assert.AreEqual("Property", Utils.GetPropertyNameUI("Property")); Assert.AreEqual("Property 1", Utils.GetPropertyNameUI("Property1")); Assert.AreEqual("Property Name", Utils.GetPropertyNameUI("PropertyName")); From 6a46efcf459327c0e730306c8bb7e3cb6124c071 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Fri, 30 Dec 2022 18:40:11 -0600 Subject: [PATCH 14/16] Fixed missing the scene animation proxy from the content context menu. --- Source/Editor/Content/Proxy/SceneAnimationProxy.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Content/Proxy/SceneAnimationProxy.cs b/Source/Editor/Content/Proxy/SceneAnimationProxy.cs index 30409208e..d275be6a1 100644 --- a/Source/Editor/Content/Proxy/SceneAnimationProxy.cs +++ b/Source/Editor/Content/Proxy/SceneAnimationProxy.cs @@ -36,6 +36,7 @@ namespace FlaxEditor.Content /// A asset proxy object. /// /// + [ContentContextMenu("New/Animation/Scene Animation")] public class SceneAnimationProxy : BinaryAssetProxy { /// From 0cd562c5286cb9d6c7af11a30cdf4d3163ea4652 Mon Sep 17 00:00:00 2001 From: Chandler Cox Date: Sun, 8 Jan 2023 16:04:55 -0600 Subject: [PATCH 15/16] Fix in visject cm sometimes not populating all options once search box is cleared. --- Source/Editor/Surface/ContextMenu/VisjectCM.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCM.cs b/Source/Editor/Surface/ContextMenu/VisjectCM.cs index 5057e4445..f537c2714 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCM.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCM.cs @@ -397,6 +397,13 @@ namespace FlaxEditor.Surface.ContextMenu return; Profiler.BeginEvent("VisjectCM.OnSearchFilterChanged"); + + if (string.IsNullOrEmpty(_searchBox.Text)) + { + ResetView(); + Profiler.EndEvent(); + return; + } // Update groups LockChildrenRecursive(); From 986fa13585181d6c8386a8c3989c85fd73b7a492 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Fri, 13 Jan 2023 11:37:53 +0100 Subject: [PATCH 16/16] Reuse texture sampling code for procedural sampling node #872 --- Source/Editor/Surface/Archetypes/Textures.cs | 81 +----- .../MaterialGenerator.Textures.cpp | 244 ++++++------------ 2 files changed, 84 insertions(+), 241 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Textures.cs b/Source/Editor/Surface/Archetypes/Textures.cs index 842667173..258bfd41a 100644 --- a/Source/Editor/Surface/Archetypes/Textures.cs +++ b/Source/Editor/Surface/Archetypes/Textures.cs @@ -86,71 +86,6 @@ namespace FlaxEditor.Surface.Archetypes } } - // TODO merge the above and below function into one? - internal class ProceduralSampleNode : SurfaceNode - { - private ComboBox _textureGroupPicker; - - public ProceduralSampleNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) - : base(id, context, nodeArch, groupArch) - { - } - - public override void OnValuesChanged() - { - base.OnValuesChanged(); - - UpdateUI(); - } - - public override void OnLoaded() - { - base.OnLoaded(); - - UpdateUI(); - } - - private void UpdateUI() - { - if ((int)Values[1] == (int)CommonSamplerType.TextureGroup) - { - if (_textureGroupPicker == null) - { - _textureGroupPicker = new ComboBox - { - Location = new Float2(FlaxEditor.Surface.Constants.NodeMarginX + 50, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize + FlaxEditor.Surface.Constants.LayoutOffsetY * 3), - Width = 100, - Parent = this, - }; - _textureGroupPicker.SelectedIndexChanged += OnSelectedTextureGroupChanged; - var groups = GameSettings.Load(); - if (groups?.TextureGroups != null) - { - for (int i = 0; i < groups.TextureGroups.Length; i++) - _textureGroupPicker.AddItem(groups.TextureGroups[i].Name); - } - } - else - { - _textureGroupPicker.Visible = true; - } - _textureGroupPicker.SelectedIndexChanged -= OnSelectedTextureGroupChanged; - _textureGroupPicker.SelectedIndex = (int)Values[2]; - _textureGroupPicker.SelectedIndexChanged += OnSelectedTextureGroupChanged; - } - else if (_textureGroupPicker != null) - { - _textureGroupPicker.Visible = false; - } - ResizeAuto(); - } - - private void OnSelectedTextureGroupChanged(ComboBox comboBox) - { - SetValue(2, _textureGroupPicker.SelectedIndex); - } - } - /// /// The nodes for that group. /// @@ -473,24 +408,26 @@ namespace FlaxEditor.Surface.Archetypes new NodeArchetype { TypeID = 17, - Create = (id, context, arch, groupArch) => new ProceduralSampleNode(id, context, arch, groupArch), + Create = (id, context, arch, groupArch) => new SampleTextureNode(id, context, arch, groupArch), Title = "Procedural Sample Texture", Description = "Samples a texture to create a more natural look with less obvious tiling.", Flags = NodeFlags.MaterialGraph, - Size = new Float2(240, 100), + Size = new Float2(240, 110), + ConnectionsHints = ConnectionsHint.Vector, DefaultValues = new object[] { - new Float2(1.0f, 1.0f), 2, + -1.0f, 0, }, Elements = new[] { NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0), - NodeElementArchetype.Factory.Input(1, "UV", true, typeof(Float2), 1, 0), - NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 2), - NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 2, "Sampler"), - NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 2, 100, 1, typeof(CommonSamplerType)) + NodeElementArchetype.Factory.Input(1, "UVs", true, null, 1), + NodeElementArchetype.Factory.Input(2, "Offset", true, typeof(Float2), 3), + NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 4), + NodeElementArchetype.Factory.Text(0, Surface.Constants.LayoutOffsetY * 4, "Sampler"), + NodeElementArchetype.Factory.ComboBox(50, Surface.Constants.LayoutOffsetY * 4, 100, 0, typeof(CommonSamplerType)) } }, }; diff --git a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp index 2011fb2c5..103101bec 100644 --- a/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp +++ b/Source/Engine/Tools/MaterialGenerator/MaterialGenerator.Textures.cpp @@ -459,6 +459,8 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) } // Sample Texture case 9: + // Procedural Texture Sample + case 17: { enum CommonSamplerType { @@ -479,7 +481,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) // Get input boxes auto textureBox = node->GetBox(0); auto uvsBox = node->GetBox(1); - auto levelBox = node->GetBox(2); + auto levelBox = node->TryGetBox(2); auto offsetBox = node->GetBox(3); if (!textureBox->HasConnection()) { @@ -519,7 +521,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) // Get other inputs const auto level = tryGetValue(levelBox, node->Values[1]); - const bool useLevel = levelBox->HasConnection() || (int32)node->Values[1] != -1; + const bool useLevel = (levelBox && levelBox->HasConnection()) || (int32)node->Values[1] != -1; const bool useOffset = offsetBox->HasConnection(); const auto offset = useOffset ? eatBox(offsetBox->GetParent(), offsetBox->FirstConnection()) : Value::Zero; const Char* samplerName; @@ -539,32 +541,78 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) return; } - // Pick a property format string - const Char* format; - if (useLevel || !canUseSample) + // Create texture sampling code + if (node->TypeID == 9) { - if (useOffset) - format = TEXT("{0}.SampleLevel({1}, {2}, {3}, {4})"); + // Sample Texture + const Char* format; + if (useLevel || !canUseSample) + { + if (useOffset) + format = TEXT("{0}.SampleLevel({1}, {2}, {3}, {4})"); + else + format = TEXT("{0}.SampleLevel({1}, {2}, {3})"); + } else - format = TEXT("{0}.SampleLevel({1}, {2}, {3})"); + { + if (useOffset) + format = TEXT("{0}.Sample({1}, {2}, {4})"); + else + format = TEXT("{0}.Sample({1}, {2})"); + } + const String sampledValue = String::Format(format, texture.Value, samplerName, uvs.Value, level.Value, offset.Value); + textureBox->Cache = writeLocal(VariantType::Float4, sampledValue, node); } else { - if (useOffset) - format = TEXT("{0}.Sample({1}, {2}, {4})"); - else - format = TEXT("{0}.Sample({1}, {2})"); - } + // Procedural Texture Sample + textureBox->Cache = writeLocal(Value::InitForZero(ValueType::Float4), node); + createGradients(node); + auto proceduralSample = String::Format(TEXT( + " {{\n" + " float3 weights;\n" + " float2 vertex1, vertex2, vertex3;\n" + " float2 uv = {0} * 3.464; // 2 * sqrt (3);\n" + " float2 uv1, uv2, uv3;\n" + " const float2x2 gridToSkewedGrid = float2x2(1.0, 0.0, -0.57735027, 1.15470054);\n" + " float2 skewedCoord = mul(gridToSkewedGrid, uv);\n" + " int2 baseId = int2(floor(skewedCoord));\n" + " float3 temp = float3(frac(skewedCoord), 0);\n" + " temp.z = 1.0 - temp.x - temp.y;\n" + " if (temp.z > 0.0)\n" + " {{\n" + " weights = float3(temp.z, temp.y, temp.x);\n" + " vertex1 = baseId;\n" + " vertex2 = baseId + int2(0, 1);\n" + " vertex3 = baseId + int2(1, 0);\n" + " }}\n" + " else\n" + " {{\n" + " weights = float3(-temp.z, 1.0 - temp.y, 1.0 - temp.x);\n" + " vertex1 = baseId + int2(1, 1);\n" + " vertex2 = baseId + int2(1, 0);\n" + " vertex3 = baseId + int2(0, 1);\n" + " }}\n" + " uv1 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex1)) * 43758.5453);\n" + " uv2 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex2)) * 43758.5453);\n" + " uv3 = {0} + frac(sin(mul(float2x2(127.1, 311.7, 269.5, 183.3), vertex3)) * 43758.5453);\n" + " float4 tex1 = {1}.SampleGrad({4}, uv1, {2}, {3}, {6}) * weights.x;\n" + " float4 tex2 = {1}.SampleGrad({4}, uv2, {2}, {3}, {6}) * weights.y;\n" + " float4 tex3 = {1}.SampleGrad({4}, uv3, {2}, {3}, {6}) * weights.z;\n" + " {5} = tex1 + tex2 + tex3;\n" + " }}\n" + ), + uvs.Value, // {0} + texture.Value, // {1} + _ddx.Value, // {2} + _ddy.Value, // {3} + samplerName, // {4} + textureBox->Cache.Value, // {5} + offset.Value // {6} + ); - // Sample texture - const String sampledValue = String::Format(format, - texture.Value, // {0} - samplerName, // {1} - uvs.Value, // {2} - level.Value, // {3} - offset.Value // {4} - ); - textureBox->Cache = writeLocal(VariantType::Float4, sampledValue, node); + _writer.Write(*proceduralSample); + } // Decode normal map vector if (isNormalMap) @@ -627,7 +675,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) auto textureBox = node->GetBox(0); auto scaleBox = node->GetBox(1); auto blendBox = node->GetBox(2); - + if (!textureBox->HasConnection()) { // No texture to sample @@ -660,157 +708,15 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value) " {3} += {0}.Sample(SamplerLinearWrap, worldPos.xy) * normal.z;\n" " }}\n" ), - texture.Value, // {0} - scale.Value, // {1} - blend.Value, // {2} - result.Value // {3} + texture.Value, // {0} + scale.Value, // {1} + blend.Value, // {2} + result.Value // {3} ); _writer.Write(*triplanarTexture); value = result; } - // Procedural Texture Sample - case 17: - { - enum CommonSamplerType - { - LinearClamp = 0, - PointClamp = 1, - LinearWrap = 2, - PointWrap = 3, - TextureGroup = 4, - }; - const Char* SamplerNames[] - { - TEXT("SamplerLinearClamp"), - TEXT("SamplerLinearWrap"), - }; - - auto textureBox = node->GetBox(0); - auto uvsBox = node->GetBox(1); - - Value uvs; - const bool useCustomUVs = uvsBox->HasConnection(); - if (useCustomUVs) - { - // Get custom UVs - uvs = eatBox(uvsBox->GetParent(), uvsBox->FirstConnection()); - } - else - { - // Use default UVs - uvs = getUVs; - } - - if (!textureBox->HasConnection()) - { - // No texture to sample - value = Value::Zero; - break; - } - - if (!CanUseSample(_treeType)) - { - // Must sample texture in pixel shader - value = Value::Zero; - break; - } - - const auto texture = eatBox(textureBox->GetParent(), textureBox->FirstConnection()); - const auto textureParam = findParam(texture.Value); - const bool isNormalMap = textureParam->Type == MaterialParameterType::NormalMap; - - const Char* samplerName; - const int32 samplerIndex = node->Values[1].AsInt; - if (samplerIndex == TextureGroup) - { - auto& textureGroupSampler = findOrAddTextureGroupSampler(node->Values[2].AsInt); - samplerName = *textureGroupSampler.ShaderName; - } - else if (samplerIndex >= 0 && samplerIndex < ARRAY_COUNT(SamplerNames)) - { - samplerName = SamplerNames[samplerIndex]; - } - else - { - OnError(node, box, TEXT("Invalid texture sampler.")); - return; - } - - auto result = writeLocal(Value::InitForZero(ValueType::Float4), node); - createGradients(node); - - auto proceduralSample = String::Format(TEXT( - " {{\n" - " float W1, W2, W3;\n" - " float2 vertex1, vertex2, vertex3;\n" - " float2 uv = {0} * 3.464; // 2 * sqrt (3);\n" - " float2 UV1, UV2, UV3;\n" - " const float2x2 gridToSkewedGrid = float2x2( 1.0, 0.0, -0.57735027, 1.15470054 );\n" - " float2 skewedCoord = mul( gridToSkewedGrid, uv );\n" - " int2 baseId = int2( floor( skewedCoord ) );\n" - " float3 temp = float3( frac( skewedCoord ), 0 );\n" - " temp.z = 1.0 - temp.x - temp.y;\n" - " if ( temp.z > 0.0 )\n" - " {{\n" - " W1 = temp.z;\n" - " W2 = temp.y;\n" - " W3 = temp.x;\n" - " vertex1 = baseId;\n" - " vertex2 = baseId + int2( 0, 1 );\n" - " vertex3 = baseId + int2( 1, 0 );\n" - " }}\n" - " else\n" - " {{\n" - " W1 = -temp.z;\n" - " W2 = 1.0 - temp.y;\n" - " W3 = 1.0 - temp.x;\n" - " vertex1 = baseId + int2( 1, 1 );\n" - " vertex2 = baseId + int2( 1, 0 );\n" - " vertex3 = baseId + int2( 0, 1 );\n" - " }}\n" - " UV1 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex1 ) ) * 43758.5453 );\n" - " UV2 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex2 ) ) * 43758.5453 );\n" - " UV3 = {0} + frac( sin( mul( float2x2( 127.1, 311.7, 269.5, 183.3 ), vertex3 ) ) * 43758.5453 );\n" - " float4 tex1 = {1}.SampleGrad({4}, UV1, {2}, {3});\n" - " float4 tex2 = {1}.SampleGrad({4}, UV2, {2}, {3});\n" - " float4 tex3 = {1}.SampleGrad({4}, UV3, {2}, {3});\n" - ), - uvs.Value, // {0} - texture.Value, // {1} - _ddx.Value, // {2} - _ddy.Value, // {3} - samplerName // {4} - ); - - // Decode normal map vector - if (isNormalMap) - { - proceduralSample += String::Format(TEXT( - " tex1.xy = tex1.xy * 2.0 - 1.0;\n" - " tex1.z = sqrt(saturate(1.0 - dot(tex1.xy, tex1.xy))); \n" - " tex2.xy = tex2.xy * 2.0 - 1.0;\n" - " tex2.z = sqrt(saturate(1.0 - dot(tex2.xy, tex2.xy))); \n" - " tex3.xy = tex3.xy * 2.0 - 1.0;\n" - " tex3.z = sqrt(saturate(1.0 - dot(tex3.xy, tex3.xy))); \n" - )); - } - - proceduralSample += String::Format(TEXT( - " tex1 *= W1;\n" - " tex2 *= W2;\n" - " tex3 *= W3;\n" - " {0} = tex1 + tex2 + tex3;\n" - " }}\n" - ), - result.Value // {0} - ); - - _writer.Write(*proceduralSample); - value = result; - - break; - } default: break; }