Merge remote-tracking branch 'origin/master' into sdl_platform
This commit is contained in:
@@ -154,7 +154,7 @@ void ObjectsRemoval::Dispose()
|
||||
|
||||
Object::~Object()
|
||||
{
|
||||
#if BUILD_DEBUG
|
||||
#if BUILD_DEBUG && 0
|
||||
// Prevent removing object that is still reverenced by the removal service
|
||||
ASSERT(!ObjectsRemovalService::IsInPool(this));
|
||||
#endif
|
||||
|
||||
@@ -604,10 +604,11 @@ int32 MaterialParams::GetVersionHash() const
|
||||
void MaterialParams::Bind(MaterialParamsLink* link, MaterialParameter::BindMeta& meta)
|
||||
{
|
||||
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;
|
||||
while (l->Down && !l->This->At(i).IsOverride())
|
||||
while (l->Down && !l->This->At(i).IsOverride() && l->Down->This->Count() == count)
|
||||
{
|
||||
l = l->Down;
|
||||
}
|
||||
|
||||
@@ -175,6 +175,12 @@ public:
|
||||
return (const T*)FindCustomBuffer(name, withLinked);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
const T* FindLinkedBuffer(const StringView& name) const
|
||||
{
|
||||
return LinkedCustomBuffers ? (const T*)LinkedCustomBuffers->FindCustomBuffer(name, true) : nullptr;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
T* GetCustomBuffer(const StringView& name, bool withLinked = true)
|
||||
{
|
||||
|
||||
@@ -2,8 +2,16 @@
|
||||
|
||||
#include "ParticleEmitterGraph.CPU.h"
|
||||
#include "Engine/Core/Random.h"
|
||||
#include "Engine/Core/Math/Vector2.h"
|
||||
#include "Engine/Core/Math/Vector3.h"
|
||||
#include "Engine/Core/Math/Vector4.h"
|
||||
#include "Engine/Core/Math/Matrix.h"
|
||||
#include "Engine/Core/Math/Quaternion.h"
|
||||
#include "Engine/Core/Math/BoundingBox.h"
|
||||
#include "Engine/Core/Math/BoundingSphere.h"
|
||||
#include "Engine/Core/Math/OrientedBoundingBox.h"
|
||||
#include "Engine/Utilities/Noise.h"
|
||||
#include "Engine/Core/Types/CommonValue.h"
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
|
||||
// ReSharper disable CppCStyleCast
|
||||
// ReSharper disable CppClangTidyClangDiagnosticCastAlign
|
||||
@@ -1468,3 +1476,89 @@ void ParticleEmitterGraphCPUExecutor::ProcessModule(ParticleEmitterGraphCPUNode*
|
||||
#undef COLLISION_LOGIC
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void ParticleEmitterGraphCPUExecutor::DebugDrawModule(ParticleEmitterGraphCPUNode* node, const Transform& transform)
|
||||
{
|
||||
// Skip modules that rely on particle data
|
||||
if (node->UsePerParticleDataResolve())
|
||||
return;
|
||||
|
||||
const Color color = Color::White;
|
||||
switch (node->TypeID)
|
||||
{
|
||||
case 202: // Position (sphere surface)
|
||||
case 211: // Position (sphere volume)
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const float radius = (float)GetValue(node->GetBox(1), 3);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, radius), color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
case 203: // Position (plane)
|
||||
{
|
||||
const Float3 center = (Float3)GetValue(node->GetBox(0), 2);
|
||||
const Float2 size = (Float2)GetValue(node->GetBox(1), 3);
|
||||
const Float3 halfExtent = Float3(size.X * 0.5f, 0.0f, size.Y * 0.5f);
|
||||
OrientedBoundingBox box(halfExtent, Transform(center));
|
||||
box.Transform(transform);
|
||||
DEBUG_DRAW_WIRE_BOX(box, color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
case 204: // Position (circle)
|
||||
case 205: // Position (disc)
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const float radius = (float)GetValue(node->GetBox(1), 3);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, 0.0f, color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
case 206: // Position (box surface)
|
||||
case 207: // Position (box volume)
|
||||
{
|
||||
const Float3 center = (Float3)GetValue(node->GetBox(0), 2);
|
||||
const Float3 size = (Float3)GetValue(node->GetBox(1), 3);
|
||||
OrientedBoundingBox box(size * 0.5f, Transform(center));
|
||||
box.Transform(transform);
|
||||
DEBUG_DRAW_WIRE_BOX(box, color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Position (cylinder)
|
||||
case 208:
|
||||
{
|
||||
const float height = (float)GetValue(node->GetBox(2), 4);
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2) + Float3(0, 0, height * 0.5f));
|
||||
const float radius = (float)GetValue(node->GetBox(1), 3);
|
||||
DEBUG_DRAW_WIRE_CYLINDER(center, transform.Orientation * Quaternion::Euler(90, 0, 0), radius, height, color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Position (line)
|
||||
case 209:
|
||||
{
|
||||
const Float3 start = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const Float3 end = transform.LocalToWorld((Float3)GetValue(node->GetBox(1), 3));
|
||||
DEBUG_DRAW_LINE(start, end, color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
// Position (torus)
|
||||
case 210:
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
const float radius = Math::Max((float)GetValue(node->GetBox(1), 3), ZeroTolerance);
|
||||
const float thickness = (float)GetValue(node->GetBox(2), 4);
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, radius + thickness), color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
|
||||
// Position (spiral)
|
||||
case 214:
|
||||
{
|
||||
const Float3 center = transform.LocalToWorld((Float3)GetValue(node->GetBox(0), 2));
|
||||
DEBUG_DRAW_WIRE_SPHERE(BoundingSphere(center, 5.0f), color, 0.0f, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "Engine/Particles/ParticleEffect.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Debug/DebugDraw.h"
|
||||
|
||||
ThreadLocal<ParticleEmitterGraphCPUContext*> ParticleEmitterGraphCPUExecutor::Context;
|
||||
|
||||
@@ -423,6 +424,23 @@ void ParticleEmitterGraphCPUExecutor::Draw(ParticleEmitter* emitter, ParticleEff
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void ParticleEmitterGraphCPUExecutor::DrawDebug(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data)
|
||||
{
|
||||
// Prepare graph data
|
||||
Init(emitter, effect, data);
|
||||
Transform transform = emitter->SimulationSpace == ParticlesSimulationSpace::Local ? effect->GetTransform() : Transform::Identity;
|
||||
|
||||
// Draw modules
|
||||
for (auto module : emitter->Graph.SpawnModules)
|
||||
DebugDrawModule(module, transform);
|
||||
for (auto module : emitter->Graph.InitModules)
|
||||
DebugDrawModule(module, transform);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void ParticleEmitterGraphCPUExecutor::Update(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, float dt, bool canSpawn)
|
||||
{
|
||||
// Prepare data
|
||||
|
||||
@@ -162,6 +162,16 @@ public:
|
||||
/// <param name="transform">The effect transform matrix.</param>
|
||||
void Draw(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data, RenderContext& renderContext, Matrix& transform);
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Draws the particles debug shapes.
|
||||
/// </summary>
|
||||
/// <param name="emitter">The owning emitter.</param>
|
||||
/// <param name="effect">The instance effect.</param>
|
||||
/// <param name="data">The instance data.</param>
|
||||
void DrawDebug(ParticleEmitter* emitter, ParticleEffect* effect, ParticleEmitterInstance& data);
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Updates the particles simulation (the CPU simulation).
|
||||
/// </summary>
|
||||
@@ -195,6 +205,9 @@ private:
|
||||
|
||||
int32 ProcessSpawnModule(int32 index);
|
||||
void ProcessModule(ParticleEmitterGraphCPUNode* node, int32 particlesStart, int32 particlesEnd);
|
||||
#if USE_EDITOR
|
||||
void DebugDrawModule(ParticleEmitterGraphCPUNode* node, const Transform& transform);
|
||||
#endif
|
||||
|
||||
FORCE_INLINE Value GetValue(Box* box, int32 defaultValueBoxIndex)
|
||||
{
|
||||
|
||||
@@ -581,10 +581,19 @@ void ParticleEffect::OnDebugDrawSelected()
|
||||
{
|
||||
DEBUG_DRAW_WIRE_BOX(_box, Color::Violet * 0.7f, 0, true);
|
||||
|
||||
// Base
|
||||
Actor::OnDebugDrawSelected();
|
||||
}
|
||||
|
||||
void ParticleEffect::OnDebugDraw()
|
||||
{
|
||||
if (ShowDebugDraw)
|
||||
{
|
||||
Particles::DebugDraw(this);
|
||||
}
|
||||
|
||||
Actor::OnDebugDraw();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void ParticleEffect::OnLayerChanged()
|
||||
|
||||
@@ -244,6 +244,13 @@ public:
|
||||
API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(80), DefaultValue(0)")
|
||||
int8 SortOrder = 0;
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// If checked, the particle emitter debug shapes will be shawn during debug drawing. This includes particle spawn location shapes display.
|
||||
/// </summary>
|
||||
API_FIELD(Attributes = "EditorDisplay(\"Particle Effect\"), EditorOrder(200)") bool ShowDebugDraw = false;
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the effect parameters collection. Those parameters are instanced from the <see cref="ParticleSystem"/> that contains a linear list of emitters and every emitter has a list of own parameters.
|
||||
@@ -394,6 +401,7 @@ public:
|
||||
void Draw(RenderContext& renderContext) override;
|
||||
#if USE_EDITOR
|
||||
void OnDebugDrawSelected() override;
|
||||
void OnDebugDraw() override;
|
||||
#endif
|
||||
void OnLayerChanged() override;
|
||||
void Serialize(SerializeStream& stream, const void* otherObj) override;
|
||||
|
||||
@@ -519,4 +519,39 @@ bool ParticleEmitter::HasShaderCode() const
|
||||
return false;
|
||||
}
|
||||
|
||||
Array<ParticleEmitter::Attribute> ParticleEmitter::GetLayout() const
|
||||
{
|
||||
Array<Attribute> result;
|
||||
ScopeLock lock(Locker);
|
||||
result.Resize(Graph.Layout.Attributes.Count());
|
||||
for (int32 i = 0; i < result.Count(); i++)
|
||||
{
|
||||
auto& dst = result[i];
|
||||
const auto& src = Graph.Layout.Attributes[i];
|
||||
dst.Name = src.Name;
|
||||
switch (src.ValueType)
|
||||
{
|
||||
case ParticleAttribute::ValueTypes::Float:
|
||||
dst.Format = PixelFormat::R32_Float;
|
||||
break;
|
||||
case ParticleAttribute::ValueTypes::Float2:
|
||||
dst.Format = PixelFormat::R32G32_Float;
|
||||
break;
|
||||
case ParticleAttribute::ValueTypes::Float3:
|
||||
dst.Format = PixelFormat::R32G32B32_Float;
|
||||
break;
|
||||
case ParticleAttribute::ValueTypes::Float4:
|
||||
dst.Format = PixelFormat::R32G32B32A32_Float;
|
||||
break;
|
||||
case ParticleAttribute::ValueTypes::Int:
|
||||
dst.Format = PixelFormat::R32_SInt;
|
||||
break;
|
||||
case ParticleAttribute::ValueTypes::Uint:
|
||||
dst.Format = PixelFormat::R32_UInt;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -177,10 +177,16 @@ public:
|
||||
void GetReferences(Array<Guid>& assets, Array<String>& files) const override;
|
||||
bool Save(const StringView& path = StringView::Empty) override;
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the particle emitter has valid shader code present.
|
||||
/// </summary>
|
||||
API_PROPERTY() bool HasShaderCode() const;
|
||||
API_STRUCT(Internal) struct Attribute
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(Attribute);
|
||||
API_FIELD() PixelFormat Format;
|
||||
API_FIELD() String Name;
|
||||
};
|
||||
|
||||
private:
|
||||
API_PROPERTY(Internal) bool HasShaderCode() const;
|
||||
API_PROPERTY(Internal) Array<Attribute> GetLayout() const;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
@@ -933,6 +933,7 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
|
||||
const DrawPass drawModes = view.Pass & effect->DrawModes;
|
||||
if (drawModes == DrawPass::None || SpriteRenderer.Init())
|
||||
return;
|
||||
ConcurrentSystemLocker::ReadScope systemScope(SystemLocker);
|
||||
Matrix worlds[2];
|
||||
Matrix::Translation(-renderContext.View.Origin, worlds[0]); // World
|
||||
renderContext.View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local
|
||||
@@ -1065,6 +1066,28 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
|
||||
}
|
||||
}
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
void Particles::DebugDraw(ParticleEffect* effect)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Particles.DrawDebug");
|
||||
ConcurrentSystemLocker::ReadScope systemScope(SystemLocker);
|
||||
|
||||
// Draw all emitters
|
||||
for (auto& emitterData : effect->Instance.Emitters)
|
||||
{
|
||||
const auto buffer = emitterData.Buffer;
|
||||
if (!buffer)
|
||||
continue;
|
||||
auto emitter = buffer->Emitter;
|
||||
if (!emitter || !emitter->IsLoaded())
|
||||
continue;
|
||||
emitter->GraphExecutorCPU.DrawDebug(emitter, effect, emitterData);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if COMPILE_WITH_GPU_PARTICLES
|
||||
|
||||
void UpdateGPU(RenderTask* task, GPUContext* context)
|
||||
|
||||
@@ -52,6 +52,14 @@ public:
|
||||
/// <param name="effect">The owning actor.</param>
|
||||
static void DrawParticles(RenderContext& renderContext, ParticleEffect* effect);
|
||||
|
||||
#if USE_EDITOR
|
||||
/// <summary>
|
||||
/// Draws the particles debug shapes.
|
||||
/// </summary>
|
||||
/// <param name="effect">The owning actor.</param>
|
||||
static void DebugDraw(ParticleEffect* effect);
|
||||
#endif
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Enables or disables particle buffer pooling.
|
||||
|
||||
@@ -6,6 +6,89 @@
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Graphics/DynamicBuffer.h"
|
||||
|
||||
int32 ParticleAttribute::GetSize() const
|
||||
{
|
||||
switch (ValueType)
|
||||
{
|
||||
case ValueTypes::Float2:
|
||||
return 8;
|
||||
case ValueTypes::Float3:
|
||||
return 12;
|
||||
case ValueTypes::Float4:
|
||||
return 16;
|
||||
case ValueTypes::Float:
|
||||
case ValueTypes::Int:
|
||||
case ValueTypes::Uint:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void ParticleLayout::Clear()
|
||||
{
|
||||
Size = 0;
|
||||
Attributes.Clear();
|
||||
}
|
||||
|
||||
void ParticleLayout::UpdateLayout()
|
||||
{
|
||||
Size = 0;
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
Attributes[i].Offset = Size;
|
||||
Size += Attributes[i].GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
int32 ParticleLayout::FindAttribute(const StringView& name) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (name == Attributes[i].Name)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 ParticleLayout::FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32 ParticleLayout::FindAttributeOffset(const StringView& name, int32 fallbackValue) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (name == Attributes[i].Name)
|
||||
return Attributes[i].Offset;
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
int32 ParticleLayout::FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
||||
return Attributes[i].Offset;
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
|
||||
int32 ParticleLayout::AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType)
|
||||
{
|
||||
auto& a = Attributes.AddOne();
|
||||
a.Name = String(*name, name.Length());
|
||||
a.ValueType = valueType;
|
||||
return Attributes.Count() - 1;
|
||||
}
|
||||
|
||||
ParticleBuffer::ParticleBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -52,25 +52,7 @@ struct ParticleAttribute
|
||||
/// <summary>
|
||||
/// Gets the size of the attribute (in bytes).
|
||||
/// </summary>
|
||||
/// <returns>The size (in bytes).</returns>
|
||||
int32 GetSize() const
|
||||
{
|
||||
switch (ValueType)
|
||||
{
|
||||
case ValueTypes::Float2:
|
||||
return 8;
|
||||
case ValueTypes::Float3:
|
||||
return 12;
|
||||
case ValueTypes::Float4:
|
||||
return 16;
|
||||
case ValueTypes::Float:
|
||||
case ValueTypes::Int:
|
||||
case ValueTypes::Uint:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int32 GetSize() const;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -93,41 +75,19 @@ public:
|
||||
/// <summary>
|
||||
/// Clears the layout data.
|
||||
/// </summary>
|
||||
void Clear()
|
||||
{
|
||||
Size = 0;
|
||||
Attributes.Clear();
|
||||
}
|
||||
void Clear();
|
||||
|
||||
/// <summary>
|
||||
/// Updates the attributes layout (calculates offset) and updates the total size of the layout.
|
||||
/// </summary>
|
||||
void UpdateLayout()
|
||||
{
|
||||
Size = 0;
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
Attributes[i].Offset = Size;
|
||||
Size += Attributes[i].GetSize();
|
||||
}
|
||||
}
|
||||
void UpdateLayout();
|
||||
|
||||
/// <summary>
|
||||
/// Finds the attribute by the name.
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
||||
int32 FindAttribute(const StringView& name) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (name == Attributes[i].Name)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int32 FindAttribute(const StringView& name) const;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the attribute by the name and type.
|
||||
@@ -135,17 +95,7 @@ public:
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="valueType">The type.</param>
|
||||
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
||||
int32 FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int32 FindAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType) const;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the attribute offset by the name.
|
||||
@@ -153,17 +103,7 @@ public:
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
||||
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
||||
int32 FindAttributeOffset(const StringView& name, int32 fallbackValue = 0) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (name == Attributes[i].Name)
|
||||
{
|
||||
return Attributes[i].Offset;
|
||||
}
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
int32 FindAttributeOffset(const StringView& name, int32 fallbackValue = 0) const;
|
||||
|
||||
/// <summary>
|
||||
/// Finds the attribute offset by the name.
|
||||
@@ -172,17 +112,7 @@ public:
|
||||
/// <param name="valueType">The type.</param>
|
||||
/// <param name="fallbackValue">The fallback value to return if attribute is missing.</param>
|
||||
/// <returns>The attribute offset or fallback value if cannot find it.</returns>
|
||||
int32 FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue = 0) const
|
||||
{
|
||||
for (int32 i = 0; i < Attributes.Count(); i++)
|
||||
{
|
||||
if (Attributes[i].ValueType == valueType && name == Attributes[i].Name)
|
||||
{
|
||||
return Attributes[i].Offset;
|
||||
}
|
||||
}
|
||||
return fallbackValue;
|
||||
}
|
||||
int32 FindAttributeOffset(const StringView& name, ParticleAttribute::ValueTypes valueType, int32 fallbackValue = 0) const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the attribute offset by the attribute index.
|
||||
@@ -201,13 +131,7 @@ public:
|
||||
/// <param name="name">The name.</param>
|
||||
/// <param name="valueType">The value type.</param>
|
||||
/// <returns>The attribute index or -1 if cannot find it.</returns>
|
||||
int32 AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType)
|
||||
{
|
||||
auto& a = Attributes.AddOne();
|
||||
a.Name = String(*name, name.Length());
|
||||
a.ValueType = valueType;
|
||||
return Attributes.Count() - 1;
|
||||
}
|
||||
int32 AddAttribute(const StringView& name, ParticleAttribute::ValueTypes valueType);
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -69,6 +69,7 @@ struct ShadowAtlasLightTile
|
||||
{
|
||||
ShadowsAtlasRectTile* RectTile;
|
||||
ShadowsAtlasRectTile* StaticRectTile;
|
||||
const ShadowsAtlasRectTile* LinkedRectTile;
|
||||
Matrix WorldToShadow;
|
||||
float FramesToUpdate; // Amount of frames (with fraction) until the next shadow update can happen
|
||||
bool SkipUpdate;
|
||||
@@ -94,6 +95,7 @@ struct ShadowAtlasLightTile
|
||||
void ClearStatic()
|
||||
{
|
||||
StaticRectTile = nullptr;
|
||||
LinkedRectTile = nullptr;
|
||||
FramesToUpdate = 0;
|
||||
SkipUpdate = false;
|
||||
}
|
||||
@@ -301,6 +303,7 @@ public:
|
||||
GPUTexture* StaticShadowMapAtlas = nullptr;
|
||||
DynamicTypedBuffer ShadowsBuffer;
|
||||
GPUBufferView* ShadowsBufferView = nullptr;
|
||||
const ShadowsCustomBuffer* LinkedShadows = nullptr;
|
||||
RectPackAtlas<ShadowsAtlasRectTile> Atlas;
|
||||
RectPackAtlas<ShadowsAtlasRectTile> StaticAtlas;
|
||||
Dictionary<Guid, ShadowAtlasLight> Lights;
|
||||
@@ -1046,6 +1049,32 @@ void ShadowsPass::SetupLight(ShadowsCustomBuffer& shadows, RenderContext& render
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowsPass::ClearShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, QuadShaderData& quadShaderData) const
|
||||
{
|
||||
// Color.r is used by PS_DepthClear in Quad shader to clear depth
|
||||
quadShaderData.Color = Float4::One;
|
||||
context->UpdateCB(quadShaderCB, &quadShaderData);
|
||||
context->BindCB(0, quadShaderCB);
|
||||
|
||||
// Clear tile depth
|
||||
context->SetState(_psDepthClear);
|
||||
context->DrawFullscreenTriangle();
|
||||
}
|
||||
|
||||
void ShadowsPass::CopyShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, QuadShaderData& quadShaderData, const GPUTexture* srcShadowMap, const ShadowsAtlasRectTile* srcTile) const
|
||||
{
|
||||
// Color.xyzw is used by PS_DepthCopy in Quad shader to scale input texture UVs
|
||||
const float staticAtlasResolutionInv = 1.0f / (float)srcShadowMap->Width();
|
||||
quadShaderData.Color = Float4(srcTile->Width, srcTile->Height, srcTile->X, srcTile->Y) * staticAtlasResolutionInv;
|
||||
context->UpdateCB(quadShaderCB, &quadShaderData);
|
||||
context->BindCB(0, quadShaderCB);
|
||||
|
||||
// Copy tile depth
|
||||
context->BindSR(0, srcShadowMap->View());
|
||||
context->SetState(_psDepthCopy);
|
||||
context->DrawFullscreenTriangle();
|
||||
}
|
||||
|
||||
void ShadowsPass::Dispose()
|
||||
{
|
||||
// Base
|
||||
@@ -1070,26 +1099,26 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch&
|
||||
// Early out and skip shadows setup if no lights is actively casting shadows
|
||||
// RenderBuffers will automatically free any old ShadowsCustomBuffer after a few frames if we don't update LastFrameUsed
|
||||
Array<RenderLightData*, RendererAllocation> shadowedLights;
|
||||
for (auto& light : renderContext.List->DirectionalLights)
|
||||
if (_shadowMapFormat != PixelFormat::Unknown && EnumHasAllFlags(renderContext.View.Flags, ViewFlags::Shadows) && !checkIfSkipPass())
|
||||
{
|
||||
if (light.CanRenderShadow(renderContext.View))
|
||||
shadowedLights.Add(&light);
|
||||
}
|
||||
for (auto& light : renderContext.List->SpotLights)
|
||||
{
|
||||
if (light.CanRenderShadow(renderContext.View))
|
||||
shadowedLights.Add(&light);
|
||||
}
|
||||
for (auto& light : renderContext.List->PointLights)
|
||||
{
|
||||
if (light.CanRenderShadow(renderContext.View))
|
||||
shadowedLights.Add(&light);
|
||||
for (auto& light : renderContext.List->DirectionalLights)
|
||||
{
|
||||
if (light.CanRenderShadow(renderContext.View))
|
||||
shadowedLights.Add(&light);
|
||||
}
|
||||
for (auto& light : renderContext.List->SpotLights)
|
||||
{
|
||||
if (light.CanRenderShadow(renderContext.View))
|
||||
shadowedLights.Add(&light);
|
||||
}
|
||||
for (auto& light : renderContext.List->PointLights)
|
||||
{
|
||||
if (light.CanRenderShadow(renderContext.View))
|
||||
shadowedLights.Add(&light);
|
||||
}
|
||||
}
|
||||
const auto currentFrame = Engine::FrameCount;
|
||||
if (_shadowMapFormat == PixelFormat::Unknown ||
|
||||
EnumHasNoneFlags(renderContext.View.Flags, ViewFlags::Shadows) ||
|
||||
checkIfSkipPass() ||
|
||||
shadowedLights.IsEmpty())
|
||||
if (shadowedLights.IsEmpty())
|
||||
{
|
||||
// Invalidate any existing custom buffer that could have been used by the same task (eg. when rendering 6 sides of env probe)
|
||||
if (auto* old = (ShadowsCustomBuffer*)renderContext.Buffers->FindCustomBuffer<ShadowsCustomBuffer>(TEXT("Shadows"), false))
|
||||
@@ -1102,11 +1131,14 @@ void ShadowsPass::SetupShadows(RenderContext& renderContext, RenderContextBatch&
|
||||
|
||||
// Initialize shadow atlas
|
||||
auto& shadows = *renderContext.Buffers->GetCustomBuffer<ShadowsCustomBuffer>(TEXT("Shadows"), false);
|
||||
shadows.LinkedShadows = renderContext.Buffers->FindLinkedBuffer<ShadowsCustomBuffer>(TEXT("Shadows"));
|
||||
if (shadows.LinkedShadows && (shadows.LinkedShadows->LastFrameUsed != currentFrame || shadows.LinkedShadows->ViewOrigin != renderContext.View.Origin))
|
||||
shadows.LinkedShadows = nullptr; // Don't use incompatible linked shadows buffer
|
||||
if (shadows.LastFrameUsed == currentFrame)
|
||||
shadows.Reset();
|
||||
shadows.LastFrameUsed = currentFrame;
|
||||
shadows.MaxShadowsQuality = Math::Clamp(Math::Min<int32>((int32)Graphics::ShadowsQuality, (int32)renderContext.View.MaxShadowsQuality), 0, (int32)Quality::MAX - 1);
|
||||
shadows.EnableStaticShadows = !renderContext.View.IsOfflinePass && !renderContext.View.IsSingleFrame;
|
||||
shadows.EnableStaticShadows = !renderContext.View.IsOfflinePass && !renderContext.View.IsSingleFrame && !shadows.LinkedShadows;
|
||||
int32 atlasResolution;
|
||||
switch (Graphics::ShadowMapsQuality)
|
||||
{
|
||||
@@ -1325,6 +1357,29 @@ RETRY_ATLAS_SETUP:
|
||||
SetupLight(shadows, renderContext, renderContextBatch, *(RenderSpotLightData*)light, atlasLight);
|
||||
else //if (light->IsDirectionalLight)
|
||||
SetupLight(shadows, renderContext, renderContextBatch, *(RenderDirectionalLightData*)light, atlasLight);
|
||||
|
||||
// Check if that light exists in linked shadows buffer to reuse shadow maps
|
||||
const ShadowAtlasLight* linkedAtlasLight;
|
||||
if (shadows.LinkedShadows && ((linkedAtlasLight = shadows.LinkedShadows->Lights.TryGet(light->ID))) && linkedAtlasLight->TilesCount == atlasLight.TilesCount)
|
||||
{
|
||||
for (int32 tileIndex = 0; tileIndex < atlasLight.TilesCount; tileIndex++)
|
||||
{
|
||||
auto& tile = atlasLight.Tiles[tileIndex];
|
||||
tile.LinkedRectTile = nullptr;
|
||||
auto& linkedTile = linkedAtlasLight->Tiles[tileIndex];
|
||||
|
||||
// Check if both lights use the same projections
|
||||
if (tile.WorldToShadow == linkedTile.WorldToShadow && linkedTile.RectTile)
|
||||
{
|
||||
tile.LinkedRectTile = linkedTile.RectTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& tile : atlasLight.Tiles)
|
||||
tile.LinkedRectTile = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shadows.StaticAtlas.IsInitialized())
|
||||
@@ -1495,29 +1550,21 @@ void ShadowsPass::RenderShadowMaps(RenderContextBatch& renderContextBatch)
|
||||
|
||||
// Set viewport for tile
|
||||
context->SetViewportAndScissors(tile.CachedViewport);
|
||||
if (tile.StaticRectTile && atlasLight.StaticState == ShadowAtlasLight::CopyStaticShadow)
|
||||
if (tile.LinkedRectTile)
|
||||
{
|
||||
// Color.xyzw is used by PS_DepthCopy in Quad shader to scale input texture UVs
|
||||
const float staticAtlasResolutionInv = 1.0f / shadows.StaticShadowMapAtlas->Width();
|
||||
quadShaderData.Color = Float4(tile.StaticRectTile->Width, tile.StaticRectTile->Height, tile.StaticRectTile->X, tile.StaticRectTile->Y) * staticAtlasResolutionInv;
|
||||
context->UpdateCB(quadShaderCB, &quadShaderData);
|
||||
context->BindCB(0, quadShaderCB);
|
||||
|
||||
// Copy tile depth
|
||||
context->BindSR(0, shadows.StaticShadowMapAtlas->View());
|
||||
context->SetState(_psDepthCopy);
|
||||
context->DrawFullscreenTriangle();
|
||||
// Copy linked shadow
|
||||
ASSERT(shadows.LinkedShadows);
|
||||
CopyShadowMapTile(context, quadShaderCB, quadShaderData, shadows.LinkedShadows->ShadowMapAtlas, tile.LinkedRectTile);
|
||||
}
|
||||
else if (tile.StaticRectTile && atlasLight.StaticState == ShadowAtlasLight::CopyStaticShadow)
|
||||
{
|
||||
// Copy static shadow
|
||||
CopyShadowMapTile(context, quadShaderCB, quadShaderData, shadows.StaticShadowMapAtlas, tile.StaticRectTile);
|
||||
}
|
||||
else if (!shadows.ClearShadowMapAtlas)
|
||||
{
|
||||
// Color.r is used by PS_DepthClear in Quad shader to clear depth
|
||||
quadShaderData.Color = Float4::One;
|
||||
context->UpdateCB(quadShaderCB, &quadShaderData);
|
||||
context->BindCB(0, quadShaderCB);
|
||||
|
||||
// Clear tile depth
|
||||
context->SetState(_psDepthClear);
|
||||
context->DrawFullscreenTriangle();
|
||||
// Clear shadow
|
||||
ClearShadowMapTile(context, quadShaderCB, quadShaderData);
|
||||
}
|
||||
|
||||
// Draw objects depth
|
||||
|
||||
@@ -60,6 +60,8 @@ private:
|
||||
static void SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderDirectionalLightData& light, ShadowAtlasLight& atlasLight);
|
||||
static void SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderPointLightData& light, ShadowAtlasLight& atlasLight);
|
||||
static void SetupLight(ShadowsCustomBuffer& shadows, RenderContext& renderContext, RenderContextBatch& renderContextBatch, RenderSpotLightData& light, ShadowAtlasLight& atlasLight);
|
||||
void ClearShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, struct QuadShaderData& quadShaderData) const;
|
||||
void CopyShadowMapTile(GPUContext* context, GPUConstantBuffer* quadShaderCB, struct QuadShaderData& quadShaderData, const GPUTexture* srcShadowMap, const struct ShadowsAtlasRectTile* srcTile) const;
|
||||
|
||||
#if COMPILE_WITH_DEV_ENV
|
||||
void OnShaderReloading(Asset* obj)
|
||||
|
||||
@@ -431,8 +431,6 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
|
||||
GET_VERTEX(1, 1);
|
||||
#undef GET_VERTEX
|
||||
|
||||
// TODO: use SIMD for those calculations
|
||||
|
||||
// Calculate normals for quad two vertices
|
||||
Float3 n0 = Float3::Normalize((v00 - v01) ^ (v01 - v10));
|
||||
Float3 n1 = Float3::Normalize((v11 - v10) ^ (v10 - v01));
|
||||
@@ -446,6 +444,7 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Smooth normals
|
||||
for (int32 z = 1; z < normalsSize.Y - 1; z++)
|
||||
{
|
||||
@@ -466,8 +465,6 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
|
||||
GET_NORMAL(2, 2);
|
||||
#undef GET_VERTEX
|
||||
|
||||
// TODO: use SIMD for those calculations
|
||||
|
||||
/*
|
||||
* The current vertex is (11). Calculate average for the nearby vertices.
|
||||
* 00 01 02
|
||||
@@ -481,6 +478,7 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
|
||||
normalsPerVertex[i11] = Float3::Lerp(n11, avg, 0.6f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Write back to the data container
|
||||
const auto ptr = (Color32*)data;
|
||||
@@ -525,10 +523,9 @@ void UpdateNormalsAndHoles(const TerrainDataUpdateInfo& info, const float* heigh
|
||||
const int32 textureIndex = tz + tx;
|
||||
const int32 heightmapIndex = hz + hx;
|
||||
const int32 normalIndex = sz + sx;
|
||||
#if BUILD_DEBUG
|
||||
ASSERT(normalIndex >= 0 && normalIndex < normalsLength);
|
||||
#endif
|
||||
Float3 normal = Float3::NormalizeFast(normalsPerVertex[normalIndex]) * 0.5f + 0.5f;
|
||||
ASSERT_LOW_LAYER(normalIndex >= 0 && normalIndex < normalsLength);
|
||||
Float3 normal = Float3::NormalizeFast(normalsPerVertex[normalIndex]);
|
||||
normal = normal * 0.5f + 0.5f;
|
||||
|
||||
if (holesMask && !holesMask[heightmapIndex])
|
||||
normal = Float3::One;
|
||||
@@ -1247,6 +1244,11 @@ void TerrainPatch::ClearCache()
|
||||
|
||||
void TerrainPatch::CacheHeightData()
|
||||
{
|
||||
if (Heightmap == nullptr)
|
||||
{
|
||||
LOG(Error, "Missing heightmap.");
|
||||
return;
|
||||
}
|
||||
PROFILE_CPU_NAMED("Terrain.CacheHeightData");
|
||||
const TerrainDataUpdateInfo info(this);
|
||||
|
||||
@@ -1745,7 +1747,7 @@ bool TerrainPatch::UpdateHeightData(TerrainDataUpdateInfo& info, const Int2& mod
|
||||
// Prepare data for the uploading to GPU
|
||||
ASSERT(Heightmap);
|
||||
auto texture = Heightmap->GetTexture();
|
||||
ASSERT(texture->ResidentMipLevels() > 0);
|
||||
ASSERT(texture->IsAllocated());
|
||||
const int32 textureSize = texture->Width();
|
||||
const PixelFormat pixelFormat = texture->Format();
|
||||
const int32 pixelStride = PixelFormatExtensions::SizeInBytes(pixelFormat);
|
||||
|
||||
@@ -10,6 +10,10 @@ void MaterialGenerator::ProcessGroupMaterial(Box* box, Node* node, Value& value)
|
||||
{
|
||||
switch (node->TypeID)
|
||||
{
|
||||
// Material
|
||||
case 1:
|
||||
value = tryGetValue(box, Value::Zero);
|
||||
break;
|
||||
// World Position
|
||||
case 2:
|
||||
value = Value(VariantType::Float3, TEXT("input.WorldPosition.xyz"));
|
||||
|
||||
@@ -286,7 +286,7 @@ void ShaderGenerator::ProcessGroupMath(Box* box, Node* node, Value& value)
|
||||
case 29:
|
||||
{
|
||||
Value inXY = tryGetValue(node->GetBox(0), Value::Zero).AsFloat2();
|
||||
value = writeLocal(ValueType::Float3, String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}.xy, {0}.xy))))"), inXY.Value), node);
|
||||
value = writeLocal(ValueType::Float3, String::Format(TEXT("float3({0}, sqrt(saturate(1.0 - dot({0}, {0}))))"), inXY.Value), node);
|
||||
break;
|
||||
}
|
||||
// Mad
|
||||
|
||||
Reference in New Issue
Block a user