Merge remote-tracking branch 'origin/master' into gi
This commit is contained in:
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
BIN
Content/Editor/Camera/M_Camera.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/CubeTexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Decal.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Particle.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Surface.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/SurfaceAdditive.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/SingleColor/Terrain.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/DefaultFontMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/FoliageBrushMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/MaterialWire.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/SelectionOutlineMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Gizmo/VertexColorsPreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Highlight Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/Icons/IconsMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/IesProfilePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
@@ -6,6 +6,10 @@
|
|||||||
#include "./Flax/LightingCommon.hlsl"
|
#include "./Flax/LightingCommon.hlsl"
|
||||||
#if USE_REFLECTIONS
|
#if USE_REFLECTIONS
|
||||||
#include "./Flax/ReflectionsCommon.hlsl"
|
#include "./Flax/ReflectionsCommon.hlsl"
|
||||||
|
#define MATERIAL_REFLECTIONS_SSR 1
|
||||||
|
#if MATERIAL_REFLECTIONS == MATERIAL_REFLECTIONS_SSR
|
||||||
|
#include "./Flax/SSR.hlsl"
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#include "./Flax/Lighting.hlsl"
|
#include "./Flax/Lighting.hlsl"
|
||||||
#include "./Flax/ShadowsSampling.hlsl"
|
#include "./Flax/ShadowsSampling.hlsl"
|
||||||
@@ -93,9 +97,29 @@ float4 PS_Forward(PixelInput input) : SV_Target0
|
|||||||
light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight);
|
light += GetLighting(ViewPos, localLight, gBuffer, shadowMask, true, isSpotLight);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if USE_REFLECTIONS
|
|
||||||
// Calculate reflections
|
// Calculate reflections
|
||||||
light.rgb += GetEnvProbeLighting(ViewPos, EnvProbe, EnvironmentProbe, gBuffer) * light.a;
|
#if USE_REFLECTIONS
|
||||||
|
float3 reflections = SampleReflectionProbe(ViewPos, EnvProbe, EnvironmentProbe, gBuffer.WorldPos, gBuffer.Normal, gBuffer.Roughness).rgb;
|
||||||
|
|
||||||
|
#if MATERIAL_REFLECTIONS == MATERIAL_REFLECTIONS_SSR
|
||||||
|
// Screen Space Reflections
|
||||||
|
Texture2D sceneDepthTexture = MATERIAL_REFLECTIONS_SSR_DEPTH; // Material Generator inserts depth and color buffers and plugs it via internal define
|
||||||
|
Texture2D sceneColorTexture = MATERIAL_REFLECTIONS_SSR_COLOR;
|
||||||
|
float2 screenUV = materialInput.SvPosition.xy * ScreenSize.zw;
|
||||||
|
float stepSize = ScreenSize.z; // 1 / screenWidth
|
||||||
|
float maxSamples = 32;
|
||||||
|
float worldAntiSelfOcclusionBias = 0.1f;
|
||||||
|
float brdfBias = 0.82f;
|
||||||
|
float drawDistance = 5000.0f;
|
||||||
|
float3 hit = TraceSceenSpaceReflection(screenUV, gBuffer, sceneDepthTexture, ViewPos, ViewMatrix, ViewProjectionMatrix, stepSize, maxSamples, false, 0.0f, worldAntiSelfOcclusionBias, brdfBias, drawDistance);
|
||||||
|
if (hit.z > 0)
|
||||||
|
{
|
||||||
|
float3 screenColor = sceneColorTexture.SampleLevel(SamplerPointClamp, hit.xy, 0).rgb;
|
||||||
|
reflections = lerp(reflections, screenColor, hit.z);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
light.rgb += reflections * GetReflectionSpecularLighting(ViewPos, gBuffer) * light.a;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Add lighting (apply ambient occlusion)
|
// Add lighting (apply ambient occlusion)
|
||||||
|
|||||||
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Particle Material Color.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Particles/Smoke Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/SpriteMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Circle Brush Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Terrain/Highlight Terrain Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
BIN
Content/Editor/TexturePreviewMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
BIN
Content/Editor/Wires Debug Material.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultDeformableMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/DefaultTerrainMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SingleColorMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
BIN
Content/Engine/SkyboxMaterial.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/SSR.flax
(Stored with Git LFS)
BIN
Content/Shaders/SSR.flax
(Stored with Git LFS)
Binary file not shown.
@@ -689,6 +689,30 @@ namespace FlaxEditor.Content.GUI
|
|||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnCharInput(char c)
|
||||||
|
{
|
||||||
|
if (base.OnCharInput(c))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (char.IsLetterOrDigit(c) && _items.Count != 0)
|
||||||
|
{
|
||||||
|
// Jump to the item starting with this character
|
||||||
|
c = char.ToLowerInvariant(c);
|
||||||
|
for (int i = 0; i < _items.Count; i++)
|
||||||
|
{
|
||||||
|
var name = _items[i].ShortName;
|
||||||
|
if (!string.IsNullOrEmpty(name) && char.ToLowerInvariant(name[0]) == c)
|
||||||
|
{
|
||||||
|
Select(_items[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void PerformLayoutBeforeChildren()
|
protected override void PerformLayoutBeforeChildren()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// Enables using prefab-related features of the properties editor (eg. revert to prefab option).
|
/// Enables using prefab-related features of the properties editor (eg. revert to prefab option).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
UsePrefab = 1 << 1,
|
UsePrefab = 1 << 1,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables using default-value-related features of the properties editor (eg. revert to default option).
|
/// Enables using default-value-related features of the properties editor (eg. revert to default option).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -88,7 +88,6 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// <seealso cref="FlaxEditor.CustomEditors.SyncPointEditor" />
|
/// <seealso cref="FlaxEditor.CustomEditors.SyncPointEditor" />
|
||||||
protected class RootEditor : SyncPointEditor
|
protected class RootEditor : SyncPointEditor
|
||||||
{
|
{
|
||||||
private readonly string _noSelectionText;
|
|
||||||
private CustomEditor _overrideEditor;
|
private CustomEditor _overrideEditor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -109,13 +108,18 @@ namespace FlaxEditor.CustomEditors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The text to show when no object is selected.
|
||||||
|
/// </summary>
|
||||||
|
public string NoSelectionText;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="RootEditor"/> class.
|
/// Initializes a new instance of the <see cref="RootEditor"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="noSelectionText">The text to show when no item is selected.</param>
|
/// <param name="noSelectionText">The text to show when no item is selected.</param>
|
||||||
public RootEditor(string noSelectionText)
|
public RootEditor(string noSelectionText)
|
||||||
{
|
{
|
||||||
_noSelectionText = noSelectionText ?? "No selection";
|
NoSelectionText = noSelectionText ?? "No selection";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -154,7 +158,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var label = layout.Label(_noSelectionText, TextAlignment.Center);
|
var label = layout.Label(NoSelectionText, TextAlignment.Center);
|
||||||
label.Label.Height = 20.0f;
|
label.Label.Height = 20.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,6 +255,20 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public object Owner;
|
public object Owner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the text to show when no object is selected.
|
||||||
|
/// </summary>
|
||||||
|
public string NoSelectionText
|
||||||
|
{
|
||||||
|
get => Editor.NoSelectionText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Editor.NoSelectionText = value;
|
||||||
|
if (SelectionCount == 0)
|
||||||
|
BuildLayoutOnUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool _buildOnUpdate;
|
private bool _buildOnUpdate;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -49,10 +49,10 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
Parent = this,
|
Parent = this,
|
||||||
Bounds = new Rectangle((Width - addScriptButtonWidth) / 2, 1, addScriptButtonWidth, 18),
|
Bounds = new Rectangle((Width - addScriptButtonWidth) / 2, 1, addScriptButtonWidth, 18),
|
||||||
};
|
};
|
||||||
addScriptButton.ButtonClicked += AddScriptButtonOnClicked;
|
addScriptButton.ButtonClicked += OnAddScriptButtonClicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddScriptButtonOnClicked(Button button)
|
private void OnAddScriptButtonClicked(Button button)
|
||||||
{
|
{
|
||||||
var scripts = Editor.Instance.CodeEditing.Scripts.Get();
|
var scripts = Editor.Instance.CodeEditing.Scripts.Get();
|
||||||
if (scripts.Count == 0)
|
if (scripts.Count == 0)
|
||||||
|
|||||||
@@ -716,7 +716,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
|
|
||||||
// Header
|
// Header
|
||||||
if (item.Header != null)
|
if (item.Header != null)
|
||||||
itemLayout.Header(item.Header.Text);
|
itemLayout.Header(item.Header);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -279,6 +279,16 @@ namespace FlaxEditor.CustomEditors
|
|||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal LabelElement Header(HeaderAttribute header)
|
||||||
|
{
|
||||||
|
var element = Header(header.Text);
|
||||||
|
if (header.FontSize != -1)
|
||||||
|
element.Label.Font = new FontReference(element.Label.Font.Font, header.FontSize);
|
||||||
|
if (header.Color != 0)
|
||||||
|
element.Label.TextColor = Color.FromRGBA(header.Color);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds new text box element.
|
/// Adds new text box element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -258,6 +258,24 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1)
|
NodeElementArchetype.Factory.Output(0, string.Empty, null, 1)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new NodeArchetype
|
||||||
|
{
|
||||||
|
TypeID = 14,
|
||||||
|
Title = "Array Add Unique",
|
||||||
|
Description = "Adds the unique item to the array (to the end). Does nothing it specified item was already added.",
|
||||||
|
Flags = NodeFlags.VisualScriptGraph | NodeFlags.AnimGraph,
|
||||||
|
Size = new Vector2(170, 40),
|
||||||
|
ConnectionsHints = ConnectionsHint.Array,
|
||||||
|
IndependentBoxes = new int[] { 0 },
|
||||||
|
DependentBoxes = new int[] { 1, 2 },
|
||||||
|
DependentBoxFilter = GetArrayItemType,
|
||||||
|
Elements = new[]
|
||||||
|
{
|
||||||
|
NodeElementArchetype.Factory.Input(0, "Array", true, null, 0),
|
||||||
|
NodeElementArchetype.Factory.Input(1, "Item", true, typeof(object), 1),
|
||||||
|
NodeElementArchetype.Factory.Output(0, string.Empty, null, 2)
|
||||||
|
}
|
||||||
|
},
|
||||||
// first 100 IDs reserved for arrays
|
// first 100 IDs reserved for arrays
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
GetBox(MaterialNodeBoxes.SubsurfaceColor).Enabled = false;
|
GetBox(MaterialNodeBoxes.SubsurfaceColor).Enabled = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: throw new ArgumentOutOfRangeException();
|
default: throw new ArgumentOutOfRangeException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,7 +807,26 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Input(0, "RGB", true, typeof(Vector3), 0, 0),
|
NodeElementArchetype.Factory.Input(0, "RGB", true, typeof(Vector3), 0, 0),
|
||||||
NodeElementArchetype.Factory.Output(0, "HSV", typeof(Vector3), 1),
|
NodeElementArchetype.Factory.Output(0, "HSV", typeof(Vector3), 1),
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
new NodeArchetype
|
||||||
|
{
|
||||||
|
TypeID = 38,
|
||||||
|
Title = "Custom Global Code",
|
||||||
|
Description = "Custom global HLSL shader code expression (placed before material shader code). Can contain includes to shader utilities or declare functions to reuse later.",
|
||||||
|
Flags = NodeFlags.MaterialGraph,
|
||||||
|
Size = new Vector2(300, 220),
|
||||||
|
DefaultValues = new object[]
|
||||||
|
{
|
||||||
|
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
Elements = new[]
|
||||||
|
{
|
||||||
|
NodeElementArchetype.Factory.Bool(0, 0, 1),
|
||||||
|
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
|
||||||
|
NodeElementArchetype.Factory.TextBox(0, 20, 300, 200, 0),
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
// Header
|
// Header
|
||||||
if (e.Header != null)
|
if (e.Header != null)
|
||||||
itemLayout.Header(e.Header.Text);
|
itemLayout.Header(e.Header);
|
||||||
|
|
||||||
// Values container
|
// Values container
|
||||||
var valueType = new ScriptType(e.Type);
|
var valueType = new ScriptType(e.Type);
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ namespace FlaxEditor.Surface
|
|||||||
// Header
|
// Header
|
||||||
var header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute);
|
var header = (HeaderAttribute)attributes.FirstOrDefault(x => x is HeaderAttribute);
|
||||||
if (header != null)
|
if (header != null)
|
||||||
itemLayout.Header(header.Text);
|
itemLayout.Header(header);
|
||||||
|
|
||||||
var propertyValue = new CustomValueContainer
|
var propertyValue = new CustomValueContainer
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using System;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
using FlaxEditor.GUI;
|
using FlaxEditor.GUI;
|
||||||
@@ -106,13 +107,36 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnAssetLoaded()
|
protected override void OnAssetLoaded()
|
||||||
{
|
{
|
||||||
_object = Asset.CreateInstance();
|
_object = Asset.Instance;
|
||||||
|
if (_object == null)
|
||||||
|
{
|
||||||
|
// Hint developer about cause of failure
|
||||||
|
var dataTypeName = Asset.DataTypeName;
|
||||||
|
var type = Type.GetType(dataTypeName);
|
||||||
|
if (type != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var obj = Activator.CreateInstance(type);
|
||||||
|
var data = Asset.Data;
|
||||||
|
FlaxEngine.Json.JsonSerializer.Deserialize(obj, data);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_presenter.NoSelectionText = "Failed to load asset. See log for more. " + ex.Message.Replace('\n', ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_presenter.NoSelectionText = string.Format("Missing type '{0}'.", dataTypeName);
|
||||||
|
}
|
||||||
|
}
|
||||||
_presenter.Select(_object);
|
_presenter.Select(_object);
|
||||||
_undo.Clear();
|
_undo.Clear();
|
||||||
ClearEditedFlag();
|
ClearEditedFlag();
|
||||||
|
|
||||||
// Auto-close on scripting reload if json asset is from game scripts (it might be reloaded)
|
// Auto-close on scripting reload if json asset is from game scripts (it might be reloaded)
|
||||||
if (_object != null && FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType()) && !_isRegisteredForScriptsReload)
|
if ((_object == null || FlaxEngine.Scripting.IsTypeFromGameScripts(_object.GetType())) && !_isRegisteredForScriptsReload)
|
||||||
{
|
{
|
||||||
_isRegisteredForScriptsReload = true;
|
_isRegisteredForScriptsReload = true;
|
||||||
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
ScriptsBuilder.ScriptsReloadBegin += OnScriptsReloadBegin;
|
||||||
@@ -121,6 +145,14 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
base.OnAssetLoaded();
|
base.OnAssetLoaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnAssetLoadFailed()
|
||||||
|
{
|
||||||
|
_presenter.NoSelectionText = "Failed to load the asset.";
|
||||||
|
|
||||||
|
base.OnAssetLoadFailed();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnItemReimported(ContentItem item)
|
public override void OnItemReimported(ContentItem item)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -77,6 +77,10 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
[EditorOrder(200), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
|
[EditorOrder(200), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables reflections when rendering material.")]
|
||||||
public bool EnableReflections;
|
public bool EnableReflections;
|
||||||
|
|
||||||
|
[VisibleIf(nameof(EnableReflections))]
|
||||||
|
[EditorOrder(210), DefaultValue(false), EditorDisplay("Transparency"), Tooltip("Enables Screen Space Reflections when rendering material.")]
|
||||||
|
public bool EnableScreenSpaceReflections;
|
||||||
|
|
||||||
[EditorOrder(210), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
|
[EditorOrder(210), DefaultValue(true), EditorDisplay("Transparency"), Tooltip("Enables fog effects when rendering material.")]
|
||||||
public bool EnableFog;
|
public bool EnableFog;
|
||||||
|
|
||||||
@@ -142,6 +146,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
DepthTest = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthTest) == 0;
|
DepthTest = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthTest) == 0;
|
||||||
DepthWrite = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthWrite) == 0;
|
DepthWrite = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDepthWrite) == 0;
|
||||||
EnableReflections = (info.FeaturesFlags & MaterialFeaturesFlags.DisableReflections) == 0;
|
EnableReflections = (info.FeaturesFlags & MaterialFeaturesFlags.DisableReflections) == 0;
|
||||||
|
EnableScreenSpaceReflections = (info.FeaturesFlags & MaterialFeaturesFlags.ScreenSpaceReflections) != 0;
|
||||||
EnableFog = (info.FeaturesFlags & MaterialFeaturesFlags.DisableFog) == 0;
|
EnableFog = (info.FeaturesFlags & MaterialFeaturesFlags.DisableFog) == 0;
|
||||||
EnableDistortion = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDistortion) == 0;
|
EnableDistortion = (info.FeaturesFlags & MaterialFeaturesFlags.DisableDistortion) == 0;
|
||||||
PixelNormalOffsetRefraction = (info.FeaturesFlags & MaterialFeaturesFlags.PixelNormalOffsetRefraction) != 0;
|
PixelNormalOffsetRefraction = (info.FeaturesFlags & MaterialFeaturesFlags.PixelNormalOffsetRefraction) != 0;
|
||||||
@@ -177,6 +182,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
info.FeaturesFlags |= MaterialFeaturesFlags.DisableDepthWrite;
|
info.FeaturesFlags |= MaterialFeaturesFlags.DisableDepthWrite;
|
||||||
if (!EnableReflections)
|
if (!EnableReflections)
|
||||||
info.FeaturesFlags |= MaterialFeaturesFlags.DisableReflections;
|
info.FeaturesFlags |= MaterialFeaturesFlags.DisableReflections;
|
||||||
|
if (EnableScreenSpaceReflections)
|
||||||
|
info.FeaturesFlags |= MaterialFeaturesFlags.ScreenSpaceReflections;
|
||||||
if (!EnableFog)
|
if (!EnableFog)
|
||||||
info.FeaturesFlags |= MaterialFeaturesFlags.DisableFog;
|
info.FeaturesFlags |= MaterialFeaturesFlags.DisableFog;
|
||||||
if (!EnableDistortion)
|
if (!EnableDistortion)
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ void SlotBucketInit(AnimGraphInstanceData::Bucket& bucket)
|
|||||||
bucket.Slot.TimePosition = 0.0f;
|
bucket.Slot.TimePosition = 0.0f;
|
||||||
bucket.Slot.BlendInPosition = 0.0f;
|
bucket.Slot.BlendInPosition = 0.0f;
|
||||||
bucket.Slot.BlendOutPosition = 0.0f;
|
bucket.Slot.BlendOutPosition = 0.0f;
|
||||||
|
bucket.Slot.LoopsDone = 0;
|
||||||
|
bucket.Slot.LoopsLeft = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
|
bool SortMultiBlend1D(const byte& a, const byte& b, AnimGraphNode* n)
|
||||||
|
|||||||
@@ -260,6 +260,7 @@ struct FLAXENGINE_API AnimGraphSlot
|
|||||||
float Speed = 1.0f;
|
float Speed = 1.0f;
|
||||||
float BlendInTime = 0.0f;
|
float BlendInTime = 0.0f;
|
||||||
float BlendOutTime = 0.0f;
|
float BlendOutTime = 0.0f;
|
||||||
|
int32 LoopCount = 0;
|
||||||
bool Pause = false;
|
bool Pause = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -314,6 +315,8 @@ public:
|
|||||||
float TimePosition;
|
float TimePosition;
|
||||||
float BlendInPosition;
|
float BlendInPosition;
|
||||||
float BlendOutPosition;
|
float BlendOutPosition;
|
||||||
|
int32 LoopsDone;
|
||||||
|
int32 LoopsLeft;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1924,6 +1924,8 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
bucket.TimePosition = 0.0f;
|
bucket.TimePosition = 0.0f;
|
||||||
bucket.BlendInPosition = 0.0f;
|
bucket.BlendInPosition = 0.0f;
|
||||||
bucket.BlendOutPosition = 0.0f;
|
bucket.BlendOutPosition = 0.0f;
|
||||||
|
bucket.LoopsDone = 0;
|
||||||
|
bucket.LoopsLeft = slot.LoopCount;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1940,18 +1942,27 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
ASSERT(slot.Animation && slot.Animation->IsLoaded());
|
ASSERT(slot.Animation && slot.Animation->IsLoaded());
|
||||||
const float deltaTime = slot.Pause ? 0.0f : context.DeltaTime * slot.Speed;
|
const float deltaTime = slot.Pause ? 0.0f : context.DeltaTime * slot.Speed;
|
||||||
const float length = anim->GetLength();
|
const float length = anim->GetLength();
|
||||||
|
const bool loop = bucket.LoopsLeft != 0;
|
||||||
float newTimePos = bucket.TimePosition + deltaTime;
|
float newTimePos = bucket.TimePosition + deltaTime;
|
||||||
if (newTimePos >= length)
|
if (newTimePos >= length)
|
||||||
{
|
{
|
||||||
// End playing animation
|
if (bucket.LoopsLeft == 0)
|
||||||
value = tryGetValue(node->GetBox(1), Value::Null);
|
{
|
||||||
bucket.Index = -1;
|
// End playing animation
|
||||||
slot.Animation = nullptr;
|
value = tryGetValue(node->GetBox(1), Value::Null);
|
||||||
return;
|
bucket.Index = -1;
|
||||||
|
slot.Animation = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop animation
|
||||||
|
if (bucket.LoopsLeft > 0)
|
||||||
|
bucket.LoopsLeft--;
|
||||||
|
bucket.LoopsDone++;
|
||||||
}
|
}
|
||||||
value = SampleAnimation(node, false, length, 0.0f, bucket.TimePosition, newTimePos, anim, slot.Speed);
|
value = SampleAnimation(node, loop, length, 0.0f, bucket.TimePosition, newTimePos, anim, slot.Speed);
|
||||||
bucket.TimePosition = newTimePos;
|
bucket.TimePosition = newTimePos;
|
||||||
if (slot.BlendOutTime > 0.0f && length - slot.BlendOutTime < bucket.TimePosition)
|
if (bucket.LoopsLeft == 0 && slot.BlendOutTime > 0.0f && length - slot.BlendOutTime < bucket.TimePosition)
|
||||||
{
|
{
|
||||||
// Blend out
|
// Blend out
|
||||||
auto input = tryGetValue(node->GetBox(1), Value::Null);
|
auto input = tryGetValue(node->GetBox(1), Value::Null);
|
||||||
@@ -1959,7 +1970,7 @@ void AnimGraphExecutor::ProcessGroupAnimation(Box* boxBase, Node* nodeBase, Valu
|
|||||||
const float alpha = Math::Saturate(bucket.BlendOutPosition / slot.BlendOutTime);
|
const float alpha = Math::Saturate(bucket.BlendOutPosition / slot.BlendOutTime);
|
||||||
value = Blend(node, value, input, alpha, AlphaBlendMode::HermiteCubic);
|
value = Blend(node, value, input, alpha, AlphaBlendMode::HermiteCubic);
|
||||||
}
|
}
|
||||||
else if (slot.BlendInTime > 0.0f && bucket.BlendInPosition < slot.BlendInTime)
|
else if (bucket.LoopsDone == 0 && slot.BlendInTime > 0.0f && bucket.BlendInPosition < slot.BlendInTime)
|
||||||
{
|
{
|
||||||
// Blend in
|
// Blend in
|
||||||
auto input = tryGetValue(node->GetBox(1), Value::Null);
|
auto input = tryGetValue(node->GetBox(1), Value::Null);
|
||||||
|
|||||||
@@ -430,6 +430,8 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
|||||||
options.Macros.Add({ "USE_DITHERED_LOD_TRANSITION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition ? 1 : 0] });
|
options.Macros.Add({ "USE_DITHERED_LOD_TRANSITION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition ? 1 : 0] });
|
||||||
options.Macros.Add({ "USE_GBUFFER_CUSTOM_DATA", Numbers[useCustomData ? 1 : 0] });
|
options.Macros.Add({ "USE_GBUFFER_CUSTOM_DATA", Numbers[useCustomData ? 1 : 0] });
|
||||||
options.Macros.Add({ "USE_REFLECTIONS", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections ? 0 : 1] });
|
options.Macros.Add({ "USE_REFLECTIONS", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections ? 0 : 1] });
|
||||||
|
if (!(info.FeaturesFlags & MaterialFeaturesFlags::DisableReflections) && info.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections)
|
||||||
|
options.Macros.Add({ "MATERIAL_REFLECTIONS", Numbers[1]});
|
||||||
options.Macros.Add({ "USE_FOG", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableFog ? 0 : 1] });
|
options.Macros.Add({ "USE_FOG", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::DisableFog ? 0 : 1] });
|
||||||
if (useForward)
|
if (useForward)
|
||||||
options.Macros.Add({ "USE_PIXEL_NORMAL_OFFSET_REFRACTION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::PixelNormalOffsetRefraction ? 1 : 0] });
|
options.Macros.Add({ "USE_PIXEL_NORMAL_OFFSET_REFRACTION", Numbers[info.FeaturesFlags & MaterialFeaturesFlags::PixelNormalOffsetRefraction ? 1 : 0] });
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
|
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||||
|
#include "Engine/Scripting/ManagedCLR/MField.h"
|
||||||
#include "Engine/Utilities/StringConverter.h"
|
#include "Engine/Utilities/StringConverter.h"
|
||||||
|
|
||||||
JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info)
|
JsonAssetBase::JsonAssetBase(const SpawnParams& params, const AssetInfo* info)
|
||||||
@@ -212,13 +214,11 @@ Asset::LoadResult JsonAsset::loadAsset()
|
|||||||
|
|
||||||
if (CreateInstance())
|
if (CreateInstance())
|
||||||
return LoadResult::Failed;
|
return LoadResult::Failed;
|
||||||
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
if (Instance)
|
// Reload instance when module with this type gets reloaded
|
||||||
{
|
Level::ScriptsReloadStart.Bind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
|
||||||
// Reload instance when module with this type gets reloaded
|
Level::ScriptsReloaded.Bind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
|
||||||
Level::ScriptsReloadStart.Bind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
|
|
||||||
Level::ScriptsReloaded.Bind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return LoadResult::Ok;
|
return LoadResult::Ok;
|
||||||
@@ -226,14 +226,11 @@ Asset::LoadResult JsonAsset::loadAsset()
|
|||||||
|
|
||||||
void JsonAsset::unload(bool isReloading)
|
void JsonAsset::unload(bool isReloading)
|
||||||
{
|
{
|
||||||
if (Instance)
|
|
||||||
{
|
|
||||||
#if USE_EDITOR
|
#if USE_EDITOR
|
||||||
Level::ScriptsReloadStart.Unbind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
|
Level::ScriptsReloadStart.Unbind<JsonAsset, &JsonAsset::OnScriptsReloadStart>(this);
|
||||||
Level::ScriptsReloaded.Unbind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
|
Level::ScriptsReloaded.Unbind<JsonAsset, &JsonAsset::OnScriptsReloaded>(this);
|
||||||
#endif
|
#endif
|
||||||
DeleteInstance();
|
DeleteInstance();
|
||||||
}
|
|
||||||
|
|
||||||
JsonAssetBase::unload(isReloading);
|
JsonAssetBase::unload(isReloading);
|
||||||
}
|
}
|
||||||
@@ -281,6 +278,13 @@ bool JsonAsset::CreateInstance()
|
|||||||
|
|
||||||
void JsonAsset::DeleteInstance()
|
void JsonAsset::DeleteInstance()
|
||||||
{
|
{
|
||||||
|
// C# instance
|
||||||
|
if (MObject* object = GetManagedInstance())
|
||||||
|
{
|
||||||
|
GetClass()->GetField("_instance")->SetValue(object, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// C++ instance
|
||||||
if (!Instance || !_dtor)
|
if (!Instance || !_dtor)
|
||||||
return;
|
return;
|
||||||
_dtor(Instance);
|
_dtor(Instance);
|
||||||
|
|||||||
@@ -7,27 +7,35 @@ namespace FlaxEngine
|
|||||||
{
|
{
|
||||||
partial class JsonAsset
|
partial class JsonAsset
|
||||||
{
|
{
|
||||||
|
private object _instance;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the serialized object instance from the json asset data.
|
/// Gets the instance of the serialized object from the json asset data. Cached internally.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The created object or null.</returns>
|
public object Instance => _instance ?? (_instance = CreateInstance());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the serialized object from the json asset data.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Use <see cref="Instance"/> to get cached object.</remarks>
|
||||||
|
/// <returns>The new object or null if failed.</returns>
|
||||||
public T CreateInstance<T>()
|
public T CreateInstance<T>()
|
||||||
{
|
{
|
||||||
return (T)CreateInstance();
|
return (T)CreateInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the serialized object instance from the json asset data.
|
/// Creates a new instance of the serialized object from the json asset data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The created object or null.</returns>
|
/// <remarks>Use <see cref="Instance"/> to get cached object.</remarks>
|
||||||
|
/// <returns>The new object or null if failed.</returns>
|
||||||
public object CreateInstance()
|
public object CreateInstance()
|
||||||
{
|
{
|
||||||
if (WaitForLoaded())
|
if (WaitForLoaded())
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
|
||||||
var dataTypeName = DataTypeName;
|
var dataTypeName = DataTypeName;
|
||||||
|
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
|
||||||
for (int i = 0; i < assemblies.Length; i++)
|
for (int i = 0; i < assemblies.Length; i++)
|
||||||
{
|
{
|
||||||
var assembly = assemblies[i];
|
var assembly = assemblies[i];
|
||||||
@@ -48,14 +56,14 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Debug.LogException(ex);
|
Debug.LogException(ex, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Debug.LogError(string.Format("Missing type '{0}' to create Json Asset instance.", dataTypeName), this);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,7 +123,7 @@ CreateAssetResult CreateMaterial::Create(CreateAssetContext& context)
|
|||||||
if (context.AllocateChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE))
|
if (context.AllocateChunk(SHADER_FILE_CHUNK_VISJECT_SURFACE))
|
||||||
return CreateAssetResult::CannotAllocateChunk;
|
return CreateAssetResult::CannotAllocateChunk;
|
||||||
layer->Graph.Nodes.EnsureCapacity(32);
|
layer->Graph.Nodes.EnsureCapacity(32);
|
||||||
layer->Root = (MaterialGraphNode*)&layer->Graph.Nodes[0];
|
layer->Root = &layer->Graph.Nodes[0];
|
||||||
for (auto& box : layer->Root->Boxes)
|
for (auto& box : layer->Root->Boxes)
|
||||||
box.Parent = layer->Root;
|
box.Parent = layer->Root;
|
||||||
Meta11 meta;
|
Meta11 meta;
|
||||||
|
|||||||
@@ -202,7 +202,7 @@ namespace FlaxEditor.Content.Settings
|
|||||||
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(GameSettingsAssetPath);
|
var asset = FlaxEngine.Content.LoadAsync<JsonAsset>(GameSettingsAssetPath);
|
||||||
if (asset && !asset.WaitForLoaded())
|
if (asset && !asset.WaitForLoaded())
|
||||||
{
|
{
|
||||||
if (asset.CreateInstance() is GameSettings result)
|
if (asset.Instance is GameSettings result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return new GameSettings();
|
return new GameSettings();
|
||||||
@@ -212,7 +212,7 @@ namespace FlaxEditor.Content.Settings
|
|||||||
{
|
{
|
||||||
if (asset && !asset.WaitForLoaded())
|
if (asset && !asset.WaitForLoaded())
|
||||||
{
|
{
|
||||||
if (asset.CreateInstance() is T result)
|
if (asset.Instance is T result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return new T();
|
return new T();
|
||||||
@@ -222,7 +222,7 @@ namespace FlaxEditor.Content.Settings
|
|||||||
{
|
{
|
||||||
if (asset && !asset.WaitForLoaded() && asset.DataTypeName == typename)
|
if (asset && !asset.WaitForLoaded() && asset.DataTypeName == typename)
|
||||||
{
|
{
|
||||||
if (asset.CreateInstance() is SettingsBase result)
|
if (asset.Instance is SettingsBase result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -314,7 +314,7 @@ namespace FlaxEditor.Content.Settings
|
|||||||
{
|
{
|
||||||
if (e.Value && !e.Value.WaitForLoaded() && e.Value.DataTypeName == type.FullName)
|
if (e.Value && !e.Value.WaitForLoaded() && e.Value.DataTypeName == type.FullName)
|
||||||
{
|
{
|
||||||
var custom = e.Value.CreateInstance();
|
var custom = e.Value.Instance;
|
||||||
if (custom is T result)
|
if (custom is T result)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -405,6 +405,59 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a static function (if not binded yet).
|
||||||
|
/// </summary>
|
||||||
|
template<void(*Method)(Params ...)>
|
||||||
|
void BindUnique()
|
||||||
|
{
|
||||||
|
FunctionType f;
|
||||||
|
f.template Bind<Method>();
|
||||||
|
BindUnique(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a member function (if not binded yet).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callee">The object instance.</param>
|
||||||
|
template<class T, void(T::*Method)(Params ...)>
|
||||||
|
void BindUnique(T* callee)
|
||||||
|
{
|
||||||
|
FunctionType f;
|
||||||
|
f.template Bind<T, Method>(callee);
|
||||||
|
BindUnique(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a function (if not binded yet).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="method">The method.</param>
|
||||||
|
void BindUnique(Signature method)
|
||||||
|
{
|
||||||
|
FunctionType f(method);
|
||||||
|
BindUnique(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Binds a function (if not binded yet).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="f">The function to bind.</param>
|
||||||
|
void BindUnique(const FunctionType& f)
|
||||||
|
{
|
||||||
|
const intptr size = Platform::AtomicRead(&_size);
|
||||||
|
FunctionType* bindings = (FunctionType*)Platform::AtomicRead(&_ptr);
|
||||||
|
if (bindings)
|
||||||
|
{
|
||||||
|
// Skip if already binded
|
||||||
|
for (intptr i = 0; i < size; i++)
|
||||||
|
{
|
||||||
|
if (Platform::AtomicRead((intptr volatile*)&bindings[i]._callee) == (intptr)f._callee && Platform::AtomicRead((intptr volatile*)&bindings[i]._function) == (intptr)f._function)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Bind(f);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unbinds a static function.
|
/// Unbinds a static function.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -230,6 +230,20 @@ namespace FlaxEngine
|
|||||||
a);
|
a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates <see cref="Color"/> from the RGBA value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rgb">The packed RGBA value.</param>
|
||||||
|
/// <returns>The color.</returns>
|
||||||
|
public static Color FromRGBA(uint rgb)
|
||||||
|
{
|
||||||
|
return new Color(
|
||||||
|
((rgb >> 16) & 0xff) / 255.0f,
|
||||||
|
((rgb >> 8) & 0xff) / 255.0f,
|
||||||
|
(rgb & 0xff) / 255.0f,
|
||||||
|
((rgb >> 24) & 0xff) / 255.0f);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the color value as the hexadecimal string.
|
/// Gets the color value as the hexadecimal string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ void ForwardMaterialShader::Bind(BindParameters& params)
|
|||||||
MaterialParameter::BindMeta bindMeta;
|
MaterialParameter::BindMeta bindMeta;
|
||||||
bindMeta.Context = context;
|
bindMeta.Context = context;
|
||||||
bindMeta.Constants = cb;
|
bindMeta.Constants = cb;
|
||||||
bindMeta.Input = nullptr; // forward pass materials cannot sample scene color for now
|
bindMeta.Input = params.Input;
|
||||||
bindMeta.Buffers = params.RenderContext.Buffers;
|
bindMeta.Buffers = params.RenderContext.Buffers;
|
||||||
bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth;
|
bindMeta.CanSampleDepth = GPUDevice::Instance->Limits.HasReadOnlyDepth;
|
||||||
bindMeta.CanSampleGBuffer = true;
|
bindMeta.CanSampleGBuffer = true;
|
||||||
|
|||||||
@@ -272,6 +272,11 @@ API_ENUM(Attributes="Flags") enum class MaterialFeaturesFlags : uint32
|
|||||||
/// The flag used to enable refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.
|
/// The flag used to enable refraction offset based on the difference between the per-pixel normal and the per-vertex normal. Useful for large water-like surfaces.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
PixelNormalOffsetRefraction = 1 << 9,
|
PixelNormalOffsetRefraction = 1 << 9,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The flag used to enable high-quality reflections based on the screen space raytracing. Useful for large water-like surfaces. The Forward Pass materials option.
|
||||||
|
/// </summary>
|
||||||
|
ScreenSpaceReflections = 1 << 10,
|
||||||
};
|
};
|
||||||
|
|
||||||
DECLARE_ENUM_OPERATORS(MaterialFeaturesFlags);
|
DECLARE_ENUM_OPERATORS(MaterialFeaturesFlags);
|
||||||
|
|||||||
@@ -406,11 +406,13 @@ void MaterialParameter::Bind(BindMeta& meta) const
|
|||||||
{
|
{
|
||||||
case MaterialSceneTextures::SceneDepth:
|
case MaterialSceneTextures::SceneDepth:
|
||||||
view = meta.CanSampleDepth
|
view = meta.CanSampleDepth
|
||||||
? (GPUDevice::Instance->Limits.HasReadOnlyDepth ? meta.Buffers->DepthBuffer->ViewReadOnlyDepth() : meta.Buffers->DepthBuffer->View())
|
? meta.Buffers->DepthBuffer->GetDescription().Flags & GPUTextureFlags::ReadOnlyDepthView ? meta.Buffers->DepthBuffer->ViewReadOnlyDepth() : meta.Buffers->DepthBuffer->View()
|
||||||
: GPUDevice::Instance->GetDefaultWhiteTexture()->View();
|
: GPUDevice::Instance->GetDefaultWhiteTexture()->View();
|
||||||
break;
|
break;
|
||||||
case MaterialSceneTextures::AmbientOcclusion:
|
case MaterialSceneTextures::AmbientOcclusion:
|
||||||
case MaterialSceneTextures::BaseColor:
|
case MaterialSceneTextures::BaseColor:
|
||||||
|
case MaterialSceneTextures::DiffuseColor:
|
||||||
|
case MaterialSceneTextures::SpecularColor:
|
||||||
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer0->View() : nullptr;
|
view = meta.CanSampleGBuffer ? meta.Buffers->GBuffer0->View() : nullptr;
|
||||||
break;
|
break;
|
||||||
case MaterialSceneTextures::WorldNormal:
|
case MaterialSceneTextures::WorldNormal:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Current materials shader version.
|
/// Current materials shader version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
#define MATERIAL_GRAPH_VERSION 150
|
#define MATERIAL_GRAPH_VERSION 151
|
||||||
|
|
||||||
class Material;
|
class Material;
|
||||||
class GPUShader;
|
class GPUShader;
|
||||||
|
|||||||
@@ -419,6 +419,17 @@ Array<Actor*> Actor::GetChildren(const MClass* type) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actor::DestroyChildren(float timeLeft)
|
||||||
|
{
|
||||||
|
Array<Actor*> children = Children;
|
||||||
|
const bool useGameTime = timeLeft > ZeroTolerance;
|
||||||
|
for (Actor* child : children)
|
||||||
|
{
|
||||||
|
child->SetParent(nullptr, false, false);
|
||||||
|
child->DeleteObject(timeLeft, useGameTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Actor::HasTag(const StringView& tag) const
|
bool Actor::HasTag(const StringView& tag) const
|
||||||
{
|
{
|
||||||
return HasTag() && tag == Level::Tags[_tag];
|
return HasTag() && tag == Level::Tags[_tag];
|
||||||
|
|||||||
@@ -296,23 +296,6 @@ namespace FlaxEngine
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Destroys the children. Calls Object.Destroy on every child actor and unlink them for the parent.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="timeLeft">The time left to destroy object (in seconds).</param>
|
|
||||||
[NoAnimate]
|
|
||||||
public void DestroyChildren(float timeLeft = 0.0f)
|
|
||||||
{
|
|
||||||
if (ChildrenCount == 0)
|
|
||||||
return;
|
|
||||||
Actor[] children = Children;
|
|
||||||
for (var i = 0; i < children.Length; i++)
|
|
||||||
{
|
|
||||||
children[i].Parent = null;
|
|
||||||
Destroy(children[i], timeLeft);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the matrix that transforms a point from the world space to local space of the actor.
|
/// Gets the matrix that transforms a point from the world space to local space of the actor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -244,6 +244,12 @@ public:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Destroys the children. Calls Object.Destroy on every child actor and unlinks them for this actor.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timeLeft">The time left to destroy object (in seconds).</param>
|
||||||
|
API_FUNCTION(Attributes="NoAnimate") void DestroyChildren(float timeLeft = 0.0f);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ void AnimatedModel::ClearBlendShapeWeights()
|
|||||||
_blendShapes.Clear();
|
_blendShapes.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed, float blendInTime, float blendOutTime)
|
void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed, float blendInTime, float blendOutTime, int32 loopCount)
|
||||||
{
|
{
|
||||||
CHECK(anim);
|
CHECK(anim);
|
||||||
for (auto& slot : GraphInstance.Slots)
|
for (auto& slot : GraphInstance.Slots)
|
||||||
@@ -334,6 +334,7 @@ void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* ani
|
|||||||
{
|
{
|
||||||
slot.Pause = false;
|
slot.Pause = false;
|
||||||
slot.BlendInTime = blendInTime;
|
slot.BlendInTime = blendInTime;
|
||||||
|
slot.LoopCount = loopCount;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,6 +352,7 @@ void AnimatedModel::PlaySlotAnimation(const StringView& slotName, Animation* ani
|
|||||||
slot.Speed = speed;
|
slot.Speed = speed;
|
||||||
slot.BlendInTime = blendInTime;
|
slot.BlendInTime = blendInTime;
|
||||||
slot.BlendOutTime = blendOutTime;
|
slot.BlendOutTime = blendOutTime;
|
||||||
|
slot.LoopCount = loopCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedModel::StopSlotAnimation()
|
void AnimatedModel::StopSlotAnimation()
|
||||||
|
|||||||
@@ -313,7 +313,8 @@ public:
|
|||||||
/// <param name="speed">The playback speed.</param>
|
/// <param name="speed">The playback speed.</param>
|
||||||
/// <param name="blendInTime">The animation blending in time (in seconds). Cam be used to smooth the slot animation playback with the input pose when starting the animation.</param>
|
/// <param name="blendInTime">The animation blending in time (in seconds). Cam be used to smooth the slot animation playback with the input pose when starting the animation.</param>
|
||||||
/// <param name="blendOutTime">The animation blending out time (in seconds). Cam be used to smooth the slot animation playback with the input pose when ending animation.</param>
|
/// <param name="blendOutTime">The animation blending out time (in seconds). Cam be used to smooth the slot animation playback with the input pose when ending animation.</param>
|
||||||
API_FUNCTION() void PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed = 1.0f, float blendInTime = 0.2f, float blendOutTime = 0.2f);
|
/// <param name="loopCount">The amount of loops to play the animation: 0 to play once, -1 to play infinite, 1 or higher to loop once or more.</param>
|
||||||
|
API_FUNCTION() void PlaySlotAnimation(const StringView& slotName, Animation* anim, float speed = 1.0f, float blendInTime = 0.2f, float blendOutTime = 0.2f, int32 loopCount = 0);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Stops all the animations playback on the all slots in Anim Graph.
|
/// Stops all the animations playback on the all slots in Anim Graph.
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ namespace FlaxEngine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the font object described by the structure.
|
/// Gets the font object described by the structure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Th font or null if descriptor is invalid.</returns>
|
/// <returns>The font or null if descriptor is invalid.</returns>
|
||||||
public Font GetFont()
|
public Font GetFont()
|
||||||
{
|
{
|
||||||
if (_cachedFont)
|
if (_cachedFont)
|
||||||
|
|||||||
@@ -74,10 +74,7 @@ void ForwardPass::Dispose()
|
|||||||
void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output)
|
void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTexture* output)
|
||||||
{
|
{
|
||||||
PROFILE_GPU_CPU("Forward");
|
PROFILE_GPU_CPU("Forward");
|
||||||
|
auto context = GPUDevice::Instance->GetMainContext();
|
||||||
// Cache data
|
|
||||||
auto device = GPUDevice::Instance;
|
|
||||||
auto context = device->GetMainContext();
|
|
||||||
auto& view = renderContext.View;
|
auto& view = renderContext.View;
|
||||||
auto mainCache = renderContext.List;
|
auto mainCache = renderContext.List;
|
||||||
|
|
||||||
@@ -141,6 +138,6 @@ void ForwardPass::Render(RenderContext& renderContext, GPUTexture* input, GPUTex
|
|||||||
// Run forward pass
|
// Run forward pass
|
||||||
view.Pass = DrawPass::Forward;
|
view.Pass = DrawPass::Forward;
|
||||||
context->SetRenderTarget(depthBufferHandle, output->View());
|
context->SetRenderTarget(depthBufferHandle, output->View());
|
||||||
mainCache->ExecuteDrawCalls(renderContext, forwardList);
|
mainCache->ExecuteDrawCalls(renderContext, forwardList, input->View());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -554,7 +554,7 @@ bool CanUseInstancing(DrawPass pass)
|
|||||||
return pass == DrawPass::GBuffer || pass == DrawPass::Depth;
|
return pass == DrawPass::GBuffer || pass == DrawPass::Depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list)
|
void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input)
|
||||||
{
|
{
|
||||||
if (list.IsEmpty())
|
if (list.IsEmpty())
|
||||||
return;
|
return;
|
||||||
@@ -626,6 +626,7 @@ DRAW:
|
|||||||
|
|
||||||
// Execute draw calls
|
// Execute draw calls
|
||||||
MaterialBase::BindParameters bindParams(context, renderContext);
|
MaterialBase::BindParameters bindParams(context, renderContext);
|
||||||
|
bindParams.Input = input;
|
||||||
if (useInstancing)
|
if (useInstancing)
|
||||||
{
|
{
|
||||||
int32 instanceBufferOffset = 0;
|
int32 instanceBufferOffset = 0;
|
||||||
|
|||||||
@@ -570,9 +570,10 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="renderContext">The rendering context.</param>
|
/// <param name="renderContext">The rendering context.</param>
|
||||||
/// <param name="listType">The collected draw calls list type.</param>
|
/// <param name="listType">The collected draw calls list type.</param>
|
||||||
API_FUNCTION() FORCE_INLINE void ExecuteDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, DrawCallsListType listType)
|
/// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
|
||||||
|
API_FUNCTION() FORCE_INLINE void ExecuteDrawCalls(API_PARAM(Ref) const RenderContext& renderContext, DrawCallsListType listType, GPUTextureView* input = nullptr)
|
||||||
{
|
{
|
||||||
ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType]);
|
ExecuteDrawCalls(renderContext, DrawCallsLists[(int32)listType], input);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -580,7 +581,8 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="renderContext">The rendering context.</param>
|
/// <param name="renderContext">The rendering context.</param>
|
||||||
/// <param name="list">The collected draw calls list.</param>
|
/// <param name="list">The collected draw calls list.</param>
|
||||||
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list);
|
/// <param name="input">The input scene color. It's optional and used in forward/postFx rendering.</param>
|
||||||
|
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list, GPUTextureView* input = nullptr);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -448,8 +448,9 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Run forward pass
|
// Run forward pass
|
||||||
auto forwardPassResult = renderContext.Buffers->RT1_FloatRGB;
|
GPUTexture* frameBuffer = renderContext.Buffers->RT1_FloatRGB;
|
||||||
ForwardPass::Instance()->Render(renderContext, lightBuffer, forwardPassResult);
|
GPUTexture* tempBuffer = renderContext.Buffers->RT2_FloatRGB;
|
||||||
|
ForwardPass::Instance()->Render(renderContext, lightBuffer, frameBuffer);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
context->ResetRenderTarget();
|
context->ResetRenderTarget();
|
||||||
@@ -462,16 +463,10 @@ void RenderInner(SceneRenderTask* task, RenderContext& renderContext)
|
|||||||
{
|
{
|
||||||
context->SetRenderTarget(task->GetOutputView());
|
context->SetRenderTarget(task->GetOutputView());
|
||||||
context->SetViewportAndScissors(task->GetOutputViewport());
|
context->SetViewportAndScissors(task->GetOutputViewport());
|
||||||
context->Draw(forwardPassResult);
|
context->Draw(frameBuffer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare buffers for post processing frame
|
|
||||||
GPUTexture* frameBuffer = renderContext.Buffers->RT1_FloatRGB;
|
|
||||||
GPUTexture* tempBuffer = renderContext.Buffers->RT2_FloatRGB;
|
|
||||||
if (forwardPassResult == tempBuffer)
|
|
||||||
Swap(frameBuffer, tempBuffer);
|
|
||||||
|
|
||||||
// Material and Custom PostFx
|
// Material and Custom PostFx
|
||||||
renderContext.List->RunMaterialPostFxPass(context, renderContext, MaterialPostFxLocation::BeforePostProcessingPass, frameBuffer, tempBuffer);
|
renderContext.List->RunMaterialPostFxPass(context, renderContext, MaterialPostFxLocation::BeforePostProcessingPass, frameBuffer, tempBuffer);
|
||||||
renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::BeforePostProcessingPass, frameBuffer, tempBuffer);
|
renderContext.List->RunCustomPostFxPass(context, renderContext, PostProcessEffectLocation::BeforePostProcessingPass, frameBuffer, tempBuffer);
|
||||||
|
|||||||
@@ -42,13 +42,10 @@ PACK_STRUCT(struct Data
|
|||||||
float TemporalScale;
|
float TemporalScale;
|
||||||
|
|
||||||
float RayTraceStep;
|
float RayTraceStep;
|
||||||
float NoTemporalEffect;
|
float TemporalEffect;
|
||||||
float Intensity;
|
float Intensity;
|
||||||
float FadeOutDistance;
|
float FadeOutDistance;
|
||||||
|
|
||||||
Vector3 Dummy0;
|
|
||||||
float InvFadeDistance;
|
|
||||||
|
|
||||||
Matrix ViewMatrix;
|
Matrix ViewMatrix;
|
||||||
Matrix ViewProjectionMatrix;
|
Matrix ViewProjectionMatrix;
|
||||||
});
|
});
|
||||||
@@ -248,11 +245,10 @@ void ScreenSpaceReflectionsPass::Render(RenderContext& renderContext, GPUTexture
|
|||||||
data.MaxColorMiplevel = settings.UseColorBufferMips ? (float)colorBufferMips - 2.0f : 0.0f;
|
data.MaxColorMiplevel = settings.UseColorBufferMips ? (float)colorBufferMips - 2.0f : 0.0f;
|
||||||
data.RayTraceStep = static_cast<float>(settings.DepthResolution) / (float)width;
|
data.RayTraceStep = static_cast<float>(settings.DepthResolution) / (float)width;
|
||||||
data.Intensity = settings.Intensity;
|
data.Intensity = settings.Intensity;
|
||||||
data.FadeOutDistance = settings.FadeOutDistance;
|
data.FadeOutDistance = Math::Max(settings.FadeOutDistance, 100.0f);
|
||||||
data.InvFadeDistance = 1.0f / settings.FadeDistance;
|
|
||||||
data.TemporalScale = settings.TemporalScale;
|
data.TemporalScale = settings.TemporalScale;
|
||||||
data.TemporalResponse = settings.TemporalResponse;
|
data.TemporalResponse = settings.TemporalResponse;
|
||||||
data.NoTemporalEffect = useTemporal ? 0.0f : 1.0f;
|
data.TemporalEffect = useTemporal ? 1.0f : 0.0f;
|
||||||
if (useTemporal)
|
if (useTemporal)
|
||||||
{
|
{
|
||||||
const float time = Time::Draw.UnscaledTime.GetTotalSeconds();
|
const float time = Time::Draw.UnscaledTime.GetTotalSeconds();
|
||||||
|
|||||||
@@ -17,6 +17,16 @@ namespace FlaxEngine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public string Text;
|
public string Text;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The custom header font size.
|
||||||
|
/// </summary>
|
||||||
|
public int FontSize;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The custom header color (as 32-bit uint).
|
||||||
|
/// </summary>
|
||||||
|
public uint Color;
|
||||||
|
|
||||||
private HeaderAttribute()
|
private HeaderAttribute()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -25,9 +35,13 @@ namespace FlaxEngine
|
|||||||
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class.
|
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="text">The header text.</param>
|
/// <param name="text">The header text.</param>
|
||||||
public HeaderAttribute(string text)
|
/// <param name="fontSize">The header text font size (-1 to use default which is 14).</param>
|
||||||
|
/// <param name="color">The header color (as 32-bit uint, 0 to use default).</param>
|
||||||
|
public HeaderAttribute(string text, int fontSize = -1, uint color = 0)
|
||||||
{
|
{
|
||||||
Text = text;
|
Text = text;
|
||||||
|
FontSize = fontSize;
|
||||||
|
Color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace FlaxEngine
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="HeaderAttribute"/> class.
|
/// Initializes a new instance of the <see cref="VisibleIfAttribute"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="memberName">The name of the field or property of the object. Must be a bool type.</param>
|
/// <param name="memberName">The name of the field or property of the object. Must be a bool type.</param>
|
||||||
/// <param name="invert">True if invert member value when computing the visibility value.</param>
|
/// <param name="invert">True if invert member value when computing the visibility value.</param>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ MaterialValue* MaterialGenerator::sampleTextureRaw(Node* caller, Value& value, B
|
|||||||
ASSERT(texture && box);
|
ASSERT(texture && box);
|
||||||
|
|
||||||
// Cache data
|
// Cache data
|
||||||
const auto parent = box->GetParent<MaterialGraphNode>();
|
const auto parent = box->GetParent<ShaderGraphNode<>>();
|
||||||
const bool isCubemap = texture->Type == MaterialParameterType::CubeTexture;
|
const bool isCubemap = texture->Type == MaterialParameterType::CubeTexture;
|
||||||
const bool isArray = texture->Type == MaterialParameterType::GPUTextureArray;
|
const bool isArray = texture->Type == MaterialParameterType::GPUTextureArray;
|
||||||
const bool isVolume = texture->Type == MaterialParameterType::GPUTextureVolume;
|
const bool isVolume = texture->Type == MaterialParameterType::GPUTextureVolume;
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
|||||||
|
|
||||||
// Cache data
|
// Cache data
|
||||||
MaterialLayer* baseLayer = GetRootLayer();
|
MaterialLayer* baseLayer = GetRootLayer();
|
||||||
MaterialGraphNode* baseNode = baseLayer->Root;
|
auto* baseNode = baseLayer->Root;
|
||||||
_treeLayerVarName = baseLayer->GetVariableName(nullptr);
|
_treeLayerVarName = baseLayer->GetVariableName(nullptr);
|
||||||
_treeLayer = baseLayer;
|
_treeLayer = baseLayer;
|
||||||
_graphStack.Add(&_treeLayer->Graph);
|
_graphStack.Add(&_treeLayer->Graph);
|
||||||
@@ -398,6 +398,14 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
|||||||
_writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold);
|
_writer.Write(TEXT("#define MATERIAL_MASK_THRESHOLD ({0})\n"), baseLayer->MaskThreshold);
|
||||||
_writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count());
|
_writer.Write(TEXT("#define CUSTOM_VERTEX_INTERPOLATORS_COUNT ({0})\n"), _vsToPsInterpolants.Count());
|
||||||
_writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold);
|
_writer.Write(TEXT("#define MATERIAL_OPACITY_THRESHOLD ({0})\n"), baseLayer->OpacityThreshold);
|
||||||
|
if (materialInfo.BlendMode != MaterialBlendMode::Opaque && !(materialInfo.FeaturesFlags & MaterialFeaturesFlags::DisableReflections) && materialInfo.FeaturesFlags & MaterialFeaturesFlags::ScreenSpaceReflections)
|
||||||
|
{
|
||||||
|
// Inject depth and color buffers for Screen Space Reflections used by transparent material
|
||||||
|
auto sceneDepthTexture = findOrAddSceneTexture(MaterialSceneTextures::SceneDepth);
|
||||||
|
auto sceneColorTexture = findOrAddSceneTexture(MaterialSceneTextures::SceneColor);
|
||||||
|
_writer.Write(TEXT("#define MATERIAL_REFLECTIONS_SSR_DEPTH ({0})\n"), sceneDepthTexture.ShaderName);
|
||||||
|
_writer.Write(TEXT("#define MATERIAL_REFLECTIONS_SSR_COLOR ({0})\n"), sceneColorTexture.ShaderName);
|
||||||
|
}
|
||||||
WRITE_FEATURES(Defines);
|
WRITE_FEATURES(Defines);
|
||||||
inputs[In_Defines] = _writer.ToString();
|
inputs[In_Defines] = _writer.ToString();
|
||||||
_writer.Clear();
|
_writer.Clear();
|
||||||
@@ -447,6 +455,7 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
|||||||
}
|
}
|
||||||
for (auto f : features)
|
for (auto f : features)
|
||||||
{
|
{
|
||||||
|
// Process SRV slots used in template
|
||||||
const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources];
|
const auto& text = Features[f].Inputs[(int32)FeatureTemplateInputsMapping::Resources];
|
||||||
const Char* str = text.Get();
|
const Char* str = text.Get();
|
||||||
int32 prevIdx = 0, idx = 0;
|
int32 prevIdx = 0, idx = 0;
|
||||||
@@ -483,6 +492,21 @@ bool MaterialGenerator::Generate(WriteStream& source, MaterialInfo& materialInfo
|
|||||||
// Utilities
|
// Utilities
|
||||||
{
|
{
|
||||||
WRITE_FEATURES(Utilities);
|
WRITE_FEATURES(Utilities);
|
||||||
|
Array<Graph*, InlinedAllocation<8>> graphs;
|
||||||
|
_functions.GetValues(graphs);
|
||||||
|
for (MaterialLayer* layer : _layers)
|
||||||
|
graphs.Add(&layer->Graph);
|
||||||
|
for (Graph* graph : graphs)
|
||||||
|
{
|
||||||
|
for (const MaterialGraph::Node& node : graph->Nodes)
|
||||||
|
{
|
||||||
|
if (node.Type == GRAPH_NODE_MAKE_TYPE(1, 38) && (bool)node.Values[1])
|
||||||
|
{
|
||||||
|
// Custom Global Code
|
||||||
|
_writer.Write((StringView)node.Values[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
inputs[In_Utilities] = _writer.ToString();
|
inputs[In_Utilities] = _writer.ToString();
|
||||||
_writer.Clear();
|
_writer.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ void MaterialLayer::Prepare()
|
|||||||
const auto node = &Graph.Nodes[i];
|
const auto node = &Graph.Nodes[i];
|
||||||
if (node->Type == ROOT_NODE_TYPE)
|
if (node->Type == ROOT_NODE_TYPE)
|
||||||
{
|
{
|
||||||
Root = (MaterialGraphNode*)node;
|
Root = node;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +159,7 @@ MaterialLayer* MaterialLayer::Load(const Guid& id, ReadStream* graphData, const
|
|||||||
{
|
{
|
||||||
if (layer->Graph.Nodes[i].Type == ROOT_NODE_TYPE)
|
if (layer->Graph.Nodes[i].Type == ROOT_NODE_TYPE)
|
||||||
{
|
{
|
||||||
layer->Root = (MaterialGraphNode*)&layer->Graph.Nodes[i];
|
layer->Root = &layer->Graph.Nodes[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,7 +233,7 @@ void MaterialLayer::createRootNode()
|
|||||||
#undef INIT_BOX
|
#undef INIT_BOX
|
||||||
|
|
||||||
// Mark as root
|
// Mark as root
|
||||||
Root = (MaterialGraphNode*)&rootNode;
|
Root = &rootNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public:
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Root node
|
/// Root node
|
||||||
/// </summary>
|
/// </summary>
|
||||||
MaterialGraphNode* Root;
|
MaterialGraph::Node* Root;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Material structure variable name (different for every layer sampling with different UVs, default UVs are a first index)
|
/// Material structure variable name (different for every layer sampling with different UVs, default UVs are a first index)
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
#include "Engine/Core/Enums.h"
|
#include "Engine/Core/Enums.h"
|
||||||
#include "Engine/Visject/ShaderGraph.h"
|
#include "Engine/Visject/ShaderGraph.h"
|
||||||
|
|
||||||
class MaterialGraphNode : public ShaderGraphNode<>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
class MaterialGraph : public ShaderGraph<>
|
class MaterialGraph : public ShaderGraph<>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace FlaxEngine.Utilities
|
|||||||
/// Requests the mesh data.
|
/// Requests the mesh data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="model">The model to get it's data.</param>
|
/// <param name="model">The model to get it's data.</param>
|
||||||
/// <returns>True if ahs valid data to access, otherwise false if it's during downloading.</returns>
|
/// <returns>True if has valid data to access, otherwise false if it's during downloading.</returns>
|
||||||
public bool RequestMeshData(Model model)
|
public bool RequestMeshData(Model model)
|
||||||
{
|
{
|
||||||
if (model == null)
|
if (model == null)
|
||||||
|
|||||||
@@ -1350,6 +1350,13 @@ void VisjectExecutor::ProcessGroupCollections(Box* box, Node* node, Value& value
|
|||||||
array.Reverse();
|
array.Reverse();
|
||||||
value = MoveTemp(v);
|
value = MoveTemp(v);
|
||||||
break;
|
break;
|
||||||
|
// Add Unique
|
||||||
|
case 14:
|
||||||
|
b = node->GetBox(1);
|
||||||
|
ENSURE(b->HasConnection(), TEXT("Missing value to add."));
|
||||||
|
array.AddUnique(eatBox(b->GetParent<Node>(), b->FirstConnection()));
|
||||||
|
value = MoveTemp(v);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,18 +42,13 @@ float4 SampleReflectionProbe(float3 viewPos, TextureCube probe, ProbeData data,
|
|||||||
return probeSample * fade;
|
return probeSample * fade;
|
||||||
}
|
}
|
||||||
|
|
||||||
float3 GetEnvProbeLighting(float3 viewPos, TextureCube probe, ProbeData data, GBufferSample gBuffer)
|
// Calculates the reflective environment lighting to multiply the raw reflection color for the specular light (eg. from Env Probe or SSR).
|
||||||
|
float3 GetReflectionSpecularLighting(float3 viewPos, GBufferSample gBuffer)
|
||||||
{
|
{
|
||||||
// Calculate reflections
|
|
||||||
float3 reflections = SampleReflectionProbe(viewPos, probe, data, gBuffer.WorldPos, gBuffer.Normal, gBuffer.Roughness).rgb;
|
|
||||||
|
|
||||||
// Calculate specular color
|
|
||||||
float3 specularColor = GetSpecularColor(gBuffer);
|
float3 specularColor = GetSpecularColor(gBuffer);
|
||||||
|
|
||||||
// Calculate reflecion color
|
|
||||||
float3 V = normalize(viewPos - gBuffer.WorldPos);
|
float3 V = normalize(viewPos - gBuffer.WorldPos);
|
||||||
float NoV = saturate(dot(gBuffer.Normal, V));
|
float NoV = saturate(dot(gBuffer.Normal, V));
|
||||||
return reflections * EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV);
|
return EnvBRDFApprox(specularColor, gBuffer.Roughness, NoV);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
154
Source/Shaders/SSR.hlsl
Normal file
154
Source/Shaders/SSR.hlsl
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
#include "./Flax/Common.hlsl"
|
||||||
|
#include "./Flax/BRDF.hlsl"
|
||||||
|
#include "./Flax/Random.hlsl"
|
||||||
|
#include "./Flax/MonteCarlo.hlsl"
|
||||||
|
#include "./Flax/GBufferCommon.hlsl"
|
||||||
|
|
||||||
|
float max2(float2 v)
|
||||||
|
{
|
||||||
|
return max(v.x, v.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
float2 RandN2(float2 pos, float2 random)
|
||||||
|
{
|
||||||
|
return frac(sin(dot(pos.xy + random, float2(12.9898, 78.233))) * float2(43758.5453, 28001.8384));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1:-1 to 0:1
|
||||||
|
float2 ClipToUv(float2 clipPos)
|
||||||
|
{
|
||||||
|
return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// go into clip space (-1:1 from bottom/left to up/right)
|
||||||
|
float3 ProjectWorldToClip(float3 wsPos, float4x4 viewProjectionMatrix)
|
||||||
|
{
|
||||||
|
float4 clipPos = mul(float4(wsPos, 1), viewProjectionMatrix);
|
||||||
|
return clipPos.xyz / clipPos.w;
|
||||||
|
}
|
||||||
|
|
||||||
|
// go into UV space. (0:1 from top/left to bottom/right)
|
||||||
|
float3 ProjectWorldToUv(float3 wsPos, float4x4 viewProjectionMatrix)
|
||||||
|
{
|
||||||
|
float3 clipPos = ProjectWorldToClip(wsPos, viewProjectionMatrix);
|
||||||
|
return float3(ClipToUv(clipPos.xy), clipPos.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float3 TangentToWorld(float3 N, float4 H)
|
||||||
|
{
|
||||||
|
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
|
||||||
|
float3 T = normalize(cross(upVector, N));
|
||||||
|
float3 B = cross(N, T);
|
||||||
|
return float3((T * H.x) + (B * H.y) + (N * H.z));
|
||||||
|
}
|
||||||
|
|
||||||
|
float RayAttenBorder(float2 pos, float value)
|
||||||
|
{
|
||||||
|
float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y));
|
||||||
|
return saturate(borderDist > value ? 1.0 : borderDist / value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Screen Space Reflection ray tracing utility.
|
||||||
|
// Returns: xy: hitUV, z: hitMask, where hitUV is the result UV of hit pixel, hitMask is the normalized sample weight (0 if no hit).
|
||||||
|
float3 TraceSceenSpaceReflection(float2 uv, GBufferSample gBuffer, Texture2D depthBuffer, float3 viewPos, float4x4 viewMatrix, float4x4 viewProjectionMatrix, float stepSize, float maxSamples = 20, bool temporal = false, float temporalTime = 0.0f, float worldAntiSelfOcclusionBias = 0.1f, float brdfBias = 0.82f, float drawDistance = 5000.0f, float roughnessThreshold = 0.4f, float edgeFade = 0.1f)
|
||||||
|
{
|
||||||
|
// Reject invalid pixels
|
||||||
|
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > roughnessThreshold || gBuffer.ViewPos.z > drawDistance)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Calculate view space normal vector
|
||||||
|
float3 normalVS = mul(gBuffer.Normal, (float3x3)viewMatrix);
|
||||||
|
|
||||||
|
// Randomize it a little
|
||||||
|
float2 jitter = RandN2(uv, temporalTime);
|
||||||
|
float2 Xi = jitter;
|
||||||
|
Xi.y = lerp(Xi.y, 0.0, brdfBias);
|
||||||
|
float3 H = temporal ? TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness)) : gBuffer.Normal;
|
||||||
|
|
||||||
|
// Calculate normalized view space reflection vector
|
||||||
|
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
|
||||||
|
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
float3 viewWS = normalize(gBuffer.WorldPos - viewPos);
|
||||||
|
float3 reflectWS = reflect(viewWS, H.xyz);
|
||||||
|
|
||||||
|
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * worldAntiSelfOcclusionBias;
|
||||||
|
float3 startUV = ProjectWorldToUv(startWS, viewProjectionMatrix);
|
||||||
|
float3 endUV = ProjectWorldToUv(startWS + reflectWS, viewProjectionMatrix);
|
||||||
|
|
||||||
|
float3 rayUV = endUV - startUV;
|
||||||
|
rayUV *= stepSize / max2(abs(rayUV.xy));
|
||||||
|
float3 startUv = startUV + rayUV * 2;
|
||||||
|
|
||||||
|
float3 currOffset = startUv;
|
||||||
|
float3 rayStep = rayUV * 2;
|
||||||
|
|
||||||
|
// Calculate number of samples
|
||||||
|
float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz;
|
||||||
|
samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f;
|
||||||
|
float numSamples = min(maxSamples, samplesToEdge.x);
|
||||||
|
rayStep *= samplesToEdge.x / numSamples;
|
||||||
|
|
||||||
|
// Calculate depth difference error
|
||||||
|
float depthDiffError = 1.3f * abs(rayStep.z);
|
||||||
|
|
||||||
|
// Ray trace
|
||||||
|
float currSampleIndex = 0;
|
||||||
|
float currSample, depthDiff;
|
||||||
|
LOOP
|
||||||
|
while (currSampleIndex < numSamples)
|
||||||
|
{
|
||||||
|
// Sample depth buffer and calculate depth difference
|
||||||
|
currSample = SAMPLE_RT(depthBuffer, currOffset.xy).r;
|
||||||
|
depthDiff = currOffset.z - currSample;
|
||||||
|
|
||||||
|
// Check intersection
|
||||||
|
if (depthDiff >= 0)
|
||||||
|
{
|
||||||
|
if (depthDiff < depthDiffError)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currOffset -= rayStep;
|
||||||
|
rayStep *= 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move forward
|
||||||
|
currOffset += rayStep;
|
||||||
|
currSampleIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if has valid result after ray tracing
|
||||||
|
if (currSampleIndex >= numSamples)
|
||||||
|
{
|
||||||
|
// All samples done but no result
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
float2 hitUV = currOffset.xy;
|
||||||
|
|
||||||
|
// Fade rays close to screen edge
|
||||||
|
const float fadeStart = 0.9f;
|
||||||
|
const float fadeEnd = 1.0f;
|
||||||
|
const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart);
|
||||||
|
float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f;
|
||||||
|
float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp);
|
||||||
|
fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp);
|
||||||
|
fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder);
|
||||||
|
fadeOnBorder *= RayAttenBorder(hitUV, edgeFade);
|
||||||
|
|
||||||
|
// Fade rays on high roughness
|
||||||
|
float roughnessFade = saturate((roughnessThreshold - gBuffer.Roughness) * 20);
|
||||||
|
|
||||||
|
// Fade on distance
|
||||||
|
float distanceFade = saturate((drawDistance - gBuffer.ViewPos.z) / drawDistance);
|
||||||
|
|
||||||
|
// Output: xy: hitUV, z: hitMask
|
||||||
|
return float3(hitUV, fadeOnBorder * roughnessFade * distanceFade);
|
||||||
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
// Copyright (c) 2012-2022 Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
#include "./Flax/Common.hlsl"
|
#include "./Flax/Common.hlsl"
|
||||||
#include "./Flax/BRDF.hlsl"
|
|
||||||
#include "./Flax/Random.hlsl"
|
|
||||||
#include "./Flax/MonteCarlo.hlsl"
|
|
||||||
#include "./Flax/LightingCommon.hlsl"
|
#include "./Flax/LightingCommon.hlsl"
|
||||||
#include "./Flax/GBuffer.hlsl"
|
|
||||||
#include "./Flax/ReflectionsCommon.hlsl"
|
#include "./Flax/ReflectionsCommon.hlsl"
|
||||||
#include "./Flax/BRDF.hlsl"
|
#include "./Flax/SSR.hlsl"
|
||||||
|
#include "./Flax/GBuffer.hlsl"
|
||||||
|
|
||||||
// Enable/disable luminance filter to reduce reflections highlights
|
// Enable/disable luminance filter to reduce reflections highlights
|
||||||
#define SSR_REDUCE_HIGHLIGHTS 1
|
#define SSR_REDUCE_HIGHLIGHTS 1
|
||||||
@@ -34,13 +31,10 @@ float TemporalResponse;
|
|||||||
float TemporalScale;
|
float TemporalScale;
|
||||||
|
|
||||||
float RayTraceStep;
|
float RayTraceStep;
|
||||||
float NoTemporalEffect;
|
float TemporalEffect;
|
||||||
float Intensity;
|
float Intensity;
|
||||||
float FadeOutDistance;
|
float FadeOutDistance;
|
||||||
|
|
||||||
float3 Dummy0;
|
|
||||||
float InvFadeDistance;
|
|
||||||
|
|
||||||
float4x4 ViewMatrix;
|
float4x4 ViewMatrix;
|
||||||
float4x4 ViewProjectionMatrix;
|
float4x4 ViewProjectionMatrix;
|
||||||
|
|
||||||
@@ -52,50 +46,6 @@ Texture2D Texture0 : register(t4);
|
|||||||
Texture2D Texture1 : register(t5);
|
Texture2D Texture1 : register(t5);
|
||||||
Texture2D Texture2 : register(t6);
|
Texture2D Texture2 : register(t6);
|
||||||
|
|
||||||
float max2(float2 v)
|
|
||||||
{
|
|
||||||
return max(v.x, v.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
float2 RandN2(float2 pos, float2 random)
|
|
||||||
{
|
|
||||||
return frac(sin(dot(pos.xy + random, float2(12.9898, 78.233))) * float2(43758.5453, 28001.8384));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1:-1 to 0:1
|
|
||||||
float2 ClipToUv(float2 clipPos)
|
|
||||||
{
|
|
||||||
return clipPos * float2(0.5, -0.5) + float2(0.5, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
// go into clip space (-1:1 from bottom/left to up/right)
|
|
||||||
float3 ProjectWorldToClip(float3 wsPos)
|
|
||||||
{
|
|
||||||
float4 clipPos = mul(float4(wsPos, 1), ViewProjectionMatrix);
|
|
||||||
return clipPos.xyz / clipPos.w;
|
|
||||||
}
|
|
||||||
|
|
||||||
// go into UV space. (0:1 from top/left to bottom/right)
|
|
||||||
float3 ProjectWorldToUv(float3 wsPos)
|
|
||||||
{
|
|
||||||
float3 clipPos = ProjectWorldToClip(wsPos);
|
|
||||||
return float3(ClipToUv(clipPos.xy), clipPos.z);
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 TangentToWorld(float3 N, float4 H)
|
|
||||||
{
|
|
||||||
float3 upVector = abs(N.z) < 0.999 ? float3(0.0, 0.0, 1.0) : float3(1.0, 0.0, 0.0);
|
|
||||||
float3 T = normalize(cross(upVector, N));
|
|
||||||
float3 B = cross(N, T);
|
|
||||||
return float4((T * H.x) + (B * H.y) + (N * H.z), H.w);
|
|
||||||
}
|
|
||||||
|
|
||||||
float RayAttenBorder(float2 pos, float value)
|
|
||||||
{
|
|
||||||
float borderDist = min(1.0 - max(pos.x, pos.y), min(pos.x, pos.y));
|
|
||||||
return saturate(borderDist > value ? 1.0 : borderDist / value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pixel Shader for screen space reflections rendering - combine pass
|
// Pixel Shader for screen space reflections rendering - combine pass
|
||||||
META_PS(true, FEATURE_LEVEL_ES2)
|
META_PS(true, FEATURE_LEVEL_ES2)
|
||||||
float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
|
float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
|
||||||
@@ -135,112 +85,15 @@ float4 PS_CombinePass(Quad_VS2PS input) : SV_Target0
|
|||||||
META_PS(true, FEATURE_LEVEL_ES2)
|
META_PS(true, FEATURE_LEVEL_ES2)
|
||||||
float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
|
float4 PS_RayTracePass(Quad_VS2PS input) : SV_Target0
|
||||||
{
|
{
|
||||||
float2 uv = input.TexCoord;
|
|
||||||
|
|
||||||
// Sample GBuffer
|
// Sample GBuffer
|
||||||
GBufferData gBufferData = GetGBufferData();
|
GBufferData gBufferData = GetGBufferData();
|
||||||
GBufferSample gBuffer = SampleGBuffer(gBufferData, uv);
|
GBufferSample gBuffer = SampleGBuffer(gBufferData, input.TexCoord);
|
||||||
|
|
||||||
// Reject invalid pixels
|
// Trace depth buffer to find intersection
|
||||||
if (gBuffer.ShadingModel == SHADING_MODEL_UNLIT || gBuffer.Roughness > RoughnessFade || gBuffer.ViewPos.z > FadeOutDistance)
|
float3 hit = TraceSceenSpaceReflection(input.TexCoord, gBuffer, Depth, gBufferData.ViewPos, ViewMatrix, ViewProjectionMatrix, RayTraceStep, MaxTraceSamples, TemporalEffect, TemporalTime, WorldAntiSelfOcclusionBias, BRDFBias, FadeOutDistance, RoughnessFade, EdgeFadeFactor);
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Calculate view space normal vector
|
|
||||||
float3 normalVS = mul(gBuffer.Normal, (float3x3)ViewMatrix);
|
|
||||||
|
|
||||||
// Randomize it a little
|
|
||||||
float2 jitter = RandN2(uv, TemporalTime);
|
|
||||||
float2 Xi = jitter;
|
|
||||||
Xi.y = lerp(Xi.y, 0.0, BRDFBias);
|
|
||||||
|
|
||||||
float4 H = TangentToWorld(gBuffer.Normal, ImportanceSampleGGX(Xi, gBuffer.Roughness));
|
|
||||||
if (NoTemporalEffect)
|
|
||||||
H.xyz = gBuffer.Normal;
|
|
||||||
|
|
||||||
// Calculate normalized view space reflection vector
|
|
||||||
float3 reflectVS = normalize(reflect(gBuffer.ViewPos, normalVS));
|
|
||||||
if (gBuffer.ViewPos.z < 1.0 && reflectVS.z < 0.4)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
float3 viewWS = normalize(gBuffer.WorldPos - GBuffer.ViewPos);
|
|
||||||
float3 reflectWS = reflect(viewWS, H.xyz);
|
|
||||||
|
|
||||||
float3 startWS = gBuffer.WorldPos + gBuffer.Normal * WorldAntiSelfOcclusionBias;
|
|
||||||
float3 startUV = ProjectWorldToUv(startWS);
|
|
||||||
float3 endUV = ProjectWorldToUv(startWS + reflectWS);
|
|
||||||
|
|
||||||
float3 rayUV = endUV - startUV;
|
|
||||||
rayUV *= RayTraceStep / max2(abs(rayUV.xy));
|
|
||||||
float3 startUv = startUV + rayUV * 2;
|
|
||||||
|
|
||||||
float3 currOffset = startUv;
|
|
||||||
float3 rayStep = rayUV * 2;
|
|
||||||
|
|
||||||
// Calculate number of samples
|
|
||||||
float3 samplesToEdge = ((sign(rayStep.xyz) * 0.5 + 0.5) - currOffset.xyz) / rayStep.xyz;
|
|
||||||
samplesToEdge.x = min(samplesToEdge.x, min(samplesToEdge.y, samplesToEdge.z)) * 1.05f;
|
|
||||||
float numSamples = min(MaxTraceSamples, samplesToEdge.x);
|
|
||||||
rayStep *= samplesToEdge.x / numSamples;
|
|
||||||
|
|
||||||
// Calculate depth difference error
|
|
||||||
float depthDiffError = 1.3f * abs(rayStep.z);
|
|
||||||
|
|
||||||
// Ray trace
|
|
||||||
float currSampleIndex = 0;
|
|
||||||
float currSample, depthDiff;
|
|
||||||
LOOP
|
|
||||||
while (currSampleIndex < numSamples)
|
|
||||||
{
|
|
||||||
// Sample depth buffer and calculate depth difference
|
|
||||||
currSample = SampleZ(currOffset.xy);
|
|
||||||
depthDiff = currOffset.z - currSample;
|
|
||||||
|
|
||||||
// Check intersection
|
|
||||||
if (depthDiff >= 0)
|
|
||||||
{
|
|
||||||
if (depthDiff < depthDiffError)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currOffset -= rayStep;
|
|
||||||
rayStep *= 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move forward
|
|
||||||
currOffset += rayStep;
|
|
||||||
currSampleIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if has valid result after ray tracing
|
|
||||||
if (currSampleIndex >= numSamples)
|
|
||||||
{
|
|
||||||
// All samples done but no result
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float2 hitUV = currOffset.xy;
|
|
||||||
|
|
||||||
// Fade rays close to screen edge
|
|
||||||
const float fadeStart = 0.9f;
|
|
||||||
const float fadeEnd = 1.0f;
|
|
||||||
const float fadeDiffRcp = 1.0f / (fadeEnd - fadeStart);
|
|
||||||
float2 boundary = abs(hitUV - float2(0.5f, 0.5f)) * 2.0f;
|
|
||||||
float fadeOnBorder = 1.0f - saturate((boundary.x - fadeStart) * fadeDiffRcp);
|
|
||||||
fadeOnBorder *= 1.0f - saturate((boundary.y - fadeStart) * fadeDiffRcp);
|
|
||||||
fadeOnBorder = smoothstep(0.0f, 1.0f, fadeOnBorder);
|
|
||||||
fadeOnBorder *= RayAttenBorder(hitUV, EdgeFadeFactor);
|
|
||||||
|
|
||||||
// Fade rays on high roughness
|
|
||||||
float roughnessFade = saturate((RoughnessFade - gBuffer.Roughness) * 20);
|
|
||||||
|
|
||||||
// Fade on distance
|
|
||||||
float distanceFade = saturate((FadeOutDistance - gBuffer.ViewPos.z) * InvFadeDistance);
|
|
||||||
|
|
||||||
// Output: xy: hitUV, z: hitMask, w: unused
|
// Output: xy: hitUV, z: hitMask, w: unused
|
||||||
return float4(hitUV, fadeOnBorder * roughnessFade * distanceFade * Intensity, 0);
|
return float4(hit.xy, hit.z * Intensity, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef RESOLVE_SAMPLES
|
#ifndef RESOLVE_SAMPLES
|
||||||
|
|||||||
Reference in New Issue
Block a user