Merge branch '1.11' of https://gitlab.flaxengine.com/flax/flaxengine into 1.11
This commit is contained in:
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
BIN
Content/Editor/DebugMaterials/DDGIDebugProbes.flax
(Stored with Git LFS)
Binary file not shown.
@@ -28,6 +28,13 @@ TextureCube SkyLightTexture : register(t__SRV__);
|
|||||||
Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
Buffer<float4> ShadowsBuffer : register(t__SRV__);
|
||||||
Texture2D<float> ShadowMap : register(t__SRV__);
|
Texture2D<float> ShadowMap : register(t__SRV__);
|
||||||
@4// Forward Shading: Utilities
|
@4// Forward Shading: Utilities
|
||||||
|
// Public accessors for lighting data, use them as data binding might change but those methods will remain.
|
||||||
|
LightData GetDirectionalLight() { return DirectionalLight; }
|
||||||
|
LightData GetSkyLight() { return SkyLight; }
|
||||||
|
ProbeData GetEnvironmentProbe() { return EnvironmentProbe; }
|
||||||
|
ExponentialHeightFogData GetExponentialHeightFog() { return ExponentialHeightFog; }
|
||||||
|
uint GetLocalLightsCount() { return LocalLightsCount; }
|
||||||
|
LightData GetLocalLight(uint i) { return LocalLights[i]; }
|
||||||
@5// Forward Shading: Shaders
|
@5// Forward Shading: Shaders
|
||||||
|
|
||||||
// Pixel Shader function for Forward Pass
|
// Pixel Shader function for Forward Pass
|
||||||
@@ -76,9 +83,8 @@ void PS_Forward(
|
|||||||
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
|
gBuffer.ShadingModel = MATERIAL_SHADING_MODEL;
|
||||||
|
|
||||||
// Calculate lighting from a single directional light
|
// Calculate lighting from a single directional light
|
||||||
float4 shadowMask = 1.0f;
|
|
||||||
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
|
ShadowSample shadow = SampleDirectionalLightShadow(DirectionalLight, ShadowsBuffer, ShadowMap, gBuffer);
|
||||||
shadowMask = GetShadowMask(shadow);
|
float4 shadowMask = GetShadowMask(shadow);
|
||||||
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
|
float4 light = GetLighting(ViewPos, DirectionalLight, gBuffer, shadowMask, false, false);
|
||||||
|
|
||||||
// Calculate lighting from sky light
|
// Calculate lighting from sky light
|
||||||
@@ -143,9 +149,9 @@ void PS_Forward(
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if USE_FOG
|
#if USE_FOG && MATERIAL_SHADING_MODEL != SHADING_MODEL_UNLIT
|
||||||
// Calculate exponential height fog
|
// Calculate exponential height fog
|
||||||
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0);
|
float4 fog = GetExponentialHeightFog(ExponentialHeightFog, materialInput.WorldPosition, ViewPos, 0, gBuffer.ViewPos.z);
|
||||||
|
|
||||||
// Apply fog to the output color
|
// Apply fog to the output color
|
||||||
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
|
#if MATERIAL_BLEND == MATERIAL_BLEND_OPAQUE
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#include "./Flax/Common.hlsl"
|
#include "./Flax/Common.hlsl"
|
||||||
#include "./Flax/MaterialCommon.hlsl"
|
#include "./Flax/MaterialCommon.hlsl"
|
||||||
#include "./Flax/GBufferCommon.hlsl"
|
#include "./Flax/GBufferCommon.hlsl"
|
||||||
|
#include "./Flax/TerrainCommon.hlsl"
|
||||||
@7
|
@7
|
||||||
// Primary constant buffer (with additional material parameters)
|
// Primary constant buffer (with additional material parameters)
|
||||||
META_CB_BEGIN(0, Data)
|
META_CB_BEGIN(0, Data)
|
||||||
@@ -334,7 +335,7 @@ VertexOutput VS(TerrainVertexInput input)
|
|||||||
float lodValue = CurrentLOD;
|
float lodValue = CurrentLOD;
|
||||||
float morphAlpha = lodCalculated - CurrentLOD;
|
float morphAlpha = lodCalculated - CurrentLOD;
|
||||||
|
|
||||||
// Sample heightmap
|
// Sample heightmap and splatmaps
|
||||||
float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
|
float2 heightmapUVs = input.TexCoord * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
|
||||||
#if USE_SMOOTH_LOD_TRANSITION
|
#if USE_SMOOTH_LOD_TRANSITION
|
||||||
float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
float4 heightmapValueThisLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
||||||
@@ -342,7 +343,6 @@ VertexOutput VS(TerrainVertexInput input)
|
|||||||
float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
|
float2 heightmapUVsNextLOD = nextLODPos * HeightmapUVScaleBias.xy + HeightmapUVScaleBias.zw;
|
||||||
float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
|
float4 heightmapValueNextLOD = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
|
||||||
float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha);
|
float4 heightmapValue = lerp(heightmapValueThisLOD, heightmapValueNextLOD, morphAlpha);
|
||||||
bool isHole = max(heightmapValueThisLOD.b + heightmapValueThisLOD.a, heightmapValueNextLOD.b + heightmapValueNextLOD.a) >= 1.9f;
|
|
||||||
#if USE_TERRAIN_LAYERS
|
#if USE_TERRAIN_LAYERS
|
||||||
float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
float4 splatmapValueThisLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
||||||
float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
|
float4 splatmapValueNextLOD = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVsNextLOD, lodValue + 1);
|
||||||
@@ -355,7 +355,6 @@ VertexOutput VS(TerrainVertexInput input)
|
|||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
float4 heightmapValue = Heightmap.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
||||||
bool isHole = (heightmapValue.b + heightmapValue.a) >= 1.9f;
|
|
||||||
#if USE_TERRAIN_LAYERS
|
#if USE_TERRAIN_LAYERS
|
||||||
float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
float4 splatmap0Value = Splatmap0.SampleLevel(SamplerPointClamp, heightmapUVs, lodValue);
|
||||||
#if TERRAIN_LAYERS_DATA_SIZE > 1
|
#if TERRAIN_LAYERS_DATA_SIZE > 1
|
||||||
@@ -363,12 +362,11 @@ VertexOutput VS(TerrainVertexInput input)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
float height = (float)((int)(heightmapValue.x * 255.0) + ((int)(heightmapValue.y * 255) << 8)) / 65535.0;
|
float height = DecodeHeightmapHeight(heightmapValue);
|
||||||
|
|
||||||
// Extract normal and the holes mask
|
// Extract normal and the holes mask
|
||||||
float2 normalTemp = float2(heightmapValue.b, heightmapValue.a) * 2.0f - 1.0f;
|
bool isHole;
|
||||||
float3 normal = float3(normalTemp.x, sqrt(1.0 - saturate(dot(normalTemp, normalTemp))), normalTemp.y);
|
float3 normal = DecodeHeightmapNormal(heightmapValue, isHole);
|
||||||
normal = normalize(normal);
|
|
||||||
output.Geometry.HolesMask = isHole ? 0 : 1;
|
output.Geometry.HolesMask = isHole ? 0 : 1;
|
||||||
if (isHole)
|
if (isHole)
|
||||||
{
|
{
|
||||||
|
|||||||
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/Shaders/Editor/Grid.flax
(Stored with Git LFS)
BIN
Content/Shaders/Editor/Grid.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
BIN
Content/Shaders/Fog.flax
(Stored with Git LFS)
Binary file not shown.
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
BIN
Content/Shaders/ProbesFilter.flax
(Stored with Git LFS)
Binary file not shown.
@@ -267,6 +267,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=comperand/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=comperand/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=coord/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=coord/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=cubemap/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=cubemap/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=DDGI/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deformer/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Deformer/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=deformers/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=deformers/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=defragmentation/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=defragmentation/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
@@ -20,13 +20,6 @@ class PlatformTools;
|
|||||||
#define GAME_BUILD_DOTNET_RUNTIME_MAX_VER 9
|
#define GAME_BUILD_DOTNET_RUNTIME_MAX_VER 9
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if OFFICIAL_BUILD
|
|
||||||
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
|
|
||||||
#define GAME_BUILD_DOTNET_VER TEXT("-dotnet=" MACRO_TO_STR(GAME_BUILD_DOTNET_RUNTIME_MIN_VER))
|
|
||||||
#else
|
|
||||||
#define GAME_BUILD_DOTNET_VER TEXT("")
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Game building options. Used as flags.
|
/// Game building options. Used as flags.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -374,6 +367,8 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
void GetBuildPlatformName(const Char*& platform, const Char*& architecture) const;
|
void GetBuildPlatformName(const Char*& platform, const Char*& architecture) const;
|
||||||
|
|
||||||
|
String GetDotnetCommandArg() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -312,6 +312,14 @@ void CookingData::GetBuildPlatformName(const Char*& platform, const Char*& archi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String CookingData::GetDotnetCommandArg() const
|
||||||
|
{
|
||||||
|
int32 version = Tools->GetDotnetVersion();
|
||||||
|
if (version == 0)
|
||||||
|
return String::Empty;
|
||||||
|
return String::Format(TEXT("-dotnet={}"), version);
|
||||||
|
}
|
||||||
|
|
||||||
void CookingData::StepProgress(const String& info, const float stepProgress) const
|
void CookingData::StepProgress(const String& info, const float stepProgress) const
|
||||||
{
|
{
|
||||||
const float singleStepProgress = 1.0f / (StepsCount + 1);
|
const float singleStepProgress = 1.0f / (StepsCount + 1);
|
||||||
|
|||||||
@@ -195,4 +195,9 @@ bool GDKPlatformTools::OnPostProcess(CookingData& data, GDKPlatformSettings* pla
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int32 GDKPlatformTools::GetDotnetVersion() const
|
||||||
|
{
|
||||||
|
return GAME_BUILD_DOTNET_RUNTIME_MIN_VER;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
// [PlatformTools]
|
// [PlatformTools]
|
||||||
|
int32 GetDotnetVersion() const override;
|
||||||
DotNetAOTModes UseAOT() const override;
|
DotNetAOTModes UseAOT() const override;
|
||||||
bool OnDeployBinaries(CookingData& data) override;
|
bool OnDeployBinaries(CookingData& data) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -70,6 +70,20 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
virtual ArchitectureType GetArchitecture() const = 0;
|
virtual ArchitectureType GetArchitecture() const = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the .Net version to use for the cooked game.
|
||||||
|
/// </summary>
|
||||||
|
virtual int32 GetDotnetVersion() const
|
||||||
|
{
|
||||||
|
#if OFFICIAL_BUILD
|
||||||
|
// Use the fixed .NET SDK version in packaged builds for compatibility (FlaxGame is precompiled with it)
|
||||||
|
return GAME_BUILD_DOTNET_RUNTIME_MIN_VER;
|
||||||
|
#else
|
||||||
|
// Use the highest version found on a system (Flax.Build will decide)
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the value indicating whenever platform requires AOT (needs C# assemblies to be precompiled).
|
/// Gets the value indicating whenever platform requires AOT (needs C# assemblies to be precompiled).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -189,7 +189,7 @@ bool CompileScriptsStep::Perform(CookingData& data)
|
|||||||
const String logFile = data.CacheDirectory / TEXT("CompileLog.txt");
|
const String logFile = data.CacheDirectory / TEXT("CompileLog.txt");
|
||||||
auto args = String::Format(
|
auto args = String::Format(
|
||||||
TEXT("-log -logfile=\"{4}\" -build -mutex -buildtargets={0} -platform={1} -arch={2} -configuration={3} -aotMode={5} {6}"),
|
TEXT("-log -logfile=\"{4}\" -build -mutex -buildtargets={0} -platform={1} -arch={2} -configuration={3} -aotMode={5} {6}"),
|
||||||
target, platform, architecture, configuration, logFile, ToString(data.Tools->UseAOT()), GAME_BUILD_DOTNET_VER);
|
target, platform, architecture, configuration, logFile, ToString(data.Tools->UseAOT()), data.GetDotnetCommandArg());
|
||||||
#if PLATFORM_WINDOWS
|
#if PLATFORM_WINDOWS
|
||||||
if (data.Platform == BuildPlatform::LinuxX64)
|
if (data.Platform == BuildPlatform::LinuxX64)
|
||||||
#elif PLATFORM_LINUX
|
#elif PLATFORM_LINUX
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
{
|
{
|
||||||
// Ask Flax.Build to provide .NET SDK location for the current platform
|
// Ask Flax.Build to provide .NET SDK location for the current platform
|
||||||
String sdks;
|
String sdks;
|
||||||
bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), GAME_BUILD_DOTNET_VER), data.CacheDirectory);
|
bool failed = ScriptsBuilder::RunBuildTool(String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printSDKs {}"), data.GetDotnetCommandArg()), data.CacheDirectory);
|
||||||
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
||||||
int32 idx = sdks.Find(TEXT("DotNetSdk, "), StringSearchCase::CaseSensitive);
|
int32 idx = sdks.Find(TEXT("DotNetSdk, "), StringSearchCase::CaseSensitive);
|
||||||
if (idx != -1)
|
if (idx != -1)
|
||||||
@@ -200,7 +200,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
String sdks;
|
String sdks;
|
||||||
const Char *platformName, *archName;
|
const Char *platformName, *archName;
|
||||||
data.GetBuildPlatformName(platformName, archName);
|
data.GetBuildPlatformName(platformName, archName);
|
||||||
String args = String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printDotNetRuntime -platform={} -arch={} {}"), platformName, archName, GAME_BUILD_DOTNET_VER);
|
String args = String::Format(TEXT("-log -logMessagesOnly -logFileWithConsole -logfile=SDKs.txt -printDotNetRuntime -platform={} -arch={} {}"), platformName, archName, data.GetDotnetCommandArg());
|
||||||
bool failed = ScriptsBuilder::RunBuildTool(args, data.CacheDirectory);
|
bool failed = ScriptsBuilder::RunBuildTool(args, data.CacheDirectory);
|
||||||
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
failed |= File::ReadAllText(data.CacheDirectory / TEXT("SDKs.txt"), sdks);
|
||||||
Array<String> parts;
|
Array<String> parts;
|
||||||
@@ -244,10 +244,13 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
}
|
}
|
||||||
if (version.IsEmpty())
|
if (version.IsEmpty())
|
||||||
{
|
{
|
||||||
|
int32 minVer = GAME_BUILD_DOTNET_RUNTIME_MIN_VER, maxVer = GAME_BUILD_DOTNET_RUNTIME_MAX_VER;
|
||||||
if (srcDotnetFromEngine)
|
if (srcDotnetFromEngine)
|
||||||
{
|
{
|
||||||
// Detect version from runtime files inside Engine Platform folder
|
// Detect version from runtime files inside Engine Platform folder
|
||||||
for (int32 i = GAME_BUILD_DOTNET_RUNTIME_MAX_VER; i >= GAME_BUILD_DOTNET_RUNTIME_MIN_VER; i--)
|
if (data.Tools->GetDotnetVersion() != 0)
|
||||||
|
minVer = maxVer = data.Tools->GetDotnetVersion();
|
||||||
|
for (int32 i = maxVer; i >= minVer; i--)
|
||||||
{
|
{
|
||||||
// Check runtime files inside Engine Platform folder
|
// Check runtime files inside Engine Platform folder
|
||||||
String testPath1 = srcDotnet / String::Format(TEXT("lib/net{}.0"), i);
|
String testPath1 = srcDotnet / String::Format(TEXT("lib/net{}.0"), i);
|
||||||
@@ -262,7 +265,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
}
|
}
|
||||||
if (version.IsEmpty())
|
if (version.IsEmpty())
|
||||||
{
|
{
|
||||||
data.Error(String::Format(TEXT("Failed to find supported .NET {} version for the current host platform."), GAME_BUILD_DOTNET_RUNTIME_MIN_VER));
|
data.Error(String::Format(TEXT("Failed to find supported .NET {} version (min {}) for the current host platform."), maxVer, minVer));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +367,7 @@ bool DeployDataStep::Perform(CookingData& data)
|
|||||||
const String logFile = data.CacheDirectory / TEXT("StripDotnetLibs.txt");
|
const String logFile = data.CacheDirectory / TEXT("StripDotnetLibs.txt");
|
||||||
String args = String::Format(
|
String args = String::Format(
|
||||||
TEXT("-log -logfile=\"{}\" -runDotNetClassLibStripping -mutex -binaries=\"{}\" {}"),
|
TEXT("-log -logfile=\"{}\" -runDotNetClassLibStripping -mutex -binaries=\"{}\" {}"),
|
||||||
logFile, data.DataOutputPath, GAME_BUILD_DOTNET_VER);
|
logFile, data.DataOutputPath, data.GetDotnetCommandArg());
|
||||||
for (const String& define : data.CustomDefines)
|
for (const String& define : data.CustomDefines)
|
||||||
{
|
{
|
||||||
args += TEXT(" -D");
|
args += TEXT(" -D");
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ bool PrecompileAssembliesStep::Perform(CookingData& data)
|
|||||||
const String logFile = data.CacheDirectory / TEXT("AOTLog.txt");
|
const String logFile = data.CacheDirectory / TEXT("AOTLog.txt");
|
||||||
String args = String::Format(
|
String args = String::Format(
|
||||||
TEXT("-log -logfile=\"{}\" -runDotNetAOT -mutex -platform={} -arch={} -configuration={} -aotMode={} -binaries=\"{}\" -intermediate=\"{}\" {}"),
|
TEXT("-log -logfile=\"{}\" -runDotNetAOT -mutex -platform={} -arch={} -configuration={} -aotMode={} -binaries=\"{}\" -intermediate=\"{}\" {}"),
|
||||||
logFile, platform, architecture, configuration, ToString(aotMode), data.DataOutputPath, data.ManagedCodeOutputPath, GAME_BUILD_DOTNET_VER);
|
logFile, platform, architecture, configuration, ToString(aotMode), data.DataOutputPath, data.ManagedCodeOutputPath, data.GetDotnetCommandArg());
|
||||||
if (!buildSettings.SkipUnusedDotnetLibsPackaging)
|
if (!buildSettings.SkipUnusedDotnetLibsPackaging)
|
||||||
args += TEXT(" -skipUnusedDotnetLibs=false"); // Run AOT on whole class library (not just used libs)
|
args += TEXT(" -skipUnusedDotnetLibs=false"); // Run AOT on whole class library (not just used libs)
|
||||||
for (const String& define : data.CustomDefines)
|
for (const String& define : data.CustomDefines)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Copyright (c) Wojciech Figat. All rights reserved.
|
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using FlaxEditor.Content.Settings;
|
using FlaxEditor.Content.Settings;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
@@ -16,6 +15,11 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
{
|
{
|
||||||
private int _layersCount;
|
private int _layersCount;
|
||||||
private List<CheckBox> _checkBoxes;
|
private List<CheckBox> _checkBoxes;
|
||||||
|
private VerticalPanel _upperRightCell;
|
||||||
|
private VerticalPanel _bottomLeftCell;
|
||||||
|
private UniformGridPanel _grid;
|
||||||
|
private Border _horizontalHighlight;
|
||||||
|
private Border _verticalHighlight;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
|
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
|
||||||
@@ -37,12 +41,29 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
Parent = panel,
|
Parent = panel,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
|
_horizontalHighlight = new Border()
|
||||||
|
{
|
||||||
|
Parent = panel,
|
||||||
|
BorderColor = style.Foreground,
|
||||||
|
BorderWidth = 1.0f,
|
||||||
|
Visible = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
_verticalHighlight = new Border()
|
||||||
|
{
|
||||||
|
Parent = panel,
|
||||||
|
BorderColor = style.Foreground,
|
||||||
|
BorderWidth = 1.0f,
|
||||||
|
Visible = false,
|
||||||
|
};
|
||||||
|
|
||||||
var upperLeftCell = new Label
|
var upperLeftCell = new Label
|
||||||
{
|
{
|
||||||
Parent = gridPanel,
|
Parent = gridPanel,
|
||||||
};
|
};
|
||||||
|
|
||||||
var upperRightCell = new VerticalPanel
|
_upperRightCell = new VerticalPanel
|
||||||
{
|
{
|
||||||
ClipChildren = false,
|
ClipChildren = false,
|
||||||
Pivot = new Float2(0.00001f, 0.0f),
|
Pivot = new Float2(0.00001f, 0.0f),
|
||||||
@@ -54,7 +75,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
Parent = gridPanel,
|
Parent = gridPanel,
|
||||||
};
|
};
|
||||||
|
|
||||||
var bottomLeftCell = new VerticalPanel
|
_bottomLeftCell = new VerticalPanel
|
||||||
{
|
{
|
||||||
Pivot = Float2.Zero,
|
Pivot = Float2.Zero,
|
||||||
Spacing = 0,
|
Spacing = 0,
|
||||||
@@ -63,7 +84,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
Parent = gridPanel,
|
Parent = gridPanel,
|
||||||
};
|
};
|
||||||
|
|
||||||
var grid = new UniformGridPanel(0)
|
_grid = new UniformGridPanel(0)
|
||||||
{
|
{
|
||||||
SlotsHorizontally = layersCount,
|
SlotsHorizontally = layersCount,
|
||||||
SlotsVertically = layersCount,
|
SlotsVertically = layersCount,
|
||||||
@@ -74,13 +95,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
int layerIndex = 0;
|
int layerIndex = 0;
|
||||||
for (; layerIndex < layerNames.Length; layerIndex++)
|
for (; layerIndex < layerNames.Length; layerIndex++)
|
||||||
{
|
{
|
||||||
upperRightCell.AddChild(new Label
|
_upperRightCell.AddChild(new Label
|
||||||
{
|
{
|
||||||
Height = labelsHeight,
|
Height = labelsHeight,
|
||||||
Text = layerNames[layerNames.Length - layerIndex - 1],
|
Text = layerNames[layerNames.Length - layerIndex - 1],
|
||||||
HorizontalAlignment = TextAlignment.Near,
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
});
|
});
|
||||||
bottomLeftCell.AddChild(new Label
|
_bottomLeftCell.AddChild(new Label
|
||||||
{
|
{
|
||||||
Height = labelsHeight,
|
Height = labelsHeight,
|
||||||
Text = layerNames[layerIndex],
|
Text = layerNames[layerIndex],
|
||||||
@@ -90,13 +111,13 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
for (; layerIndex < layersCount; layerIndex++)
|
for (; layerIndex < layersCount; layerIndex++)
|
||||||
{
|
{
|
||||||
string name = "Layer " + layerIndex;
|
string name = "Layer " + layerIndex;
|
||||||
upperRightCell.AddChild(new Label
|
_upperRightCell.AddChild(new Label
|
||||||
{
|
{
|
||||||
Height = labelsHeight,
|
Height = labelsHeight,
|
||||||
Text = name,
|
Text = name,
|
||||||
HorizontalAlignment = TextAlignment.Near,
|
HorizontalAlignment = TextAlignment.Near,
|
||||||
});
|
});
|
||||||
bottomLeftCell.AddChild(new Label
|
_bottomLeftCell.AddChild(new Label
|
||||||
{
|
{
|
||||||
Height = labelsHeight,
|
Height = labelsHeight,
|
||||||
Text = name,
|
Text = name,
|
||||||
@@ -118,7 +139,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
var box = new CheckBox(0, 0, true)
|
var box = new CheckBox(0, 0, true)
|
||||||
{
|
{
|
||||||
Tag = new Float2(_layersCount - column - 1, row),
|
Tag = new Float2(_layersCount - column - 1, row),
|
||||||
Parent = grid,
|
Parent = _grid,
|
||||||
Checked = GetBit(column, row),
|
Checked = GetBit(column, row),
|
||||||
};
|
};
|
||||||
box.StateChanged += OnCheckBoxChanged;
|
box.StateChanged += OnCheckBoxChanged;
|
||||||
@@ -126,7 +147,7 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
}
|
}
|
||||||
for (; column < layersCount; column++)
|
for (; column < layersCount; column++)
|
||||||
{
|
{
|
||||||
grid.AddChild(new Label());
|
_grid.AddChild(new Label());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,6 +162,18 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Refresh()
|
public override void Refresh()
|
||||||
{
|
{
|
||||||
|
int selectedColumn = -1;
|
||||||
|
int selectedRow = -1;
|
||||||
|
var style = FlaxEngine.GUI.Style.Current;
|
||||||
|
bool mouseOverGrid = _grid.IsMouseOver;
|
||||||
|
|
||||||
|
// Only hide highlights if mouse is not over the grid to reduce flickering
|
||||||
|
if (!mouseOverGrid)
|
||||||
|
{
|
||||||
|
_horizontalHighlight.Visible = false;
|
||||||
|
_verticalHighlight.Visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Sync check boxes
|
// Sync check boxes
|
||||||
for (int i = 0; i < _checkBoxes.Count; i++)
|
for (int i = 0; i < _checkBoxes.Count; i++)
|
||||||
{
|
{
|
||||||
@@ -148,6 +181,39 @@ namespace FlaxEditor.CustomEditors.Dedicated
|
|||||||
int column = (int)((Float2)box.Tag).X;
|
int column = (int)((Float2)box.Tag).X;
|
||||||
int row = (int)((Float2)box.Tag).Y;
|
int row = (int)((Float2)box.Tag).Y;
|
||||||
box.Checked = GetBit(column, row);
|
box.Checked = GetBit(column, row);
|
||||||
|
|
||||||
|
if (box.IsMouseOver)
|
||||||
|
{
|
||||||
|
selectedColumn = column;
|
||||||
|
selectedRow = row;
|
||||||
|
|
||||||
|
_horizontalHighlight.X = _grid.X - _bottomLeftCell.Width;
|
||||||
|
_horizontalHighlight.Y = _grid.Y + box.Y;
|
||||||
|
_horizontalHighlight.Width = _bottomLeftCell.Width + box.Width + box.X;
|
||||||
|
_horizontalHighlight.Height = box.Height;
|
||||||
|
_horizontalHighlight.Visible = true;
|
||||||
|
|
||||||
|
_verticalHighlight.X = _grid.X + box.X;
|
||||||
|
_verticalHighlight.Y = _grid.Y - _upperRightCell.Height;
|
||||||
|
_verticalHighlight.Width = box.Width;
|
||||||
|
_verticalHighlight.Height = _upperRightCell.Height + box.Height + box.Y;
|
||||||
|
_verticalHighlight.Visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _checkBoxes.Count; i++)
|
||||||
|
{
|
||||||
|
var box = _checkBoxes[i];
|
||||||
|
int column = (int)((Float2)box.Tag).X;
|
||||||
|
int row = (int)((Float2)box.Tag).Y;
|
||||||
|
|
||||||
|
if (!mouseOverGrid)
|
||||||
|
box.ImageColor = style.BorderSelected;
|
||||||
|
else if (selectedColumn > -1 && selectedRow > -1)
|
||||||
|
{
|
||||||
|
bool isRowOrColumn = column == selectedColumn || row == selectedRow;
|
||||||
|
box.ImageColor = style.BorderSelected * (isRowOrColumn ? 1.2f : 0.75f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
new OptionType("Texture 9-Slicing", typeof(Texture9SlicingBrush)),
|
new OptionType("Texture 9-Slicing", typeof(Texture9SlicingBrush)),
|
||||||
new OptionType("Sprite 9-Slicing", typeof(Sprite9SlicingBrush)),
|
new OptionType("Sprite 9-Slicing", typeof(Sprite9SlicingBrush)),
|
||||||
new OptionType("Video", typeof(VideoBrush)),
|
new OptionType("Video", typeof(VideoBrush)),
|
||||||
|
new OptionType("UI Brush", typeof(UIBrush)),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -604,6 +604,9 @@ namespace FlaxEditor.CustomEditors.Editors
|
|||||||
root.SortChildrenRecursive();
|
root.SortChildrenRecursive();
|
||||||
root.Expand(true);
|
root.Expand(true);
|
||||||
|
|
||||||
|
if (Input.GetKey(KeyboardKeys.Shift))
|
||||||
|
root.ExpandAll(true);
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -287,10 +287,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// <returns>The created element.</returns>
|
/// <returns>The created element.</returns>
|
||||||
public ImageElement Image(SpriteHandle sprite)
|
public ImageElement Image(SpriteHandle sprite)
|
||||||
{
|
{
|
||||||
var element = new ImageElement();
|
return Image(new SpriteBrush(sprite));
|
||||||
element.Image.Brush = new SpriteBrush(sprite);
|
|
||||||
OnAddElement(element);
|
|
||||||
return element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -300,10 +297,7 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// <returns>The created element.</returns>
|
/// <returns>The created element.</returns>
|
||||||
public ImageElement Image(Texture texture)
|
public ImageElement Image(Texture texture)
|
||||||
{
|
{
|
||||||
var element = new ImageElement();
|
return Image(new TextureBrush(texture));
|
||||||
element.Image.Brush = new TextureBrush(texture);
|
|
||||||
OnAddElement(element);
|
|
||||||
return element;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -312,9 +306,19 @@ namespace FlaxEditor.CustomEditors
|
|||||||
/// <param name="texture">The GPU texture.</param>
|
/// <param name="texture">The GPU texture.</param>
|
||||||
/// <returns>The created element.</returns>
|
/// <returns>The created element.</returns>
|
||||||
public ImageElement Image(GPUTexture texture)
|
public ImageElement Image(GPUTexture texture)
|
||||||
|
{
|
||||||
|
return Image(new GPUTextureBrush(texture));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds brush image to the layout.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="brush">The brush.</param>
|
||||||
|
/// <returns>The created element.</returns>
|
||||||
|
public ImageElement Image(IBrush brush)
|
||||||
{
|
{
|
||||||
var element = new ImageElement();
|
var element = new ImageElement();
|
||||||
element.Image.Brush = new GPUTextureBrush(texture);
|
element.Image.Brush = brush;
|
||||||
OnAddElement(element);
|
OnAddElement(element);
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -670,6 +670,8 @@ namespace FlaxEditor
|
|||||||
{
|
{
|
||||||
FlaxEngine.Networking.NetworkManager.Stop(); // Shutdown any multiplayer from playmode
|
FlaxEngine.Networking.NetworkManager.Stop(); // Shutdown any multiplayer from playmode
|
||||||
PlayModeEnding?.Invoke();
|
PlayModeEnding?.Invoke();
|
||||||
|
for (int i = 0; i < _modules.Count; i++)
|
||||||
|
_modules[i].OnPlayEnding();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void OnPlayEnd()
|
internal void OnPlayEnd()
|
||||||
|
|||||||
@@ -299,6 +299,7 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
// Select asset
|
// Select asset
|
||||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||||
|
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (Button1Rect.Contains(location))
|
else if (Button1Rect.Contains(location))
|
||||||
@@ -312,6 +313,7 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
// Select asset
|
// Select asset
|
||||||
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
Editor.Instance.Windows.ContentWin.Select(Validator.SelectedItem);
|
||||||
|
Editor.Instance.Windows.ContentWin.ClearItemsSearch();
|
||||||
}
|
}
|
||||||
else if (Button3Rect.Contains(location))
|
else if (Button3Rect.Contains(location))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -114,9 +114,10 @@ namespace FlaxEditor.GUI.ContextMenu
|
|||||||
public ContextMenuBase()
|
public ContextMenuBase()
|
||||||
: base(0, 0, 120, 32)
|
: base(0, 0, 120, 32)
|
||||||
{
|
{
|
||||||
_direction = ContextMenuDirection.RightDown;
|
|
||||||
Visible = false;
|
Visible = false;
|
||||||
|
AutoFocus = true;
|
||||||
|
|
||||||
|
_direction = ContextMenuDirection.RightDown;
|
||||||
_isSubMenu = true;
|
_isSubMenu = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,8 @@ namespace FlaxEditor.GUI.Dialogs
|
|||||||
public ColorSelector(float wheelSize)
|
public ColorSelector(float wheelSize)
|
||||||
: base(0, 0, wheelSize, wheelSize)
|
: base(0, 0, wheelSize, wheelSize)
|
||||||
{
|
{
|
||||||
|
AutoFocus = true;
|
||||||
|
|
||||||
_colorWheelSprite = Editor.Instance.Icons.ColorWheel128;
|
_colorWheelSprite = Editor.Instance.Icons.ColorWheel128;
|
||||||
_wheelRect = new Rectangle(0, 0, wheelSize, wheelSize);
|
_wheelRect = new Rectangle(0, 0, wheelSize, wheelSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,6 @@ namespace FlaxEditor.GUI.Docking
|
|||||||
internal DockPanelProxy(DockPanel panel)
|
internal DockPanelProxy(DockPanel panel)
|
||||||
: base(0, 0, 64, 64)
|
: base(0, 0, 64, 64)
|
||||||
{
|
{
|
||||||
AutoFocus = false;
|
|
||||||
|
|
||||||
_panel = panel;
|
_panel = panel;
|
||||||
AnchorPreset = AnchorPresets.StretchAll;
|
AnchorPreset = AnchorPresets.StretchAll;
|
||||||
Offsets = Margin.Zero;
|
Offsets = Margin.Zero;
|
||||||
|
|||||||
@@ -368,6 +368,8 @@ namespace FlaxEditor.GUI.Input
|
|||||||
public SliderControl(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue)
|
public SliderControl(float value, float x = 0, float y = 0, float width = 120, float min = float.MinValue, float max = float.MaxValue)
|
||||||
: base(x, y, width, TextBox.DefaultHeight)
|
: base(x, y, width, TextBox.DefaultHeight)
|
||||||
{
|
{
|
||||||
|
AutoFocus = true;
|
||||||
|
|
||||||
_min = min;
|
_min = min;
|
||||||
_max = max;
|
_max = max;
|
||||||
_value = Mathf.Clamp(value, min, max);
|
_value = Mathf.Clamp(value, min, max);
|
||||||
|
|||||||
@@ -227,9 +227,8 @@ namespace FlaxEditor.GUI
|
|||||||
{
|
{
|
||||||
int order = -1 * SortScore.CompareTo(otherItem.SortScore);
|
int order = -1 * SortScore.CompareTo(otherItem.SortScore);
|
||||||
if (order == 0)
|
if (order == 0)
|
||||||
{
|
|
||||||
order = string.Compare(Name, otherItem.Name, StringComparison.Ordinal);
|
order = string.Compare(Name, otherItem.Name, StringComparison.Ordinal);
|
||||||
}
|
|
||||||
return order;
|
return order;
|
||||||
}
|
}
|
||||||
return base.Compare(other);
|
return base.Compare(other);
|
||||||
@@ -509,7 +508,7 @@ namespace FlaxEditor.GUI
|
|||||||
OnSearchFilterChanged();
|
OnSearchFilterChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Item> GetVisibleItems()
|
private List<Item> GetVisibleItems(bool ignoreFoldedCategories)
|
||||||
{
|
{
|
||||||
var result = new List<Item>();
|
var result = new List<Item>();
|
||||||
var items = ItemsPanel.Children;
|
var items = ItemsPanel.Children;
|
||||||
@@ -523,7 +522,7 @@ namespace FlaxEditor.GUI
|
|||||||
for (int i = 0; i < _categoryPanels.Count; i++)
|
for (int i = 0; i < _categoryPanels.Count; i++)
|
||||||
{
|
{
|
||||||
var category = _categoryPanels[i];
|
var category = _categoryPanels[i];
|
||||||
if (!category.Visible)
|
if (!category.Visible || (ignoreFoldedCategories && category is DropPanel panel && panel.IsClosed))
|
||||||
continue;
|
continue;
|
||||||
for (int j = 0; j < category.Children.Count; j++)
|
for (int j = 0; j < category.Children.Count; j++)
|
||||||
{
|
{
|
||||||
@@ -535,6 +534,12 @@ namespace FlaxEditor.GUI
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ExpandToItem(Item item)
|
||||||
|
{
|
||||||
|
if (item.Parent is DropPanel dropPanel)
|
||||||
|
dropPanel.Open(false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
protected override void OnShow()
|
protected override void OnShow()
|
||||||
{
|
{
|
||||||
@@ -563,8 +568,17 @@ namespace FlaxEditor.GUI
|
|||||||
case KeyboardKeys.Escape:
|
case KeyboardKeys.Escape:
|
||||||
Hide();
|
Hide();
|
||||||
return true;
|
return true;
|
||||||
|
case KeyboardKeys.Backspace:
|
||||||
|
// Allow the user to quickly focus the searchbar
|
||||||
|
if (_searchBox != null && !_searchBox.IsFocused)
|
||||||
|
{
|
||||||
|
_searchBox.Focus();
|
||||||
|
_searchBox.SelectAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case KeyboardKeys.ArrowDown:
|
case KeyboardKeys.ArrowDown:
|
||||||
{
|
case KeyboardKeys.ArrowUp:
|
||||||
if (RootWindow.FocusedControl == null)
|
if (RootWindow.FocusedControl == null)
|
||||||
{
|
{
|
||||||
// Focus search box if nothing is focused
|
// Focus search box if nothing is focused
|
||||||
@@ -572,39 +586,28 @@ namespace FlaxEditor.GUI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus the first visible item or then next one
|
// Get the next item
|
||||||
var items = GetVisibleItems();
|
bool controlDown = Root.GetKey(KeyboardKeys.Control);
|
||||||
|
var items = GetVisibleItems(!controlDown);
|
||||||
var focusedIndex = items.IndexOf(focusedItem);
|
var focusedIndex = items.IndexOf(focusedItem);
|
||||||
if (focusedIndex == -1)
|
|
||||||
focusedIndex = -1;
|
// If the user hasn't selected anything yet and is holding control, focus first folded item
|
||||||
if (focusedIndex + 1 < items.Count)
|
if (focusedIndex == -1 && controlDown)
|
||||||
{
|
focusedIndex = GetVisibleItems(true).Count - 1;
|
||||||
var item = items[focusedIndex + 1];
|
|
||||||
item.Focus();
|
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||||
_scrollPanel.ScrollViewTo(item);
|
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, items.Count - 1);
|
||||||
return true;
|
var nextItem = items[nextIndex];
|
||||||
}
|
|
||||||
break;
|
// Focus the next item
|
||||||
}
|
nextItem.Focus();
|
||||||
case KeyboardKeys.ArrowUp:
|
|
||||||
if (focusedItem != null)
|
// Allow the user to expand groups while scrolling
|
||||||
{
|
if (controlDown)
|
||||||
// Focus the previous visible item or the search box
|
ExpandToItem(nextItem);
|
||||||
var items = GetVisibleItems();
|
|
||||||
var focusedIndex = items.IndexOf(focusedItem);
|
_scrollPanel.ScrollViewTo(nextItem);
|
||||||
if (focusedIndex == 0)
|
return true;
|
||||||
{
|
|
||||||
_searchBox?.Focus();
|
|
||||||
}
|
|
||||||
else if (focusedIndex > 0)
|
|
||||||
{
|
|
||||||
var item = items[focusedIndex - 1];
|
|
||||||
item.Focus();
|
|
||||||
_scrollPanel.ScrollViewTo(item);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case KeyboardKeys.Return:
|
case KeyboardKeys.Return:
|
||||||
if (focusedItem != null)
|
if (focusedItem != null)
|
||||||
{
|
{
|
||||||
@@ -614,7 +617,7 @@ namespace FlaxEditor.GUI
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Select first item if no item is focused (most likely to be the best result), saves the user from pressing arrow down first
|
// Select first item if no item is focused (most likely to be the best result), saves the user from pressing arrow down first
|
||||||
var visibleItems = GetVisibleItems();
|
var visibleItems = GetVisibleItems(true);
|
||||||
if (visibleItems.Count > 0)
|
if (visibleItems.Count > 0)
|
||||||
{
|
{
|
||||||
OnClickItem(visibleItems[0]);
|
OnClickItem(visibleItems[0]);
|
||||||
|
|||||||
@@ -266,6 +266,19 @@ namespace FlaxEditor.GUI
|
|||||||
return AddChild(new MainMenuButton(text));
|
return AddChild(new MainMenuButton(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or adds a button.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The button text</param>
|
||||||
|
/// <returns>The existing or created button control.</returns>
|
||||||
|
public MainMenuButton GetOrAddButton(string text)
|
||||||
|
{
|
||||||
|
MainMenuButton result = GetButton(text);
|
||||||
|
if (result == null)
|
||||||
|
result = AddButton(text);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the button.
|
/// Gets the button.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -122,6 +122,14 @@ namespace FlaxEditor.GUI
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnClicked()
|
||||||
|
{
|
||||||
|
if (AutoCheck)
|
||||||
|
Checked = !Checked;
|
||||||
|
Clicked?.Invoke();
|
||||||
|
(Parent as ToolStrip)?.OnButtonClicked(this);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
@@ -196,11 +204,7 @@ namespace FlaxEditor.GUI
|
|||||||
if (button == MouseButton.Left && _primaryMouseDown)
|
if (button == MouseButton.Left && _primaryMouseDown)
|
||||||
{
|
{
|
||||||
_primaryMouseDown = false;
|
_primaryMouseDown = false;
|
||||||
if (AutoCheck)
|
OnClicked();
|
||||||
Checked = !Checked;
|
|
||||||
Clicked?.Invoke();
|
|
||||||
(Parent as ToolStrip)?.OnButtonClicked(this);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (button == MouseButton.Right && _secondaryMouseDown)
|
if (button == MouseButton.Right && _secondaryMouseDown)
|
||||||
@@ -215,6 +219,18 @@ namespace FlaxEditor.GUI
|
|||||||
return base.OnMouseUp(location, button);
|
return base.OnMouseUp(location, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDoubleClick(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (button == MouseButton.Left)
|
||||||
|
{
|
||||||
|
OnClicked();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnMouseLeave()
|
public override void OnMouseLeave()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool DrawRootTreeLine = true;
|
public bool DrawRootTreeLine = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when the deferred layout operation was performed.
|
||||||
|
/// </summary>
|
||||||
|
public event Action AfterDeferredLayout;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the margin for the child tree nodes.
|
/// Gets or sets the margin for the child tree nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -375,6 +380,7 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
if (_deferLayoutUpdate)
|
if (_deferLayoutUpdate)
|
||||||
{
|
{
|
||||||
base.PerformLayout();
|
base.PerformLayout();
|
||||||
|
AfterDeferredLayout?.Invoke();
|
||||||
_deferLayoutUpdate = false;
|
_deferLayoutUpdate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -319,6 +319,8 @@ namespace FlaxEditor.GUI.Tree
|
|||||||
public TreeNode(bool canChangeOrder, SpriteHandle iconCollapsed, SpriteHandle iconOpened)
|
public TreeNode(bool canChangeOrder, SpriteHandle iconCollapsed, SpriteHandle iconOpened)
|
||||||
: base(0, 0, 64, 16)
|
: base(0, 0, 64, 16)
|
||||||
{
|
{
|
||||||
|
AutoFocus = true;
|
||||||
|
|
||||||
_canChangeOrder = canChangeOrder;
|
_canChangeOrder = canChangeOrder;
|
||||||
_animationProgress = 1.0f;
|
_animationProgress = 1.0f;
|
||||||
_cachedHeight = _headerHeight;
|
_cachedHeight = _headerHeight;
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ namespace FlaxEditor.Gizmo
|
|||||||
// Ensure player is not moving objects
|
// Ensure player is not moving objects
|
||||||
if (ActiveAxis != Axis.None)
|
if (ActiveAxis != Axis.None)
|
||||||
return;
|
return;
|
||||||
|
Profiler.BeginEvent("Pick");
|
||||||
|
|
||||||
// Get mouse ray and try to hit any object
|
// Get mouse ray and try to hit any object
|
||||||
var ray = Owner.MouseRay;
|
var ray = Owner.MouseRay;
|
||||||
@@ -243,6 +244,8 @@ namespace FlaxEditor.Gizmo
|
|||||||
{
|
{
|
||||||
sceneEditing.Deselect();
|
sceneEditing.Deselect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Profiler.EndEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
|
#include "Engine/Scripting/Internal/MainThreadManagedInvokeAction.h"
|
||||||
#include "Engine/Content/Assets/VisualScript.h"
|
#include "Engine/Content/Assets/VisualScript.h"
|
||||||
#include "Engine/Content/Content.h"
|
#include "Engine/Content/Content.h"
|
||||||
|
#include "Engine/Level/Actor.h"
|
||||||
#include "Engine/CSG/CSGBuilder.h"
|
#include "Engine/CSG/CSGBuilder.h"
|
||||||
#include "Engine/Engine/CommandLine.h"
|
#include "Engine/Engine/CommandLine.h"
|
||||||
#include "Engine/Renderer/ProbesRenderer.h"
|
#include "Engine/Renderer/ProbesRenderer.h"
|
||||||
@@ -74,7 +75,7 @@ void OnLightmapsBuildFinished(bool failed)
|
|||||||
OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false);
|
OnLightmapsBake(ShadowsOfMordor::BuildProgressStep::GenerateLightmapCharts, 0, 0, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
|
void OnBakeEvent(bool started, Actor* e)
|
||||||
{
|
{
|
||||||
if (Internal_EnvProbeBake == nullptr)
|
if (Internal_EnvProbeBake == nullptr)
|
||||||
{
|
{
|
||||||
@@ -82,7 +83,7 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
|
|||||||
ASSERT(Internal_EnvProbeBake);
|
ASSERT(Internal_EnvProbeBake);
|
||||||
}
|
}
|
||||||
|
|
||||||
MObject* probeObj = e.Actor ? e.Actor->GetManagedInstance() : nullptr;
|
MObject* probeObj = e ? e->GetManagedInstance() : nullptr;
|
||||||
|
|
||||||
MainThreadManagedInvokeAction::ParamsBuilder params;
|
MainThreadManagedInvokeAction::ParamsBuilder params;
|
||||||
params.AddParam(started);
|
params.AddParam(started);
|
||||||
@@ -90,12 +91,12 @@ void OnBakeEvent(bool started, const ProbesRenderer::Entry& e)
|
|||||||
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
|
MainThreadManagedInvokeAction::Invoke(Internal_EnvProbeBake, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnRegisterBake(const ProbesRenderer::Entry& e)
|
void OnRegisterBake(Actor* e)
|
||||||
{
|
{
|
||||||
OnBakeEvent(true, e);
|
OnBakeEvent(true, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnFinishBake(const ProbesRenderer::Entry& e)
|
void OnFinishBake(Actor* e)
|
||||||
{
|
{
|
||||||
OnBakeEvent(false, e);
|
OnBakeEvent(false, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,13 @@ namespace FlaxEditor.Modules
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when Editor will leave the play mode.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void OnPlayEnding()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when Editor leaves the play mode.
|
/// Called when Editor leaves the play mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -711,7 +711,11 @@ namespace FlaxEditor.Modules
|
|||||||
|
|
||||||
private void OnActorChildNodesDispose(ActorNode node)
|
private void OnActorChildNodesDispose(ActorNode node)
|
||||||
{
|
{
|
||||||
|
if (Selection.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
// TODO: cache if selection contains any actor child node and skip this loop if no need to iterate
|
// TODO: cache if selection contains any actor child node and skip this loop if no need to iterate
|
||||||
|
// TODO: or build a hash set with selected nodes for quick O(1) checks (cached until selection changes)
|
||||||
|
|
||||||
// Deselect child nodes
|
// Deselect child nodes
|
||||||
for (int i = 0; i < node.ChildNodes.Count; i++)
|
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using FlaxEditor.SceneGraph;
|
using FlaxEditor.SceneGraph;
|
||||||
using FlaxEditor.SceneGraph.Actors;
|
using FlaxEditor.SceneGraph.Actors;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
@@ -658,6 +659,48 @@ namespace FlaxEditor.Modules
|
|||||||
//node?.TreeNode.OnActiveChanged();
|
//node?.TreeNode.OnActiveChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnActorDestroyChildren(Actor actor)
|
||||||
|
{
|
||||||
|
// Instead of doing OnActorParentChanged for every child lets remove all of them at once from that actor
|
||||||
|
ActorNode node = GetActorNode(actor);
|
||||||
|
if (node != null)
|
||||||
|
{
|
||||||
|
if (Editor.SceneEditing.HasSthSelected)
|
||||||
|
{
|
||||||
|
// Clear selection if one of the removed actors is selected
|
||||||
|
var selection = new HashSet<Actor>();
|
||||||
|
foreach (var e in Editor.SceneEditing.Selection)
|
||||||
|
{
|
||||||
|
if (e is ActorNode q && q.Actor)
|
||||||
|
selection.Add(q.Actor);
|
||||||
|
}
|
||||||
|
var count = actor.ChildrenCount;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
var child = actor.GetChild(i);
|
||||||
|
if (selection.Contains(child))
|
||||||
|
{
|
||||||
|
Editor.SceneEditing.Deselect();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all child nodes (upfront remove all nodes to run faster)
|
||||||
|
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||||
|
{
|
||||||
|
if (node.ChildNodes[i] is ActorNode child)
|
||||||
|
child.parentNode = null;
|
||||||
|
}
|
||||||
|
node.TreeNode.DisposeChildren();
|
||||||
|
for (int i = 0; i < node.ChildNodes.Count; i++)
|
||||||
|
{
|
||||||
|
node.ChildNodes[i].Dispose();
|
||||||
|
}
|
||||||
|
node.ChildNodes.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the actor node.
|
/// Gets the actor node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -709,6 +752,7 @@ namespace FlaxEditor.Modules
|
|||||||
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
|
Level.ActorOrderInParentChanged += OnActorOrderInParentChanged;
|
||||||
Level.ActorNameChanged += OnActorNameChanged;
|
Level.ActorNameChanged += OnActorNameChanged;
|
||||||
Level.ActorActiveChanged += OnActorActiveChanged;
|
Level.ActorActiveChanged += OnActorActiveChanged;
|
||||||
|
Level.ActorDestroyChildren += OnActorDestroyChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -726,6 +770,7 @@ namespace FlaxEditor.Modules
|
|||||||
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
|
Level.ActorOrderInParentChanged -= OnActorOrderInParentChanged;
|
||||||
Level.ActorNameChanged -= OnActorNameChanged;
|
Level.ActorNameChanged -= OnActorNameChanged;
|
||||||
Level.ActorActiveChanged -= OnActorActiveChanged;
|
Level.ActorActiveChanged -= OnActorActiveChanged;
|
||||||
|
Level.ActorDestroyChildren -= OnActorDestroyChildren;
|
||||||
|
|
||||||
// Cleanup graph
|
// Cleanup graph
|
||||||
Root.Dispose();
|
Root.Dispose();
|
||||||
|
|||||||
@@ -1223,6 +1223,13 @@ namespace FlaxEditor.Modules
|
|||||||
Windows[i].OnPlayBegin();
|
Windows[i].OnPlayBegin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayEnding()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Windows.Count; i++)
|
||||||
|
Windows[i].OnPlayEnding();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnPlayEnd()
|
public override void OnPlayEnd()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -139,6 +139,10 @@ namespace FlaxEditor.Options
|
|||||||
[EditorDisplay("Common"), EditorOrder(240)]
|
[EditorDisplay("Common"), EditorOrder(240)]
|
||||||
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
|
public InputBinding ToggleFullscreen = new InputBinding(KeyboardKeys.F11);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Ctrl+BackQuote")]
|
||||||
|
[EditorDisplay("Common"), EditorOrder(250)]
|
||||||
|
public InputBinding FocusConsoleCommand = new InputBinding(KeyboardKeys.BackQuote, KeyboardKeys.Control);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region File
|
#region File
|
||||||
@@ -647,5 +651,45 @@ namespace FlaxEditor.Options
|
|||||||
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
public InputBinding VisualScriptDebuggerWindow = new InputBinding(KeyboardKeys.None);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Node editors
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Shift+W")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4500)]
|
||||||
|
public InputBinding NodesAlignTop = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Shift+A")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4510)]
|
||||||
|
public InputBinding NodesAlignLeft = new InputBinding(KeyboardKeys.A, KeyboardKeys.Shift);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Shift+S")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4520)]
|
||||||
|
public InputBinding NodesAlignBottom = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Shift+D")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4530)]
|
||||||
|
public InputBinding NodesAlignRight = new InputBinding(KeyboardKeys.D, KeyboardKeys.Shift);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Alt+Shift+W")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4540)]
|
||||||
|
public InputBinding NodesAlignMiddle = new InputBinding(KeyboardKeys.W, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Alt+Shift+S")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4550)]
|
||||||
|
public InputBinding NodesAlignCenter = new InputBinding(KeyboardKeys.S, KeyboardKeys.Shift, KeyboardKeys.Alt);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "Q")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4560)]
|
||||||
|
public InputBinding NodesAutoFormat = new InputBinding(KeyboardKeys.Q);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "None")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4570)]
|
||||||
|
public InputBinding NodesDistributeHorizontal = new InputBinding(KeyboardKeys.None);
|
||||||
|
|
||||||
|
[DefaultValue(typeof(InputBinding), "None")]
|
||||||
|
[EditorDisplay("Node editors"), EditorOrder(4580)]
|
||||||
|
public InputBinding NodesDistributeVertical = new InputBinding(KeyboardKeys.None);
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,9 +76,13 @@ namespace FlaxEditor.SceneGraph.Actors
|
|||||||
// Skip removing this terrain file sif it's still referenced
|
// Skip removing this terrain file sif it's still referenced
|
||||||
var sceneReferences = Editor.GetAssetReferences(e.SceneId);
|
var sceneReferences = Editor.GetAssetReferences(e.SceneId);
|
||||||
if (sceneReferences != null && sceneReferences.Contains(e.TerrainId))
|
if (sceneReferences != null && sceneReferences.Contains(e.TerrainId))
|
||||||
|
{
|
||||||
|
Debug.Log($"Skip removing files used by terrain {e.TerrainId} on scene {e.SceneId} as it's still in use");
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Delete files
|
// Delete files
|
||||||
|
Debug.Log($"Removing files used by removed terrain {e.TerrainId} on scene {e.SceneId}");
|
||||||
foreach (var file in e.Files)
|
foreach (var file in e.Files)
|
||||||
{
|
{
|
||||||
if (file != null && File.Exists(file))
|
if (file != null && File.Exists(file))
|
||||||
|
|||||||
@@ -97,13 +97,16 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
||||||
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
|
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
|
||||||
{
|
{
|
||||||
|
Profiler.BeginEvent("RayCastScene");
|
||||||
var data = new RayCastData
|
var data = new RayCastData
|
||||||
{
|
{
|
||||||
Ray = ray,
|
Ray = ray,
|
||||||
View = view,
|
View = view,
|
||||||
Flags = flags
|
Flags = flags
|
||||||
};
|
};
|
||||||
return RayCast(ref data, out distance, out _);
|
var result = RayCast(ref data, out distance, out _);
|
||||||
|
Profiler.EndEvent();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -117,13 +120,16 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
/// <returns>Hit object or null if there is no intersection at all.</returns>
|
||||||
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, out Vector3 normal, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
|
public SceneGraphNode RayCast(ref Ray ray, ref Ray view, out Real distance, out Vector3 normal, RayCastData.FlagTypes flags = RayCastData.FlagTypes.None)
|
||||||
{
|
{
|
||||||
|
Profiler.BeginEvent("RayCastScene");
|
||||||
var data = new RayCastData
|
var data = new RayCastData
|
||||||
{
|
{
|
||||||
Ray = ray,
|
Ray = ray,
|
||||||
View = view,
|
View = view,
|
||||||
Flags = flags
|
Flags = flags
|
||||||
};
|
};
|
||||||
return RayCast(ref data, out distance, out normal);
|
var result = RayCast(ref data, out distance, out normal);
|
||||||
|
Profiler.EndEvent();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Quaternion RaycastNormalRotation(ref Vector3 normal)
|
internal static Quaternion RaycastNormalRotation(ref Vector3 normal)
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace FlaxEditor.SceneGraph
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The parent node.
|
/// The parent node.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected SceneGraphNode parentNode;
|
internal SceneGraphNode parentNode;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the children list.
|
/// Gets the children list.
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ namespace FlaxEditor.States
|
|||||||
private readonly List<Guid> _scenesToLoad = new List<Guid>();
|
private readonly List<Guid> _scenesToLoad = new List<Guid>();
|
||||||
private readonly List<Scene> _scenesToUnload = new List<Scene>();
|
private readonly List<Scene> _scenesToUnload = new List<Scene>();
|
||||||
private Guid _lastSceneFromRequest;
|
private Guid _lastSceneFromRequest;
|
||||||
|
private bool _sameSceneReload = false;
|
||||||
|
|
||||||
internal ChangingScenesState(Editor editor)
|
internal ChangingScenesState(Editor editor)
|
||||||
: base(editor)
|
: base(editor)
|
||||||
@@ -164,10 +165,22 @@ namespace FlaxEditor.States
|
|||||||
{
|
{
|
||||||
Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state.");
|
Assert.AreEqual(Guid.Empty, _lastSceneFromRequest, "Invalid state.");
|
||||||
|
|
||||||
// Bind events
|
// Bind events, only bind loading event and error if re-loading the same scene to avoid issues.
|
||||||
Level.SceneLoaded += OnSceneEvent;
|
if (_scenesToUnload.Count == 1 && _scenesToLoad.Count == 1)
|
||||||
Level.SceneLoadError += OnSceneEvent;
|
{
|
||||||
Level.SceneUnloaded += OnSceneEvent;
|
if (_scenesToLoad[0] == _scenesToUnload[0].ID)
|
||||||
|
{
|
||||||
|
Level.SceneLoaded += OnSceneEvent;
|
||||||
|
Level.SceneLoadError += OnSceneEvent;
|
||||||
|
_sameSceneReload = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_sameSceneReload)
|
||||||
|
{
|
||||||
|
Level.SceneLoaded += OnSceneEvent;
|
||||||
|
Level.SceneLoadError += OnSceneEvent;
|
||||||
|
Level.SceneUnloaded += OnSceneEvent;
|
||||||
|
}
|
||||||
|
|
||||||
// Push scenes changing requests
|
// Push scenes changing requests
|
||||||
for (int i = 0; i < _scenesToUnload.Count; i++)
|
for (int i = 0; i < _scenesToUnload.Count; i++)
|
||||||
@@ -210,9 +223,18 @@ namespace FlaxEditor.States
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unbind events
|
// Unbind events
|
||||||
Level.SceneLoaded -= OnSceneEvent;
|
if (_sameSceneReload)
|
||||||
Level.SceneLoadError -= OnSceneEvent;
|
{
|
||||||
Level.SceneUnloaded -= OnSceneEvent;
|
Level.SceneLoaded -= OnSceneEvent;
|
||||||
|
Level.SceneLoadError -= OnSceneEvent;
|
||||||
|
_sameSceneReload = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Level.SceneLoaded -= OnSceneEvent;
|
||||||
|
Level.SceneLoadError -= OnSceneEvent;
|
||||||
|
Level.SceneUnloaded -= OnSceneEvent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSceneEvent(Scene scene, Guid sceneId)
|
private void OnSceneEvent(Scene scene, Guid sceneId)
|
||||||
|
|||||||
@@ -233,6 +233,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height)
|
public BlendPointsEditor(Animation.MultiBlend node, bool is2D, float x, float y, float width, float height)
|
||||||
: base(x, y, width, height)
|
: base(x, y, width, height)
|
||||||
{
|
{
|
||||||
|
AutoFocus = true;
|
||||||
|
|
||||||
_node = node;
|
_node = node;
|
||||||
_is2D = is2D;
|
_is2D = is2D;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using FlaxEditor.Scripting;
|
|||||||
using FlaxEditor.Surface.Elements;
|
using FlaxEditor.Surface.Elements;
|
||||||
using FlaxEditor.Windows.Assets;
|
using FlaxEditor.Windows.Assets;
|
||||||
using FlaxEngine;
|
using FlaxEngine;
|
||||||
|
using FlaxEngine.GUI;
|
||||||
|
|
||||||
namespace FlaxEditor.Surface.Archetypes
|
namespace FlaxEditor.Surface.Archetypes
|
||||||
{
|
{
|
||||||
@@ -123,7 +124,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
case MaterialDomain.Particle:
|
case MaterialDomain.Particle:
|
||||||
case MaterialDomain.Deformable:
|
case MaterialDomain.Deformable:
|
||||||
{
|
{
|
||||||
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit;
|
bool isNotUnlit = info.ShadingModel != MaterialShadingModel.Unlit && info.ShadingModel != MaterialShadingModel.CustomLit;
|
||||||
|
bool isOpaque = info.BlendMode == MaterialBlendMode.Opaque;
|
||||||
bool withTess = info.TessellationMode != TessellationMethod.None;
|
bool withTess = info.TessellationMode != TessellationMethod.None;
|
||||||
|
|
||||||
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
|
GetBox(MaterialNodeBoxes.Color).IsActive = isNotUnlit;
|
||||||
@@ -134,8 +136,8 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
|
GetBox(MaterialNodeBoxes.Roughness).IsActive = isNotUnlit;
|
||||||
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
|
GetBox(MaterialNodeBoxes.AmbientOcclusion).IsActive = isNotUnlit;
|
||||||
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
|
GetBox(MaterialNodeBoxes.Normal).IsActive = isNotUnlit;
|
||||||
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || info.BlendMode != MaterialBlendMode.Opaque;
|
GetBox(MaterialNodeBoxes.Opacity).IsActive = info.ShadingModel == MaterialShadingModel.Subsurface || info.ShadingModel == MaterialShadingModel.Foliage || !isOpaque;
|
||||||
GetBox(MaterialNodeBoxes.Refraction).IsActive = info.BlendMode != MaterialBlendMode.Opaque;
|
GetBox(MaterialNodeBoxes.Refraction).IsActive = !isOpaque;
|
||||||
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
|
GetBox(MaterialNodeBoxes.PositionOffset).IsActive = true;
|
||||||
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
|
GetBox(MaterialNodeBoxes.TessellationMultiplier).IsActive = withTess;
|
||||||
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;
|
GetBox(MaterialNodeBoxes.WorldDisplacement).IsActive = withTess;
|
||||||
@@ -260,6 +262,211 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if false // TODO: finish code editor based on RichTextBoxBase with text block parsing for custom styling
|
||||||
|
internal sealed class CustomCodeTextBox : RichTextBoxBase
|
||||||
|
{
|
||||||
|
protected override void OnParseTextBlocks()
|
||||||
|
{
|
||||||
|
base.OnParseTextBlocks();
|
||||||
|
|
||||||
|
// Single block for a whole text
|
||||||
|
// TODO: implement code parsing with HLSL syntax
|
||||||
|
var font = Style.Current.FontMedium;
|
||||||
|
var style = new TextBlockStyle
|
||||||
|
{
|
||||||
|
Font = new FontReference(font),
|
||||||
|
Color = Style.Current.Foreground,
|
||||||
|
BackgroundSelectedBrush = new SolidColorBrush(Style.Current.BackgroundSelected),
|
||||||
|
};
|
||||||
|
_textBlocks.Clear();
|
||||||
|
_textBlocks.Add(new TextBlock
|
||||||
|
{
|
||||||
|
Range = new TextRange
|
||||||
|
{
|
||||||
|
StartIndex = 0,
|
||||||
|
EndIndex = TextLength,
|
||||||
|
},
|
||||||
|
Style = style,
|
||||||
|
Bounds = new Rectangle(Float2.Zero, font.MeasureText(Text)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
internal sealed class CustomCodeTextBox : TextBox
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
base.Draw();
|
||||||
|
|
||||||
|
// Draw border
|
||||||
|
if (!IsFocused)
|
||||||
|
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BorderNormal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class CustomCodeNode : SurfaceNode
|
||||||
|
{
|
||||||
|
private Rectangle _resizeButtonRect;
|
||||||
|
private Float2 _startResizingSize;
|
||||||
|
private Float2 _startResizingCornerOffset;
|
||||||
|
private bool _isResizing;
|
||||||
|
private CustomCodeTextBox _textBox;
|
||||||
|
|
||||||
|
private int SizeValueIndex => Archetype.TypeID == 8 ? 1 : 3; // Index of the Size stored in Values array
|
||||||
|
|
||||||
|
private Float2 SizeValue
|
||||||
|
{
|
||||||
|
get => (Float2)Values[SizeValueIndex];
|
||||||
|
set => SetValue(SizeValueIndex, value, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomCodeNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
|
||||||
|
: base(id, context, nodeArch, groupArch)
|
||||||
|
{
|
||||||
|
Float2 pos = new Float2(FlaxEditor.Surface.Constants.NodeMarginX, FlaxEditor.Surface.Constants.NodeMarginY + FlaxEditor.Surface.Constants.NodeHeaderSize), size;
|
||||||
|
if (nodeArch.TypeID == 8)
|
||||||
|
{
|
||||||
|
pos += new Float2(60, 0);
|
||||||
|
size = new Float2(172, 200);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pos += new Float2(0, 40);
|
||||||
|
size = new Float2(300, 200);
|
||||||
|
}
|
||||||
|
_textBox = new CustomCodeTextBox
|
||||||
|
{
|
||||||
|
IsMultiline = true,
|
||||||
|
Location = pos,
|
||||||
|
Size = size,
|
||||||
|
Parent = this,
|
||||||
|
AnchorMax = Float2.One,
|
||||||
|
};
|
||||||
|
_textBox.EditEnd += () => SetValue(0, _textBox.Text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanSelect(ref Float2 location)
|
||||||
|
{
|
||||||
|
return base.CanSelect(ref location) && !_resizeButtonRect.MakeOffsetted(Location).Contains(ref location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnSurfaceLoaded(SurfaceNodeActions action)
|
||||||
|
{
|
||||||
|
base.OnSurfaceLoaded(action);
|
||||||
|
|
||||||
|
_textBox.Text = (string)Values[0];
|
||||||
|
|
||||||
|
var size = SizeValue;
|
||||||
|
if (Surface != null && Surface.GridSnappingEnabled)
|
||||||
|
size = Surface.SnapToGrid(size, true);
|
||||||
|
Resize(size.X, size.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnValuesChanged()
|
||||||
|
{
|
||||||
|
base.OnValuesChanged();
|
||||||
|
|
||||||
|
var size = SizeValue;
|
||||||
|
Resize(size.X, size.Y);
|
||||||
|
_textBox.Text = (string)Values[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateRectangles()
|
||||||
|
{
|
||||||
|
base.UpdateRectangles();
|
||||||
|
|
||||||
|
const float buttonMargin = FlaxEditor.Surface.Constants.NodeCloseButtonMargin;
|
||||||
|
const float buttonSize = FlaxEditor.Surface.Constants.NodeCloseButtonSize;
|
||||||
|
_resizeButtonRect = new Rectangle(_closeButtonRect.Left, Height - buttonSize - buttonMargin - 4, buttonSize, buttonSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
base.Draw();
|
||||||
|
|
||||||
|
var style = Style.Current;
|
||||||
|
if (_isResizing)
|
||||||
|
{
|
||||||
|
Render2D.FillRectangle(_resizeButtonRect, style.Selection);
|
||||||
|
Render2D.DrawRectangle(_resizeButtonRect, style.SelectionBorder);
|
||||||
|
}
|
||||||
|
Render2D.DrawSprite(style.Scale, _resizeButtonRect, _resizeButtonRect.Contains(_mousePosition) && Surface.CanEdit ? style.Foreground : style.ForegroundGrey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnLostFocus()
|
||||||
|
{
|
||||||
|
if (_isResizing)
|
||||||
|
EndResizing();
|
||||||
|
|
||||||
|
base.OnLostFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnEndMouseCapture()
|
||||||
|
{
|
||||||
|
if (_isResizing)
|
||||||
|
EndResizing();
|
||||||
|
|
||||||
|
base.OnEndMouseCapture();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (base.OnMouseDown(location, button))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (button == MouseButton.Left && _resizeButtonRect.Contains(ref location) && Surface.CanEdit)
|
||||||
|
{
|
||||||
|
// Start sliding
|
||||||
|
_isResizing = true;
|
||||||
|
_startResizingSize = Size;
|
||||||
|
_startResizingCornerOffset = Size - location;
|
||||||
|
StartMouseCapture();
|
||||||
|
Cursor = CursorType.SizeNWSE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnMouseMove(Float2 location)
|
||||||
|
{
|
||||||
|
if (_isResizing)
|
||||||
|
{
|
||||||
|
var emptySize = CalculateNodeSize(0, 0);
|
||||||
|
var size = Float2.Max(location - emptySize + _startResizingCornerOffset, new Float2(240, 160));
|
||||||
|
Resize(size.X, size.Y);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
base.OnMouseMove(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
if (button == MouseButton.Left && _isResizing)
|
||||||
|
{
|
||||||
|
EndResizing();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OnMouseUp(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EndResizing()
|
||||||
|
{
|
||||||
|
Cursor = CursorType.Default;
|
||||||
|
EndMouseCapture();
|
||||||
|
_isResizing = false;
|
||||||
|
if (_startResizingSize != Size)
|
||||||
|
{
|
||||||
|
var emptySize = CalculateNodeSize(0, 0);
|
||||||
|
SizeValue = Size - emptySize;
|
||||||
|
Surface.MarkAsEdited(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal enum MaterialTemplateInputsMapping
|
internal enum MaterialTemplateInputsMapping
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -410,13 +617,15 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
{
|
{
|
||||||
TypeID = 8,
|
TypeID = 8,
|
||||||
|
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
|
||||||
Title = "Custom Code",
|
Title = "Custom Code",
|
||||||
Description = "Custom HLSL shader code expression",
|
Description = "Custom HLSL shader code expression",
|
||||||
Flags = NodeFlags.MaterialGraph,
|
Flags = NodeFlags.MaterialGraph,
|
||||||
Size = new Float2(300, 200),
|
Size = new Float2(300, 200),
|
||||||
DefaultValues = new object[]
|
DefaultValues = new object[]
|
||||||
{
|
{
|
||||||
"// Here you can add HLSL code\nOutput0 = Input0;"
|
"// Here you can add HLSL code\nOutput0 = Input0;",
|
||||||
|
new Float2(300, 200),
|
||||||
},
|
},
|
||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
@@ -433,8 +642,6 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
|
NodeElementArchetype.Factory.Output(1, "Output1", typeof(Float4), 9),
|
||||||
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
|
NodeElementArchetype.Factory.Output(2, "Output2", typeof(Float4), 10),
|
||||||
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
|
NodeElementArchetype.Factory.Output(3, "Output3", typeof(Float4), 11),
|
||||||
|
|
||||||
NodeElementArchetype.Factory.TextBox(60, 0, 175, 200, 0),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
@@ -874,6 +1081,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
{
|
{
|
||||||
TypeID = 38,
|
TypeID = 38,
|
||||||
|
Create = (id, context, arch, groupArch) => new CustomCodeNode(id, context, arch, groupArch),
|
||||||
Title = "Custom Global Code",
|
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.",
|
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,
|
Flags = NodeFlags.MaterialGraph,
|
||||||
@@ -883,6 +1091,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
|
"// Here you can add HLSL code\nfloat4 GetCustomColor()\n{\n\treturn float4(1, 0, 0, 1);\n}",
|
||||||
true,
|
true,
|
||||||
(int)MaterialTemplateInputsMapping.Utilities,
|
(int)MaterialTemplateInputsMapping.Utilities,
|
||||||
|
new Float2(300, 240),
|
||||||
},
|
},
|
||||||
Elements = new[]
|
Elements = new[]
|
||||||
{
|
{
|
||||||
@@ -890,7 +1099,6 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
|
NodeElementArchetype.Factory.Text(20, 0, "Enabled"),
|
||||||
NodeElementArchetype.Factory.Text(0, 20, "Location"),
|
NodeElementArchetype.Factory.Text(0, 20, "Location"),
|
||||||
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
|
NodeElementArchetype.Factory.Enum(50, 20, 120, 2, typeof(MaterialTemplateInputsMapping)),
|
||||||
NodeElementArchetype.Factory.TextBox(0, 40, 300, 200, 0),
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
new NodeArchetype
|
new NodeArchetype
|
||||||
|
|||||||
@@ -459,7 +459,7 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
AlternativeTitles = new string[] { "Lightmap TexCoord" },
|
AlternativeTitles = new string[] { "Lightmap TexCoord" },
|
||||||
Description = "Lightmap UVs",
|
Description = "Lightmap UVs",
|
||||||
Flags = NodeFlags.MaterialGraph,
|
Flags = NodeFlags.MaterialGraph,
|
||||||
Size = new Float2(110, 30),
|
Size = new Float2(110, 20),
|
||||||
Elements = new []
|
Elements = new []
|
||||||
{
|
{
|
||||||
NodeElementArchetype.Factory.Output(0, "UVs", typeof(Float2), 0)
|
NodeElementArchetype.Factory.Output(0, "UVs", typeof(Float2), 0)
|
||||||
@@ -493,6 +493,19 @@ namespace FlaxEditor.Surface.Archetypes
|
|||||||
NodeElementArchetype.Factory.Bool(190, Surface.Constants.LayoutOffsetY * 4, 4),
|
NodeElementArchetype.Factory.Bool(190, Surface.Constants.LayoutOffsetY * 4, 4),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
new NodeArchetype
|
||||||
|
{
|
||||||
|
TypeID = 24,
|
||||||
|
Title = "Texture Size",
|
||||||
|
Description = "Gets the size of the texture (in pixels). If texture is during streaming, then returns size of the highest resident mip.",
|
||||||
|
Flags = NodeFlags.ParticleEmitterGraph,
|
||||||
|
Size = new Float2(160, 20),
|
||||||
|
Elements = new[]
|
||||||
|
{
|
||||||
|
NodeElementArchetype.Factory.Input(0, "Texture", true, typeof(FlaxEngine.Object), 0),
|
||||||
|
NodeElementArchetype.Factory.Output(0, "Size", typeof(Float3), 1),
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ namespace FlaxEditor.Surface.Elements
|
|||||||
{
|
{
|
||||||
ParentNode = parentNode;
|
ParentNode = parentNode;
|
||||||
Archetype = archetype;
|
Archetype = archetype;
|
||||||
|
AutoFocus = true;
|
||||||
|
|
||||||
var back = Style.Current.TextBoxBackground;
|
var back = Style.Current.TextBoxBackground;
|
||||||
var grayOutFactor = 0.6f;
|
var grayOutFactor = 0.6f;
|
||||||
|
|||||||
43
Source/Editor/Surface/NodeAlignmentType.cs
Normal file
43
Source/Editor/Surface/NodeAlignmentType.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright (c) Wojciech Figat. All rights reserved.
|
||||||
|
|
||||||
|
using FlaxEngine;
|
||||||
|
|
||||||
|
namespace FlaxEditor.Surface
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Node Alignment type
|
||||||
|
/// </summary>
|
||||||
|
[HideInEditor]
|
||||||
|
public enum NodeAlignmentType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Align nodes vertically to top, matching top-most node
|
||||||
|
/// </summary>
|
||||||
|
Top,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align nodes vertically to middle, using average of all nodes
|
||||||
|
/// </summary>
|
||||||
|
Middle,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align nodes vertically to bottom, matching bottom-most node
|
||||||
|
/// </summary>
|
||||||
|
Bottom,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align nodes horizontally to left, matching left-most node
|
||||||
|
/// </summary>
|
||||||
|
Left,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align nodes horizontally to center, using average of all nodes
|
||||||
|
/// </summary>
|
||||||
|
Center,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align nodes horizontally to right, matching right-most node
|
||||||
|
/// </summary>
|
||||||
|
Right,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -59,6 +59,7 @@ namespace FlaxEditor.Surface
|
|||||||
protected SurfaceControl(VisjectSurfaceContext context, float width, float height)
|
protected SurfaceControl(VisjectSurfaceContext context, float width, float height)
|
||||||
: base(0, 0, width, height)
|
: base(0, 0, width, height)
|
||||||
{
|
{
|
||||||
|
AutoFocus = true;
|
||||||
ClipChildren = false;
|
ClipChildren = false;
|
||||||
|
|
||||||
Surface = context.Surface;
|
Surface = context.Surface;
|
||||||
|
|||||||
@@ -40,6 +40,11 @@ namespace FlaxEditor.Surface
|
|||||||
[HideInEditor]
|
[HideInEditor]
|
||||||
public class SurfaceNode : SurfaceControl
|
public class SurfaceNode : SurfaceControl
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The box to draw a highlight around. Drawing will be skipped if null.
|
||||||
|
/// </summary>
|
||||||
|
internal Box highlightBox;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Flag used to discard node values setting during event sending for node UI flushing.
|
/// Flag used to discard node values setting during event sending for node UI flushing.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -912,7 +917,7 @@ namespace FlaxEditor.Surface
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnTestTooltipOverControl(ref Float2 location)
|
public override bool OnTestTooltipOverControl(ref Float2 location)
|
||||||
{
|
{
|
||||||
return _headerRect.Contains(ref location) && ShowTooltip;
|
return _headerRect.Contains(ref location) && ShowTooltip && !Surface.IsConnecting && !Surface.IsBoxSelecting;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -1070,7 +1075,7 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
// Header
|
// Header
|
||||||
var headerColor = style.BackgroundHighlighted;
|
var headerColor = style.BackgroundHighlighted;
|
||||||
if (_headerRect.Contains(ref _mousePosition))
|
if (_headerRect.Contains(ref _mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting)
|
||||||
headerColor *= 1.07f;
|
headerColor *= 1.07f;
|
||||||
Render2D.FillRectangle(_headerRect, headerColor);
|
Render2D.FillRectangle(_headerRect, headerColor);
|
||||||
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
Render2D.DrawText(style.FontLarge, Title, _headerRect, style.Foreground, TextAlignment.Center, TextAlignment.Center);
|
||||||
@@ -1078,7 +1083,8 @@ namespace FlaxEditor.Surface
|
|||||||
// Close button
|
// Close button
|
||||||
if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit)
|
if ((Archetype.Flags & NodeFlags.NoCloseButton) == 0 && Surface.CanEdit)
|
||||||
{
|
{
|
||||||
Render2D.DrawSprite(style.Cross, _closeButtonRect, _closeButtonRect.Contains(_mousePosition) ? style.Foreground : style.ForegroundGrey);
|
bool highlightClose = _closeButtonRect.Contains(_mousePosition) && !Surface.IsConnecting && !Surface.IsBoxSelecting;
|
||||||
|
Render2D.DrawSprite(style.Cross, _closeButtonRect, highlightClose ? style.Foreground : style.ForegroundGrey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Footer
|
// Footer
|
||||||
@@ -1101,6 +1107,9 @@ namespace FlaxEditor.Surface
|
|||||||
Render2D.DrawSprite(icon, new Rectangle(-7, -7, 16, 16), new Color(0.9f, 0.9f, 0.9f));
|
Render2D.DrawSprite(icon, new Rectangle(-7, -7, 16, 16), new Color(0.9f, 0.9f, 0.9f));
|
||||||
Render2D.DrawSprite(icon, new Rectangle(-6, -6, 14, 14), new Color(0.894117647f, 0.0784313725f, 0.0f));
|
Render2D.DrawSprite(icon, new Rectangle(-6, -6, 14, 14), new Color(0.894117647f, 0.0784313725f, 0.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (highlightBox != null)
|
||||||
|
Render2D.DrawRectangle(highlightBox.Bounds, style.BorderHighlighted, 2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -1123,8 +1132,9 @@ namespace FlaxEditor.Surface
|
|||||||
if (base.OnMouseUp(location, button))
|
if (base.OnMouseUp(location, button))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Close
|
// Close/ delete
|
||||||
if (button == MouseButton.Left && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
bool canDelete = !Surface.IsConnecting && !Surface.WasBoxSelecting && !Surface.WasMovingSelection;
|
||||||
|
if (button == MouseButton.Left && canDelete && (Archetype.Flags & NodeFlags.NoCloseButton) == 0 && _closeButtonRect.Contains(ref location))
|
||||||
{
|
{
|
||||||
Surface.Delete(this);
|
Surface.Delete(this);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -191,7 +191,16 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
private ContextMenuButton _cmCopyButton;
|
private ContextMenuButton _cmCopyButton;
|
||||||
private ContextMenuButton _cmDuplicateButton;
|
private ContextMenuButton _cmDuplicateButton;
|
||||||
|
private ContextMenuChildMenu _cmFormatNodesMenu;
|
||||||
private ContextMenuButton _cmFormatNodesConnectionButton;
|
private ContextMenuButton _cmFormatNodesConnectionButton;
|
||||||
|
private ContextMenuButton _cmAlignNodesTopButton;
|
||||||
|
private ContextMenuButton _cmAlignNodesMiddleButton;
|
||||||
|
private ContextMenuButton _cmAlignNodesBottomButton;
|
||||||
|
private ContextMenuButton _cmAlignNodesLeftButton;
|
||||||
|
private ContextMenuButton _cmAlignNodesCenterButton;
|
||||||
|
private ContextMenuButton _cmAlignNodesRightButton;
|
||||||
|
private ContextMenuButton _cmDistributeNodesHorizontallyButton;
|
||||||
|
private ContextMenuButton _cmDistributeNodesVerticallyButton;
|
||||||
private ContextMenuButton _cmRemoveNodeConnectionsButton;
|
private ContextMenuButton _cmRemoveNodeConnectionsButton;
|
||||||
private ContextMenuButton _cmRemoveBoxConnectionsButton;
|
private ContextMenuButton _cmRemoveBoxConnectionsButton;
|
||||||
private readonly Float2 ContextMenuOffset = new Float2(5);
|
private readonly Float2 ContextMenuOffset = new Float2(5);
|
||||||
@@ -399,10 +408,26 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
menu.AddSeparator();
|
menu.AddSeparator();
|
||||||
|
|
||||||
_cmFormatNodesConnectionButton = menu.AddButton("Format node(s)", () => { FormatGraph(SelectedNodes); });
|
_cmFormatNodesMenu = menu.AddChildMenu("Format node(s)");
|
||||||
_cmFormatNodesConnectionButton.Enabled = CanEdit && HasNodesSelection;
|
_cmFormatNodesMenu.Enabled = CanEdit && HasNodesSelection;
|
||||||
|
|
||||||
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections to that node(s)", () =>
|
_cmFormatNodesConnectionButton = _cmFormatNodesMenu.ContextMenu.AddButton("Auto format", Editor.Instance.Options.Options.Input.NodesAutoFormat, () => { FormatGraph(SelectedNodes); });
|
||||||
|
|
||||||
|
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||||
|
_cmAlignNodesTopButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align top", Editor.Instance.Options.Options.Input.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); });
|
||||||
|
_cmAlignNodesMiddleButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align middle", Editor.Instance.Options.Options.Input.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); });
|
||||||
|
_cmAlignNodesBottomButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align bottom", Editor.Instance.Options.Options.Input.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); });
|
||||||
|
|
||||||
|
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||||
|
_cmAlignNodesLeftButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align left", Editor.Instance.Options.Options.Input.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); });
|
||||||
|
_cmAlignNodesCenterButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align center", Editor.Instance.Options.Options.Input.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); });
|
||||||
|
_cmAlignNodesRightButton = _cmFormatNodesMenu.ContextMenu.AddButton("Align right", Editor.Instance.Options.Options.Input.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); });
|
||||||
|
|
||||||
|
_cmFormatNodesMenu.ContextMenu.AddSeparator();
|
||||||
|
_cmDistributeNodesHorizontallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute horizontally", Editor.Instance.Options.Options.Input.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); });
|
||||||
|
_cmDistributeNodesVerticallyButton = _cmFormatNodesMenu.ContextMenu.AddButton("Distribute vertically", Editor.Instance.Options.Options.Input.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); });
|
||||||
|
|
||||||
|
_cmRemoveNodeConnectionsButton = menu.AddButton("Remove all connections", () =>
|
||||||
{
|
{
|
||||||
var nodes = ((List<SurfaceNode>)menu.Tag);
|
var nodes = ((List<SurfaceNode>)menu.Tag);
|
||||||
|
|
||||||
@@ -428,8 +453,10 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
MarkAsEdited();
|
MarkAsEdited();
|
||||||
});
|
});
|
||||||
_cmRemoveNodeConnectionsButton.Enabled = CanEdit;
|
bool anyConnection = SelectedNodes.Any(n => n.GetBoxes().Any(b => b.HasAnyConnection));
|
||||||
_cmRemoveBoxConnectionsButton = menu.AddButton("Remove all connections to that box", () =>
|
_cmRemoveNodeConnectionsButton.Enabled = CanEdit && anyConnection;
|
||||||
|
|
||||||
|
_cmRemoveBoxConnectionsButton = menu.AddButton("Remove all socket connections", () =>
|
||||||
{
|
{
|
||||||
var boxUnderMouse = (Box)_cmRemoveBoxConnectionsButton.Tag;
|
var boxUnderMouse = (Box)_cmRemoveBoxConnectionsButton.Tag;
|
||||||
if (Undo != null)
|
if (Undo != null)
|
||||||
@@ -450,6 +477,16 @@ namespace FlaxEditor.Surface
|
|||||||
var boxUnderMouse = GetChildAtRecursive(location) as Box;
|
var boxUnderMouse = GetChildAtRecursive(location) as Box;
|
||||||
_cmRemoveBoxConnectionsButton.Enabled = boxUnderMouse != null && boxUnderMouse.HasAnyConnection;
|
_cmRemoveBoxConnectionsButton.Enabled = boxUnderMouse != null && boxUnderMouse.HasAnyConnection;
|
||||||
_cmRemoveBoxConnectionsButton.Tag = boxUnderMouse;
|
_cmRemoveBoxConnectionsButton.Tag = boxUnderMouse;
|
||||||
|
|
||||||
|
if (boxUnderMouse != null)
|
||||||
|
{
|
||||||
|
boxUnderMouse.ParentNode.highlightBox = boxUnderMouse;
|
||||||
|
menu.VisibleChanged += (c) =>
|
||||||
|
{
|
||||||
|
if (!c.Visible)
|
||||||
|
boxUnderMouse.ParentNode.highlightBox = null;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
controlUnderMouse?.OnShowSecondaryContextMenu(menu, controlUnderMouse.PointFromParent(location));
|
controlUnderMouse?.OnShowSecondaryContextMenu(menu, controlUnderMouse.PointFromParent(location));
|
||||||
|
|||||||
@@ -225,7 +225,11 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
_rootControl.DrawComments();
|
_rootControl.DrawComments();
|
||||||
|
|
||||||
if (IsSelecting)
|
// Reset input flags here because this is the closest to Update we have
|
||||||
|
WasBoxSelecting = IsBoxSelecting;
|
||||||
|
WasMovingSelection = IsMovingSelection;
|
||||||
|
|
||||||
|
if (IsBoxSelecting)
|
||||||
{
|
{
|
||||||
DrawSelection();
|
DrawSelection();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -282,5 +282,122 @@ namespace FlaxEditor.Surface
|
|||||||
|
|
||||||
return maxOffset;
|
return maxOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Align given nodes on a graph using the given alignment type.
|
||||||
|
/// Ignores any potential overlap.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodes">List of nodes</param>
|
||||||
|
/// <param name="alignmentType">Alignemnt type</param>
|
||||||
|
public void AlignNodes(List<SurfaceNode> nodes, NodeAlignmentType alignmentType)
|
||||||
|
{
|
||||||
|
if(nodes.Count <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var undoActions = new List<MoveNodesAction>();
|
||||||
|
var boundingBox = GetNodesBounds(nodes);
|
||||||
|
for(int i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
var centerY = boundingBox.Center.Y - (nodes[i].Height / 2);
|
||||||
|
var centerX = boundingBox.Center.X - (nodes[i].Width / 2);
|
||||||
|
|
||||||
|
var newLocation = alignmentType switch
|
||||||
|
{
|
||||||
|
NodeAlignmentType.Top => new Float2(nodes[i].Location.X, boundingBox.Top),
|
||||||
|
NodeAlignmentType.Middle => new Float2(nodes[i].Location.X, centerY),
|
||||||
|
NodeAlignmentType.Bottom => new Float2(nodes[i].Location.X, boundingBox.Bottom - nodes[i].Height),
|
||||||
|
|
||||||
|
NodeAlignmentType.Left => new Float2(boundingBox.Left, nodes[i].Location.Y),
|
||||||
|
NodeAlignmentType.Center => new Float2(centerX, nodes[i].Location.Y),
|
||||||
|
NodeAlignmentType.Right => new Float2(boundingBox.Right - nodes[i].Width, nodes[i].Location.Y),
|
||||||
|
|
||||||
|
_ => throw new NotImplementedException($"Unsupported node alignment type: {alignmentType}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
var locationDelta = newLocation - nodes[i].Location;
|
||||||
|
nodes[i].Location = newLocation;
|
||||||
|
|
||||||
|
if(Undo != null)
|
||||||
|
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkAsEdited(false);
|
||||||
|
Undo?.AddAction(new MultiUndoAction(undoActions, $"Align nodes ({alignmentType})"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Distribute the given nodes as equally as possible inside the bounding box, if no fit can be done it will use a default pad of 10 pixels between nodes.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodes">List of nodes</param>
|
||||||
|
/// <param name="vertically">If false will be done horizontally, if true will be done vertically</param>
|
||||||
|
public void DistributeNodes(List<SurfaceNode> nodes, bool vertically)
|
||||||
|
{
|
||||||
|
if(nodes.Count <= 1)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var undoActions = new List<MoveNodesAction>();
|
||||||
|
var boundingBox = GetNodesBounds(nodes);
|
||||||
|
float padding = 10;
|
||||||
|
float totalSize = 0;
|
||||||
|
for (int i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
if (vertically)
|
||||||
|
{
|
||||||
|
totalSize += nodes[i].Height;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
totalSize += nodes[i].Width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(vertically)
|
||||||
|
{
|
||||||
|
nodes.Sort((leftValue, rightValue) => { return leftValue.Y.CompareTo(rightValue.Y); });
|
||||||
|
|
||||||
|
float position = boundingBox.Top;
|
||||||
|
if(totalSize < boundingBox.Height)
|
||||||
|
{
|
||||||
|
padding = (boundingBox.Height - totalSize) / nodes.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
var newLocation = new Float2(nodes[i].X, position);
|
||||||
|
var locationDelta = newLocation - nodes[i].Location;
|
||||||
|
nodes[i].Location = newLocation;
|
||||||
|
|
||||||
|
position += nodes[i].Height + padding;
|
||||||
|
|
||||||
|
if (Undo != null)
|
||||||
|
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodes.Sort((leftValue, rightValue) => { return leftValue.X.CompareTo(rightValue.X); });
|
||||||
|
|
||||||
|
float position = boundingBox.Left;
|
||||||
|
if(totalSize < boundingBox.Width)
|
||||||
|
{
|
||||||
|
padding = (boundingBox.Width - totalSize) / nodes.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < nodes.Count; i++)
|
||||||
|
{
|
||||||
|
var newLocation = new Float2(position, nodes[i].Y);
|
||||||
|
var locationDelta = newLocation - nodes[i].Location;
|
||||||
|
nodes[i].Location = newLocation;
|
||||||
|
|
||||||
|
position += nodes[i].Width + padding;
|
||||||
|
|
||||||
|
if (Undo != null)
|
||||||
|
undoActions.Add(new MoveNodesAction(Context, new[] { nodes[i].ID }, locationDelta));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MarkAsEdited(false);
|
||||||
|
Undo?.AddAction(new MultiUndoAction(undoActions, vertically ? "Distribute nodes vertically" : "Distribute nodes horizontally"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace FlaxEditor.Surface
|
|||||||
private Float2 _movingNodesDelta;
|
private Float2 _movingNodesDelta;
|
||||||
private Float2 _gridRoundingDelta;
|
private Float2 _gridRoundingDelta;
|
||||||
private HashSet<SurfaceNode> _movingNodes;
|
private HashSet<SurfaceNode> _movingNodes;
|
||||||
|
private HashSet<SurfaceNode> _temporarySelectedNodes;
|
||||||
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
|
private readonly Stack<InputBracket> _inputBrackets = new Stack<InputBracket>();
|
||||||
|
|
||||||
private class InputBracket
|
private class InputBracket
|
||||||
@@ -130,13 +131,34 @@ namespace FlaxEditor.Surface
|
|||||||
if (_rootControl.Children[i] is SurfaceControl control)
|
if (_rootControl.Children[i] is SurfaceControl control)
|
||||||
{
|
{
|
||||||
var select = control.IsSelectionIntersecting(ref selectionRect);
|
var select = control.IsSelectionIntersecting(ref selectionRect);
|
||||||
if (select != control.IsSelected)
|
|
||||||
|
if (Root.GetKey(KeyboardKeys.Shift))
|
||||||
{
|
{
|
||||||
control.IsSelected = select;
|
if (select == control.IsSelected && _temporarySelectedNodes.Contains(control))
|
||||||
selectionChanged = true;
|
{
|
||||||
|
control.IsSelected = !select;
|
||||||
|
selectionChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Root.GetKey(KeyboardKeys.Control))
|
||||||
|
{
|
||||||
|
if (select != control.IsSelected && !_temporarySelectedNodes.Contains(control))
|
||||||
|
{
|
||||||
|
control.IsSelected = select;
|
||||||
|
selectionChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (select != control.IsSelected)
|
||||||
|
{
|
||||||
|
control.IsSelected = select;
|
||||||
|
selectionChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectionChanged)
|
if (selectionChanged)
|
||||||
SelectionChanged?.Invoke();
|
SelectionChanged?.Invoke();
|
||||||
}
|
}
|
||||||
@@ -461,6 +483,19 @@ namespace FlaxEditor.Surface
|
|||||||
// Cache data
|
// Cache data
|
||||||
_isMovingSelection = false;
|
_isMovingSelection = false;
|
||||||
_mousePos = location;
|
_mousePos = location;
|
||||||
|
if(_temporarySelectedNodes == null)
|
||||||
|
_temporarySelectedNodes = new HashSet<SurfaceNode>();
|
||||||
|
else
|
||||||
|
_temporarySelectedNodes.Clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < _rootControl.Children.Count; i++)
|
||||||
|
{
|
||||||
|
if (_rootControl.Children[i] is SurfaceNode node && node.IsSelected)
|
||||||
|
{
|
||||||
|
_temporarySelectedNodes.Add(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (button == MouseButton.Left)
|
if (button == MouseButton.Left)
|
||||||
{
|
{
|
||||||
_leftMouseDown = true;
|
_leftMouseDown = true;
|
||||||
@@ -488,9 +523,11 @@ namespace FlaxEditor.Surface
|
|||||||
// Check if user is pressing control
|
// Check if user is pressing control
|
||||||
if (Root.GetKey(KeyboardKeys.Control))
|
if (Root.GetKey(KeyboardKeys.Control))
|
||||||
{
|
{
|
||||||
// Add/remove from selection
|
AddToSelection(controlUnderMouse);
|
||||||
controlUnderMouse.IsSelected = !controlUnderMouse.IsSelected;
|
}
|
||||||
SelectionChanged?.Invoke();
|
else if (Root.GetKey(KeyboardKeys.Shift))
|
||||||
|
{
|
||||||
|
RemoveFromSelection(controlUnderMouse);
|
||||||
}
|
}
|
||||||
// Check if node isn't selected
|
// Check if node isn't selected
|
||||||
else if (!controlUnderMouse.IsSelected)
|
else if (!controlUnderMouse.IsSelected)
|
||||||
@@ -500,10 +537,14 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Start moving selected nodes
|
// Start moving selected nodes
|
||||||
StartMouseCapture();
|
if (!Root.GetKey(KeyboardKeys.Shift))
|
||||||
_movingSelectionViewPos = _rootControl.Location;
|
{
|
||||||
_movingNodesDelta = Float2.Zero;
|
StartMouseCapture();
|
||||||
OnGetNodesToMove();
|
_movingSelectionViewPos = _rootControl.Location;
|
||||||
|
_movingNodesDelta = Float2.Zero;
|
||||||
|
OnGetNodesToMove();
|
||||||
|
}
|
||||||
|
|
||||||
Focus();
|
Focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -515,7 +556,12 @@ namespace FlaxEditor.Surface
|
|||||||
{
|
{
|
||||||
// Start selecting or commenting
|
// Start selecting or commenting
|
||||||
StartMouseCapture();
|
StartMouseCapture();
|
||||||
ClearSelection();
|
|
||||||
|
if (!Root.GetKey(KeyboardKeys.Control) && !Root.GetKey(KeyboardKeys.Shift))
|
||||||
|
{
|
||||||
|
ClearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
Focus();
|
Focus();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -232,15 +232,25 @@ namespace FlaxEditor.Surface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether user is selecting nodes.
|
/// Gets a value indicating whether user is box selecting nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
|
public bool IsBoxSelecting => _leftMouseDown && !_isMovingSelection && _connectionInstigator == null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether user was previously box selecting nodes.
|
||||||
|
/// </summary>
|
||||||
|
public bool WasBoxSelecting { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether user is moving selected nodes.
|
/// Gets a value indicating whether user is moving selected nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator == null;
|
public bool IsMovingSelection => _leftMouseDown && _isMovingSelection && _connectionInstigator == null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether user was previously moving selected nodes.
|
||||||
|
/// </summary>
|
||||||
|
public bool WasMovingSelection { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether user is connecting nodes.
|
/// Gets a value indicating whether user is connecting nodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -405,6 +415,15 @@ namespace FlaxEditor.Surface
|
|||||||
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
new InputActionsContainer.Binding(options => options.Paste, Paste),
|
||||||
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
new InputActionsContainer.Binding(options => options.Cut, Cut),
|
||||||
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
new InputActionsContainer.Binding(options => options.Duplicate, Duplicate),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAutoFormat, () => { FormatGraph(SelectedNodes); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAlignTop, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Top); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAlignMiddle, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Middle); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAlignBottom, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Bottom); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAlignLeft, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Left); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAlignCenter, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Center); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesAlignRight, () => { AlignNodes(SelectedNodes, NodeAlignmentType.Right); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesDistributeHorizontal, () => { DistributeNodes(SelectedNodes, false); }),
|
||||||
|
new InputActionsContainer.Binding(options => options.NodesDistributeVertical, () => { DistributeNodes(SelectedNodes, true); }),
|
||||||
});
|
});
|
||||||
|
|
||||||
Context.ControlSpawned += OnSurfaceControlSpawned;
|
Context.ControlSpawned += OnSurfaceControlSpawned;
|
||||||
@@ -710,6 +729,18 @@ namespace FlaxEditor.Surface
|
|||||||
SelectionChanged?.Invoke();
|
SelectionChanged?.Invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the specified control from the selection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="control">The control.</param>
|
||||||
|
public void RemoveFromSelection(SurfaceControl control)
|
||||||
|
{
|
||||||
|
if (!control.IsSelected)
|
||||||
|
return;
|
||||||
|
control.IsSelected = false;
|
||||||
|
SelectionChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Selects the specified control.
|
/// Selects the specified control.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1518,6 +1518,7 @@ namespace FlaxEditor.Utilities
|
|||||||
inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution());
|
inputActions.Add(options => options.OpenScriptsProject, () => Editor.Instance.CodeEditing.OpenSolution());
|
||||||
inputActions.Add(options => options.GenerateScriptsProject, () => Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync());
|
inputActions.Add(options => options.GenerateScriptsProject, () => Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync());
|
||||||
inputActions.Add(options => options.RecompileScripts, ScriptsBuilder.Compile);
|
inputActions.Add(options => options.RecompileScripts, ScriptsBuilder.Compile);
|
||||||
|
inputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string ToPathProject(string path)
|
internal static string ToPathProject(string path)
|
||||||
|
|||||||
@@ -340,6 +340,13 @@ namespace FlaxEditor.Viewport
|
|||||||
{
|
{
|
||||||
_debugDrawData.Clear();
|
_debugDrawData.Clear();
|
||||||
|
|
||||||
|
if (task is SceneRenderTask sceneRenderTask)
|
||||||
|
{
|
||||||
|
// Sync debug view to avoid lag on culling/LODing
|
||||||
|
var view = sceneRenderTask.View;
|
||||||
|
DebugDraw.SetView(ref view);
|
||||||
|
}
|
||||||
|
|
||||||
// Collect selected objects debug shapes and visuals
|
// Collect selected objects debug shapes and visuals
|
||||||
var selectedParents = TransformGizmo.SelectedParents;
|
var selectedParents = TransformGizmo.SelectedParents;
|
||||||
if (selectedParents.Count > 0)
|
if (selectedParents.Count > 0)
|
||||||
@@ -374,14 +381,7 @@ namespace FlaxEditor.Viewport
|
|||||||
// Draw selected objects debug shapes and visuals
|
// Draw selected objects debug shapes and visuals
|
||||||
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
|
if (DrawDebugDraw && (renderContext.View.Flags & ViewFlags.DebugDraw) == ViewFlags.DebugDraw)
|
||||||
{
|
{
|
||||||
unsafe
|
_debugDrawData.DrawActors(true);
|
||||||
{
|
|
||||||
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
|
||||||
{
|
|
||||||
DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true);
|
DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,7 +243,12 @@ namespace FlaxEditor.Viewport
|
|||||||
_tempDebugDrawContext = DebugDraw.AllocateContext();
|
_tempDebugDrawContext = DebugDraw.AllocateContext();
|
||||||
DebugDraw.SetContext(_tempDebugDrawContext);
|
DebugDraw.SetContext(_tempDebugDrawContext);
|
||||||
DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f);
|
DebugDraw.UpdateContext(_tempDebugDrawContext, 1.0f);
|
||||||
|
if (task is SceneRenderTask sceneRenderTask)
|
||||||
|
{
|
||||||
|
// Sync debug view to avoid lag on culling/LODing
|
||||||
|
var view = sceneRenderTask.View;
|
||||||
|
DebugDraw.SetView(ref view);
|
||||||
|
}
|
||||||
for (int i = 0; i < selectedParents.Count; i++)
|
for (int i = 0; i < selectedParents.Count; i++)
|
||||||
{
|
{
|
||||||
if (selectedParents[i].IsActiveInHierarchy)
|
if (selectedParents[i].IsActiveInHierarchy)
|
||||||
@@ -643,14 +648,7 @@ namespace FlaxEditor.Viewport
|
|||||||
if (selectedParents[i].IsActiveInHierarchy)
|
if (selectedParents[i].IsActiveInHierarchy)
|
||||||
selectedParents[i].OnDebugDraw(_debugDrawData);
|
selectedParents[i].OnDebugDraw(_debugDrawData);
|
||||||
}
|
}
|
||||||
|
_debugDrawData.DrawActors();
|
||||||
unsafe
|
|
||||||
{
|
|
||||||
fixed (IntPtr* actors = _debugDrawData.ActorsPtrs)
|
|
||||||
{
|
|
||||||
DebugDraw.DrawActors(new IntPtr(actors), _debugDrawData.ActorsCount, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug draw all actors in prefab and collect actors
|
// Debug draw all actors in prefab and collect actors
|
||||||
var view = Task.View;
|
var view = Task.View;
|
||||||
|
|||||||
@@ -264,6 +264,7 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
{
|
{
|
||||||
DebugDraw.SetContext(_debugDrawContext);
|
DebugDraw.SetContext(_debugDrawContext);
|
||||||
DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1));
|
DebugDraw.UpdateContext(_debugDrawContext, 1.0f / Mathf.Max(Engine.FramesPerSecond, 1));
|
||||||
|
DebugDraw.SetView(ref renderContext.View);
|
||||||
CustomDebugDraw?.Invoke(context, ref renderContext);
|
CustomDebugDraw?.Invoke(context, ref renderContext);
|
||||||
OnDebugDraw(context, ref renderContext);
|
OnDebugDraw(context, ref renderContext);
|
||||||
DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true);
|
DebugDraw.Draw(ref renderContext, target.View(), targetDepth.View(), true);
|
||||||
|
|||||||
@@ -246,6 +246,14 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void OnDebugDraw(GPUContext context, ref RenderContext renderContext)
|
||||||
|
{
|
||||||
|
base.OnDebugDraw(context, ref renderContext);
|
||||||
|
|
||||||
|
_previewEffect.OnDebugDraw();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Draw()
|
public override void Draw()
|
||||||
{
|
{
|
||||||
@@ -295,7 +303,8 @@ namespace FlaxEditor.Viewport.Previews
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnDestroy()
|
public override void OnDestroy()
|
||||||
{
|
{
|
||||||
// Cleanup objects
|
if (IsDisposing)
|
||||||
|
return;
|
||||||
_previewEffect.ParticleSystem = null;
|
_previewEffect.ParticleSystem = null;
|
||||||
Object.Destroy(ref _previewEffect);
|
Object.Destroy(ref _previewEffect);
|
||||||
Object.Destroy(ref _boundsModel);
|
Object.Destroy(ref _boundsModel);
|
||||||
|
|||||||
@@ -88,6 +88,18 @@ namespace FlaxEditor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Draws the collected actors via <see cref="DebugDraw"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawScenes">True if draw all loaded scenes too, otherwise will draw only provided actors.</param>
|
||||||
|
public unsafe void DrawActors(bool drawScenes = false)
|
||||||
|
{
|
||||||
|
fixed (IntPtr* actors = ActorsPtrs)
|
||||||
|
{
|
||||||
|
DebugDraw.DrawActors(new IntPtr(actors), _actors.Count, drawScenes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when task calls <see cref="SceneRenderTask.CollectDrawCalls" /> event.
|
/// Called when task calls <see cref="SceneRenderTask.CollectDrawCalls" /> event.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -236,6 +236,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
var group = layout.Group("General");
|
var group = layout.Group("General");
|
||||||
|
|
||||||
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
|
var minScreenSize = group.FloatValue("Min Screen Size", "The minimum screen size to draw model (the bottom limit). Used to cull small models. Set to 0 to disable this feature.");
|
||||||
|
minScreenSize.ValueBox.SlideSpeed = 0.005f;
|
||||||
minScreenSize.ValueBox.MinValue = 0.0f;
|
minScreenSize.ValueBox.MinValue = 0.0f;
|
||||||
minScreenSize.ValueBox.MaxValue = 1.0f;
|
minScreenSize.ValueBox.MaxValue = 1.0f;
|
||||||
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
|
minScreenSize.ValueBox.Value = proxy.Asset.MinScreenSize;
|
||||||
@@ -476,12 +477,12 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs), VisibleIf("ShowUVs")]
|
[EditorOrder(1), EditorDisplay(null, "LOD"), Limit(0, Model.MaxLODs, 0.01f), VisibleIf("ShowUVs")]
|
||||||
[Tooltip("Level Of Detail index to preview UVs layout.")]
|
[Tooltip("Level Of Detail index to preview UVs layout at.")]
|
||||||
public int LOD = 0;
|
public int LOD = 0;
|
||||||
|
|
||||||
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000), VisibleIf("ShowUVs")]
|
[EditorOrder(2), EditorDisplay(null, "Mesh"), Limit(-1, 1000000, 0.01f), VisibleIf("ShowUVs")]
|
||||||
[Tooltip("Mesh index to preview UVs layout. Use -1 for all meshes")]
|
[Tooltip("Mesh index to show UVs layout for. Use -1 to display all UVs of all meshes")]
|
||||||
public int Mesh = -1;
|
public int Mesh = -1;
|
||||||
|
|
||||||
private bool ShowUVs => _uvChannel != UVChannel.None;
|
private bool ShowUVs => _uvChannel != UVChannel.None;
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
|
|
||||||
var resolution = group.FloatValue("Resolution Scale", Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
|
var resolution = group.FloatValue("Resolution Scale", Window.Editor.CodeDocs.GetTooltip(typeof(ModelTool.Options), nameof(ModelImportSettings.Settings.SDFResolution)));
|
||||||
|
resolution.ValueBox.SlideSpeed = 0.001f;
|
||||||
resolution.ValueBox.MinValue = 0.0001f;
|
resolution.ValueBox.MinValue = 0.0001f;
|
||||||
resolution.ValueBox.MaxValue = 100.0f;
|
resolution.ValueBox.MaxValue = 100.0f;
|
||||||
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;
|
resolution.ValueBox.Value = sdf.Texture != null ? sdf.ResolutionScale : 1.0f;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FlaxEditor.Content;
|
using FlaxEditor.Content;
|
||||||
using FlaxEditor.CustomEditors;
|
using FlaxEditor.CustomEditors;
|
||||||
|
using FlaxEditor.GUI;
|
||||||
using FlaxEditor.Scripting;
|
using FlaxEditor.Scripting;
|
||||||
using FlaxEditor.Surface;
|
using FlaxEditor.Surface;
|
||||||
using FlaxEditor.Viewport.Previews;
|
using FlaxEditor.Viewport.Previews;
|
||||||
@@ -112,8 +113,56 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private sealed class LayoutTabProxy
|
||||||
|
{
|
||||||
|
[EditorDisplay("Layout"), CustomEditor(typeof(Editor)), NoSerialize]
|
||||||
|
// ReSharper disable once UnusedAutoPropertyAccessor.Local
|
||||||
|
public ParticleEmitterWindow Window;
|
||||||
|
|
||||||
|
private class Editor : CustomEditor
|
||||||
|
{
|
||||||
|
public override DisplayStyle Style => DisplayStyle.InlineIntoParent;
|
||||||
|
|
||||||
|
public override void Initialize(LayoutElementsContainer layout)
|
||||||
|
{
|
||||||
|
var window = (ParticleEmitterWindow)Values[0];
|
||||||
|
var emitter = window.Preview.Emitter;
|
||||||
|
if (emitter == null || !emitter.IsLoaded)
|
||||||
|
return;
|
||||||
|
var attributes = emitter.Layout;
|
||||||
|
var size = 0;
|
||||||
|
var height = 14;
|
||||||
|
foreach (var attribute in attributes)
|
||||||
|
{
|
||||||
|
layout.Label($" - {GetAttributeType(attribute.Format)} {attribute.Name}").Label.Height = height;
|
||||||
|
size += PixelFormatExtensions.SizeInBytes(attribute.Format);
|
||||||
|
}
|
||||||
|
var capacity = 0;
|
||||||
|
if (window.Surface != null && window.Surface.RootNode != null && window.Surface.RootNode.Values.Length > 0)
|
||||||
|
capacity = (int)window.Surface.RootNode.Values[0];
|
||||||
|
layout.Space(10);
|
||||||
|
layout.Label($"Particle size: {size} bytes\nParticle buffer size: {Utilities.Utils.FormatBytesCount((ulong)(size * capacity))}").Label.Height = height * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAttributeType(PixelFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case PixelFormat.R32_Float: return "float";
|
||||||
|
case PixelFormat.R32G32_Float: return "Float2";
|
||||||
|
case PixelFormat.R32G32B32_Float: return "Float3";
|
||||||
|
case PixelFormat.R32G32B32A32_Float: return "Float4";
|
||||||
|
case PixelFormat.R32_SInt: return "int";
|
||||||
|
case PixelFormat.R32_UInt: return "uint";
|
||||||
|
default: return format.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private readonly PropertiesProxy _properties;
|
private readonly PropertiesProxy _properties;
|
||||||
private Tab _previewTab;
|
private Tab _previewTab, _layoutTab;
|
||||||
|
private ToolStripButton _showSourceCodeButton;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public ParticleEmitterWindow(Editor editor, AssetItem item)
|
public ParticleEmitterWindow(Editor editor, AssetItem item)
|
||||||
@@ -125,18 +174,22 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
PlaySimulation = true,
|
PlaySimulation = true,
|
||||||
Parent = _split2.Panel1
|
Parent = _split2.Panel1
|
||||||
};
|
};
|
||||||
|
_preview.PreviewActor.ShowDebugDraw = true;
|
||||||
|
_preview.ShowDebugDraw = true;
|
||||||
|
|
||||||
// Asset properties proxy
|
// Asset properties proxy
|
||||||
_properties = new PropertiesProxy();
|
_properties = new PropertiesProxy();
|
||||||
|
|
||||||
// Preview properties editor
|
// Preview properties editor
|
||||||
_previewTab = new Tab("Preview");
|
_previewTab = new Tab("Preview");
|
||||||
_previewTab.Presenter.Select(new PreviewProxy
|
_previewTab.Presenter.Select(new PreviewProxy { Window = this });
|
||||||
{
|
|
||||||
Window = this,
|
|
||||||
});
|
|
||||||
_tabs.AddTab(_previewTab);
|
_tabs.AddTab(_previewTab);
|
||||||
|
|
||||||
|
// Particle data layout
|
||||||
|
_layoutTab = new Tab("Layout");
|
||||||
|
_layoutTab.Presenter.Select(new LayoutTabProxy { Window = this });
|
||||||
|
_tabs.AddTab(_layoutTab);
|
||||||
|
|
||||||
// Surface
|
// Surface
|
||||||
_surface = new ParticleEmitterSurface(this, Save, _undo)
|
_surface = new ParticleEmitterSurface(this, Save, _undo)
|
||||||
{
|
{
|
||||||
@@ -146,7 +199,8 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
// Toolstrip
|
// Toolstrip
|
||||||
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
SurfaceUtils.PerformCommonSetup(this, _toolstrip, _surface, out _saveButton, out _undoButton, out _redoButton);
|
||||||
_toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode).LinkTooltip("Show generated shader source code");
|
_showSourceCodeButton = _toolstrip.AddButton(editor.Icons.Code64, ShowSourceCode);
|
||||||
|
_showSourceCodeButton.LinkTooltip("Show generated shader source code");
|
||||||
_toolstrip.AddSeparator();
|
_toolstrip.AddSeparator();
|
||||||
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
_toolstrip.AddButton(editor.Icons.Docs64, () => Platform.OpenUrl(Utilities.Constants.DocsUrl + "manual/particles/index.html")).LinkTooltip("See documentation to learn more");
|
||||||
}
|
}
|
||||||
@@ -234,6 +288,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
_asset.WaitForLoaded();
|
_asset.WaitForLoaded();
|
||||||
_preview.PreviewActor.ResetSimulation();
|
_preview.PreviewActor.ResetSimulation();
|
||||||
_previewTab.Presenter.BuildLayoutOnUpdate();
|
_previewTab.Presenter.BuildLayoutOnUpdate();
|
||||||
|
_layoutTab.Presenter.BuildLayoutOnUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,6 +305,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
// Init asset properties and parameters proxy
|
// Init asset properties and parameters proxy
|
||||||
_properties.OnLoad(this);
|
_properties.OnLoad(this);
|
||||||
_previewTab.Presenter.BuildLayoutOnUpdate();
|
_previewTab.Presenter.BuildLayoutOnUpdate();
|
||||||
|
_layoutTab.Presenter.BuildLayoutOnUpdate();
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -285,5 +341,15 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public SearchAssetTypes AssetType => SearchAssetTypes.ParticleEmitter;
|
public SearchAssetTypes AssetType => SearchAssetTypes.ParticleEmitter;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Update(float deltaTime)
|
||||||
|
{
|
||||||
|
base.Update(deltaTime);
|
||||||
|
|
||||||
|
if (_asset == null)
|
||||||
|
return;
|
||||||
|
_showSourceCodeButton.Enabled = _asset.HasShaderCode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -97,10 +97,7 @@ namespace FlaxEditor.Windows.Assets
|
|||||||
|
|
||||||
// For single node selected scroll view so user can see it
|
// For single node selected scroll view so user can see it
|
||||||
if (nodes.Count == 1)
|
if (nodes.Count == 1)
|
||||||
{
|
ScrollToSelectedNode();
|
||||||
nodes[0].ExpandAllParents(true);
|
|
||||||
ScrollViewTo(nodes[0]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update properties editor
|
// Update properties editor
|
||||||
|
|||||||
@@ -352,24 +352,12 @@ namespace FlaxEditor.Windows
|
|||||||
editor.Options.Apply(editor.Options.Options);
|
editor.Options.Apply(editor.Options.Options);
|
||||||
}).SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
|
}).SetAutoCheck(true).LinkTooltip("Performs auto pause on error");
|
||||||
toolstrip.AddSeparator();
|
toolstrip.AddSeparator();
|
||||||
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () =>
|
_groupButtons[0] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Error32, () => { OnGroupButtonPressed(0); })
|
||||||
{
|
.SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
|
||||||
UpdateLogTypeVisibility(LogGroup.Error, _groupButtons[0].Checked);
|
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () => { OnGroupButtonPressed(1); })
|
||||||
editor.Options.Options.Interface.DebugLogShowErrorMessages = _groupButtons[0].Checked;
|
.SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
|
||||||
editor.Options.Apply(editor.Options.Options);
|
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () => { OnGroupButtonPressed(2); })
|
||||||
}).SetAutoCheck(true).LinkTooltip("Shows/hides error messages");
|
.SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
|
||||||
_groupButtons[1] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Warning32, () =>
|
|
||||||
{
|
|
||||||
UpdateLogTypeVisibility(LogGroup.Warning, _groupButtons[1].Checked);
|
|
||||||
editor.Options.Options.Interface.DebugLogShowWarningMessages = _groupButtons[1].Checked;
|
|
||||||
editor.Options.Apply(editor.Options.Options);
|
|
||||||
}).SetAutoCheck(true).LinkTooltip("Shows/hides warning messages");
|
|
||||||
_groupButtons[2] = (ToolStripButton)toolstrip.AddButton(editor.Icons.Info32, () =>
|
|
||||||
{
|
|
||||||
UpdateLogTypeVisibility(LogGroup.Info, _groupButtons[2].Checked);
|
|
||||||
editor.Options.Options.Interface.DebugLogShowInfoMessages = _groupButtons[2].Checked;
|
|
||||||
editor.Options.Apply(editor.Options.Options);
|
|
||||||
}).SetAutoCheck(true).LinkTooltip("Shows/hides info messages");
|
|
||||||
UpdateCount();
|
UpdateCount();
|
||||||
|
|
||||||
// Split panel
|
// Split panel
|
||||||
@@ -418,6 +406,27 @@ namespace FlaxEditor.Windows
|
|||||||
OnEditorOptionsChanged(Editor.Options.Options);
|
OnEditorOptionsChanged(Editor.Options.Options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnGroupButtonPressed(int index)
|
||||||
|
{
|
||||||
|
UpdateLogTypeVisibility((LogGroup)index, _groupButtons[index].Checked);
|
||||||
|
if (Input.GetKey(KeyboardKeys.Shift))
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)LogGroup.Max; i++)
|
||||||
|
{
|
||||||
|
if (i == index)
|
||||||
|
continue;
|
||||||
|
_groupButtons[i].Checked = !_groupButtons[index].Checked;
|
||||||
|
UpdateLogTypeVisibility((LogGroup)i, _groupButtons[i].Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var options = Editor.Options.Options.Interface;
|
||||||
|
options.DebugLogShowErrorMessages = _groupButtons[0].Checked;
|
||||||
|
options.DebugLogShowWarningMessages = _groupButtons[1].Checked;
|
||||||
|
options.DebugLogShowInfoMessages = _groupButtons[2].Checked;
|
||||||
|
Editor.Options.Apply(Editor.Options.Options);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnEditorOptionsChanged(EditorOptions options)
|
private void OnEditorOptionsChanged(EditorOptions options)
|
||||||
{
|
{
|
||||||
_timestampsFormats = options.Interface.DebugLogTimestampsFormat;
|
_timestampsFormats = options.Interface.DebugLogTimestampsFormat;
|
||||||
@@ -438,6 +447,10 @@ namespace FlaxEditor.Windows
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
|
lock (_locker)
|
||||||
|
{
|
||||||
|
_pendingEntries.Clear();
|
||||||
|
}
|
||||||
if (_entriesPanel == null)
|
if (_entriesPanel == null)
|
||||||
return;
|
return;
|
||||||
RemoveEntries();
|
RemoveEntries();
|
||||||
@@ -455,15 +468,9 @@ namespace FlaxEditor.Windows
|
|||||||
// Create new entry
|
// Create new entry
|
||||||
switch (_timestampsFormats)
|
switch (_timestampsFormats)
|
||||||
{
|
{
|
||||||
case InterfaceOptions.TimestampsFormats.Utc:
|
case InterfaceOptions.TimestampsFormats.Utc: desc.Title = $"[{DateTime.UtcNow}] {desc.Title}"; break;
|
||||||
desc.Title = $"[{DateTime.UtcNow}] {desc.Title}";
|
case InterfaceOptions.TimestampsFormats.LocalTime: desc.Title = $"[{DateTime.Now}] {desc.Title}"; break;
|
||||||
break;
|
case InterfaceOptions.TimestampsFormats.TimeSinceStartup: desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title; break;
|
||||||
case InterfaceOptions.TimestampsFormats.LocalTime:
|
|
||||||
desc.Title = $"[{DateTime.Now}] {desc.Title}";
|
|
||||||
break;
|
|
||||||
case InterfaceOptions.TimestampsFormats.TimeSinceStartup:
|
|
||||||
desc.Title = string.Format("[{0:g}] ", TimeSpan.FromSeconds(Time.TimeSinceStartup)) + desc.Title;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
var newEntry = new LogEntry(this, ref desc);
|
var newEntry = new LogEntry(this, ref desc);
|
||||||
|
|
||||||
@@ -732,10 +739,10 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnPlayBegin()
|
public override void OnPlayBeginning()
|
||||||
{
|
{
|
||||||
// Clear on Play
|
// Clear on Play
|
||||||
if (_clearOnPlayButton.Checked)
|
if (Editor.Options.Options.Interface.DebugLogClearOnPlay)
|
||||||
{
|
{
|
||||||
Clear();
|
Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,6 +219,13 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when Editor will leave the play mode.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void OnPlayEnding()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when Editor leaves the play mode.
|
/// Called when Editor leaves the play mode.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -405,6 +405,7 @@ namespace FlaxEditor.Windows
|
|||||||
return;
|
return;
|
||||||
Editor.Instance.SceneEditing.Delete();
|
Editor.Instance.SceneEditing.Delete();
|
||||||
});
|
});
|
||||||
|
InputActions.Add(options => options.FocusConsoleCommand, () => Editor.Instance.Windows.OutputLogWin.FocusCommand());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangeViewportRatio(ViewportScaleOptions v)
|
private void ChangeViewportRatio(ViewportScaleOptions v)
|
||||||
@@ -509,13 +510,7 @@ namespace FlaxEditor.Windows
|
|||||||
selectedParents[i].OnDebugDraw(drawDebugData);
|
selectedParents[i].OnDebugDraw(drawDebugData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsafe
|
drawDebugData.DrawActors(true);
|
||||||
{
|
|
||||||
fixed (IntPtr* actors = drawDebugData.ActorsPtrs)
|
|
||||||
{
|
|
||||||
DebugDraw.DrawActors(new IntPtr(actors), drawDebugData.ActorsCount, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugDraw.Draw(ref renderContext, task.OutputView);
|
DebugDraw.Draw(ref renderContext, task.OutputView);
|
||||||
@@ -1181,6 +1176,12 @@ namespace FlaxEditor.Windows
|
|||||||
if (!_cursorVisible)
|
if (!_cursorVisible)
|
||||||
Screen.CursorVisible = true;
|
Screen.CursorVisible = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Editor.IsPlayMode && IsDocked && IsSelected && RootWindow.FocusedControl == null)
|
||||||
|
{
|
||||||
|
// Game UI cleared focus so regain it to maintain UI navigation just like game window does
|
||||||
|
FlaxEngine.Scripting.InvokeOnUpdate(Focus);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -830,6 +830,15 @@ namespace FlaxEditor.Windows
|
|||||||
OnOutputTextChanged();
|
OnOutputTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Focus the debug command line and ensure that the output log window is visible.
|
||||||
|
/// </summary>
|
||||||
|
public void FocusCommand()
|
||||||
|
{
|
||||||
|
FocusOrShow();
|
||||||
|
_commandLineBox.Focus();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void Update(float deltaTime)
|
public override void Update(float deltaTime)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ namespace FlaxEditor.Windows.Profiler
|
|||||||
{
|
{
|
||||||
_warningText = new Label
|
_warningText = new Label
|
||||||
{
|
{
|
||||||
Text = "Detailed memory profiling is disabled. Run with command line: -mem",
|
Text = "Detailed memory profiling is disabled. Run with command line '-mem'",
|
||||||
TextColor = Color.Red,
|
TextColor = Color.Red,
|
||||||
Visible = false,
|
Visible = false,
|
||||||
Parent = layout,
|
Parent = layout,
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ namespace FlaxEditor.Windows.Profiler
|
|||||||
{
|
{
|
||||||
var gpuResource = _gpuResourcesCached[i];
|
var gpuResource = _gpuResourcesCached[i];
|
||||||
ref var resource = ref resources[i];
|
ref var resource = ref resources[i];
|
||||||
|
if (!gpuResource)
|
||||||
|
continue;
|
||||||
|
|
||||||
// Try to reuse cached resource info
|
// Try to reuse cached resource info
|
||||||
var gpuResourceId = gpuResource.ID;
|
var gpuResourceId = gpuResource.ID;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace FlaxEditor.Windows
|
|||||||
private Panel _sceneTreePanel;
|
private Panel _sceneTreePanel;
|
||||||
private bool _isUpdatingSelection;
|
private bool _isUpdatingSelection;
|
||||||
private bool _isMouseDown;
|
private bool _isMouseDown;
|
||||||
|
private bool _blockSceneTreeScroll = false;
|
||||||
|
|
||||||
private DragAssets _dragAssets;
|
private DragAssets _dragAssets;
|
||||||
private DragActorType _dragActorType;
|
private DragActorType _dragActorType;
|
||||||
@@ -34,6 +35,7 @@ namespace FlaxEditor.Windows
|
|||||||
private DragScriptItems _dragScriptItems;
|
private DragScriptItems _dragScriptItems;
|
||||||
private DragHandlers _dragHandlers;
|
private DragHandlers _dragHandlers;
|
||||||
private bool _isDropping = false;
|
private bool _isDropping = false;
|
||||||
|
private bool _forceScrollNodeToView = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Scene tree panel.
|
/// Scene tree panel.
|
||||||
@@ -91,6 +93,15 @@ namespace FlaxEditor.Windows
|
|||||||
_tree.SelectedChanged += Tree_OnSelectedChanged;
|
_tree.SelectedChanged += Tree_OnSelectedChanged;
|
||||||
_tree.RightClick += OnTreeRightClick;
|
_tree.RightClick += OnTreeRightClick;
|
||||||
_tree.Parent = _sceneTreePanel;
|
_tree.Parent = _sceneTreePanel;
|
||||||
|
_tree.AfterDeferredLayout += () =>
|
||||||
|
{
|
||||||
|
if (_forceScrollNodeToView)
|
||||||
|
{
|
||||||
|
_forceScrollNodeToView = false;
|
||||||
|
ScrollToSelectedNode();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
headerPanel.Parent = this;
|
headerPanel.Parent = this;
|
||||||
|
|
||||||
// Setup input actions
|
// Setup input actions
|
||||||
@@ -102,6 +113,34 @@ namespace FlaxEditor.Windows
|
|||||||
InputActions.Add(options => options.Rename, RenameSelection);
|
InputActions.Add(options => options.Rename, RenameSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayBeginning()
|
||||||
|
{
|
||||||
|
base.OnPlayBeginning();
|
||||||
|
_blockSceneTreeScroll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayBegin()
|
||||||
|
{
|
||||||
|
base.OnPlayBegin();
|
||||||
|
_blockSceneTreeScroll = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayEnding()
|
||||||
|
{
|
||||||
|
base.OnPlayEnding();
|
||||||
|
_blockSceneTreeScroll = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnPlayEnd()
|
||||||
|
{
|
||||||
|
base.OnPlayEnd();
|
||||||
|
_blockSceneTreeScroll = true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables vertical and horizontal scrolling on the scene tree panel.
|
/// Enables or disables vertical and horizontal scrolling on the scene tree panel.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -142,6 +181,16 @@ namespace FlaxEditor.Windows
|
|||||||
root.TreeNode.UpdateFilter(query);
|
root.TreeNode.UpdateFilter(query);
|
||||||
|
|
||||||
_tree.UnlockChildrenRecursive();
|
_tree.UnlockChildrenRecursive();
|
||||||
|
|
||||||
|
// When keep the selected nodes in a view
|
||||||
|
var nodeSelection = _tree.Selection;
|
||||||
|
if (nodeSelection.Count != 0)
|
||||||
|
{
|
||||||
|
var node = nodeSelection[nodeSelection.Count - 1];
|
||||||
|
node.Expand(true);
|
||||||
|
_forceScrollNodeToView = true;
|
||||||
|
}
|
||||||
|
|
||||||
PerformLayout();
|
PerformLayout();
|
||||||
PerformLayout();
|
PerformLayout();
|
||||||
}
|
}
|
||||||
@@ -250,7 +299,7 @@ namespace FlaxEditor.Windows
|
|||||||
_tree.Select(nodes);
|
_tree.Select(nodes);
|
||||||
|
|
||||||
// For single node selected scroll view so user can see it
|
// For single node selected scroll view so user can see it
|
||||||
if (nodes.Count == 1)
|
if (nodes.Count == 1 && !_blockSceneTreeScroll)
|
||||||
{
|
{
|
||||||
nodes[0].ExpandAllParents(true);
|
nodes[0].ExpandAllParents(true);
|
||||||
_sceneTreePanel.ScrollViewTo(nodes[0]);
|
_sceneTreePanel.ScrollViewTo(nodes[0]);
|
||||||
@@ -260,6 +309,12 @@ namespace FlaxEditor.Windows
|
|||||||
_isUpdatingSelection = false;
|
_isUpdatingSelection = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void OnEditorStateChanged()
|
||||||
|
{
|
||||||
|
_blockSceneTreeScroll = Editor.StateMachine.ReloadingScriptsState.IsActive;
|
||||||
|
}
|
||||||
|
|
||||||
private bool ValidateDragAsset(AssetItem assetItem)
|
private bool ValidateDragAsset(AssetItem assetItem)
|
||||||
{
|
{
|
||||||
if (assetItem.IsOfType<SceneAsset>())
|
if (assetItem.IsOfType<SceneAsset>())
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
if (value == _selectedItem || (value != null && !_matchedItems.Contains(value)))
|
if (value == _selectedItem || (value != null && !_matchedItems.Contains(value)))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Restore the previous selected item to the non-selected color
|
||||||
if (_selectedItem != null)
|
if (_selectedItem != null)
|
||||||
{
|
{
|
||||||
_selectedItem.BackgroundColor = Color.Transparent;
|
_selectedItem.BackgroundColor = Color.Transparent;
|
||||||
@@ -54,6 +55,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
|
_selectedItem.BackgroundColor = Style.Current.BackgroundSelected;
|
||||||
if (_matchedItems.Count > VisibleItemCount)
|
if (_matchedItems.Count > VisibleItemCount)
|
||||||
{
|
{
|
||||||
|
_selectedItem.Focus();
|
||||||
_resultPanel.ScrollViewTo(_selectedItem, true);
|
_resultPanel.ScrollViewTo(_selectedItem, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,39 +182,17 @@ namespace FlaxEditor.Windows.Search
|
|||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case KeyboardKeys.ArrowDown:
|
case KeyboardKeys.ArrowDown:
|
||||||
{
|
|
||||||
if (_matchedItems.Count == 0)
|
|
||||||
return true;
|
|
||||||
int currentPos;
|
|
||||||
if (_selectedItem != null)
|
|
||||||
{
|
|
||||||
currentPos = _matchedItems.IndexOf(_selectedItem) + 1;
|
|
||||||
if (currentPos >= _matchedItems.Count)
|
|
||||||
currentPos--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentPos = 0;
|
|
||||||
}
|
|
||||||
SelectedItem = _matchedItems[currentPos];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case KeyboardKeys.ArrowUp:
|
case KeyboardKeys.ArrowUp:
|
||||||
{
|
{
|
||||||
if (_matchedItems.Count == 0)
|
if (_matchedItems.Count == 0)
|
||||||
return true;
|
return true;
|
||||||
int currentPos;
|
|
||||||
if (_selectedItem != null)
|
var focusedIndex = _matchedItems.IndexOf(_selectedItem);
|
||||||
{
|
int delta = key == KeyboardKeys.ArrowDown ? -1 : 1;
|
||||||
currentPos = _matchedItems.IndexOf(_selectedItem) - 1;
|
int nextIndex = Mathf.Wrap(focusedIndex - delta, 0, _matchedItems.Count - 1);
|
||||||
if (currentPos < 0)
|
var nextItem = _matchedItems[nextIndex];
|
||||||
currentPos = 0;
|
|
||||||
}
|
SelectedItem = nextItem;
|
||||||
else
|
|
||||||
{
|
|
||||||
currentPos = 0;
|
|
||||||
}
|
|
||||||
SelectedItem = _matchedItems[currentPos];
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case KeyboardKeys.Return:
|
case KeyboardKeys.Return:
|
||||||
@@ -234,6 +214,17 @@ namespace FlaxEditor.Windows.Search
|
|||||||
Hide();
|
Hide();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
case KeyboardKeys.Backspace:
|
||||||
|
{
|
||||||
|
// Alow the user to quickly focus the searchbar
|
||||||
|
if (_searchBox != null && !_searchBox.IsFocused)
|
||||||
|
{
|
||||||
|
_searchBox.Focus();
|
||||||
|
_searchBox.SelectAll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnKeyDown(key);
|
return base.OnKeyDown(key);
|
||||||
|
|||||||
@@ -20,6 +20,11 @@ namespace FlaxEditor.Windows.Search
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Image _icon;
|
protected Image _icon;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The color of the accent strip.
|
||||||
|
/// </summary>
|
||||||
|
protected Color _accentColor;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The item name.
|
/// The item name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -56,7 +61,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
var icon = new Image
|
var icon = new Image
|
||||||
{
|
{
|
||||||
Size = new Float2(logoSize),
|
Size = new Float2(logoSize),
|
||||||
Location = new Float2(5, (height - logoSize) / 2)
|
Location = new Float2(7, (height - logoSize) / 2)
|
||||||
};
|
};
|
||||||
_icon = icon;
|
_icon = icon;
|
||||||
|
|
||||||
@@ -74,6 +79,20 @@ namespace FlaxEditor.Windows.Search
|
|||||||
typeLabel.TextColor = Style.Current.ForegroundGrey;
|
typeLabel.TextColor = Style.Current.ForegroundGrey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnMouseDown(Float2 location, MouseButton button)
|
||||||
|
{
|
||||||
|
// Select and focus the item on right click to prevent the search from being cleared
|
||||||
|
if (button == MouseButton.Right)
|
||||||
|
{
|
||||||
|
_finder.SelectedItem = this;
|
||||||
|
_finder.Hand = true;
|
||||||
|
Focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return base.OnMouseUp(location, button);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool OnMouseUp(Float2 location, MouseButton button)
|
public override bool OnMouseUp(Float2 location, MouseButton button)
|
||||||
{
|
{
|
||||||
@@ -86,6 +105,15 @@ namespace FlaxEditor.Windows.Search
|
|||||||
return base.OnMouseUp(location, button);
|
return base.OnMouseUp(location, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override void Draw()
|
||||||
|
{
|
||||||
|
if (IsMouseOver)
|
||||||
|
Render2D.FillRectangle(new Rectangle(Float2.Zero, Size), Style.Current.BackgroundHighlighted);
|
||||||
|
|
||||||
|
base.Draw();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override void OnMouseEnter(Float2 location)
|
public override void OnMouseEnter(Float2 location)
|
||||||
{
|
{
|
||||||
@@ -93,12 +121,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
|
|
||||||
var root = RootWindow;
|
var root = RootWindow;
|
||||||
if (root != null)
|
if (root != null)
|
||||||
{
|
|
||||||
root.Cursor = CursorType.Hand;
|
root.Cursor = CursorType.Hand;
|
||||||
}
|
|
||||||
|
|
||||||
_finder.SelectedItem = this;
|
|
||||||
_finder.Hand = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -128,6 +151,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
{
|
{
|
||||||
_asset = item;
|
_asset = item;
|
||||||
_asset.AddReference(this);
|
_asset.AddReference(this);
|
||||||
|
_accentColor = Editor.Instance.ContentDatabase.GetProxy(item).AccentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@@ -176,9 +200,7 @@ namespace FlaxEditor.Windows.Search
|
|||||||
{
|
{
|
||||||
string importLocation = System.IO.Path.GetDirectoryName(importPath);
|
string importLocation = System.IO.Path.GetDirectoryName(importPath);
|
||||||
if (!string.IsNullOrEmpty(importLocation) && System.IO.Directory.Exists(importLocation))
|
if (!string.IsNullOrEmpty(importLocation) && System.IO.Directory.Exists(importLocation))
|
||||||
{
|
|
||||||
cm.AddButton("Show import location", () => FileSystem.ShowFileExplorer(importLocation));
|
cm.AddButton("Show import location", () => FileSystem.ShowFileExplorer(importLocation));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cm.AddSeparator();
|
cm.AddSeparator();
|
||||||
@@ -212,6 +234,10 @@ namespace FlaxEditor.Windows.Search
|
|||||||
// Draw icon
|
// Draw icon
|
||||||
var iconRect = _icon.Bounds;
|
var iconRect = _icon.Bounds;
|
||||||
_asset.DrawThumbnail(ref iconRect);
|
_asset.DrawThumbnail(ref iconRect);
|
||||||
|
|
||||||
|
// Draw color strip
|
||||||
|
var rect = iconRect with { Width = 2, Height = Height, Location = new Float2(2, 0) };
|
||||||
|
Render2D.FillRectangle(rect, _accentColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -242,9 +242,8 @@ void Asset::AddReference(IAssetReference* ref, bool week)
|
|||||||
if (ref)
|
if (ref)
|
||||||
{
|
{
|
||||||
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
|
//PROFILE_MEM(EngineDelegate); // Include references tracking memory within Delegate memory
|
||||||
Locker.Lock();
|
ScopeLock lock(_referencesLocker);
|
||||||
_references.Add(ref);
|
_references.Add(ref);
|
||||||
Locker.Unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,9 +256,8 @@ void Asset::RemoveReference(IAssetReference* ref, bool week)
|
|||||||
{
|
{
|
||||||
if (ref)
|
if (ref)
|
||||||
{
|
{
|
||||||
Locker.Lock();
|
ScopeLock lock(_referencesLocker);
|
||||||
_references.Remove(ref);
|
_references.Remove(ref);
|
||||||
Locker.Unlock();
|
|
||||||
}
|
}
|
||||||
if (!week)
|
if (!week)
|
||||||
Platform::InterlockedDecrement(&_refCount);
|
Platform::InterlockedDecrement(&_refCount);
|
||||||
@@ -681,6 +679,7 @@ void Asset::onLoaded_MainThread()
|
|||||||
ASSERT(IsInMainThread());
|
ASSERT(IsInMainThread());
|
||||||
|
|
||||||
// Send event
|
// Send event
|
||||||
|
ScopeLock lock(_referencesLocker);
|
||||||
for (const auto& e : _references)
|
for (const auto& e : _references)
|
||||||
e.Item->OnAssetLoaded(this, this);
|
e.Item->OnAssetLoaded(this, this);
|
||||||
OnLoaded(this);
|
OnLoaded(this);
|
||||||
@@ -696,6 +695,7 @@ void Asset::onUnload_MainThread()
|
|||||||
CancelStreaming();
|
CancelStreaming();
|
||||||
|
|
||||||
// Send event
|
// Send event
|
||||||
|
ScopeLock lock(_referencesLocker);
|
||||||
for (const auto& e : _references)
|
for (const auto& e : _references)
|
||||||
e.Item->OnAssetUnloaded(this, this);
|
e.Item->OnAssetUnloaded(this, this);
|
||||||
OnUnloaded(this);
|
OnUnloaded(this);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Engine/Core/Types/String.h"
|
#include "Engine/Core/Types/String.h"
|
||||||
#include "Engine/Platform/CriticalSection.h"
|
#include "Engine/Platform/CriticalSection.h"
|
||||||
#include "Engine/Scripting/ScriptingObject.h"
|
#include "Engine/Scripting/ScriptingObject.h"
|
||||||
|
#include "Engine/Threading/ConcurrentSystemLocker.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Types.h"
|
#include "Types.h"
|
||||||
|
|
||||||
@@ -63,6 +64,7 @@ protected:
|
|||||||
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
|
int8 _isVirtual : 1; // Indicates that asset is pure virtual (generated or temporary, has no storage so won't be saved)
|
||||||
|
|
||||||
HashSet<IAssetReference*> _references;
|
HashSet<IAssetReference*> _references;
|
||||||
|
CriticalSection _referencesLocker; // TODO: convert into a single interlocked exchange for the current thread owning lock
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -414,16 +414,18 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
|||||||
// Prepare
|
// Prepare
|
||||||
auto& info = _shaderHeader.Material.Info;
|
auto& info = _shaderHeader.Material.Info;
|
||||||
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
|
const bool isSurfaceOrTerrainOrDeformable = info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Terrain || info.Domain == MaterialDomain::Deformable;
|
||||||
|
const bool isOpaque = info.BlendMode == MaterialBlendMode::Opaque;
|
||||||
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
|
const bool useCustomData = info.ShadingModel == MaterialShadingModel::Subsurface || info.ShadingModel == MaterialShadingModel::Foliage;
|
||||||
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && info.BlendMode != MaterialBlendMode::Opaque) || info.Domain == MaterialDomain::Particle;
|
const bool useForward = ((info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable) && !isOpaque) || info.Domain == MaterialDomain::Particle;
|
||||||
const bool useTess =
|
const bool useTess =
|
||||||
info.TessellationMode != TessellationMethod::None &&
|
info.TessellationMode != TessellationMethod::None &&
|
||||||
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
|
RenderTools::CanSupportTessellation(options.Profile) && isSurfaceOrTerrainOrDeformable;
|
||||||
const bool useDistortion =
|
const bool useDistortion =
|
||||||
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
|
(info.Domain == MaterialDomain::Surface || info.Domain == MaterialDomain::Deformable || info.Domain == MaterialDomain::Particle) &&
|
||||||
info.BlendMode != MaterialBlendMode::Opaque &&
|
!isOpaque &&
|
||||||
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
|
EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseRefraction) &&
|
||||||
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
|
(info.FeaturesFlags & MaterialFeaturesFlags::DisableDistortion) == MaterialFeaturesFlags::None;
|
||||||
|
const MaterialShadingModel shadingModel = info.ShadingModel == MaterialShadingModel::CustomLit ? MaterialShadingModel::Unlit : info.ShadingModel;
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
static const char* Numbers[] =
|
static const char* Numbers[] =
|
||||||
@@ -435,7 +437,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
|||||||
// Setup shader macros
|
// Setup shader macros
|
||||||
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
|
options.Macros.Add({ "MATERIAL_DOMAIN", Numbers[(int32)info.Domain] });
|
||||||
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
|
options.Macros.Add({ "MATERIAL_BLEND", Numbers[(int32)info.BlendMode] });
|
||||||
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)info.ShadingModel] });
|
options.Macros.Add({ "MATERIAL_SHADING_MODEL", Numbers[(int32)shadingModel] });
|
||||||
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
|
options.Macros.Add({ "MATERIAL_MASKED", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseMask) ? 1 : 0] });
|
||||||
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
|
options.Macros.Add({ "DECAL_BLEND_MODE", Numbers[(int32)info.DecalBlendingMode] });
|
||||||
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
|
options.Macros.Add({ "USE_EMISSIVE", Numbers[EnumHasAnyFlags(info.UsageFlags, MaterialUsageFlags::UseEmissive) ? 1 : 0] });
|
||||||
@@ -492,7 +494,7 @@ void Material::InitCompilationOptions(ShaderCompilationOptions& options)
|
|||||||
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
|
options.Macros.Add({ "IS_PARTICLE", Numbers[info.Domain == MaterialDomain::Particle ? 1 : 0] });
|
||||||
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
|
options.Macros.Add({ "IS_DEFORMABLE", Numbers[info.Domain == MaterialDomain::Deformable ? 1 : 0] });
|
||||||
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
|
options.Macros.Add({ "USE_FORWARD", Numbers[useForward ? 1 : 0] });
|
||||||
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && info.BlendMode == MaterialBlendMode::Opaque ? 1 : 0] });
|
options.Macros.Add({ "USE_DEFERRED", Numbers[isSurfaceOrTerrainOrDeformable && isOpaque ? 1 : 0] });
|
||||||
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
|
options.Macros.Add({ "USE_DISTORTION", Numbers[useDistortion ? 1 : 0] });
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1003,7 +1003,7 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
|
|||||||
FileSystem::DeleteFile(tmpPath);
|
FileSystem::DeleteFile(tmpPath);
|
||||||
|
|
||||||
// Reload storage
|
// Reload storage
|
||||||
if (auto storage = ContentStorageManager::GetStorage(dstPath))
|
if (auto storage = ContentStorageManager::GetStorage(dstPath, false))
|
||||||
{
|
{
|
||||||
storage->Reload();
|
storage->Reload();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
|
#include "Engine/Tools/AudioTool/OggVorbisDecoder.h"
|
||||||
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
|
#include "Engine/Tools/AudioTool/OggVorbisEncoder.h"
|
||||||
#include "Engine/Serialization/JsonWriters.h"
|
#include "Engine/Serialization/JsonWriters.h"
|
||||||
|
#include "Engine/Platform/MessageBox.h"
|
||||||
|
|
||||||
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
|
bool ImportAudio::TryGetImportOptions(const StringView& path, Options& options)
|
||||||
{
|
{
|
||||||
@@ -118,6 +119,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define HANDLE_VORBIS(chunkIndex, dataPtr, dataSize) \
|
#define HANDLE_VORBIS(chunkIndex, dataPtr, dataSize) \
|
||||||
|
MessageBox::Show(TEXT("Vorbis format is not supported."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning);
|
||||||
LOG(Warning, "Vorbis format is not supported."); \
|
LOG(Warning, "Vorbis format is not supported."); \
|
||||||
return CreateAssetResult::Error;
|
return CreateAssetResult::Error;
|
||||||
#endif
|
#endif
|
||||||
@@ -140,6 +142,7 @@ CreateAssetResult ImportAudio::Import(CreateAssetContext& context, AudioDecoder&
|
|||||||
break; \
|
break; \
|
||||||
default: \
|
default: \
|
||||||
{ \
|
{ \
|
||||||
|
MessageBox::Show(TEXT("Unknown audio format."), TEXT("Import warning"), MessageBoxButtons::OK, MessageBoxIcon::Warning); \
|
||||||
LOG(Warning, "Unknown audio format."); \
|
LOG(Warning, "Unknown audio format."); \
|
||||||
return CreateAssetResult::Error; \
|
return CreateAssetResult::Error; \
|
||||||
} \
|
} \
|
||||||
|
|||||||
@@ -658,13 +658,7 @@ public:
|
|||||||
--_count;
|
--_count;
|
||||||
T* data = _allocation.Get();
|
T* data = _allocation.Get();
|
||||||
if (index < _count)
|
if (index < _count)
|
||||||
{
|
Memory::MoveAssignItems(data + index, data + (index + 1), _count - index);
|
||||||
T* dst = data + index;
|
|
||||||
T* src = data + (index + 1);
|
|
||||||
const int32 count = _count - index;
|
|
||||||
for (int32 i = 0; i < count; ++i)
|
|
||||||
dst[i] = MoveTemp(src[i]);
|
|
||||||
}
|
|
||||||
Memory::DestructItems(data + _count, 1);
|
Memory::DestructItems(data + _count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -709,7 +703,7 @@ public:
|
|||||||
--_count;
|
--_count;
|
||||||
T* data = _allocation.Get();
|
T* data = _allocation.Get();
|
||||||
if (_count)
|
if (_count)
|
||||||
data[index] = data[_count];
|
data[index] = MoveTemp(data[_count]);
|
||||||
Memory::DestructItems(data + _count, 1);
|
Memory::DestructItems(data + _count, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,8 +209,8 @@ public:
|
|||||||
bool Get(const int32 index) const
|
bool Get(const int32 index) const
|
||||||
{
|
{
|
||||||
ASSERT(index >= 0 && index < _count);
|
ASSERT(index >= 0 && index < _count);
|
||||||
const ItemType offset = index / sizeof(ItemType);
|
const ItemType offset = index / 64;
|
||||||
const ItemType bitMask = (ItemType)(int32)(1 << (index & ((int32)sizeof(ItemType) - 1)));
|
const ItemType bitMask = 1ull << (index & 63ull);
|
||||||
const ItemType item = ((ItemType*)_allocation.Get())[offset];
|
const ItemType item = ((ItemType*)_allocation.Get())[offset];
|
||||||
return (item & bitMask) != 0;
|
return (item & bitMask) != 0;
|
||||||
}
|
}
|
||||||
@@ -223,13 +223,13 @@ public:
|
|||||||
void Set(const int32 index, const bool value)
|
void Set(const int32 index, const bool value)
|
||||||
{
|
{
|
||||||
ASSERT(index >= 0 && index < _count);
|
ASSERT(index >= 0 && index < _count);
|
||||||
const ItemType offset = index / sizeof(ItemType);
|
const ItemType offset = index / 64;
|
||||||
const ItemType bitMask = (ItemType)(int32)(1 << (index & ((int32)sizeof(ItemType) - 1)));
|
const ItemType bitMask = 1ull << (index & 63ull);
|
||||||
ItemType& item = ((ItemType*)_allocation.Get())[offset];
|
ItemType& item = ((ItemType*)_allocation.Get())[offset];
|
||||||
if (value)
|
if (value)
|
||||||
item |= bitMask;
|
item |= bitMask; // Set the bit
|
||||||
else
|
else
|
||||||
item &= ~bitMask; // Clear the bit
|
item &= ~bitMask; // Unset the bit
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -163,6 +163,15 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an empty <see cref="Dictionary"/> without reserving any space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The custom allocation tag.</param>
|
||||||
|
Dictionary(typename Base::AllocationTag tag)
|
||||||
|
: Base(tag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes <see cref="Dictionary"/> by reserving space.
|
/// Initializes <see cref="Dictionary"/> by reserving space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -140,6 +140,15 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes an empty <see cref="HashSet"/> without reserving any space.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="tag">The custom allocation tag.</param>
|
||||||
|
HashSet(typename Base::AllocationTag tag)
|
||||||
|
: Base(tag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes <see cref="HashSet"/> by reserving space.
|
/// Initializes <see cref="HashSet"/> by reserving space.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ class HashSetBase
|
|||||||
public:
|
public:
|
||||||
// Type of allocation data used to store hash set buckets.
|
// Type of allocation data used to store hash set buckets.
|
||||||
using AllocationData = typename AllocationType::template Data<BucketType>;
|
using AllocationData = typename AllocationType::template Data<BucketType>;
|
||||||
|
using AllocationTag = typename AllocationType::Tag;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int32 _elementsCount = 0;
|
int32 _elementsCount = 0;
|
||||||
@@ -70,6 +71,11 @@ protected:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HashSetBase(AllocationTag tag)
|
||||||
|
: _allocation(tag)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void MoveToEmpty(HashSetBase&& other)
|
void MoveToEmpty(HashSetBase&& other)
|
||||||
{
|
{
|
||||||
_elementsCount = other._elementsCount;
|
_elementsCount = other._elementsCount;
|
||||||
|
|||||||
@@ -26,6 +26,8 @@
|
|||||||
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
|
_Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS \
|
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS \
|
||||||
_Pragma("clang diagnostic pop")
|
_Pragma("clang diagnostic pop")
|
||||||
|
#define PRAGMA_DISABLE_OPTIMIZATION
|
||||||
|
#define PRAGMA_ENABLE_OPTIMIZATION
|
||||||
|
|
||||||
#pragma clang diagnostic ignored "-Wswitch"
|
#pragma clang diagnostic ignored "-Wswitch"
|
||||||
#pragma clang diagnostic ignored "-Wmacro-redefined"
|
#pragma clang diagnostic ignored "-Wmacro-redefined"
|
||||||
@@ -54,6 +56,8 @@
|
|||||||
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
#define OFFSET_OF(X, Y) __builtin_offsetof(X, Y)
|
||||||
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
#define PRAGMA_DISABLE_DEPRECATION_WARNINGS
|
||||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS
|
||||||
|
#define PRAGMA_DISABLE_OPTIMIZATION
|
||||||
|
#define PRAGMA_ENABLE_OPTIMIZATION
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
|
|
||||||
@@ -86,6 +90,8 @@
|
|||||||
__pragma(warning(disable: 4996))
|
__pragma(warning(disable: 4996))
|
||||||
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS \
|
#define PRAGMA_ENABLE_DEPRECATION_WARNINGS \
|
||||||
__pragma (warning(pop))
|
__pragma (warning(pop))
|
||||||
|
#define PRAGMA_DISABLE_OPTIMIZATION __pragma(optimize("", off))
|
||||||
|
#define PRAGMA_ENABLE_OPTIMIZATION __pragma(optimize("", on))
|
||||||
|
|
||||||
#pragma warning(disable: 4251)
|
#pragma warning(disable: 4251)
|
||||||
|
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ public:
|
|||||||
bool EnableGlobalSDF = false;
|
bool EnableGlobalSDF = false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draw distance of the Global SDF. Actual value can be large when using DDGI.
|
/// Draw distance of the Global SDF. Actual value can be larger when using DDGI.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
API_FIELD(Attributes="EditorOrder(2001), EditorDisplay(\"Global SDF\"), Limit(1000), ValueCategory(Utils.ValueCategory.Distance)")
|
API_FIELD(Attributes="EditorOrder(2001), EditorDisplay(\"Global SDF\"), Limit(1000), ValueCategory(Utils.ValueCategory.Distance)")
|
||||||
float GlobalSDFDistance = 15000.0f;
|
float GlobalSDFDistance = 15000.0f;
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ void ArenaAllocator::Free()
|
|||||||
#if COMPILE_WITH_PROFILER
|
#if COMPILE_WITH_PROFILER
|
||||||
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, -(int64)page->Size, -1);
|
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, -(int64)page->Size, -1);
|
||||||
#endif
|
#endif
|
||||||
Allocator::Free(page->Memory);
|
|
||||||
Page* next = page->Next;
|
Page* next = page->Next;
|
||||||
Allocator::Free(page);
|
Allocator::Free(page);
|
||||||
page = next;
|
page = next;
|
||||||
@@ -33,22 +32,94 @@ void* ArenaAllocator::Allocate(uint64 size, uint64 alignment)
|
|||||||
// Create a new page if need to
|
// Create a new page if need to
|
||||||
if (!page)
|
if (!page)
|
||||||
{
|
{
|
||||||
uint64 pageSize = Math::Max<uint64>(_pageSize, size);
|
uint64 pageSize = Math::Max<uint64>(_pageSize, size + alignment + sizeof(Page));
|
||||||
#if COMPILE_WITH_PROFILER
|
#if COMPILE_WITH_PROFILER
|
||||||
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, (int64)pageSize, 1);
|
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, (int64)pageSize, 1);
|
||||||
#endif
|
#endif
|
||||||
page = (Page*)Allocator::Allocate(sizeof(Page));
|
page = (Page*)Allocator::Allocate(pageSize);
|
||||||
page->Memory = Allocator::Allocate(pageSize);
|
|
||||||
page->Next = _first;
|
page->Next = _first;
|
||||||
page->Offset = 0;
|
page->Offset = sizeof(Page);
|
||||||
page->Size = (uint32)pageSize;
|
page->Size = (uint32)pageSize;
|
||||||
_first = page;
|
_first = page;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allocate within a page
|
// Allocate within a page
|
||||||
page->Offset = Math::AlignUp(page->Offset, (uint32)alignment);
|
page->Offset = Math::AlignUp(page->Offset, (uint32)alignment);
|
||||||
void* mem = (byte*)page->Memory + page->Offset;
|
void* mem = (byte*)page + page->Offset;
|
||||||
page->Offset += (uint32)size;
|
page->Offset += (uint32)size;
|
||||||
|
|
||||||
return mem;
|
return mem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConcurrentArenaAllocator::Free()
|
||||||
|
{
|
||||||
|
_locker.Lock();
|
||||||
|
|
||||||
|
// Free all pages
|
||||||
|
Page* page = (Page*)_first;
|
||||||
|
while (page)
|
||||||
|
{
|
||||||
|
#if COMPILE_WITH_PROFILER
|
||||||
|
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, -(int64)page->Size, -1);
|
||||||
|
#endif
|
||||||
|
Page* next = page->Next;
|
||||||
|
if (_free1)
|
||||||
|
_free1(page);
|
||||||
|
else
|
||||||
|
_free2(page, page->Size);
|
||||||
|
page = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unlink
|
||||||
|
_first = 0;
|
||||||
|
_totalBytes = 0;
|
||||||
|
|
||||||
|
_locker.Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void* ConcurrentArenaAllocator::Allocate(uint64 size, uint64 alignment)
|
||||||
|
{
|
||||||
|
RETRY:
|
||||||
|
|
||||||
|
// Check if the current page has some space left
|
||||||
|
Page* page = (Page*)Platform::AtomicRead(&_first);
|
||||||
|
if (page)
|
||||||
|
{
|
||||||
|
int64 offset = Platform::AtomicRead(&page->Offset);
|
||||||
|
int64 offsetAligned = Math::AlignUp(offset, (int64)alignment);
|
||||||
|
int64 end = offsetAligned + size;
|
||||||
|
if (end <= page->Size)
|
||||||
|
{
|
||||||
|
// Try to allocate within a page
|
||||||
|
if (Platform::InterlockedCompareExchange(&page->Offset, end, offset) != offset)
|
||||||
|
{
|
||||||
|
// Someone else changed allocated so retry (new offset might mismatch alignment)
|
||||||
|
goto RETRY;
|
||||||
|
}
|
||||||
|
Platform::InterlockedAdd(&_totalBytes, (int64)size);
|
||||||
|
return (byte*)page + offsetAligned;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Page allocation is thread-synced
|
||||||
|
_locker.Lock();
|
||||||
|
|
||||||
|
// Check if page was unchanged by any other thread
|
||||||
|
if ((Page*)Platform::AtomicRead(&_first) == page)
|
||||||
|
{
|
||||||
|
uint64 pageSize = Math::Max<uint64>(_pageSize, size + alignment + sizeof(Page));
|
||||||
|
#if COMPILE_WITH_PROFILER
|
||||||
|
ProfilerMemory::OnGroupUpdate(ProfilerMemory::Groups::MallocArena, (int64)pageSize, 1);
|
||||||
|
#endif
|
||||||
|
page = (Page*)(_allocate1 ? _allocate1(pageSize, 16) : _allocate2(pageSize));
|
||||||
|
page->Next = (Page*)_first;
|
||||||
|
page->Offset = sizeof(Page);
|
||||||
|
page->Size = (int64)pageSize;
|
||||||
|
Platform::AtomicStore(&_first, (intptr)page);
|
||||||
|
}
|
||||||
|
|
||||||
|
_locker.Unlock();
|
||||||
|
|
||||||
|
// Use a single cde for allocation
|
||||||
|
goto RETRY;
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Allocation.h"
|
#include "Allocation.h"
|
||||||
|
#include "Engine/Platform/CriticalSection.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allocator that uses pages for stack-based allocs without freeing memory during it's lifetime.
|
/// Allocator that uses pages for stack-based allocs without freeing memory during it's lifetime.
|
||||||
@@ -12,7 +13,6 @@ class ArenaAllocator
|
|||||||
private:
|
private:
|
||||||
struct Page
|
struct Page
|
||||||
{
|
{
|
||||||
void* Memory;
|
|
||||||
Page* Next;
|
Page* Next;
|
||||||
uint32 Offset, Size;
|
uint32 Offset, Size;
|
||||||
};
|
};
|
||||||
@@ -66,21 +66,91 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocator that uses pages for stack-based allocs without freeing memory during it's lifetime. Thread-safe to allocate memory from multiple threads at once.
|
||||||
|
/// </summary>
|
||||||
|
class ConcurrentArenaAllocator
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
struct Page
|
||||||
|
{
|
||||||
|
Page* Next;
|
||||||
|
volatile int64 Offset;
|
||||||
|
int64 Size;
|
||||||
|
};
|
||||||
|
|
||||||
|
int32 _pageSize;
|
||||||
|
volatile int64 _first = 0;
|
||||||
|
volatile int64 _totalBytes = 0;
|
||||||
|
void*(*_allocate1)(uint64 size, uint64 alignment) = nullptr;
|
||||||
|
void(*_free1)(void* ptr) = nullptr;
|
||||||
|
void*(*_allocate2)(uint64 size) = nullptr;
|
||||||
|
void(*_free2)(void* ptr, uint64 size) = nullptr;
|
||||||
|
CriticalSection _locker;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ConcurrentArenaAllocator(int32 pageSizeBytes, void* (*customAllocate)(uint64 size, uint64 alignment), void(*customFree)(void* ptr))
|
||||||
|
: _pageSize(pageSizeBytes)
|
||||||
|
, _allocate1(customAllocate)
|
||||||
|
, _free1(customFree)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentArenaAllocator(int32 pageSizeBytes, void* (*customAllocate)(uint64 size), void(*customFree)(void* ptr, uint64 size))
|
||||||
|
: _pageSize(pageSizeBytes)
|
||||||
|
, _allocate2(customAllocate)
|
||||||
|
, _free2(customFree)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcurrentArenaAllocator(int32 pageSizeBytes = 1024 * 1024) // 1 MB by default
|
||||||
|
: ConcurrentArenaAllocator(pageSizeBytes, Allocator::Allocate, Allocator::Free)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~ConcurrentArenaAllocator()
|
||||||
|
{
|
||||||
|
Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets the total amount of bytes allocated in arena (excluding alignment).
|
||||||
|
int64 GetTotalBytes() const
|
||||||
|
{
|
||||||
|
return Platform::AtomicRead(&_totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocates a chunk of unitialized memory.
|
||||||
|
void* Allocate(uint64 size, uint64 alignment = 1);
|
||||||
|
|
||||||
|
// Frees all memory allocations within allocator.
|
||||||
|
void Free();
|
||||||
|
|
||||||
|
// Creates a new object within the arena allocator.
|
||||||
|
template<class T, class... Args>
|
||||||
|
inline T* New(Args&&...args)
|
||||||
|
{
|
||||||
|
T* ptr = (T*)Allocate(sizeof(T));
|
||||||
|
new(ptr) T(Forward<Args>(args)...);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The memory allocation policy that uses a part of shared page allocator. Allocations are performed in stack-manner, and free is no-op.
|
/// The memory allocation policy that uses a part of shared page allocator. Allocations are performed in stack-manner, and free is no-op.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ArenaAllocation
|
template<typename ArenaType>
|
||||||
|
class ArenaAllocationBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum { HasSwap = true };
|
enum { HasSwap = true };
|
||||||
typedef ArenaAllocator* Tag;
|
typedef ArenaType* Tag;
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class Data
|
class Data
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
T* _data = nullptr;
|
T* _data = nullptr;
|
||||||
ArenaAllocator* _arena = nullptr;
|
ArenaType* _arena = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FORCE_INLINE Data()
|
FORCE_INLINE Data()
|
||||||
@@ -138,7 +208,17 @@ public:
|
|||||||
FORCE_INLINE void Swap(Data& other)
|
FORCE_INLINE void Swap(Data& other)
|
||||||
{
|
{
|
||||||
::Swap(_data, other._data);
|
::Swap(_data, other._data);
|
||||||
::Swap(_arena, other._arena);
|
_arena = other._arena; // TODO: find a better way to move allocation with AllocationUtils::MoveToEmpty to preserve/maintain allocation tag ownership
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The memory allocation policy that uses a part of shared page allocator. Allocations are performed in stack-manner, and free is no-op.
|
||||||
|
/// </summary>
|
||||||
|
typedef ArenaAllocationBase<ArenaAllocator> ArenaAllocation;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The memory allocation policy that uses a part of shared page allocator. Allocations are performed in stack-manner, and free is no-op.
|
||||||
|
/// </summary>
|
||||||
|
typedef ArenaAllocationBase<ConcurrentArenaAllocator> ConcurrentArenaAllocation;
|
||||||
|
|||||||
@@ -104,12 +104,6 @@ public:
|
|||||||
{
|
{
|
||||||
new(dst) T();
|
new(dst) T();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs the item in the memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The optimized version is noop.</remarks>
|
|
||||||
/// <param name="dst">The address of the memory location to construct.</param>
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCE_INLINE static typename TEnableIf<TIsTriviallyConstructible<T>::Value>::Type ConstructItem(T* dst)
|
FORCE_INLINE static typename TEnableIf<TIsTriviallyConstructible<T>::Value>::Type ConstructItem(T* dst)
|
||||||
{
|
{
|
||||||
@@ -132,13 +126,6 @@ public:
|
|||||||
++(T*&)dst;
|
++(T*&)dst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs the range of items in the memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The optimized version is noop.</remarks>
|
|
||||||
/// <param name="dst">The address of the first memory location to construct.</param>
|
|
||||||
/// <param name="count">The number of element to construct. Can be equal 0.</param>
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCE_INLINE static typename TEnableIf<TIsTriviallyConstructible<T>::Value>::Type ConstructItems(T* dst, int32 count)
|
FORCE_INLINE static typename TEnableIf<TIsTriviallyConstructible<T>::Value>::Type ConstructItems(T* dst, int32 count)
|
||||||
{
|
{
|
||||||
@@ -163,14 +150,6 @@ public:
|
|||||||
++src;
|
++src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs the range of items in the memory from the set of arguments.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The optimized version uses low-level memory copy.</remarks>
|
|
||||||
/// <param name="dst">The address of the first memory location to construct.</param>
|
|
||||||
/// <param name="src">The address of the first memory location to pass to the constructor.</param>
|
|
||||||
/// <param name="count">The number of element to construct. Can be equal 0.</param>
|
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
FORCE_INLINE static typename TEnableIf<TIsBitwiseConstructible<T, U>::Value>::Type ConstructItems(T* dst, const U* src, int32 count)
|
FORCE_INLINE static typename TEnableIf<TIsBitwiseConstructible<T, U>::Value>::Type ConstructItems(T* dst, const U* src, int32 count)
|
||||||
{
|
{
|
||||||
@@ -187,12 +166,6 @@ public:
|
|||||||
{
|
{
|
||||||
dst->~T();
|
dst->~T();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Destructs the item in the memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The optimized version is noop.</remarks>
|
|
||||||
/// <param name="dst">The address of the memory location to destruct.</param>
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCE_INLINE static typename TEnableIf<TIsTriviallyDestructible<T>::Value>::Type DestructItem(T* dst)
|
FORCE_INLINE static typename TEnableIf<TIsTriviallyDestructible<T>::Value>::Type DestructItem(T* dst)
|
||||||
{
|
{
|
||||||
@@ -213,13 +186,6 @@ public:
|
|||||||
++dst;
|
++dst;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Destructs the range of items in the memory.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The optimized version is noop.</remarks>
|
|
||||||
/// <param name="dst">The address of the first memory location to destruct.</param>
|
|
||||||
/// <param name="count">The number of element to destruct. Can be equal 0.</param>
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
FORCE_INLINE static typename TEnableIf<TIsTriviallyDestructible<T>::Value>::Type DestructItems(T* dst, int32 count)
|
FORCE_INLINE static typename TEnableIf<TIsTriviallyDestructible<T>::Value>::Type DestructItems(T* dst, int32 count)
|
||||||
{
|
{
|
||||||
@@ -242,15 +208,7 @@ public:
|
|||||||
++src;
|
++src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
template<typename T, typename U>
|
||||||
/// <summary>
|
|
||||||
/// Copies the range of items using the assignment operator.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>The optimized version is low-level memory copy.</remarks>
|
|
||||||
/// <param name="dst">The address of the first memory location to start assigning to.</param>
|
|
||||||
/// <param name="src">The address of the first memory location to assign from.</param>
|
|
||||||
/// <param name="count">The number of element to assign. Can be equal 0.</param>
|
|
||||||
template<typename T>
|
|
||||||
FORCE_INLINE static typename TEnableIf<TIsTriviallyCopyAssignable<T>::Value>::Type CopyItems(T* dst, const T* src, int32 count)
|
FORCE_INLINE static typename TEnableIf<TIsTriviallyCopyAssignable<T>::Value>::Type CopyItems(T* dst, const T* src, int32 count)
|
||||||
{
|
{
|
||||||
Platform::MemoryCopy(dst, src, count * sizeof(T));
|
Platform::MemoryCopy(dst, src, count * sizeof(T));
|
||||||
@@ -273,16 +231,31 @@ public:
|
|||||||
++src;
|
++src;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
template<typename T, typename U>
|
||||||
|
FORCE_INLINE static typename TEnableIf<TIsBitwiseConstructible<T, U>::Value>::Type MoveItems(T* dst, U* src, int32 count)
|
||||||
|
{
|
||||||
|
Platform::MemoryCopy(dst, src, count * sizeof(U));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moves the range of items in the memory from the set of arguments.
|
/// Moves the range of items using the assignment operator.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>The optimized version uses low-level memory copy.</remarks>
|
/// <remarks>The optimized version uses low-level memory copy.</remarks>
|
||||||
/// <param name="dst">The address of the first memory location to move.</param>
|
/// <param name="dst">The address of the first memory location to move.</param>
|
||||||
/// <param name="src">The address of the first memory location to pass to the move constructor.</param>
|
/// <param name="src">The address of the first memory location to pass to the move constructor.</param>
|
||||||
/// <param name="count">The number of element to move. Can be equal 0.</param>
|
/// <param name="count">The number of element to move. Can be equal 0.</param>
|
||||||
template<typename T, typename U>
|
template<typename T, typename U>
|
||||||
FORCE_INLINE static typename TEnableIf<TIsBitwiseConstructible<T, U>::Value>::Type MoveItems(T* dst, U* src, int32 count)
|
FORCE_INLINE static typename TEnableIf<!TIsBitwiseConstructible<T, U>::Value>::Type MoveAssignItems(T* dst, U* src, int32 count)
|
||||||
|
{
|
||||||
|
while (count--)
|
||||||
|
{
|
||||||
|
*dst = MoveTemp(*src);
|
||||||
|
++(T*&)dst;
|
||||||
|
++src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template<typename T, typename U>
|
||||||
|
FORCE_INLINE static typename TEnableIf<TIsBitwiseConstructible<T, U>::Value>::Type MoveAssignItems(T* dst, U* src, int32 count)
|
||||||
{
|
{
|
||||||
Platform::MemoryCopy(dst, src, count * sizeof(U));
|
Platform::MemoryCopy(dst, src, count * sizeof(U));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ void ObjectsRemoval::Dispose()
|
|||||||
|
|
||||||
Object::~Object()
|
Object::~Object()
|
||||||
{
|
{
|
||||||
#if BUILD_DEBUG
|
#if BUILD_DEBUG && 0
|
||||||
// Prevent removing object that is still reverenced by the removal service
|
// Prevent removing object that is still reverenced by the removal service
|
||||||
ASSERT(!ObjectsRemovalService::IsInPool(this));
|
//ASSERT(!ObjectsRemovalService::IsInPool(this));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -357,7 +357,8 @@ struct DebugDrawContext
|
|||||||
DebugDrawData DebugDrawDefault;
|
DebugDrawData DebugDrawDefault;
|
||||||
DebugDrawData DebugDrawDepthTest;
|
DebugDrawData DebugDrawDepthTest;
|
||||||
Float3 LastViewPos = Float3::Zero;
|
Float3 LastViewPos = Float3::Zero;
|
||||||
Matrix LastViewProj = Matrix::Identity;
|
Matrix LastViewProjection = Matrix::Identity;
|
||||||
|
BoundingFrustum LastViewFrustum;
|
||||||
|
|
||||||
inline int32 Count() const
|
inline int32 Count() const
|
||||||
{
|
{
|
||||||
@@ -779,9 +780,23 @@ Vector3 DebugDraw::GetViewPos()
|
|||||||
return Context->LastViewPos;
|
return Context->LastViewPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BoundingFrustum DebugDraw::GetViewFrustum()
|
||||||
|
{
|
||||||
|
return Context->LastViewFrustum;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugDraw::SetView(const RenderView& view)
|
||||||
|
{
|
||||||
|
Context->LastViewPos = view.Position;
|
||||||
|
Context->LastViewProjection = view.Projection;
|
||||||
|
Context->LastViewFrustum = view.Frustum;
|
||||||
|
}
|
||||||
|
|
||||||
void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTextureView* depthBuffer, bool enableDepthTest)
|
void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTextureView* depthBuffer, bool enableDepthTest)
|
||||||
{
|
{
|
||||||
PROFILE_GPU_CPU("Debug Draw");
|
PROFILE_GPU_CPU("Debug Draw");
|
||||||
|
const RenderView& view = renderContext.View;
|
||||||
|
SetView(view);
|
||||||
|
|
||||||
// Ensure to have shader loaded and any lines to render
|
// Ensure to have shader loaded and any lines to render
|
||||||
const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count();
|
const int32 debugDrawDepthTestCount = Context->DebugDrawDepthTest.Count();
|
||||||
@@ -791,7 +806,6 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
if (renderContext.Buffers == nullptr || !DebugDrawVB)
|
if (renderContext.Buffers == nullptr || !DebugDrawVB)
|
||||||
return;
|
return;
|
||||||
auto context = GPUDevice::Instance->GetMainContext();
|
auto context = GPUDevice::Instance->GetMainContext();
|
||||||
const RenderView& view = renderContext.View;
|
|
||||||
if (Context->Origin != view.Origin)
|
if (Context->Origin != view.Origin)
|
||||||
{
|
{
|
||||||
// Teleport existing debug shapes to maintain their location
|
// Teleport existing debug shapes to maintain their location
|
||||||
@@ -800,8 +814,6 @@ void DebugDraw::Draw(RenderContext& renderContext, GPUTextureView* target, GPUTe
|
|||||||
Context->DebugDrawDepthTest.Teleport(delta);
|
Context->DebugDrawDepthTest.Teleport(delta);
|
||||||
Context->Origin = view.Origin;
|
Context->Origin = view.Origin;
|
||||||
}
|
}
|
||||||
Context->LastViewPos = view.Position;
|
|
||||||
Context->LastViewProj = view.Projection;
|
|
||||||
TaaJitterRemoveContext taaJitterRemove(view);
|
TaaJitterRemoveContext taaJitterRemove(view);
|
||||||
|
|
||||||
// Fallback to task buffers
|
// Fallback to task buffers
|
||||||
@@ -1383,7 +1395,7 @@ void DebugDraw::DrawWireSphere(const BoundingSphere& sphere, const Color& color,
|
|||||||
int32 index;
|
int32 index;
|
||||||
const Float3 centerF = sphere.Center - Context->Origin;
|
const Float3 centerF = sphere.Center - Context->Origin;
|
||||||
const float radiusF = (float)sphere.Radius;
|
const float radiusF = (float)sphere.Radius;
|
||||||
const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(centerF, radiusF, Context->LastViewPos, Context->LastViewProj);
|
const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(centerF, radiusF, Context->LastViewPos, Context->LastViewProjection);
|
||||||
if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * 0.25f)
|
if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD0_SCREEN_SIZE * 0.25f)
|
||||||
index = 0;
|
index = 0;
|
||||||
else if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * 0.25f)
|
else if (screenRadiusSquared > DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * DEBUG_DRAW_SPHERE_LOD1_SCREEN_SIZE * 0.25f)
|
||||||
|
|||||||
@@ -76,6 +76,11 @@ API_CLASS(Static) class FLAXENGINE_API DebugDraw
|
|||||||
|
|
||||||
// Gets the last view position when rendering the current context. Can be used for custom culling or LODing when drawing more complex shapes.
|
// Gets the last view position when rendering the current context. Can be used for custom culling or LODing when drawing more complex shapes.
|
||||||
static Vector3 GetViewPos();
|
static Vector3 GetViewPos();
|
||||||
|
// Gets the last view frustum when rendering the current context. Can be used for custom culling or LODing when drawing more complex shapes.
|
||||||
|
static BoundingFrustum GetViewFrustum();
|
||||||
|
|
||||||
|
// Sets the rendering view information beforehand.
|
||||||
|
API_FUNCTION() static void SetView(API_PARAM(ref) const RenderView& view);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Draws the collected debug shapes to the output.
|
/// Draws the collected debug shapes to the output.
|
||||||
|
|||||||
@@ -103,17 +103,17 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan
|
|||||||
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
for (int32 meshIndex = 0; meshIndex < meshes.Count(); meshIndex++)
|
||||||
{
|
{
|
||||||
auto& drawCall = drawCallsLists[lod][meshIndex];
|
auto& drawCall = drawCallsLists[lod][meshIndex];
|
||||||
if (!drawCall.DrawCall.Material)
|
if (!drawCall.Material)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DrawKey key;
|
DrawKey key;
|
||||||
key.Mat = drawCall.DrawCall.Material;
|
key.Mat = drawCall.Material;
|
||||||
key.Geo = &meshes.Get()[meshIndex];
|
key.Geo = &meshes.Get()[meshIndex];
|
||||||
key.Lightmap = instance.Lightmap.TextureIndex;
|
key.Lightmap = instance.Lightmap.TextureIndex;
|
||||||
auto* e = result.TryGet(key);
|
auto* e = result.TryGet(key);
|
||||||
if (!e)
|
if (!e)
|
||||||
{
|
{
|
||||||
e = &result[key];
|
e = &result.Add(key, BatchedDrawCall(renderContext.List))->Value;
|
||||||
ASSERT_LOW_LAYER(key.Mat);
|
ASSERT_LOW_LAYER(key.Mat);
|
||||||
e->DrawCall.Material = key.Mat;
|
e->DrawCall.Material = key.Mat;
|
||||||
e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
|
e->DrawCall.Surface.Lightmap = EnumHasAnyFlags(_staticFlags, StaticFlags::Lightmap) && _scene ? _scene->LightmapsData.GetReadyLightmap(key.Lightmap) : nullptr;
|
||||||
@@ -127,7 +127,7 @@ void Foliage::DrawInstance(RenderContext& renderContext, FoliageInstance& instan
|
|||||||
const Float3 translation = transform.Translation - renderContext.View.Origin;
|
const Float3 translation = transform.Translation - renderContext.View.Origin;
|
||||||
Matrix::Transformation(transform.Scale, transform.Orientation, translation, world);
|
Matrix::Transformation(transform.Scale, transform.Orientation, translation, world);
|
||||||
constexpr float worldDeterminantSign = 1.0f;
|
constexpr float worldDeterminantSign = 1.0f;
|
||||||
instanceData.Store(world, world, instance.Lightmap.UVsArea, drawCall.DrawCall.Surface.GeometrySize, instance.Random, worldDeterminantSign, lodDitherFactor);
|
instanceData.Store(world, world, instance.Lightmap.UVsArea, drawCall.Surface.GeometrySize, instance.Random, worldDeterminantSign, lodDitherFactor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,7 +430,7 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr
|
|||||||
{
|
{
|
||||||
const auto& mesh = meshes.Get()[meshIndex];
|
const auto& mesh = meshes.Get()[meshIndex];
|
||||||
auto& drawCall = drawCallsList.Get()[meshIndex];
|
auto& drawCall = drawCallsList.Get()[meshIndex];
|
||||||
drawCall.DrawCall.Material = nullptr;
|
drawCall.Material = nullptr; // DrawInstance skips draw calls from meshes with unset material
|
||||||
|
|
||||||
// Check entry visibility
|
// Check entry visibility
|
||||||
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
|
const auto& entry = type.Entries[mesh.GetMaterialSlotIndex()];
|
||||||
@@ -455,13 +455,13 @@ void Foliage::DrawType(RenderContext& renderContext, const FoliageType& type, Dr
|
|||||||
if (drawModes == DrawPass::None)
|
if (drawModes == DrawPass::None)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
drawCall.DrawCall.Material = material;
|
drawCall.Material = material;
|
||||||
drawCall.DrawCall.Surface.GeometrySize = mesh.GetBox().GetSize();
|
drawCall.Surface.GeometrySize = mesh.GetBox().GetSize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw instances of the foliage type
|
// Draw instances of the foliage type
|
||||||
BatchedDrawCalls result;
|
BatchedDrawCalls result(&renderContext.List->Memory);
|
||||||
DrawCluster(renderContext, type.Root, type, drawCallsLists, result);
|
DrawCluster(renderContext, type.Root, type, drawCallsLists, result);
|
||||||
|
|
||||||
// Submit draw calls with valid instances added
|
// Submit draw calls with valid instances added
|
||||||
@@ -998,6 +998,12 @@ void Foliage::RemoveAllInstances()
|
|||||||
RebuildClusters();
|
RebuildClusters();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Foliage::RemoveLightmap()
|
||||||
|
{
|
||||||
|
for (auto& e : Instances)
|
||||||
|
e.RemoveLightmap();
|
||||||
|
}
|
||||||
|
|
||||||
static float GlobalDensityScale = 1.0f;
|
static float GlobalDensityScale = 1.0f;
|
||||||
|
|
||||||
float Foliage::GetGlobalDensityScale()
|
float Foliage::GetGlobalDensityScale()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "FoliageInstance.h"
|
#include "FoliageInstance.h"
|
||||||
#include "FoliageCluster.h"
|
#include "FoliageCluster.h"
|
||||||
#include "FoliageType.h"
|
#include "FoliageType.h"
|
||||||
|
#include "Engine/Core/Memory/ArenaAllocation.h"
|
||||||
#include "Engine/Level/Actor.h"
|
#include "Engine/Level/Actor.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -139,6 +140,11 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
API_FUNCTION() void RemoveAllInstances();
|
API_FUNCTION() void RemoveAllInstances();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the lightmap data from the foliage instances.
|
||||||
|
/// </summary>
|
||||||
|
API_FUNCTION() void RemoveLightmap();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices.
|
/// Gets the global density scale for all foliage instances. The default value is 1. Use values from range 0-1. Lower values decrease amount of foliage instances in-game. Use it to tweak game performance for slower devices.
|
||||||
@@ -173,8 +179,8 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Array<struct BatchedDrawCall, InlinedAllocation<8>> DrawCallsList;
|
typedef Array<struct DrawCall, InlinedAllocation<8>> DrawCallsList;
|
||||||
typedef Dictionary<DrawKey, struct BatchedDrawCall, class RendererAllocation> BatchedDrawCalls;
|
typedef Dictionary<DrawKey, struct BatchedDrawCall, ConcurrentArenaAllocation> BatchedDrawCalls;
|
||||||
void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, const FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
void DrawInstance(RenderContext& renderContext, FoliageInstance& instance, const FoliageType& type, Model* model, int32 lod, float lodDitherFactor, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||||
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
void DrawCluster(RenderContext& renderContext, FoliageCluster* cluster, const FoliageType& type, DrawCallsList* drawCallsLists, BatchedDrawCalls& result) const;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ bool DeferredMaterialShader::CanUseLightmap() const
|
|||||||
bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const
|
bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const
|
||||||
{
|
{
|
||||||
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
|
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
|
||||||
return true;
|
return _instanced;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeferredMaterialShader::Bind(BindParameters& params)
|
void DeferredMaterialShader::Bind(BindParameters& params)
|
||||||
@@ -42,6 +42,8 @@ void DeferredMaterialShader::Bind(BindParameters& params)
|
|||||||
|
|
||||||
// Setup features
|
// Setup features
|
||||||
const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv);
|
const bool useLightmap = _info.BlendMode == MaterialBlendMode::Opaque && LightmapFeature::Bind(params, cb, srv);
|
||||||
|
if (_info.ShadingModel == MaterialShadingModel::CustomLit)
|
||||||
|
ForwardShadingFeature::Bind(params, cb, srv);
|
||||||
|
|
||||||
// Setup parameters
|
// Setup parameters
|
||||||
MaterialParameter::BindMeta bindMeta;
|
MaterialParameter::BindMeta bindMeta;
|
||||||
@@ -112,6 +114,9 @@ void DeferredMaterialShader::Unload()
|
|||||||
|
|
||||||
bool DeferredMaterialShader::Load()
|
bool DeferredMaterialShader::Load()
|
||||||
{
|
{
|
||||||
|
// TODO: support instancing when using ForwardShadingFeature
|
||||||
|
_instanced = _info.BlendMode == MaterialBlendMode::Opaque && _info.ShadingModel != MaterialShadingModel::CustomLit;
|
||||||
|
|
||||||
bool failed = false;
|
bool failed = false;
|
||||||
auto psDesc = GPUPipelineState::Description::Default;
|
auto psDesc = GPUPipelineState::Description::Default;
|
||||||
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
|
psDesc.DepthWriteEnable = (_info.FeaturesFlags & MaterialFeaturesFlags::DisableDepthWrite) == MaterialFeaturesFlags::None;
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ private:
|
|||||||
private:
|
private:
|
||||||
Cache _cache;
|
Cache _cache;
|
||||||
Cache _cacheInstanced;
|
Cache _cacheInstanced;
|
||||||
|
bool _instanced;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DeferredMaterialShader(const StringView& name)
|
DeferredMaterialShader(const StringView& name)
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ DrawPass ForwardMaterialShader::GetDrawModes() const
|
|||||||
bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const
|
bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const
|
||||||
{
|
{
|
||||||
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
|
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, };
|
||||||
return true;
|
return false; // TODO: support instancing when using ForwardShadingFeature
|
||||||
}
|
}
|
||||||
|
|
||||||
void ForwardMaterialShader::Bind(BindParameters& params)
|
void ForwardMaterialShader::Bind(BindParameters& params)
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ API_ENUM() enum class MaterialShadingModel : byte
|
|||||||
/// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object.
|
/// The foliage material. Intended for foliage materials like leaves and grass that need light scattering to transport simulation through the thin object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Foliage = 3,
|
Foliage = 3,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The custom lit shader that calculates own lighting such as Cel Shading. It has access to the scene lights data during both GBuffer and Forward pass rendering.
|
||||||
|
/// </summary>
|
||||||
|
CustomLit = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -605,10 +605,11 @@ int32 MaterialParams::GetVersionHash() const
|
|||||||
void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta)
|
void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta)
|
||||||
{
|
{
|
||||||
ASSERT(link && link->This);
|
ASSERT(link && link->This);
|
||||||
for (int32 i = 0; i < link->This->Count(); i++)
|
const int32 count = link->This->Count();
|
||||||
|
for (int32 i = 0; i < count; i++)
|
||||||
{
|
{
|
||||||
MaterialParamsLink* l = link;
|
MaterialParamsLink* l = link;
|
||||||
while (l->Down && !l->This->At(i).IsOverride())
|
while (l->Down && !l->This->At(i).IsOverride() && l->Down->This->Count() == count)
|
||||||
{
|
{
|
||||||
l = l->Down;
|
l = l->Down;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
|
#include "Engine/Tools/ModelTool/VertexTriangleAdjacency.h"
|
||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
#include "Engine/Platform/Platform.h"
|
#include "Engine/Platform/Platform.h"
|
||||||
|
#include "Engine/Platform/MessageBox.h"
|
||||||
#define USE_MIKKTSPACE 1
|
#define USE_MIKKTSPACE 1
|
||||||
#include "ThirdParty/MikkTSpace/mikktspace.h"
|
#include "ThirdParty/MikkTSpace/mikktspace.h"
|
||||||
#if USE_ASSIMP
|
#if USE_ASSIMP
|
||||||
@@ -181,6 +182,7 @@ bool MeshData::GenerateLightmapUVs()
|
|||||||
for (int32 i = 0; i < (int32)vb.size(); i++)
|
for (int32 i = 0; i < (int32)vb.size(); i++)
|
||||||
lightmapChannel.Get()[i] = *(Float2*)&vb[i].uv;
|
lightmapChannel.Get()[i] = *(Float2*)&vb[i].uv;
|
||||||
#else
|
#else
|
||||||
|
MessageBox::Show(TEXT("Model lightmap UVs generation is not supported on this platform."), TEXT("Import error"), MessageBoxButtons::OK, MessageBoxIcon::Error);
|
||||||
LOG(Error, "Model lightmap UVs generation is not supported on this platform.");
|
LOG(Error, "Model lightmap UVs generation is not supported on this platform.");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -200,12 +200,19 @@ void SceneRenderTask::RemoveGlobalCustomPostFx(PostProcessEffect* fx)
|
|||||||
|
|
||||||
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
|
void SceneRenderTask::CollectPostFxVolumes(RenderContext& renderContext)
|
||||||
{
|
{
|
||||||
|
PROFILE_CPU();
|
||||||
|
|
||||||
// Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on)
|
// Cache WorldPosition used for PostFx volumes blending (RenderView caches it later on)
|
||||||
renderContext.View.WorldPosition = renderContext.View.Origin + renderContext.View.Position;
|
renderContext.View.WorldPosition = renderContext.View.Origin + renderContext.View.Position;
|
||||||
|
|
||||||
if (EnumHasAllFlags(ActorsSource, ActorsSources::Scenes))
|
if (EnumHasAllFlags(ActorsSource, ActorsSources::Scenes))
|
||||||
{
|
{
|
||||||
Level::CollectPostFxVolumes(renderContext);
|
//ScopeLock lock(Level::ScenesLock);
|
||||||
|
for (Scene* scene : Level::Scenes)
|
||||||
|
{
|
||||||
|
if (scene->IsActiveInHierarchy())
|
||||||
|
scene->Rendering.CollectPostFxVolumes(renderContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomActors))
|
if (EnumHasAllFlags(ActorsSource, ActorsSources::CustomActors))
|
||||||
{
|
{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user