Merge remote-tracking branch 'origin/master' into 1.11

This commit is contained in:
Wojtek Figat
2025-06-25 10:48:11 +02:00
12 changed files with 214 additions and 16 deletions

View File

@@ -26,6 +26,7 @@ namespace FlaxEditor.CustomEditors.Editors
new OptionType("Texture 9-Slicing", typeof(Texture9SlicingBrush)),
new OptionType("Sprite 9-Slicing", typeof(Sprite9SlicingBrush)),
new OptionType("Video", typeof(VideoBrush)),
new OptionType("UI Brush", typeof(UIBrush)),
};
}
}

View File

@@ -287,10 +287,7 @@ namespace FlaxEditor.CustomEditors
/// <returns>The created element.</returns>
public ImageElement Image(SpriteHandle sprite)
{
var element = new ImageElement();
element.Image.Brush = new SpriteBrush(sprite);
OnAddElement(element);
return element;
return Image(new SpriteBrush(sprite));
}
/// <summary>
@@ -300,10 +297,7 @@ namespace FlaxEditor.CustomEditors
/// <returns>The created element.</returns>
public ImageElement Image(Texture texture)
{
var element = new ImageElement();
element.Image.Brush = new TextureBrush(texture);
OnAddElement(element);
return element;
return Image(new TextureBrush(texture));
}
/// <summary>
@@ -312,9 +306,19 @@ namespace FlaxEditor.CustomEditors
/// <param name="texture">The GPU texture.</param>
/// <returns>The created element.</returns>
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();
element.Image.Brush = new GPUTextureBrush(texture);
element.Image.Brush = brush;
OnAddElement(element);
return element;
}

View File

@@ -459,7 +459,7 @@ namespace FlaxEditor.Surface.Archetypes
AlternativeTitles = new string[] { "Lightmap TexCoord" },
Description = "Lightmap UVs",
Flags = NodeFlags.MaterialGraph,
Size = new Float2(110, 30),
Size = new Float2(110, 20),
Elements = new []
{
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),
}
},
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),
}
},
};
}
}

View File

@@ -197,6 +197,7 @@ void EnvironmentProbe::Draw(RenderContext& renderContext)
data.Position = position;
data.Radius = radius;
data.Brightness = Brightness;
data.SortOrder = SortOrder;
data.HashID = GetHash(_id);
renderContext.List->EnvironmentProbes.Add(data);
}
@@ -234,6 +235,7 @@ void EnvironmentProbe::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE_MEMBER(Radius, _radius);
SERIALIZE(CubemapResolution);
SERIALIZE(Brightness);
SERIALIZE(SortOrder);
SERIALIZE(UpdateMode);
SERIALIZE(CaptureNearPlane);
SERIALIZE_MEMBER(IsCustomProbe, _isUsingCustomProbe);
@@ -248,6 +250,7 @@ void EnvironmentProbe::Deserialize(DeserializeStream& stream, ISerializeModifier
DESERIALIZE_MEMBER(Radius, _radius);
DESERIALIZE(CubemapResolution);
DESERIALIZE(Brightness);
DESERIALIZE(SortOrder);
DESERIALIZE(UpdateMode);
DESERIALIZE(CaptureNearPlane);
DESERIALIZE_MEMBER(IsCustomProbe, _isUsingCustomProbe);

View File

@@ -51,6 +51,12 @@ public:
API_FIELD(Attributes="EditorOrder(10), Limit(0, 1000, 0.01f), EditorDisplay(\"Probe\")")
float Brightness = 1.0f;
/// <summary>
/// The probe rendering order. The higher values are render later (on top).
/// </summary>
API_FIELD(Attributes = "EditorOrder(25), EditorDisplay(\"Probe\")")
int32 SortOrder = 0;
/// <summary>
/// The probe update mode.
/// </summary>

View File

@@ -162,6 +162,13 @@ void ParticleEmitterGraphCPUExecutor::ProcessGroupTextures(Box* box, Node* node,
value = Value::Zero;
break;
}
// Texture Size
case 24:
{
// TODO: support sampling textures in CPU particles
value = Value::Zero;
break;
}
default:
break;
}

View File

@@ -34,8 +34,8 @@ bool ParticleEmitterGPUGenerator::loadTexture(Node* caller, Box* box, const Seri
Value location = tryGetValue(locationBox, Value::InitForZero(VariantType::Float2));
// Convert into a proper type
if (isCubemap || isVolume || isArray)
location = Value::Cast(location, VariantType::Float3);
if (isVolume || isArray)
location = Value::Cast(location, VariantType::Float4);
else
location = Value::Cast(location, VariantType::Float3);
@@ -332,6 +332,21 @@ void ParticleEmitterGPUGenerator::ProcessGroupTextures(Box* box, Node* node, Val
value = box == gradientBox ? gradient : distance;
break;
}
// Texture Size
case 24:
{
value = Value::Zero;
auto textureBox = node->GetBox(0);
if (!textureBox->HasConnection())
break;
const auto texture = eatBox(textureBox->GetParent<Node>(), textureBox->FirstConnection());
const auto textureParam = findParam(texture.Value);
if (!textureParam)
break;
value = writeLocal(VariantType::Float2, node);
_writer.Write(TEXT("\t{0}.GetDimensions({1}.x, {1}.y);\n"), textureParam->ShaderName, value.Value);
break;
}
default:
break;
}

View File

@@ -9,7 +9,9 @@
#include "Engine/Core/Log.h"
#include "Engine/Core/Types/DataContainer.h"
#include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h"
#include "Engine/Graphics/Textures/GPUTexture.h"
#include "Engine/Level/Level.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Serialization/MemoryReadStream.h"
#include "Engine/Serialization/MemoryWriteStream.h"
#include "Engine/Threading/Threading.h"
@@ -60,6 +62,35 @@ ParticleEffect* ParticleEmitter::Spawn(Actor* parent, const Transform& transform
return effect;
}
void ParticleEmitter::WaitForAsset(Asset* asset)
{
// TODO: make a tool for it?
if (auto* texture = Cast<TextureBase>(asset))
{
// Wait for asset to be loaded
if (!texture->WaitForLoaded() && !IsInMainThread() && texture->GetTexture())
{
PROFILE_CPU_NAMED("WaitForTexture");
// Mark as used by rendering and wait for streaming to load it in
double waitTimeSeconds = 10000;
double now = Platform::GetTimeSeconds();
double end = now + waitTimeSeconds;
const int32 mipLevels = texture->GetMipLevels();
constexpr float requestedResidency = 0.7f;
const int32 minMipLevels = Math::Max(Math::Min(mipLevels, 7), (int32)(requestedResidency * (float)mipLevels));
while (texture->GetResidentMipLevels() < minMipLevels &&
now < end &&
!texture->HasStreamingError())
{
texture->GetTexture()->LastRenderTime = now;
Platform::Sleep(1);
now = Platform::GetTimeSeconds();
}
}
}
}
#if COMPILE_WITH_PARTICLE_GPU_GRAPH && COMPILE_WITH_SHADER_COMPILER
namespace
@@ -297,6 +328,40 @@ Asset::LoadResult ParticleEmitter::load()
SimulationMode = ParticlesSimulationMode::CPU;
#endif
// Wait for resources used by the emitter to be loaded
// eg. texture used to place particles on spawn needs to be available
bool waitForAsset = false;
for (const auto& node : Graph.Nodes)
{
if ((node.Type == GRAPH_NODE_MAKE_TYPE(5, 1) || node.Type == GRAPH_NODE_MAKE_TYPE(5, 2)) && node.Assets.Count() > 0)
{
const auto texture = node.Assets[0].As<TextureBase>();
if (texture)
{
if (!waitForAsset)
{
waitForAsset = true;
Particles::SystemLocker.End(true);
}
WaitForAsset(texture);
}
}
}
for (const auto& parameter : Graph.Parameters)
{
if (parameter.Type.Type == VariantType::Asset)
{
if (!waitForAsset)
{
waitForAsset = true;
Particles::SystemLocker.End(true);
}
WaitForAsset((Asset*)parameter.Value);
}
}
if (waitForAsset)
Particles::SystemLocker.Begin(true);
return LoadResult::Ok;
}

View File

@@ -168,6 +168,9 @@ public:
/// <returns>The spawned effect.</returns>
API_FUNCTION() ParticleEffect* Spawn(Actor* parent, const Transform& transform, float duration = MAX_float, bool autoDestroy = false);
private:
void WaitForAsset(Asset* asset);
public:
// [BinaryAsset]
#if USE_EDITOR

View File

@@ -335,12 +335,17 @@ void ReflectionsPass::Dispose()
bool SortProbes(RenderEnvironmentProbeData const& p1, RenderEnvironmentProbeData const& p2)
{
// Compare by radius
int32 res = static_cast<int32>(p2.Radius - p1.Radius);
// Compare by Sort Order
int32 res = p1.SortOrder - p2.SortOrder;
if (res == 0)
{
// Compare by ID to prevent flickering
res = p2.HashID - p1.HashID;
// Compare by radius
res = static_cast<int32>(p2.Radius - p1.Radius);
if (res == 0)
{
// Compare by ID to prevent flickering
res = p2.HashID - p1.HashID;
}
}
return res < 0;
}

View File

@@ -155,6 +155,7 @@ struct RenderEnvironmentProbeData
Float3 Position;
float Radius;
float Brightness;
int32 SortOrder;
uint32 HashID;
void SetShaderData(ShaderEnvProbeData& data) const;

View File

@@ -0,0 +1,75 @@
// Copyright (c) Wojciech Figat. All rights reserved.
namespace FlaxEngine.GUI
{
/// <summary>
/// Asset with <see cref="IBrush"/> that can be reused in different UI controls.
/// </summary>
/// <seealso cref="IBrush" />
/// <seealso cref="UIBrush" />
public class UIBrushAsset
{
/// <summary>
/// Brush object.
/// </summary>
public IBrush Brush;
}
/// <summary>
/// Implementation of <see cref="IBrush"/> for <see cref="FlaxEngine.VideoPlayer"/> frame displaying.
/// </summary>
/// <seealso cref="IBrush" />
/// <seealso cref="UIBrushAsset" />
public sealed class UIBrush : IBrush
{
/// <summary>
/// The UI Brush asset to use.
/// </summary>
public JsonAssetReference<UIBrushAsset> Asset;
/// <summary>
/// Initializes a new instance of the <see cref="UIBrush"/> class.
/// </summary>
public UIBrush()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="UIBrush"/> struct.
/// </summary>
/// <param name="asset">The UI Brush asset to use.</param>
public UIBrush(JsonAssetReference<UIBrushAsset> asset)
{
Asset = asset;
}
/// <summary>
/// Initializes a new instance of the <see cref="UIBrush"/> struct.
/// </summary>
/// <param name="asset">The UI Brush asset to use.</param>
public UIBrush(JsonAsset asset)
{
Asset = asset;
}
/// <inheritdoc />
public Float2 Size
{
get
{
var asset = (UIBrushAsset)Asset.Asset?.Instance;
if (asset != null && asset.Brush != null)
return asset.Brush.Size;
return Float2.Zero;
}
}
/// <inheritdoc />
public void Draw(Rectangle rect, Color color)
{
var asset = (UIBrushAsset)Asset.Asset?.Instance;
if (asset != null && asset.Brush != null)
asset.Brush.Draw(rect, color);
}
}
}