Merge remote-tracking branch 'origin/master' into 1.9
This commit is contained in:
@@ -119,6 +119,20 @@ void PS_Forward(
|
||||
float3 screenColor = sceneColorTexture.SampleLevel(SamplerPointClamp, hit.xy, 0).rgb;
|
||||
reflections = lerp(reflections, screenColor, hit.z);
|
||||
}
|
||||
|
||||
// Fallback to software tracing if possible
|
||||
#if USE_GLOBAL_SURFACE_ATLAS && CAN_USE_GLOBAL_SURFACE_ATLAS
|
||||
if (hit.z < REFLECTIONS_HIT_THRESHOLD)
|
||||
{
|
||||
float3 reflectWS = ScreenSpaceReflectionDirection(screenUV, gBuffer, ViewPos);
|
||||
float4 surfaceAtlas;
|
||||
if (TraceSDFSoftwareReflections(gBuffer, reflectWS, surfaceAtlas))
|
||||
{
|
||||
float3 screenColor = sceneColorTexture.SampleLevel(SamplerPointClamp, hit.xy, 0).rgb;
|
||||
reflections = lerp(surfaceAtlas, float4(screenColor, 1), hit.z);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
light.rgb += reflections * GetReflectionSpecularLighting(ViewPos, gBuffer) * light.a;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
||||
|
||||
@0// SDF Reflections: Defines
|
||||
#define USE_GLOBAL_SURFACE_ATLAS 1
|
||||
@1// SDF Reflections: Includes
|
||||
#include "./Flax/GlobalSignDistanceField.hlsl"
|
||||
#include "./Flax/GI/GlobalSurfaceAtlas.hlsl"
|
||||
@2// SDF Reflections: Constants
|
||||
GlobalSDFData GlobalSDF;
|
||||
GlobalSurfaceAtlasData GlobalSurfaceAtlas;
|
||||
@3// SDF Reflections: Resources
|
||||
Texture3D<float> GlobalSDFTex : register(t__SRV__);
|
||||
Texture3D<float> GlobalSDFMip : register(t__SRV__);
|
||||
ByteAddressBuffer GlobalSurfaceAtlasChunks : register(t__SRV__);
|
||||
ByteAddressBuffer RWGlobalSurfaceAtlasCulledObjects : register(t__SRV__);
|
||||
Buffer<float4> GlobalSurfaceAtlasObjects : register(t__SRV__);
|
||||
Texture2D GlobalSurfaceAtlasDepth : register(t__SRV__);
|
||||
Texture2D GlobalSurfaceAtlasTex : register(t__SRV__);
|
||||
@4// SDF Reflections: Utilities
|
||||
bool TraceSDFSoftwareReflections(GBufferSample gBuffer, float3 reflectWS, out float4 surfaceAtlas)
|
||||
{
|
||||
GlobalSDFTrace sdfTrace;
|
||||
float maxDistance = GLOBAL_SDF_WORLD_SIZE;
|
||||
float selfOcclusionBias = GlobalSDF.CascadeVoxelSize[0];
|
||||
sdfTrace.Init(gBuffer.WorldPos + gBuffer.Normal * selfOcclusionBias, reflectWS, 0.0f, maxDistance);
|
||||
GlobalSDFHit sdfHit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, sdfTrace);
|
||||
if (sdfHit.IsHit())
|
||||
{
|
||||
float3 hitPosition = sdfHit.GetHitPosition(sdfTrace);
|
||||
float surfaceThreshold = GetGlobalSurfaceAtlasThreshold(GlobalSDF, sdfHit);
|
||||
surfaceAtlas = SampleGlobalSurfaceAtlas(GlobalSurfaceAtlas, GlobalSurfaceAtlasChunks, RWGlobalSurfaceAtlasCulledObjects, GlobalSurfaceAtlasObjects, GlobalSurfaceAtlasDepth, GlobalSurfaceAtlasTex, hitPosition, -reflectWS, surfaceThreshold);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@5// SDF Reflections: Shaders
|
||||
@@ -719,7 +719,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
Proxy.Insert(0, proxy);
|
||||
if (rebuild)
|
||||
Rebuild();
|
||||
Rebuild(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -731,7 +731,7 @@ namespace FlaxEditor.Modules
|
||||
{
|
||||
Proxy.Remove(proxy);
|
||||
if (rebuild)
|
||||
Rebuild();
|
||||
Rebuild(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1263,7 +1263,9 @@ namespace FlaxEditor.Modules
|
||||
|
||||
// Lazy-rebuilds
|
||||
if (_rebuildFlag)
|
||||
{
|
||||
RebuildInternal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
@@ -316,7 +316,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
|
||||
var memberReader = xmlReader.ReadSubtree();
|
||||
if (memberReader.ReadToDescendant("summary"))
|
||||
{
|
||||
result[rawName] = memberReader.ReadInnerXml().Replace('\n', ' ').Trim();
|
||||
// Remove <see cref=""/> and replace them with the captured group (the content of the cref). Additionally, getting rid of prefixes
|
||||
const string crefPattern = @"<see\s+cref=""(?:[A-Z]:FlaxEngine\.)?([^""]+)""\s*\/>";
|
||||
result[rawName] = Regex.Replace(memberReader.ReadInnerXml(), crefPattern, "$1").Replace('\n', ' ').Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -375,6 +375,13 @@ namespace FlaxEditor.Options
|
||||
[EditorDisplay("Visject"), EditorOrder(550)]
|
||||
public float ConnectionCurvature { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the visject connection curvature.
|
||||
/// </summary>
|
||||
[DefaultValue(true)]
|
||||
[EditorDisplay("Visject"), EditorOrder(550), Tooltip("Shows/hides the description panel in the visual scripting context menu.")]
|
||||
public bool VisualScriptingDescriptionPanel { get; set; } = true;
|
||||
|
||||
private static FontAsset DefaultFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.PrimaryFont);
|
||||
private static FontAsset ConsoleFont => FlaxEngine.Content.LoadAsyncInternal<FontAsset>(EditorAssets.InconsolataRegularFont);
|
||||
|
||||
|
||||
@@ -184,30 +184,103 @@ namespace FlaxEditor.SceneGraph.GUI
|
||||
}
|
||||
else
|
||||
{
|
||||
var text = Text;
|
||||
if (QueryFilterHelper.Match(filterText, text, out QueryFilterHelper.Range[] ranges))
|
||||
var splitFilter = filterText.Split(',');
|
||||
var hasAllFilters = true;
|
||||
foreach (var filter in splitFilter)
|
||||
{
|
||||
// Update highlights
|
||||
if (_highlights == null)
|
||||
_highlights = new List<Rectangle>(ranges.Length);
|
||||
else
|
||||
_highlights.Clear();
|
||||
var font = Style.Current.FontSmall;
|
||||
var textRect = TextRect;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
if (string.IsNullOrEmpty(filter))
|
||||
continue;
|
||||
var trimmedFilter = filter.Trim();
|
||||
var hasFilter = false;
|
||||
|
||||
// Check if script
|
||||
if (trimmedFilter.Contains("s:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var start = font.GetCharPosition(text, ranges[i].StartIndex);
|
||||
var end = font.GetCharPosition(text, ranges[i].EndIndex);
|
||||
_highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
|
||||
// Check for any scripts
|
||||
if (trimmedFilter.Equals("s:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Actor != null)
|
||||
{
|
||||
if (Actor.ScriptsCount > 0)
|
||||
{
|
||||
hasFilter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var scriptText = trimmedFilter.Replace("s:", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||
var scriptFound = false;
|
||||
if (Actor != null)
|
||||
{
|
||||
foreach (var script in Actor.Scripts)
|
||||
{
|
||||
var name = TypeUtils.GetTypeDisplayName(script.GetType());
|
||||
var nameNoSpaces = name.Replace(" ", "");
|
||||
if (name.Contains(scriptText, StringComparison.OrdinalIgnoreCase) || nameNoSpaces.Contains(scriptText, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
scriptFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasFilter = scriptFound;
|
||||
}
|
||||
}
|
||||
// Check for actor type
|
||||
else if (trimmedFilter.Contains("a:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (trimmedFilter.Equals("a:", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (Actor != null)
|
||||
hasFilter = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Actor !=null)
|
||||
{
|
||||
var actorTypeText = trimmedFilter.Replace("a:", "", StringComparison.OrdinalIgnoreCase).Trim();
|
||||
var name = TypeUtils.GetTypeDisplayName(Actor.GetType());
|
||||
var nameNoSpaces = name.Replace(" ", "");
|
||||
if (name.Contains(actorTypeText, StringComparison.OrdinalIgnoreCase) || nameNoSpaces.Contains(actorTypeText, StringComparison.OrdinalIgnoreCase))
|
||||
hasFilter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Match text
|
||||
else
|
||||
{
|
||||
var text = Text;
|
||||
if (QueryFilterHelper.Match(trimmedFilter, text, out QueryFilterHelper.Range[] ranges))
|
||||
{
|
||||
// Update highlights
|
||||
if (_highlights == null)
|
||||
_highlights = new List<Rectangle>(ranges.Length);
|
||||
else
|
||||
_highlights.Clear();
|
||||
var font = Style.Current.FontSmall;
|
||||
var textRect = TextRect;
|
||||
for (int i = 0; i < ranges.Length; i++)
|
||||
{
|
||||
var start = font.GetCharPosition(text, ranges[i].StartIndex);
|
||||
var end = font.GetCharPosition(text, ranges[i].EndIndex);
|
||||
_highlights.Add(new Rectangle(start.X + textRect.X, textRect.Y, end.X - start.X, textRect.Height));
|
||||
}
|
||||
hasFilter = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasFilter)
|
||||
{
|
||||
hasAllFilters = false;
|
||||
break;
|
||||
}
|
||||
isThisVisible = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hide
|
||||
|
||||
isThisVisible = hasAllFilters;
|
||||
if (!hasAllFilters)
|
||||
_highlights?.Clear();
|
||||
isThisVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update children
|
||||
|
||||
@@ -59,8 +59,8 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
Op1(1, "Bitwise NOT", "Negates the value using bitwise operation", new[] { "!", "~" }),
|
||||
Op2(2, "Bitwise AND", "Performs a bitwise conjunction on two values", new[] { "&" }),
|
||||
Op2(3, "Bitwise OR", "", new[] { "|" }),
|
||||
Op2(4, "Bitwise XOR", "", new[] { "^" }),
|
||||
Op2(3, "Bitwise OR", "Performs a bitwise disjunction on two values", new[] { "|" }),
|
||||
Op2(4, "Bitwise XOR", "Performs a bitwise exclusive disjunction on two values", new[] { "^" }),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +58,11 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
public static NodeArchetype[] Nodes =
|
||||
{
|
||||
Op1(1, "Boolean NOT", "Negates the boolean value", new[] { "!", "~" }),
|
||||
Op2(2, "Boolean AND", "Performs a logical conjunction on two values", new[] { "&&" }),
|
||||
Op2(3, "Boolean OR", "Returns true if either (or both) of its operands is true", new[] { "||" }),
|
||||
Op2(4, "Boolean XOR", "", new[] { "^" }),
|
||||
Op2(5, "Boolean NOR", ""),
|
||||
Op2(6, "Boolean NAND", ""),
|
||||
Op2(2, "Boolean AND", "Performs a logical conjunction on two values. Returns true if both of its operands are true", new[] { "&&" }),
|
||||
Op2(3, "Boolean OR", "Performs a logical disjunction on two values. Returns true if either (or both) of its operands is true", new[] { "||" }),
|
||||
Op2(4, "Boolean XOR", "Performs a logical exclusive disjunction on two values. Returns true if both of its operands are different", new[] { "^" }),
|
||||
Op2(5, "Boolean NOR", "Performs a logical disjunction on two values and negates it. Returns true if both of its operands are false"),
|
||||
Op2(6, "Boolean NAND", "Performs a logical conjunction on two values and negates it. Returns true if either (or both) of its operands are false"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +182,13 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
|
||||
internal static void GetInputOutputDescription(NodeArchetype nodeArch, out (string, ScriptType)[] inputs, out (string, ScriptType)[] outputs)
|
||||
{
|
||||
var type = new ScriptType(nodeArch.DefaultValues[0].GetType());
|
||||
inputs = null;
|
||||
outputs = [(type.Name, type)];
|
||||
}
|
||||
}
|
||||
|
||||
private class ArrayNode : SurfaceNode
|
||||
@@ -321,6 +328,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
array.SetValue(value, box.ID - 1);
|
||||
SetValue(0, array);
|
||||
}
|
||||
|
||||
internal static void GetInputOutputDescription(NodeArchetype nodeArch, out (string, ScriptType)[] inputs, out (string, ScriptType)[] outputs)
|
||||
{
|
||||
inputs = null;
|
||||
outputs = [("", new ScriptType(typeof(Array)))];
|
||||
}
|
||||
}
|
||||
|
||||
private class DictionaryNode : SurfaceNode
|
||||
@@ -449,6 +462,12 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
array.SetValue(value, box.ID - 1);
|
||||
SetValue(0, array);
|
||||
}
|
||||
|
||||
internal static void GetInputOutputDescription(NodeArchetype nodeArch, out (string, ScriptType)[] inputs, out (string, ScriptType)[] outputs)
|
||||
{
|
||||
inputs = null;
|
||||
outputs = [("", new ScriptType(typeof(System.Collections.Generic.Dictionary<int, string>)))];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -743,6 +762,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Enum",
|
||||
Create = (id, context, arch, groupArch) => new EnumNode(id, context, arch, groupArch),
|
||||
Description = "Enum constant value.",
|
||||
GetInputOutputDescription = EnumNode.GetInputOutputDescription,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
DefaultValues = new object[]
|
||||
@@ -779,6 +799,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Array",
|
||||
Create = (id, context, arch, groupArch) => new ArrayNode(id, context, arch, groupArch),
|
||||
Description = "Constant array value.",
|
||||
GetInputOutputDescription = ArrayNode.GetInputOutputDescription,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Float2(150, 20),
|
||||
DefaultValues = new object[] { new int[] { 0, 1, 2 } },
|
||||
@@ -790,6 +811,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Title = "Dictionary",
|
||||
Create = (id, context, arch, groupArch) => new DictionaryNode(id, context, arch, groupArch),
|
||||
Description = "Creates an empty dictionary.",
|
||||
GetInputOutputDescription = DictionaryNode.GetInputOutputDescription,
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||
Size = new Float2(150, 40),
|
||||
DefaultValues = new object[] { typeof(int).FullName, typeof(string).FullName },
|
||||
|
||||
@@ -245,6 +245,29 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void GetInputOutputDescription(NodeArchetype nodeArch, out (string, ScriptType)[] inputs, out (string, ScriptType)[] outputs)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray();
|
||||
var fieldsLength = fields.Length;
|
||||
inputs = new (string, ScriptType)[fieldsLength];
|
||||
for (var i = 0; i < fieldsLength; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
inputs[i] = (field.Name, field.ValueType);
|
||||
}
|
||||
|
||||
outputs = [(type.Name, type)];
|
||||
return;
|
||||
}
|
||||
|
||||
inputs = null;
|
||||
outputs = null;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class UnpackStructureNode : StructureNode
|
||||
@@ -283,6 +306,29 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static void GetInputOutputDescription(NodeArchetype nodeArch, out (string, ScriptType)[] inputs, out (string, ScriptType)[] outputs)
|
||||
{
|
||||
var typeName = (string)nodeArch.DefaultValues[0];
|
||||
var type = TypeUtils.GetType(typeName);
|
||||
if (type)
|
||||
{
|
||||
inputs = [(type.Name, type)];
|
||||
|
||||
var fields = type.GetMembers(BindingFlags.Public | BindingFlags.Instance).Where(x => x.IsField).ToArray();
|
||||
var fieldsLength = fields.Length;
|
||||
outputs = new (string, ScriptType)[fieldsLength];
|
||||
for (var i = 0; i < fieldsLength; i++)
|
||||
{
|
||||
var field = fields[i];
|
||||
outputs[i] = (field.Name, field.ValueType);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
inputs = null;
|
||||
outputs = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -411,6 +457,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Create = (id, context, arch, groupArch) => new PackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = PackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = PackStructureNode.IsOutputCompatible,
|
||||
GetInputOutputDescription = PackStructureNode.GetInputOutputDescription,
|
||||
Description = "Makes the structure data to from the components.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
@@ -523,6 +570,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Create = (id, context, arch, groupArch) => new UnpackStructureNode(id, context, arch, groupArch),
|
||||
IsInputCompatible = UnpackStructureNode.IsInputCompatible,
|
||||
IsOutputCompatible = UnpackStructureNode.IsOutputCompatible,
|
||||
GetInputOutputDescription = UnpackStructureNode.GetInputOutputDescription,
|
||||
Description = "Breaks the structure data to allow extracting components from it.",
|
||||
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph | NodeFlags.NoSpawnViaGUI,
|
||||
Size = new Float2(180, 20),
|
||||
|
||||
@@ -122,7 +122,7 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
{
|
||||
Name = module.Title,
|
||||
Tag = module.TypeID,
|
||||
TooltipText = module.Description,
|
||||
TooltipText = $"{(string.IsNullOrEmpty(module.Signature) ? module.Title : module.Signature)}\n{module.Description}",
|
||||
});
|
||||
}
|
||||
cm.ItemClicked += item => AddModule((ushort)item.Tag);
|
||||
|
||||
@@ -408,9 +408,9 @@ namespace FlaxEditor.Surface.Archetypes
|
||||
Elements = new[]
|
||||
{
|
||||
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Scale", true, typeof(Float3), 1, 0),
|
||||
NodeElementArchetype.Factory.Input(1, "Scale", true, typeof(Float4), 1, 0),
|
||||
NodeElementArchetype.Factory.Input(2, "Blend", true, typeof(float), 2, 1),
|
||||
NodeElementArchetype.Factory.Output(0, "Color", typeof(Float3), 3)
|
||||
NodeElementArchetype.Factory.Output(0, "Color", typeof(Float4), 3)
|
||||
}
|
||||
},
|
||||
new NodeArchetype
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEditor.GUI.Input;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Utilities;
|
||||
@@ -39,6 +41,9 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// <returns>TThe list of surface parameters or null if failed (readonly).</returns>
|
||||
public delegate List<SurfaceParameter> ParameterGetterDelegate();
|
||||
|
||||
private const float DefaultWidth = 300;
|
||||
private const float DefaultHeight = 400;
|
||||
|
||||
private readonly List<VisjectCMGroup> _groups = new List<VisjectCMGroup>(16);
|
||||
private CheckBox _contextSensitiveToggle;
|
||||
private bool _contextSensitiveSearchEnabled = true;
|
||||
@@ -52,10 +57,31 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
private NodeArchetype _parameterGetNodeArchetype;
|
||||
private NodeArchetype _parameterSetNodeArchetype;
|
||||
|
||||
// Description panel elements
|
||||
private readonly bool _useDescriptionPanel;
|
||||
private bool _descriptionPanelVisible;
|
||||
private readonly Panel _descriptionPanel;
|
||||
private readonly Panel _descriptionPanelSeparator;
|
||||
private readonly Image _descriptionDeclaringClassImage;
|
||||
private readonly Label _descriptionSignatureLabel;
|
||||
private readonly Label _descriptionLabel;
|
||||
private readonly VerticalPanel _descriptionInputPanel;
|
||||
private readonly VerticalPanel _descriptionOutputPanel;
|
||||
private readonly SurfaceStyle _surfaceStyle;
|
||||
private VisjectCMItem _selectedItem;
|
||||
|
||||
/// <summary>
|
||||
/// The selected item
|
||||
/// </summary>
|
||||
public VisjectCMItem SelectedItem;
|
||||
public VisjectCMItem SelectedItem
|
||||
{
|
||||
get => _selectedItem;
|
||||
private set
|
||||
{
|
||||
_selectedItem = value;
|
||||
_selectedItem?.OnSelect();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when any item in this popup menu gets clicked.
|
||||
@@ -82,6 +108,11 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// </summary>
|
||||
public bool CanSetParameters;
|
||||
|
||||
/// <summary>
|
||||
/// True if the surface should make use of a description panel drawn at the bottom of the context menu
|
||||
/// </summary>
|
||||
public bool UseDescriptionPanel;
|
||||
|
||||
/// <summary>
|
||||
/// The groups archetypes. Cannot be null.
|
||||
/// </summary>
|
||||
@@ -111,6 +142,11 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
/// The parameter setter node archetype to spawn when adding the parameter getter. Can be null.
|
||||
/// </summary>
|
||||
public NodeArchetype ParameterSetNodeArchetype;
|
||||
|
||||
/// <summary>
|
||||
/// The surface style to use to draw images in the description panel
|
||||
/// </summary>
|
||||
public SurfaceStyle Style;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -127,9 +163,11 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
_parameterGetNodeArchetype = info.ParameterGetNodeArchetype ?? Archetypes.Parameters.Nodes[0];
|
||||
if (info.CanSetParameters)
|
||||
_parameterSetNodeArchetype = info.ParameterSetNodeArchetype ?? Archetypes.Parameters.Nodes[3];
|
||||
_useDescriptionPanel = info.UseDescriptionPanel;
|
||||
_surfaceStyle = info.Style;
|
||||
|
||||
// Context menu dimensions
|
||||
Size = new Float2(300, 400);
|
||||
Size = new Float2(_useDescriptionPanel ? DefaultWidth + 50f : DefaultWidth, DefaultHeight);
|
||||
|
||||
var headerPanel = new Panel(ScrollBars.None)
|
||||
{
|
||||
@@ -190,7 +228,6 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
Bounds = new Rectangle(0, _searchBox.Bottom + 1, Width, Height - _searchBox.Bottom - 2),
|
||||
Parent = this
|
||||
};
|
||||
|
||||
_panel1 = panel1;
|
||||
|
||||
// Create second panel (for groups arrangement)
|
||||
@@ -202,6 +239,70 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
};
|
||||
_groupsPanel = panel2;
|
||||
|
||||
// Create description panel elements only when description panel is about to be used
|
||||
if (_useDescriptionPanel)
|
||||
{
|
||||
_descriptionPanel = new Panel(ScrollBars.None)
|
||||
{
|
||||
Parent = this,
|
||||
Bounds = new Rectangle(0, Height, Width, 0),
|
||||
BackgroundColor = Style.Current.BackgroundNormal,
|
||||
};
|
||||
|
||||
_descriptionDeclaringClassImage = new Image(8, 12, 20, 20)
|
||||
{
|
||||
Parent = _descriptionPanel,
|
||||
Brush = new SpriteBrush(info.Style.Icons.BoxClose),
|
||||
};
|
||||
|
||||
var descriptionFontReference = new FontReference(Style.Current.FontMedium.Asset, 9f);
|
||||
_descriptionSignatureLabel = new Label(32, 8, Width - 40, 0)
|
||||
{
|
||||
Parent = _descriptionPanel,
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
VerticalAlignment = TextAlignment.Near,
|
||||
Wrapping = TextWrapping.WrapWords,
|
||||
Font = descriptionFontReference,
|
||||
Bold = true,
|
||||
AutoHeight = true,
|
||||
};
|
||||
_descriptionSignatureLabel.SetAnchorPreset(AnchorPresets.TopLeft, true);
|
||||
|
||||
_descriptionLabel = new Label(32, 0, Width - 40, 0)
|
||||
{
|
||||
Parent = _descriptionPanel,
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
VerticalAlignment = TextAlignment.Near,
|
||||
Wrapping = TextWrapping.WrapWords,
|
||||
Font = descriptionFontReference,
|
||||
AutoHeight = true,
|
||||
};
|
||||
_descriptionLabel.SetAnchorPreset(AnchorPresets.TopLeft, true);
|
||||
|
||||
_descriptionPanelSeparator = new Panel(ScrollBars.None)
|
||||
{
|
||||
Parent = _descriptionPanel,
|
||||
Bounds = new Rectangle(8, Height, Width - 16, 2),
|
||||
BackgroundColor = Style.Current.BackgroundHighlighted,
|
||||
};
|
||||
|
||||
_descriptionInputPanel = new VerticalPanel()
|
||||
{
|
||||
Parent = _descriptionPanel,
|
||||
X = 8,
|
||||
Width = Width * 0.5f - 16,
|
||||
AutoSize = true,
|
||||
};
|
||||
|
||||
_descriptionOutputPanel = new VerticalPanel()
|
||||
{
|
||||
Parent = _descriptionPanel,
|
||||
X = Width * 0.5f + 8,
|
||||
Width = Width * 0.5f - 16,
|
||||
AutoSize = true,
|
||||
};
|
||||
}
|
||||
|
||||
// Init groups
|
||||
var nodes = new List<NodeArchetype>();
|
||||
foreach (var groupArchetype in info.Groups)
|
||||
@@ -693,9 +794,189 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
{
|
||||
Focus(null);
|
||||
|
||||
if (_useDescriptionPanel)
|
||||
HideDescriptionPanel();
|
||||
|
||||
base.Hide();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the description panel and shows information about the set archetype
|
||||
/// </summary>
|
||||
/// <param name="archetype">The node archetype</param>
|
||||
public void SetDescriptionPanelArchetype(NodeArchetype archetype)
|
||||
{
|
||||
if (!_useDescriptionPanel)
|
||||
return;
|
||||
|
||||
if (archetype == null || !Editor.Instance.Options.Options.Interface.VisualScriptingDescriptionPanel)
|
||||
{
|
||||
HideDescriptionPanel();
|
||||
return;
|
||||
}
|
||||
|
||||
Profiler.BeginEvent("VisjectCM.SetDescriptionPanelArchetype");
|
||||
|
||||
_descriptionInputPanel.RemoveChildren();
|
||||
_descriptionOutputPanel.RemoveChildren();
|
||||
|
||||
// Fetch description and information from the memberInfo - mainly from nodes that got fetched asynchronously by the visual scripting editor
|
||||
ScriptType declaringType;
|
||||
if (archetype.Tag is ScriptMemberInfo memberInfo)
|
||||
{
|
||||
var name = memberInfo.Name;
|
||||
if (memberInfo.IsMethod && memberInfo.Name.StartsWith("get_") || memberInfo.Name.StartsWith("set_"))
|
||||
{
|
||||
name = memberInfo.Name.Substring(4);
|
||||
}
|
||||
|
||||
declaringType = memberInfo.DeclaringType;
|
||||
_descriptionSignatureLabel.Text = memberInfo.DeclaringType + "." + name;
|
||||
|
||||
// We have to add the Instance information manually for members that aren't static
|
||||
if (!memberInfo.IsStatic)
|
||||
AddInputOutputElement(archetype, declaringType, false, $"Instance ({memberInfo.DeclaringType.Name})");
|
||||
|
||||
// We also have to manually add the Return information as well.
|
||||
if (memberInfo.ValueType != ScriptType.Null && memberInfo.ValueType != ScriptType.Void)
|
||||
{
|
||||
// When a field has a setter we don't want it to show the input as a return output.
|
||||
if (memberInfo.IsField && archetype.Title.StartsWith("Set "))
|
||||
AddInputOutputElement(archetype, memberInfo.ValueType, false, $"({memberInfo.ValueType.Name})");
|
||||
else
|
||||
AddInputOutputElement(archetype, memberInfo.ValueType, true, $"Return ({memberInfo.ValueType.Name})");
|
||||
}
|
||||
|
||||
for (int i = 0; i < memberInfo.ParametersCount; i++)
|
||||
{
|
||||
var param = memberInfo.GetParameters()[i];
|
||||
AddInputOutputElement(archetype, param.Type, param.IsOut, $"{param.Name} ({param.Type.Name})");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Try to fetch as many informations as possible from the predefined hardcoded nodes
|
||||
_descriptionSignatureLabel.Text = string.IsNullOrEmpty(archetype.Signature) ? archetype.Title : archetype.Signature;
|
||||
declaringType = archetype.DefaultType;
|
||||
|
||||
// Some nodes are more delicate. Like Arrays or Dictionaries. In this case we let them fetch the inputs/outputs for us.
|
||||
// Otherwise, it is not possible to show a proper return type on some nodes.
|
||||
if (archetype.GetInputOutputDescription != null)
|
||||
{
|
||||
archetype.GetInputOutputDescription.Invoke(archetype, out var inputs, out var outputs);
|
||||
|
||||
foreach (var input in inputs ?? [])
|
||||
{
|
||||
AddInputOutputElement(archetype, input.Type, false, $"{input.Name} ({input.Type.Name})");
|
||||
}
|
||||
|
||||
foreach (var output in outputs ?? [])
|
||||
{
|
||||
AddInputOutputElement(archetype, output.Type, true, $"{output.Name} ({output.Type.Name})");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var element in archetype.Elements)
|
||||
{
|
||||
if (element.Type is not (NodeElementType.Input or NodeElementType.Output))
|
||||
continue;
|
||||
|
||||
var typeText = element.ConnectionsType ? element.ConnectionsType.Name : archetype.ConnectionsHints.ToString();
|
||||
AddInputOutputElement(archetype, element.ConnectionsType, element.Type == NodeElementType.Output, $"{element.Text} ({typeText})");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set declaring type icon color
|
||||
_surfaceStyle.GetConnectionColor(declaringType, archetype.ConnectionsHints, out var declaringTypeColor);
|
||||
_descriptionDeclaringClassImage.Color = declaringTypeColor;
|
||||
_descriptionDeclaringClassImage.MouseOverColor = declaringTypeColor;
|
||||
|
||||
// Calculate the description panel height. (I am doing this manually since working with autoSize on horizontal/vertical panels didn't work, especially with nesting - Nils)
|
||||
float panelHeight = _descriptionSignatureLabel.Height;
|
||||
|
||||
// If thee is no description we move the signature label down a bit to align it with the icon. Just a cosmetic check
|
||||
if (string.IsNullOrEmpty(archetype.Description))
|
||||
{
|
||||
_descriptionSignatureLabel.Y = 15;
|
||||
_descriptionLabel.Text = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
_descriptionSignatureLabel.Y = 8;
|
||||
_descriptionLabel.Y = _descriptionSignatureLabel.Bounds.Bottom + 6f;
|
||||
// Replacing multiple whitespaces with a linebreak for better readability. (Mainly doing this because the ToolTip fetching system replaces linebreaks with whitespaces - Nils)
|
||||
_descriptionLabel.Text = Regex.Replace(archetype.Description, @"\s{2,}", "\n");
|
||||
}
|
||||
|
||||
// Some padding and moving elements around
|
||||
_descriptionPanelSeparator.Y = _descriptionLabel.Bounds.Bottom + 8f;
|
||||
panelHeight += _descriptionLabel.Height + 32f;
|
||||
_descriptionInputPanel.Y = panelHeight;
|
||||
_descriptionOutputPanel.Y = panelHeight;
|
||||
panelHeight += Mathf.Max(_descriptionInputPanel.Height, _descriptionOutputPanel.Height);
|
||||
|
||||
// Forcing the description panel to at least have a minimum height to not make the window size change too much in order to reduce jittering
|
||||
// TODO: Remove the Mathf.Max and just set the height to panelHeight once the window jitter issue is fixed - Nils
|
||||
_descriptionPanel.Height = Mathf.Max(140f, panelHeight);
|
||||
Height = DefaultHeight + _descriptionPanel.Height;
|
||||
UpdateWindowSize();
|
||||
_descriptionPanelVisible = true;
|
||||
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
private void AddInputOutputElement(NodeArchetype nodeArchetype, ScriptType type, bool isOutput, string text)
|
||||
{
|
||||
// Using a panel instead of a vertical panel because auto sizing is unreliable - so we are doing this manually here as well
|
||||
var elementPanel = new Panel()
|
||||
{
|
||||
Parent = isOutput ? _descriptionOutputPanel : _descriptionInputPanel,
|
||||
Width = Width * 0.5f,
|
||||
Height = 16,
|
||||
AnchorPreset = AnchorPresets.TopLeft
|
||||
};
|
||||
|
||||
_surfaceStyle.GetConnectionColor(type, nodeArchetype.ConnectionsHints, out var typeColor);
|
||||
elementPanel.AddChild(new Image(2, 0, 12, 12)
|
||||
{
|
||||
Brush = new SpriteBrush(_surfaceStyle.Icons.BoxOpen),
|
||||
Color = typeColor,
|
||||
MouseOverColor = typeColor,
|
||||
AutoFocus = false,
|
||||
}).SetAnchorPreset(AnchorPresets.TopLeft, true);
|
||||
|
||||
// Forcing the first letter to be capital and removing the '&' char from pointer-references
|
||||
text = (char.ToUpper(text[0]) + text.Substring(1)).Replace("&", "");
|
||||
var elementText = new Label(16, 0, Width * 0.5f - 32, 16)
|
||||
{
|
||||
Text = text,
|
||||
HorizontalAlignment = TextAlignment.Near,
|
||||
VerticalAlignment = TextAlignment.Near,
|
||||
Wrapping = TextWrapping.WrapWords,
|
||||
AutoHeight = true,
|
||||
};
|
||||
elementText.SetAnchorPreset(AnchorPresets.TopLeft, true);
|
||||
elementPanel.AddChild(elementText);
|
||||
elementPanel.Height = elementText.Height;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides the description panel and resets the context menu to its original size
|
||||
/// </summary>
|
||||
private void HideDescriptionPanel()
|
||||
{
|
||||
if (!_descriptionPanelVisible)
|
||||
return;
|
||||
|
||||
_descriptionInputPanel.RemoveChildren();
|
||||
_descriptionOutputPanel.RemoveChildren();
|
||||
Height = DefaultHeight;
|
||||
UpdateWindowSize();
|
||||
_descriptionPanelVisible = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnKeyDown(KeyboardKeys key)
|
||||
{
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Utilities;
|
||||
@@ -62,7 +63,7 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
Group = group;
|
||||
_groupArchetype = groupArchetype;
|
||||
_archetype = archetype;
|
||||
TooltipText = _archetype.Description;
|
||||
TooltipText = GetTooltip();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,6 +316,23 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when selected by the visject CM
|
||||
/// </summary>
|
||||
public void OnSelect()
|
||||
{
|
||||
Group.ContextMenu.SetDescriptionPanelArchetype(_archetype);
|
||||
}
|
||||
|
||||
private string GetTooltip()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(string.IsNullOrEmpty(_archetype.Signature) ? _archetype.Title : _archetype.Signature);
|
||||
if (!string.IsNullOrEmpty(_archetype.Description))
|
||||
sb.Append("\n" + _archetype.Description);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||
{
|
||||
@@ -338,6 +356,14 @@ namespace FlaxEditor.Surface.ContextMenu
|
||||
return base.OnMouseUp(location, button);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseEnter(Float2 location)
|
||||
{
|
||||
Group.ContextMenu.SetDescriptionPanelArchetype(_archetype);
|
||||
|
||||
base.OnMouseEnter(location);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void OnMouseLeave()
|
||||
{
|
||||
|
||||
@@ -90,10 +90,15 @@ namespace FlaxEditor.Surface
|
||||
public delegate SurfaceNode CreateCustomNodeFunc(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch);
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the given type is compatible with the given node archetype. Used for custom nodes
|
||||
/// Checks if the given type is compatible with the given node archetype. Used for custom nodes.
|
||||
/// </summary>
|
||||
public delegate bool IsCompatible(NodeArchetype nodeArch, ScriptType portType, ConnectionsHint hint, VisjectSurfaceContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Gets description of inputs and outputs of the archetype. Used for special cases for the description panel.
|
||||
/// </summary>
|
||||
public delegate void GetElementsDescriptionFunc(NodeArchetype nodeArch, out (string Name, ScriptType Type)[] inputs, out (string Name, ScriptType Type)[] outputs);
|
||||
|
||||
/// <summary>
|
||||
/// Unique node type ID within a single group.
|
||||
/// </summary>
|
||||
@@ -134,11 +139,21 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public string SubTitle;
|
||||
|
||||
/// <summary>
|
||||
/// Node signature for tooltip and description purposes
|
||||
/// </summary>
|
||||
public string Signature;
|
||||
|
||||
/// <summary>
|
||||
/// Short node description.
|
||||
/// </summary>
|
||||
public string Description;
|
||||
|
||||
/// <summary>
|
||||
/// Custom function to get descriptions of input and output elements. Used for description panel (optional).
|
||||
/// </summary>
|
||||
public GetElementsDescriptionFunc GetInputOutputDescription;
|
||||
|
||||
/// <summary>
|
||||
/// Alternative node titles.
|
||||
/// </summary>
|
||||
@@ -210,7 +225,9 @@ namespace FlaxEditor.Surface
|
||||
Flags = Flags,
|
||||
Title = Title,
|
||||
SubTitle = SubTitle,
|
||||
Signature = Signature,
|
||||
Description = Description,
|
||||
GetInputOutputDescription = GetInputOutputDescription,
|
||||
AlternativeTitles = (string[])AlternativeTitles?.Clone(),
|
||||
Tag = Tag,
|
||||
SortScore = SortScore,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using FlaxEditor.Scripting;
|
||||
using FlaxEditor.Surface.Elements;
|
||||
using FlaxEditor.Surface.Undo;
|
||||
@@ -124,7 +125,7 @@ namespace FlaxEditor.Surface
|
||||
Archetype = nodeArch;
|
||||
GroupArchetype = groupArch;
|
||||
AutoFocus = false;
|
||||
TooltipText = nodeArch.Description;
|
||||
TooltipText = GetTooltip();
|
||||
CullChildren = false;
|
||||
BackgroundColor = Style.Current.BackgroundNormal;
|
||||
|
||||
@@ -851,6 +852,15 @@ namespace FlaxEditor.Surface
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTooltip()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.Append(string.IsNullOrEmpty(Archetype.Signature) ? Archetype.Title : Archetype.Signature);
|
||||
if (!string.IsNullOrEmpty(Archetype.Description))
|
||||
sb.Append("\n" + Archetype.Description);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override bool ShowTooltip => base.ShowTooltip && _headerRect.Contains(ref _mousePosition) && !Surface.IsLeftMouseButtonDown && !Surface.IsRightMouseButtonDown && !Surface.IsPrimaryMenuOpened;
|
||||
|
||||
|
||||
@@ -440,12 +440,23 @@ namespace FlaxEditor.Surface
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptMemberInfoDescription(ScriptMemberInfo member)
|
||||
internal static string GetVisualScriptMemberInfoSignature(ScriptMemberInfo member)
|
||||
{
|
||||
var name = member.Name;
|
||||
var declaringType = member.DeclaringType;
|
||||
var valueType = member.ValueType;
|
||||
|
||||
// Getter/setter method of the property - we can return early here
|
||||
if (member.IsMethod && (name.StartsWith("get_") || name.StartsWith("set_")))
|
||||
{
|
||||
var flags = member.IsStatic ? BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly : BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
var property = declaringType.GetMembers(name.Substring(4), MemberTypes.Property, flags);
|
||||
if (property != null && property.Length != 0)
|
||||
{
|
||||
return GetVisualScriptMemberInfoSignature(property[0]);
|
||||
}
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
if (member.IsStatic)
|
||||
sb.Append("static ");
|
||||
@@ -456,32 +467,19 @@ namespace FlaxEditor.Surface
|
||||
sb.Append(declaringType.Name);
|
||||
sb.Append('.');
|
||||
sb.Append(name);
|
||||
|
||||
if (member.IsMethod)
|
||||
{
|
||||
// Getter/setter method of the property
|
||||
if (name.StartsWith("get_") || name.StartsWith("set_"))
|
||||
sb.Append('(');
|
||||
var parameters = member.GetParameters();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var flags = member.IsStatic ? BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly : BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
var property = declaringType.GetMembers(name.Substring(4), MemberTypes.Property, flags);
|
||||
if (property != null && property.Length != 0)
|
||||
{
|
||||
return GetVisualScriptMemberInfoDescription(property[0]);
|
||||
}
|
||||
}
|
||||
// Method
|
||||
else
|
||||
{
|
||||
sb.Append('(');
|
||||
var parameters = member.GetParameters();
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
sb.Append(", ");
|
||||
ref var param = ref parameters[i];
|
||||
param.ToString(sb);
|
||||
}
|
||||
sb.Append(')');
|
||||
if (i != 0)
|
||||
sb.Append(", ");
|
||||
ref var param = ref parameters[i];
|
||||
param.ToString(sb);
|
||||
}
|
||||
sb.Append(')');
|
||||
}
|
||||
else if (member.IsProperty)
|
||||
{
|
||||
@@ -495,8 +493,35 @@ namespace FlaxEditor.Surface
|
||||
sb.Append('}');
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptMemberShortDescription(ScriptMemberInfo member)
|
||||
{
|
||||
var name = member.Name;
|
||||
var declaringType = member.DeclaringType;
|
||||
|
||||
// Getter/setter method of the property - we can return early here
|
||||
if (member.IsMethod && (name.StartsWith("get_") || name.StartsWith("set_")))
|
||||
{
|
||||
var flags = member.IsStatic ? BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly : BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
var property = declaringType.GetMembers(name.Substring(4), MemberTypes.Property, flags);
|
||||
if (property != null && property.Length != 0)
|
||||
{
|
||||
return GetVisualScriptMemberShortDescription(property[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return Editor.Instance.CodeDocs.GetTooltip(member);
|
||||
}
|
||||
|
||||
internal static string GetVisualScriptMemberInfoDescription(ScriptMemberInfo member)
|
||||
{
|
||||
var signature = GetVisualScriptMemberInfoSignature(member);
|
||||
var sb = new StringBuilder(signature);
|
||||
|
||||
// Tooltip
|
||||
var tooltip = Editor.Instance.CodeDocs.GetTooltip(member);
|
||||
var tooltip = GetVisualScriptMemberShortDescription(member);
|
||||
if (!string.IsNullOrEmpty(tooltip))
|
||||
sb.Append("\n").Append(tooltip);
|
||||
|
||||
|
||||
@@ -237,11 +237,13 @@ namespace FlaxEditor.Surface
|
||||
return new VisjectCM(new VisjectCM.InitInfo
|
||||
{
|
||||
CanSetParameters = CanSetParameters,
|
||||
UseDescriptionPanel = UseContextMenuDescriptionPanel,
|
||||
Groups = NodeArchetypes,
|
||||
CanSpawnNode = CanUseNodeType,
|
||||
ParametersGetter = () => Parameters,
|
||||
CustomNodesGroup = GetCustomNodes(),
|
||||
ParameterGetNodeArchetype = GetParameterGetterNodeArchetype(out _),
|
||||
Style = Style,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -535,6 +535,11 @@ namespace FlaxEditor.Surface
|
||||
/// </summary>
|
||||
public virtual bool CanSetParameters => false;
|
||||
|
||||
/// <summary>
|
||||
/// True of the context menu should make use of a description panel drawn at the bottom of the menu
|
||||
/// </summary>
|
||||
public virtual bool UseContextMenuDescriptionPanel => false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether surface supports/allows live previewing graph modifications due to value sliders and color pickers. True by default but disabled for shader surfaces that generate and compile shader source at flight.
|
||||
/// </summary>
|
||||
|
||||
@@ -51,6 +51,7 @@ namespace FlaxEditor.Surface
|
||||
{
|
||||
TypeID = originalNodeId,
|
||||
Title = "Missing Node :(",
|
||||
Signature = "Missing Node :(",
|
||||
Description = ":(",
|
||||
Flags = NodeFlags.AllGraphs,
|
||||
Size = new Float2(200, 70),
|
||||
|
||||
@@ -174,6 +174,9 @@ namespace FlaxEditor.Surface
|
||||
/// <inheritdoc />
|
||||
public override bool CanSetParameters => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool UseContextMenuDescriptionPanel => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanUseNodeType(GroupArchetype groupArchetype, NodeArchetype nodeArchetype)
|
||||
{
|
||||
@@ -235,10 +238,12 @@ namespace FlaxEditor.Surface
|
||||
|
||||
var node = (NodeArchetype)Archetypes.Function.Nodes[2].Clone();
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Description = Editor.Instance.CodeDocs.GetTooltip(member);
|
||||
node.Signature = SurfaceUtils.GetVisualScriptMemberInfoSignature(member);
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberShortDescription(member);
|
||||
node.DefaultValues[0] = name;
|
||||
node.DefaultValues[1] = parameters.Length;
|
||||
node.Title = "Override " + name;
|
||||
node.Tag = member;
|
||||
nodes.Add(node);
|
||||
}
|
||||
}
|
||||
@@ -276,6 +281,7 @@ namespace FlaxEditor.Surface
|
||||
node.DefaultValues[0] = Activator.CreateInstance(scriptType.Type);
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Title = scriptTypeName;
|
||||
node.Signature = scriptTypeName;
|
||||
node.Description = Editor.Instance.CodeDocs.GetTooltip(scriptType);
|
||||
|
||||
// Create group archetype
|
||||
@@ -326,6 +332,7 @@ namespace FlaxEditor.Surface
|
||||
node.DefaultValues[0] = scriptTypeTypeName;
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Title = "Pack " + scriptTypeName;
|
||||
node.Signature = "Pack " + scriptTypeName;
|
||||
node.Description = tooltip;
|
||||
((IList<NodeArchetype>)group.Archetypes).Add(node);
|
||||
|
||||
@@ -334,6 +341,7 @@ namespace FlaxEditor.Surface
|
||||
node.DefaultValues[0] = scriptTypeTypeName;
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Title = "Unpack " + scriptTypeName;
|
||||
node.Signature = "Unpack " + scriptTypeName;
|
||||
node.Description = tooltip;
|
||||
((IList<NodeArchetype>)group.Archetypes).Add(node);
|
||||
}
|
||||
@@ -367,7 +375,8 @@ namespace FlaxEditor.Surface
|
||||
node.DefaultValues[2] = parameters.Length;
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Title = SurfaceUtils.GetMethodDisplayName((string)node.DefaultValues[1]);
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member);
|
||||
node.Signature = SurfaceUtils.GetVisualScriptMemberInfoSignature(member);
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberShortDescription(member);
|
||||
node.SubTitle = string.Format(" (in {0})", scriptTypeName);
|
||||
node.Tag = member;
|
||||
|
||||
@@ -415,8 +424,10 @@ namespace FlaxEditor.Surface
|
||||
node.DefaultValues[3] = member.IsStatic;
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Title = "Get " + name;
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member);
|
||||
node.Signature = SurfaceUtils.GetVisualScriptMemberInfoSignature(member);
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberShortDescription(member);
|
||||
node.SubTitle = string.Format(" (in {0})", scriptTypeName);
|
||||
node.Tag = member;
|
||||
|
||||
// Create group archetype
|
||||
var groupKey = new KeyValuePair<string, ushort>(scriptTypeName, 16);
|
||||
@@ -449,8 +460,10 @@ namespace FlaxEditor.Surface
|
||||
node.DefaultValues[3] = member.IsStatic;
|
||||
node.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
node.Title = "Set " + name;
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member);
|
||||
node.Signature = SurfaceUtils.GetVisualScriptMemberInfoSignature(member);
|
||||
node.Description = SurfaceUtils.GetVisualScriptMemberShortDescription(member);
|
||||
node.SubTitle = string.Format(" (in {0})", scriptTypeName);
|
||||
node.Tag = member;
|
||||
|
||||
// Create group archetype
|
||||
var groupKey = new KeyValuePair<string, ushort>(scriptTypeName, 16);
|
||||
@@ -507,8 +520,10 @@ namespace FlaxEditor.Surface
|
||||
bindNode.DefaultValues[1] = name;
|
||||
bindNode.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
bindNode.Title = "Bind " + name;
|
||||
bindNode.Description = SurfaceUtils.GetVisualScriptMemberInfoDescription(member);
|
||||
bindNode.Signature = SurfaceUtils.GetVisualScriptMemberInfoSignature(member);
|
||||
bindNode.Description = SurfaceUtils.GetVisualScriptMemberShortDescription(member);
|
||||
bindNode.SubTitle = string.Format(" (in {0})", scriptTypeName);
|
||||
bindNode.Tag = member;
|
||||
((IList<NodeArchetype>)group.Archetypes).Add(bindNode);
|
||||
|
||||
// Add Unbind event node
|
||||
@@ -517,8 +532,10 @@ namespace FlaxEditor.Surface
|
||||
unbindNode.DefaultValues[1] = name;
|
||||
unbindNode.Flags &= ~NodeFlags.NoSpawnViaGUI;
|
||||
unbindNode.Title = "Unbind " + name;
|
||||
unbindNode.Signature = bindNode.Signature;
|
||||
unbindNode.Description = bindNode.Description;
|
||||
unbindNode.SubTitle = bindNode.SubTitle;
|
||||
unbindNode.Tag = member;
|
||||
((IList<NodeArchetype>)group.Archetypes).Add(unbindNode);
|
||||
|
||||
#if DEBUG_EVENTS_SEARCHING
|
||||
|
||||
@@ -99,6 +99,16 @@ namespace FlaxEditor.Viewport
|
||||
ShowEditorPrimitives = true;
|
||||
Gizmos = new GizmosCollection(this);
|
||||
|
||||
_gridGizmo = new GridGizmo(this);
|
||||
var showGridButton = ViewWidgetShowMenu.AddButton("Grid");
|
||||
showGridButton.Clicked += () =>
|
||||
{
|
||||
_gridGizmo.Enabled = !_gridGizmo.Enabled;
|
||||
showGridButton.Checked = _gridGizmo.Enabled;
|
||||
};
|
||||
showGridButton.Checked = true;
|
||||
showGridButton.CloseMenuOnClick = false;
|
||||
|
||||
// Prepare rendering task
|
||||
Task.ActorsSource = ActorsSources.CustomActors;
|
||||
Task.ViewFlags = ViewFlags.DefaultEditor;
|
||||
@@ -267,6 +277,8 @@ namespace FlaxEditor.Viewport
|
||||
/// <inheritdoc />
|
||||
public GizmosCollection Gizmos { get; }
|
||||
|
||||
private GridGizmo _gridGizmo;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SceneRenderTask RenderTask => Task;
|
||||
|
||||
@@ -611,6 +623,14 @@ namespace FlaxEditor.Viewport
|
||||
DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, false);
|
||||
}
|
||||
}
|
||||
|
||||
// Debug draw all actors in prefab
|
||||
foreach (var child in SceneGraphRoot.ChildNodes)
|
||||
{
|
||||
if (child is not ActorNode actorNode)
|
||||
continue;
|
||||
DebugDraw.DrawActorsTree(actorNode.Actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,11 @@ using System;
|
||||
using FlaxEditor.Content;
|
||||
using FlaxEditor.CustomEditors;
|
||||
using FlaxEditor.GUI;
|
||||
using FlaxEditor.GUI.ContextMenu;
|
||||
using FlaxEngine;
|
||||
using FlaxEngine.GUI;
|
||||
using FlaxEngine.Json;
|
||||
using FlaxEngine.Utilities;
|
||||
|
||||
namespace FlaxEditor.Windows.Assets
|
||||
{
|
||||
@@ -16,6 +19,53 @@ namespace FlaxEditor.Windows.Assets
|
||||
/// <seealso cref="FlaxEditor.Windows.Assets.AssetEditorWindow" />
|
||||
public sealed class JsonAssetWindow : AssetEditorWindowBase<JsonAsset>
|
||||
{
|
||||
private class ObjectPasteUndo : IUndoAction
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string ActionString => "Object Paste Undo";
|
||||
|
||||
private JsonAssetWindow _window;
|
||||
private string _oldObject;
|
||||
private string _newObject;
|
||||
|
||||
public ObjectPasteUndo(object oldObject, object newObject, JsonAssetWindow window)
|
||||
{
|
||||
_oldObject = JsonSerializer.Serialize(oldObject);
|
||||
_newObject = JsonSerializer.Serialize(newObject);
|
||||
_window = window;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
_oldObject = null;
|
||||
_newObject = null;
|
||||
_window = null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Do()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_newObject))
|
||||
{
|
||||
_window._object = JsonSerializer.Deserialize(_newObject, TypeUtils.GetType(_window.Asset.DataTypeName).Type);
|
||||
_window.MarkAsEdited();
|
||||
_window._presenter.Select(_window._object);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Undo()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(_oldObject))
|
||||
{
|
||||
_window._object = JsonSerializer.Deserialize(_oldObject, TypeUtils.GetType(_window.Asset.DataTypeName).Type);
|
||||
_window.MarkAsEdited();
|
||||
_window._presenter.Select(_window._object);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly CustomEditorPresenter _presenter;
|
||||
private readonly ToolStripButton _saveButton;
|
||||
private readonly ToolStripButton _undoButton;
|
||||
@@ -24,6 +74,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
private object _object;
|
||||
private bool _isRegisteredForScriptsReload;
|
||||
private Label _typeText;
|
||||
private ToolStripButton _optionsButton;
|
||||
private ContextMenu _optionsCM;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the instance of the Json asset object that is being edited.
|
||||
@@ -139,6 +191,30 @@ namespace FlaxEditor.Windows.Assets
|
||||
|
||||
if (_typeText != null)
|
||||
_typeText.Dispose();
|
||||
|
||||
// Get content item for options button
|
||||
object buttonTag = null;
|
||||
var allTypes = Editor.CodeEditing.All.Get();
|
||||
foreach (var type in allTypes)
|
||||
{
|
||||
if (type.TypeName.Equals(Asset.DataTypeName, StringComparison.Ordinal))
|
||||
{
|
||||
buttonTag = type.ContentItem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_optionsButton = new ToolStripButton(_toolstrip.ItemsHeight, ref Editor.Icons.Settings12)
|
||||
{
|
||||
AnchorPreset = AnchorPresets.TopRight,
|
||||
Tag = buttonTag,
|
||||
Size = new Float2(18),
|
||||
Parent = this,
|
||||
};
|
||||
_optionsButton.LocalX -= (_optionsButton.Width + 4);
|
||||
_optionsButton.LocalY += (_toolstrip.Height - _optionsButton.Height) * 0.5f;
|
||||
_optionsButton.Clicked += OpenOptionsContextMenu;
|
||||
|
||||
var typeText = new ClickableLabel
|
||||
{
|
||||
Text = $"{Asset.DataTypeName}",
|
||||
@@ -147,9 +223,8 @@ namespace FlaxEditor.Windows.Assets
|
||||
AutoWidth = true,
|
||||
Parent = this,
|
||||
};
|
||||
typeText.LocalX += -(typeText.Width + 4);
|
||||
typeText.LocalX += -(typeText.Width + _optionsButton.Width + 8);
|
||||
typeText.LocalY += (_toolstrip.Height - typeText.Height) * 0.5f;
|
||||
typeText.RightClick = () => Clipboard.Text = Asset.DataTypeName;
|
||||
_typeText = typeText;
|
||||
|
||||
_undo.Clear();
|
||||
@@ -165,6 +240,62 @@ namespace FlaxEditor.Windows.Assets
|
||||
base.OnAssetLoaded();
|
||||
}
|
||||
|
||||
private void OpenOptionsContextMenu()
|
||||
{
|
||||
if (_optionsCM != null && _optionsCM.ContainsFocus)
|
||||
return;
|
||||
|
||||
_optionsCM = new ContextMenu();
|
||||
_optionsCM.AddButton("Copy type name", () => Clipboard.Text = Asset.DataTypeName);
|
||||
_optionsCM.AddButton("Copy asset data", () => Clipboard.Text = Asset.Data);
|
||||
_optionsCM.AddButton("Paste asset data", () =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Clipboard.Text))
|
||||
{
|
||||
var dataTypeName = Asset.DataTypeName;
|
||||
var type = TypeUtils.GetType(dataTypeName);
|
||||
if (type != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
var obj = Activator.CreateInstance(type.Type);
|
||||
var data = Clipboard.Text;
|
||||
JsonSerializer.Deserialize(obj, data);
|
||||
if (obj != null)
|
||||
{
|
||||
var undoAction = new ObjectPasteUndo(_object, obj, this);
|
||||
undoAction.Do();
|
||||
_undo.AddAction(undoAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
Editor.LogWarning("Pasted data is not the correct data type or has incomplete data");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Editor.LogWarning($"Pasted data is not the correct data type or has incomplete data. Exception: {ex}");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
_optionsCM.Enabled = !string.IsNullOrEmpty(Clipboard.Text);
|
||||
_optionsCM.AddSeparator();
|
||||
if (_optionsButton.Tag is ContentItem item)
|
||||
{
|
||||
_optionsCM.AddButton("Edit asset code", () =>
|
||||
{
|
||||
Editor.Instance.ContentEditing.Open(item);
|
||||
});
|
||||
_optionsCM.AddButton("Show asset code item in content window", () =>
|
||||
{
|
||||
Editor.Instance.Windows.ContentWin.Select(item);
|
||||
});
|
||||
}
|
||||
|
||||
_optionsCM.Show(_optionsButton, _optionsButton.PointFromScreen(Input.MouseScreenPosition));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnAssetLoadFailed()
|
||||
{
|
||||
|
||||
@@ -179,7 +179,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathd.IsOne(X * X + Y * Y);
|
||||
public bool IsNormalized => Mathd.Abs((X * X + Y * Y) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this vector is zero
|
||||
|
||||
@@ -234,7 +234,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathd.IsOne(X * X + Y * Y + Z * Z);
|
||||
public bool IsNormalized => Mathd.Abs((X * X + Y * Y + Z * Z) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized vector. Returned vector has length equal 1.
|
||||
|
||||
@@ -220,7 +220,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathd.IsOne(X * X + Y * Y + Z * Z + W * W);
|
||||
public bool IsNormalized => Mathd.Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this vector is zero
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathf.IsOne(X * X + Y * Y);
|
||||
public bool IsNormalized => Mathf.Abs((X * X + Y * Y) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this vector is zero
|
||||
|
||||
@@ -228,7 +228,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathf.IsOne(X * X + Y * Y + Z * Z);
|
||||
public bool IsNormalized => Mathf.Abs((X * X + Y * Y + Z * Z) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized vector. Returned vector has length equal 1.
|
||||
|
||||
@@ -202,7 +202,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathf.IsOne(X * X + Y * Y + Z * Z + W * W);
|
||||
public bool IsNormalized => Mathf.Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this vector is zero
|
||||
|
||||
@@ -163,7 +163,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathf.IsOne(X * X + Y * Y + Z * Z + W * W);
|
||||
public bool IsNormalized => Mathf.Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the euler angle (pitch, yaw, roll) in degrees.
|
||||
|
||||
@@ -107,7 +107,7 @@ public:
|
||||
/// </summary>
|
||||
bool IsNormalized() const
|
||||
{
|
||||
return Math::IsOne(X * X + Y * Y + Z * Z + W * W);
|
||||
return Math::Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -207,7 +207,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathr.IsOne(X * X + Y * Y);
|
||||
public bool IsNormalized => Mathr.Abs((X * X + Y * Y) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this vector is zero
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
// Gets a value indicting whether this instance is normalized.
|
||||
bool IsNormalized() const
|
||||
{
|
||||
return Math::IsOne(X * X + Y * Y);
|
||||
return Math::Abs((X * X + Y * Y) - 1.0f) < 1e-4f;
|
||||
}
|
||||
|
||||
// Gets a value indicting whether this vector is zero.
|
||||
|
||||
@@ -256,7 +256,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathr.IsOne(X * X + Y * Y + Z * Z);
|
||||
public bool IsNormalized => Mathr.Abs((X * X + Y * Y + Z * Z) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized vector. Returned vector has length equal 1.
|
||||
|
||||
@@ -136,7 +136,7 @@ public:
|
||||
// Gets a value indicting whether this instance is normalized.
|
||||
bool IsNormalized() const
|
||||
{
|
||||
return Math::IsOne(X * X + Y * Y + Z * Z);
|
||||
return Math::Abs((X * X + Y * Y + Z * Z) - 1.0f) < 1e-4f;
|
||||
}
|
||||
|
||||
// Gets a value indicting whether this vector is zero.
|
||||
|
||||
@@ -258,7 +258,7 @@ namespace FlaxEngine
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this instance is normalized.
|
||||
/// </summary>
|
||||
public bool IsNormalized => Mathr.IsOne(X * X + Y * Y + Z * Z + W * W);
|
||||
public bool IsNormalized => Mathr.Abs((X * X + Y * Y + Z * Z + W * W) - 1.0f) < 1e-4f;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicting whether this vector is zero
|
||||
|
||||
@@ -944,6 +944,12 @@ void DebugDraw::DrawActors(Actor** selectedActors, int32 selectedActorsCount, bo
|
||||
}
|
||||
}
|
||||
|
||||
void DebugDraw::DrawActorsTree(Actor* actor)
|
||||
{
|
||||
Function<bool(Actor*)> function = &DrawActorsTreeWalk;
|
||||
actor->TreeExecute(function);
|
||||
}
|
||||
|
||||
void DebugDraw::DrawAxisFromDirection(const Vector3& origin, const Vector3& direction, float size, float duration, bool depthTest)
|
||||
{
|
||||
ASSERT(direction.IsNormalized());
|
||||
|
||||
@@ -67,6 +67,12 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
||||
/// <param name="drawScenes">True if draw all debug shapes from scenes too or false if draw just from specified actor list.</param>
|
||||
API_FUNCTION() static void DrawActors(Actor** selectedActors, int32 selectedActorsCount, bool drawScenes);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the debug shapes for the given actor and the actor's children
|
||||
/// </summary>
|
||||
/// /// <param name="actor">The actor to start drawing at.</param>
|
||||
API_FUNCTION() static void DrawActorsTree(Actor* actor);
|
||||
|
||||
/// <summary>
|
||||
/// Draws the lines axis from direction.
|
||||
/// </summary>
|
||||
|
||||
@@ -195,6 +195,12 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
// Use the same time for all ticks to improve synchronization
|
||||
const double time = Platform::GetTimeSeconds();
|
||||
|
||||
// Update application (will gather data and other platform related events)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Platform.Tick");
|
||||
Platform::Tick();
|
||||
}
|
||||
|
||||
// Update game logic
|
||||
if (Time::OnBeginUpdate(time))
|
||||
{
|
||||
@@ -302,12 +308,6 @@ void Engine::OnUpdate()
|
||||
|
||||
UpdateCount++;
|
||||
|
||||
// Update application (will gather data and other platform related events)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Platform.Tick");
|
||||
Platform::Tick();
|
||||
}
|
||||
|
||||
const auto mainWindow = MainWindow;
|
||||
|
||||
#if !USE_EDITOR
|
||||
|
||||
@@ -39,7 +39,11 @@ void ForwardMaterialShader::Bind(BindParameters& params)
|
||||
|
||||
// Setup features
|
||||
if ((_info.FeaturesFlags & MaterialFeaturesFlags::GlobalIllumination) != MaterialFeaturesFlags::None)
|
||||
{
|
||||
GlobalIlluminationFeature::Bind(params, cb, srv);
|
||||
if ((_info.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections) != MaterialFeaturesFlags::None)
|
||||
SDFReflectionsFeature::Bind(params, cb, srv);
|
||||
}
|
||||
ForwardShadingFeature::Bind(params, cb, srv);
|
||||
|
||||
// Setup parameters
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
/// <summary>
|
||||
/// Current materials shader version.
|
||||
/// </summary>
|
||||
#define MATERIAL_GRAPH_VERSION 168
|
||||
#define MATERIAL_GRAPH_VERSION 169
|
||||
|
||||
class Material;
|
||||
class GPUShader;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Engine/Graphics/Textures/GPUTexture.h"
|
||||
#include "Engine/Renderer/RenderList.h"
|
||||
#include "Engine/Renderer/ShadowsPass.h"
|
||||
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||
#if USE_EDITOR
|
||||
#include "Engine/Renderer/Lightmaps.h"
|
||||
#endif
|
||||
@@ -188,6 +189,58 @@ bool GlobalIlluminationFeature::Bind(MaterialShader::BindParameters& params, Spa
|
||||
return useGI;
|
||||
}
|
||||
|
||||
bool SDFReflectionsFeature::Bind(MaterialShader::BindParameters& params, Span<byte>& cb, int32& srv)
|
||||
{
|
||||
auto& data = *(Data*)cb.Get();
|
||||
ASSERT_LOW_LAYER(cb.Length() >= sizeof(Data));
|
||||
|
||||
bool useSDFReflections = false;
|
||||
if (EnumHasAnyFlags(params.RenderContext.View.Flags, ViewFlags::Reflections))
|
||||
{
|
||||
switch (params.RenderContext.List->Settings.ScreenSpaceReflections.TraceMode)
|
||||
{
|
||||
case ReflectionsTraceMode::SoftwareTracing:
|
||||
{
|
||||
GlobalSignDistanceFieldPass::BindingData bindingDataSDF;
|
||||
GlobalSurfaceAtlasPass::BindingData bindingDataSurfaceAtlas;
|
||||
if (!GlobalSignDistanceFieldPass::Instance()->Get(params.RenderContext.Buffers, bindingDataSDF) &&
|
||||
!GlobalSurfaceAtlasPass::Instance()->Get(params.RenderContext.Buffers, bindingDataSurfaceAtlas))
|
||||
{
|
||||
useSDFReflections = true;
|
||||
|
||||
// Bind SDF and Surface Atlas data
|
||||
data.GlobalSDF = bindingDataSDF.Constants;
|
||||
data.GlobalSurfaceAtlas = bindingDataSurfaceAtlas.Constants;
|
||||
params.GPUContext->BindSR(srv + 0, bindingDataSDF.Texture ? bindingDataSDF.Texture->ViewVolume() : nullptr);
|
||||
params.GPUContext->BindSR(srv + 1, bindingDataSDF.TextureMip ? bindingDataSDF.TextureMip->ViewVolume() : nullptr);
|
||||
params.GPUContext->BindSR(srv + 2, bindingDataSurfaceAtlas.Chunks ? bindingDataSurfaceAtlas.Chunks->View() : nullptr);
|
||||
params.GPUContext->BindSR(srv + 3, bindingDataSurfaceAtlas.CulledObjects ? bindingDataSurfaceAtlas.CulledObjects->View() : nullptr);
|
||||
params.GPUContext->BindSR(srv + 4, bindingDataSurfaceAtlas.Objects ? bindingDataSurfaceAtlas.Objects->View() : nullptr);
|
||||
params.GPUContext->BindSR(srv + 5, bindingDataSurfaceAtlas.AtlasDepth->View());
|
||||
params.GPUContext->BindSR(srv + 6, bindingDataSurfaceAtlas.AtlasLighting->View());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!useSDFReflections)
|
||||
{
|
||||
// Unbind SRVs to prevent issues
|
||||
data.GlobalSDF.CascadesCount = 0;
|
||||
params.GPUContext->UnBindSR(srv + 0);
|
||||
params.GPUContext->UnBindSR(srv + 1);
|
||||
params.GPUContext->UnBindSR(srv + 2);
|
||||
params.GPUContext->UnBindSR(srv + 3);
|
||||
params.GPUContext->UnBindSR(srv + 4);
|
||||
params.GPUContext->UnBindSR(srv + 5);
|
||||
params.GPUContext->UnBindSR(srv + 6);
|
||||
}
|
||||
|
||||
cb = Span<byte>(cb.Get() + sizeof(Data), cb.Length() - sizeof(Data));
|
||||
srv += SRVs;
|
||||
return useSDFReflections;
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void ForwardShadingFeature::Generate(GeneratorData& data)
|
||||
@@ -215,6 +268,11 @@ void GlobalIlluminationFeature::Generate(GeneratorData& data)
|
||||
data.Template = TEXT("Features/GlobalIllumination.hlsl");
|
||||
}
|
||||
|
||||
void SDFReflectionsFeature::Generate(GeneratorData& data)
|
||||
{
|
||||
data.Template = TEXT("Features/SDFReflections.hlsl");
|
||||
}
|
||||
|
||||
void DistortionFeature::Generate(GeneratorData& data)
|
||||
{
|
||||
data.Template = TEXT("Features/Distortion.hlsl");
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "Engine/Core/Math/Rectangle.h"
|
||||
#include "Engine/Core/Types/Span.h"
|
||||
#include "Engine/Renderer/GI/DynamicDiffuseGlobalIllumination.h"
|
||||
#include "Engine/Renderer/GlobalSignDistanceFieldPass.h"
|
||||
#include "Engine/Renderer/GI/GlobalSurfaceAtlasPass.h"
|
||||
|
||||
// Material shader features are plugin-based functionalities that are reusable between different material domains.
|
||||
struct MaterialShaderFeature
|
||||
@@ -85,6 +87,25 @@ struct GlobalIlluminationFeature : MaterialShaderFeature
|
||||
#endif
|
||||
};
|
||||
|
||||
// Material shader feature that adds SDF Reflections feature (software reflections).
|
||||
struct SDFReflectionsFeature : MaterialShaderFeature
|
||||
{
|
||||
enum { SRVs = 7 };
|
||||
|
||||
PACK_STRUCT(struct Data
|
||||
{
|
||||
GlobalSignDistanceFieldPass::ConstantsData GlobalSDF;
|
||||
GlobalSurfaceAtlasPass::ConstantsData GlobalSurfaceAtlas;
|
||||
});
|
||||
|
||||
|
||||
|
||||
static bool Bind(MaterialShader::BindParameters& params, Span<byte>& cb, int32& srv);
|
||||
#if USE_EDITOR
|
||||
static void Generate(GeneratorData& data);
|
||||
#endif
|
||||
};
|
||||
|
||||
// Material shader feature that adds distortion vectors rendering pass.
|
||||
struct DistortionFeature : MaterialShaderFeature
|
||||
{
|
||||
|
||||
@@ -38,7 +38,7 @@ API_STRUCT() struct HingeJointDrive
|
||||
/// <summary>
|
||||
/// Target velocity of the joint.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="Limit(0)") float Velocity = 0.0f;
|
||||
API_FIELD() float Velocity = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum torque the drive is allowed to apply.
|
||||
|
||||
@@ -2923,7 +2923,7 @@ int32 LinuxPlatform::CreateProcess(CreateProcessSettings& settings)
|
||||
LOG(Info, "Working directory: {0}", settings.WorkingDirectory);
|
||||
}
|
||||
const bool captureStdOut = settings.LogOutput || settings.SaveOutput;
|
||||
const String cmdLine = settings.FileName + TEXT(" ") + settings.Arguments;
|
||||
const String cmdLine = String::Format(TEXT("\"{0}\" {1}"), settings.FileName, settings.Arguments);
|
||||
|
||||
int fildes[2];
|
||||
int32 returnCode = 0;
|
||||
|
||||
@@ -670,6 +670,17 @@ void GlobalSurfaceAtlasPass::Dispose()
|
||||
_shader = nullptr;
|
||||
}
|
||||
|
||||
bool GlobalSurfaceAtlasPass::Get(const RenderBuffers* buffers, BindingData& result)
|
||||
{
|
||||
auto* surfaceAtlasData = buffers ? buffers->FindCustomBuffer<GlobalSurfaceAtlasCustomBuffer>(TEXT("GlobalSurfaceAtlas")) : nullptr;
|
||||
if (surfaceAtlasData && surfaceAtlasData->LastFrameUsed + 1 >= Engine::FrameCount) // Allow to use Surface Atlas from the previous frame (not used currently)
|
||||
{
|
||||
result = surfaceAtlasData->Result;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GlobalSurfaceAtlasPass::OnCollectDrawCalls(RenderContextBatch& renderContextBatch)
|
||||
{
|
||||
// Check if Global Surface Atlas will be used this frame
|
||||
|
||||
@@ -61,6 +61,14 @@ private:
|
||||
void* _currentActorObject;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the Global Surface Atlas (only if enabled in Graphics Settings).
|
||||
/// </summary>
|
||||
/// <param name="buffers">The rendering context buffers.</param>
|
||||
/// <param name="result">The result Global Surface Atlas data for binding to the shaders.</param>
|
||||
/// <returns>True if there is no valid Global Surface Atlas rendered during this frame, otherwise false.</returns>
|
||||
bool Get(const RenderBuffers* buffers, BindingData& result);
|
||||
|
||||
/// <summary>
|
||||
/// Calls drawing scene objects in async early in the frame.
|
||||
/// </summary>
|
||||
|
||||
@@ -386,6 +386,7 @@ void RenderList::RunCustomPostFxPass(GPUContext* context, RenderContext& renderC
|
||||
Swap(input, output);
|
||||
}
|
||||
context->ResetRenderTarget();
|
||||
context->ResetSR();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -689,51 +689,41 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
// World Triplanar Texture
|
||||
case 16:
|
||||
{
|
||||
// Get input boxes
|
||||
auto textureBox = node->GetBox(0);
|
||||
auto scaleBox = node->GetBox(1);
|
||||
auto blendBox = node->GetBox(2);
|
||||
|
||||
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 bool canUseSample = CanUseSample(_treeType);
|
||||
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
|
||||
const auto scale = tryGetValue(scaleBox, node->Values[0]).AsFloat3();
|
||||
const auto scale = tryGetValue(scaleBox, node->Values[0]).AsFloat4();
|
||||
const auto blend = tryGetValue(blendBox, node->Values[1]).AsFloat();
|
||||
|
||||
auto result = writeLocal(Value::InitForZero(ValueType::Float4), node);
|
||||
|
||||
const String triplanarTexture = String::Format(TEXT(
|
||||
" {{\n"
|
||||
" float3 worldPos = input.WorldPosition.xyz * ({1} * 0.001f);\n"
|
||||
" float3 normal = abs(input.TBN[2]);\n"
|
||||
" normal = pow(normal, {2});\n"
|
||||
" normal = normal / (normal.x + normal.y + normal.z);\n"
|
||||
|
||||
" {3} += {0}.Sample(SamplerLinearWrap, worldPos.yz) * normal.x;\n"
|
||||
" {3} += {0}.Sample(SamplerLinearWrap, worldPos.xz) * normal.y;\n"
|
||||
" {3} += {0}.Sample(SamplerLinearWrap, worldPos.xy) * normal.z;\n"
|
||||
" {3} += {0}.{4}(SamplerLinearWrap, worldPos.yz{5}) * normal.x;\n"
|
||||
" {3} += {0}.{4}(SamplerLinearWrap, worldPos.xz{5}) * normal.y;\n"
|
||||
" {3} += {0}.{4}(SamplerLinearWrap, worldPos.xy{5}) * normal.z;\n"
|
||||
" }}\n"
|
||||
),
|
||||
texture.Value, // {0}
|
||||
scale.Value, // {1}
|
||||
blend.Value, // {2}
|
||||
result.Value // {3}
|
||||
result.Value, // {3}
|
||||
canUseSample ? TEXT("Sample") : TEXT("SampleLevel"), // {4}
|
||||
canUseSample ? TEXT("") : TEXT(", 0") // {5}
|
||||
);
|
||||
|
||||
_writer.Write(*triplanarTexture);
|
||||
value = result;
|
||||
break;
|
||||
}
|
||||
// Get Lightmap UV
|
||||
case 18:
|
||||
@@ -748,9 +738,7 @@ void MaterialGenerator::ProcessGroupTextures(Box* box, Node* node, Value& value)
|
||||
"#endif\n"
|
||||
"}}\n"
|
||||
), output.Value);
|
||||
|
||||
_writer.Write(*lightmapUV);
|
||||
|
||||
value = output;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -188,40 +188,46 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
||||
{
|
||||
case MaterialDomain::Surface:
|
||||
if (materialInfo.TessellationMode != TessellationMethod::None)
|
||||
ADD_FEATURE(TessellationFeature);
|
||||
ADD_FEATURE(TessellationFeature);
|
||||
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
|
||||
ADD_FEATURE(MotionVectorsFeature);
|
||||
ADD_FEATURE(MotionVectorsFeature);
|
||||
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
|
||||
ADD_FEATURE(LightmapFeature);
|
||||
ADD_FEATURE(LightmapFeature);
|
||||
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
|
||||
ADD_FEATURE(DeferredShadingFeature);
|
||||
ADD_FEATURE(DeferredShadingFeature);
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
|
||||
ADD_FEATURE(DistortionFeature);
|
||||
ADD_FEATURE(DistortionFeature);
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
|
||||
ADD_FEATURE(GlobalIlluminationFeature);
|
||||
{
|
||||
ADD_FEATURE(GlobalIlluminationFeature);
|
||||
|
||||
// SDF Reflections is only valid when both GI and SSR is enabled
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::ScreenSpaceReflections))
|
||||
ADD_FEATURE(SDFReflectionsFeature);
|
||||
}
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque)
|
||||
ADD_FEATURE(ForwardShadingFeature);
|
||||
ADD_FEATURE(ForwardShadingFeature);
|
||||
break;
|
||||
case MaterialDomain::Terrain:
|
||||
if (materialInfo.TessellationMode != TessellationMethod::None)
|
||||
ADD_FEATURE(TessellationFeature);
|
||||
ADD_FEATURE(TessellationFeature);
|
||||
ADD_FEATURE(LightmapFeature);
|
||||
ADD_FEATURE(DeferredShadingFeature);
|
||||
break;
|
||||
case MaterialDomain::Particle:
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && (materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None)
|
||||
ADD_FEATURE(DistortionFeature);
|
||||
ADD_FEATURE(DistortionFeature);
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && EnumHasAnyFlags(materialInfo.FeaturesFlags, MaterialFeaturesFlags::GlobalIllumination))
|
||||
ADD_FEATURE(GlobalIlluminationFeature);
|
||||
ADD_FEATURE(GlobalIlluminationFeature);
|
||||
ADD_FEATURE(ForwardShadingFeature);
|
||||
break;
|
||||
case MaterialDomain::Deformable:
|
||||
if (materialInfo.TessellationMode != TessellationMethod::None)
|
||||
ADD_FEATURE(TessellationFeature);
|
||||
ADD_FEATURE(TessellationFeature);
|
||||
if (materialInfo.BlendMode == MaterialBlendMode::Opaque)
|
||||
ADD_FEATURE(DeferredShadingFeature);
|
||||
ADD_FEATURE(DeferredShadingFeature);
|
||||
if (materialInfo.BlendMode != MaterialBlendMode::Opaque)
|
||||
ADD_FEATURE(ForwardShadingFeature);
|
||||
ADD_FEATURE(ForwardShadingFeature);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -708,7 +714,7 @@ void MaterialGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Vector Transform
|
||||
// Vector Transform
|
||||
case 30:
|
||||
{
|
||||
// Get input vector
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
#include "./Flax/GBufferCommon.hlsl"
|
||||
|
||||
// Hit depth (view space) threshold to detect if sky was hit (value above it where 1.0f is default)
|
||||
#define REFLECTIONS_HIT_THRESHOLD 0.9f
|
||||
|
||||
float GetSpecularOcclusion(float NoV, float roughnessSq, float ao)
|
||||
{
|
||||
return saturate(pow(NoV + ao, roughnessSq) - 1 + ao);
|
||||
|
||||
@@ -129,17 +129,17 @@ float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
|
||||
float mip = clamp(log2(intersectionCircleRadius * TraceSizeMax), 0.0, MaxColorMiplevel);
|
||||
float3 sampleColor = Texture0.SampleLevel(SamplerLinearClamp, screenHit.xy, mip).rgb;
|
||||
result = float4(sampleColor, screenHit.z);
|
||||
if (screenHit.z >= 0.9f)
|
||||
if (screenHit.z >= REFLECTIONS_HIT_THRESHOLD)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Calculate reflection direction (the same TraceScreenSpaceReflection)
|
||||
float3 reflectWS = ScreenSpaceReflectionDirection(input.TexCoord, gBuffer, gBufferData.ViewPos, TemporalEffect, TemporalTime, BRDFBias);
|
||||
|
||||
// Fallback to Global SDF and Global Surface Atlas tracing
|
||||
#if USE_GLOBAL_SURFACE_ATLAS && CAN_USE_GLOBAL_SURFACE_ATLAS
|
||||
// Calculate reflection direction (the same TraceScreenSpaceReflection)
|
||||
float3 reflectWS = ScreenSpaceReflectionDirection(input.TexCoord, gBuffer, gBufferData.ViewPos, TemporalEffect, TemporalTime, BRDFBias);
|
||||
|
||||
GlobalSDFTrace sdfTrace;
|
||||
float maxDistance = 100000;
|
||||
float maxDistance = GLOBAL_SDF_WORLD_SIZE;
|
||||
float selfOcclusionBias = GlobalSDF.CascadeVoxelSize[0];
|
||||
sdfTrace.Init(gBuffer.WorldPos + gBuffer.Normal * selfOcclusionBias, reflectWS, 0.0f, maxDistance);
|
||||
GlobalSDFHit sdfHit = RayTraceGlobalSDF(GlobalSDF, GlobalSDFTex, GlobalSDFMip, sdfTrace);
|
||||
|
||||
Reference in New Issue
Block a user