Add SortOrder to drawable object types for transparency sorting override

This commit is contained in:
Wojtek Figat
2023-01-26 18:37:40 +01:00
parent 997baa3b77
commit adfe3ad165
18 changed files with 183 additions and 97 deletions

View File

@@ -275,12 +275,16 @@ namespace FlaxEditor.CustomEditors
// Workaround for DefaultValueAttribute that doesn't support certain value types storage
if (Type.Type == typeof(sbyte))
_defaultValue = Convert.ToSByte(_defaultValue);
else if (Type.Type == typeof(short))
_defaultValue = Convert.ToInt16(_defaultValue);
else if (Type.Type == typeof(ushort))
_defaultValue = Convert.ToUInt16(_defaultValue);
else if (Type.Type == typeof(uint))
_defaultValue = Convert.ToUInt32(_defaultValue);
else if (Type.Type == typeof(ulong))
_defaultValue = Convert.ToUInt64(_defaultValue);
else if (Type.Type == typeof(long))
_defaultValue = Convert.ToInt64(_defaultValue);
}
}
}

View File

@@ -78,6 +78,7 @@ void ViewportIconsRenderer::DrawIcons(RenderContext& renderContext, Actor* actor
draw.PerInstanceRandom = 0;
draw.LODBias = 0;
draw.ForcedLOD = -1;
draw.SortOrder = 0;
draw.VertexColors = nullptr;
if (const auto scene = SceneObject::Cast<Scene>(actor))

View File

@@ -1173,6 +1173,7 @@ void Foliage::Draw(RenderContext& renderContext)
draw.Flags = GetStaticFlags();
draw.LODBias = 0;
draw.ForcedLOD = -1;
draw.SortOrder = 0;
draw.VertexColors = nullptr;
draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(instance.Lightmap.TextureIndex);
draw.LightmapUVs = &instance.Lightmap.UVsArea;

View File

@@ -408,7 +408,7 @@ void Mesh::Render(GPUContext* context) const
context->DrawIndexedInstanced(_triangles * 3, 1, 0, 0, 0);
}
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom) const
void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, const Matrix& world, StaticFlags flags, bool receiveDecals, DrawPass drawModes, float perInstanceRandom, int16 sortOrder) const
{
if (!material || !material->IsSurface() || !IsInitialized())
return;
@@ -446,7 +446,7 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
#endif
// Push draw call to the render list
renderContext.List->AddDrawCall(renderContext, drawModes, flags, drawCall, receiveDecals);
renderContext.List->AddDrawCall(renderContext, drawModes, flags, drawCall, receiveDecals, sortOrder);
}
void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float lodDitherFactor) const
@@ -512,7 +512,7 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
#endif
// Push draw call to the render list
renderContext.List->AddDrawCall(renderContext, drawModes, info.Flags, drawCall, entry.ReceiveDecals);
renderContext.List->AddDrawCall(renderContext, drawModes, info.Flags, drawCall, entry.ReceiveDecals, info.SortOrder);
}
void Mesh::Draw(const RenderContextBatch& renderContextBatch, const DrawInfo& info, float lodDitherFactor) const

View File

@@ -339,6 +339,11 @@ public:
/// The forced LOD to use. Value -1 disables this feature.
/// </summary>
char ForcedLOD;
/// <summary>
/// The object sorting key.
/// </summary>
int16 SortOrder;
};
/// <summary>
@@ -357,7 +362,8 @@ public:
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f) const;
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int16 sortOrder = 0) const;
/// <summary>
/// Draws the mesh.

View File

@@ -124,9 +124,7 @@ public:
FORCE_INLINE void Render(GPUContext* context)
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes.Get()[i].Render(context);
}
}
/// <summary>
@@ -139,12 +137,11 @@ public:
/// <param name="receiveDecals">True if rendered geometry can receive decals, otherwise false.</param>
/// <param name="drawModes">The draw passes to use for rendering this object.</param>
/// <param name="perInstanceRandom">The random per-instance value (normalized to range 0-1).</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f) const
/// <param name="sortOrder">Object sorting key.</param>
API_FUNCTION() void Draw(API_PARAM(Ref) const RenderContext& renderContext, MaterialBase* material, API_PARAM(Ref) const Matrix& world, StaticFlags flags = StaticFlags::None, bool receiveDecals = true, DrawPass drawModes = DrawPass::Default, float perInstanceRandom = 0.0f, int16 sortOrder = 0) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes.Get()[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom);
}
Meshes.Get()[i].Draw(renderContext, material, world, flags, receiveDecals, drawModes, perInstanceRandom, sortOrder);
}
/// <summary>
@@ -156,9 +153,7 @@ public:
FORCE_INLINE void Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info, float lodDitherFactor) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes.Get()[i].Draw(renderContext, info, lodDitherFactor);
}
}
/// <summary>
@@ -170,8 +165,6 @@ public:
FORCE_INLINE void Draw(const RenderContextBatch& renderContextBatch, const Mesh::DrawInfo& info, float lodDitherFactor) const
{
for (int32 i = 0; i < Meshes.Count(); i++)
{
Meshes.Get()[i].Draw(renderContextBatch, info, lodDitherFactor);
}
}
};

View File

@@ -297,6 +297,7 @@ void Camera::Draw(RenderContext& renderContext)
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = 0;
draw.ForcedLOD = -1;
draw.SortOrder = 0;
draw.VertexColors = nullptr;
if (draw.DrawModes != DrawPass::None)
{

View File

@@ -25,6 +25,7 @@ StaticModel::StaticModel(const SpawnParams& params)
, _forcedLod(-1)
, _vertexColorsDirty(false)
, _vertexColorsCount(0)
, _sortOrder(0)
{
_drawCategory = SceneRendering::SceneDrawAsync;
Model.Changed.Bind<StaticModel, &StaticModel::OnModelChanged>(this);
@@ -37,11 +38,21 @@ StaticModel::~StaticModel()
SAFE_DELETE_GPU_RESOURCE(_vertexColorsBuffer[lodIndex]);
}
float StaticModel::GetScaleInLightmap() const
{
return _scaleInLightmap;
}
void StaticModel::SetScaleInLightmap(float value)
{
_scaleInLightmap = value;
}
float StaticModel::GetBoundsScale() const
{
return _boundsScale;
}
void StaticModel::SetBoundsScale(float value)
{
if (Math::NearEqual(_boundsScale, value))
@@ -51,6 +62,46 @@ void StaticModel::SetBoundsScale(float value)
UpdateBounds();
}
int32 StaticModel::GetLODBias() const
{
return _lodBias;
}
void StaticModel::SetLODBias(int32 value)
{
_lodBias = static_cast<char>(Math::Clamp(value, -100, 100));
}
int32 StaticModel::GetForcedLOD() const
{
return _forcedLod;
}
void StaticModel::SetForcedLOD(int32 value)
{
_forcedLod = static_cast<char>(Math::Clamp(value, -1, 100));
}
int32 StaticModel::GetSortOrder() const
{
return _sortOrder;
}
void StaticModel::SetSortOrder(int32 value)
{
_sortOrder = (int16)Math::Clamp<int32>(value, MIN_int16, MAX_int16);
}
bool StaticModel::HasLightmap() const
{
return Lightmap.TextureIndex != INVALID_INDEX;
}
void StaticModel::RemoveLightmap()
{
Lightmap.TextureIndex = INVALID_INDEX;
}
MaterialBase* StaticModel::GetMaterial(int32 meshIndex, int32 lodIndex) const
{
auto model = Model.Get();
@@ -153,6 +204,11 @@ void StaticModel::SetVertexColor(int32 lodIndex, int32 meshIndex, int32 vertexIn
LOG(Warning, "Specified model mesh index was out of range. LOD{0} mesh {1}.", lodIndex, meshIndex);
}
bool StaticModel::HasVertexColors() const
{
return _vertexColorsCount != 0;
}
void StaticModel::RemoveVertexColors()
{
for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++)
@@ -292,6 +348,7 @@ void StaticModel::Draw(RenderContext& renderContext)
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = _lodBias;
draw.ForcedLOD = _forcedLod;
draw.SortOrder = _sortOrder;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
Model->Draw(renderContext, draw);
@@ -324,6 +381,7 @@ void StaticModel::Draw(RenderContextBatch& renderContextBatch)
draw.PerInstanceRandom = GetPerInstanceRandom();
draw.LODBias = _lodBias;
draw.ForcedLOD = _forcedLod;
draw.SortOrder = _sortOrder;
draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr;
Model->Draw(renderContextBatch, draw);
@@ -356,6 +414,7 @@ void StaticModel::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(Model);
SERIALIZE_MEMBER(LODBias, _lodBias);
SERIALIZE_MEMBER(ForcedLOD, _forcedLod);
SERIALIZE_MEMBER(SortOrder, _sortOrder);
SERIALIZE(DrawModes);
if (HasLightmap()
@@ -405,22 +464,9 @@ void StaticModel::Deserialize(DeserializeStream& stream, ISerializeModifier* mod
DESERIALIZE_MEMBER(ScaleInLightmap, _scaleInLightmap);
DESERIALIZE_MEMBER(BoundsScale, _boundsScale);
DESERIALIZE(Model);
{
const auto member = stream.FindMember("LODBias");
if (member != stream.MemberEnd() && member->value.IsInt())
{
SetLODBias(member->value.GetInt());
}
}
{
const auto member = stream.FindMember("ForcedLOD");
if (member != stream.MemberEnd() && member->value.IsInt())
{
SetForcedLOD(member->value.GetInt());
}
}
DESERIALIZE_MEMBER(LODBias, _lodBias);
DESERIALIZE_MEMBER(ForcedLOD, _forcedLod);
DESERIALIZE_MEMBER(SortOrder, _sortOrder);
DESERIALIZE(DrawModes);
DESERIALIZE_MEMBER(LightmapIndex, Lightmap.TextureIndex);
DESERIALIZE_MEMBER(LightmapArea, Lightmap.UVsArea);

View File

@@ -21,6 +21,7 @@ private:
char _forcedLod;
bool _vertexColorsDirty;
byte _vertexColorsCount;
int16 _sortOrder;
Array<Color32> _vertexColorsData[MODEL_MAX_LODS];
GPUBuffer* _vertexColorsBuffer[MODEL_MAX_LODS];
Model* _residencyChangedModel = nullptr;
@@ -53,10 +54,7 @@ public:
/// Gets the model scale in lightmap (applied to all the meshes).
/// </summary>
API_PROPERTY(Attributes="EditorOrder(10), DefaultValue(1.0f), EditorDisplay(\"Model\", \"Scale In Lightmap\"), Limit(0, 1000.0f, 0.1f)")
FORCE_INLINE float GetScaleInLightmap() const
{
return _scaleInLightmap;
}
float GetScaleInLightmap() const;
/// <summary>
/// Sets the model scale in lightmap (applied to all the meshes).
@@ -67,10 +65,7 @@ public:
/// Gets the model bounds scale. It is useful when using Position Offset to animate the vertices of the object outside of its bounds. Increasing the bounds of an object will reduce performance.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(12), DefaultValue(1.0f), EditorDisplay(\"Model\"), Limit(0, 10.0f, 0.1f)")
FORCE_INLINE float GetBoundsScale() const
{
return _boundsScale;
}
float GetBoundsScale() const;
/// <summary>
/// Sets the model bounds scale. It is useful when using Position Offset to animate the vertices of the object outside of its bounds.
@@ -81,51 +76,44 @@ public:
/// Gets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Model\", \"LOD Bias\")")
FORCE_INLINE int32 GetLODBias() const
{
return static_cast<int32>(_lodBias);
}
int32 GetLODBias() const;
/// <summary>
/// Sets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality.
/// </summary>
API_PROPERTY() void SetLODBias(int32 value)
{
_lodBias = static_cast<char>(Math::Clamp(value, -100, 100));
}
API_PROPERTY() void SetLODBias(int32 value);
/// <summary>
/// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Model\", \"Forced LOD\")")
FORCE_INLINE int32 GetForcedLOD() const
{
return static_cast<int32>(_forcedLod);
}
int32 GetForcedLOD() const;
/// <summary>
/// Sets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature.
/// </summary>
API_PROPERTY() void SetForcedLOD(int32 value)
{
_forcedLod = static_cast<char>(Math::Clamp(value, -1, 100));
}
API_PROPERTY() void SetForcedLOD(int32 value);
/// <summary>
/// Gets the model sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// </summary>
API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Model\")")
int32 GetSortOrder() const;
/// <summary>
/// Sets the model sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// </summary>
API_PROPERTY() void SetSortOrder(int32 value);
/// <summary>
/// Determines whether this model has valid lightmap data.
/// </summary>
API_PROPERTY() FORCE_INLINE bool HasLightmap() const
{
return Lightmap.TextureIndex != INVALID_INDEX;
}
API_PROPERTY() bool HasLightmap() const;
/// <summary>
/// Removes the lightmap data from the model.
/// </summary>
API_FUNCTION() FORCE_INLINE void RemoveLightmap()
{
Lightmap.TextureIndex = INVALID_INDEX;
}
API_FUNCTION() void RemoveLightmap();
/// <summary>
/// Gets the material used to render mesh at given index (overriden by model instance buffer or model default).
@@ -156,10 +144,7 @@ public:
/// <summary>
/// Returns true if model instance is using custom painted vertex colors buffer, otherwise it will use vertex colors from the original asset.
/// </summary>
API_PROPERTY() bool HasVertexColors() const
{
return _vertexColorsCount != 0;
}
API_PROPERTY() bool HasVertexColors() const;
/// <summary>
/// Removes the vertex colors buffer from this instance.

View File

@@ -576,6 +576,7 @@ void ParticleEffect::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(IsLooping);
SERIALIZE(UpdateWhenOffscreen);
SERIALIZE(DrawModes);
SERIALIZE(SortOrder);
}
void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
@@ -674,6 +675,7 @@ void ParticleEffect::Deserialize(DeserializeStream& stream, ISerializeModifier*
DESERIALIZE(IsLooping);
DESERIALIZE(UpdateWhenOffscreen);
DESERIALIZE(DrawModes);
DESERIALIZE(SortOrder);
if (_parameters.HasItems())
{

View File

@@ -246,6 +246,12 @@ public:
API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(75), DefaultValue(DrawPass.Default)")
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(80), DefaultValue(0)")
int16 SortOrder = 0;
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.

View File

@@ -154,7 +154,7 @@ void Particles::OnEffectDestroy(ParticleEffect* effect)
typedef Array<int32, FixedAllocation<PARTICLE_EMITTER_MAX_MODULES>> RenderModulesIndices;
void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices)
void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int16 sortOrder)
{
// Skip if CPU buffer is empty
if (buffer->CPU.Count == 0)
@@ -417,7 +417,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call
SpriteRenderer.SetupDrawCall(drawCall);
drawCall.InstanceCount = buffer->CPU.Count;
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder);
break;
}
@@ -445,7 +445,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
// Submit draw call
mesh.GetDrawCallGeometry(drawCall);
drawCall.InstanceCount = buffer->CPU.Count;
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder);
}
break;
@@ -505,7 +505,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.Draw.StartIndex = ribbonModulesDrawIndicesStart[ribbonModuleIndex];
drawCall.Draw.IndicesCount = ribbonModulesDrawIndicesCount[ribbonModuleIndex];
drawCall.InstanceCount = 1;
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder);
ribbonModuleIndex++;
@@ -574,7 +574,7 @@ void CleanupGPUParticlesSorting()
GPUParticlesSorting = nullptr;
}
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices)
void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCall& drawCall, DrawPass drawModes, StaticFlags staticFlags, ParticleEmitterInstance& emitterData, const RenderModulesIndices& renderModulesIndices, int16 sortOrder)
{
const auto context = GPUDevice::Instance->GetMainContext();
auto emitter = buffer->Emitter;
@@ -829,7 +829,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs);
if (dp != DrawPass::None)
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder);
indirectDrawCallIndex++;
break;
@@ -859,7 +859,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
drawCall.Draw.IndirectArgsBuffer = buffer->GPU.IndirectDrawArgsBuffer;
drawCall.Draw.IndirectArgsOffset = indirectDrawCallIndex * sizeof(GPUDrawIndexedIndirectArgs);
if (dp != DrawPass::None)
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false);
renderContext.List->AddDrawCall(renderContext, dp, staticFlags, drawCall, false, sortOrder);
indirectDrawCallIndex++;
}
@@ -887,13 +887,17 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
{
// Setup
auto& view = renderContext.View;
const auto drawModes = view.Pass & effect->DrawModes;
const DrawPass drawModes = view.Pass & effect->DrawModes;
if (drawModes == DrawPass::None || SpriteRenderer.Init())
return;
Matrix worlds[2];
Matrix::Translation(-renderContext.View.Origin, worlds[0]); // World
renderContext.View.GetWorldMatrix(effect->GetTransform(), worlds[1]); // Local
const auto staticFlags = effect->GetStaticFlags();
float worldDeterminantSigns[2];
worldDeterminantSigns[0] = Math::FloatSelect(worlds[0].RotDeterminant(), 1, -1);
worldDeterminantSigns[1] = Math::FloatSelect(worlds[1].RotDeterminant(), 1, -1);
const StaticFlags staticFlags = effect->GetStaticFlags();
const int16 sortOrder = effect->SortOrder;
// Draw lights
for (int32 emitterIndex = 0; emitterIndex < effect->Instance.Emitters.Count(); emitterIndex++)
@@ -922,7 +926,7 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
auto emitter = buffer->Emitter;
drawCall.World = worlds[(int32)emitter->SimulationSpace];
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.WorldDeterminantSign = worldDeterminantSigns[(int32)emitter->SimulationSpace];
drawCall.Particle.Particles = buffer;
// Check if need to render any module
@@ -1002,11 +1006,11 @@ void Particles::DrawParticles(RenderContext& renderContext, ParticleEffect* effe
switch (buffer->Mode)
{
case ParticlesSimulationMode::CPU:
DrawEmitterCPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices);
DrawEmitterCPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices, sortOrder);
break;
#if COMPILE_WITH_GPU_PARTICLES
case ParticlesSimulationMode::GPU:
DrawEmitterGPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices);
DrawEmitterGPU(renderContext, buffer, drawCall, drawModes, staticFlags, emitterData, renderModulesIndices, sortOrder);
break;
#endif
}

View File

@@ -412,12 +412,28 @@ void RenderList::Clear()
_instanceBuffer.Clear();
}
FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall& drawCall)
struct PackedSortKey
{
union
{
uint64 Data;
struct
{
uint32 DistanceKey;
uint16 BatchKey;
uint16 SortKey;
};
};
};
FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall& drawCall, int16 sortOrder)
{
const Float3 planeNormal = renderContext.View.Direction;
const float planePoint = -Float3::Dot(planeNormal, renderContext.View.Position);
const float distance = Float3::Dot(planeNormal, drawCall.ObjectPosition) - planePoint;
const uint32 sortKey = RenderTools::ComputeDistanceSortKey(distance);
PackedSortKey key;
key.DistanceKey = RenderTools::ComputeDistanceSortKey(distance);
uint32 batchKey = GetHash(drawCall.Geometry.IndexBuffer);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[0]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]);
@@ -427,10 +443,12 @@ FORCE_INLINE void CalculateSortKey(const RenderContext& renderContext, DrawCall&
if (drawCall.Material->CanUseInstancing(handler))
handler.GetHash(drawCall, batchKey);
batchKey += (int32)(471 * drawCall.WorldDeterminantSign);
drawCall.SortKey = (uint64)batchKey << 32 | (uint64)sortKey;
key.SortKey = (uint16)(sortOrder - MIN_int16);
key.BatchKey = (uint16)batchKey;
drawCall.SortKey = key.Data;
}
void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals)
void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals, int16 sortOrder)
{
#if ENABLE_ASSERTION_LOW_LAYERS
// Ensure that draw modes are non-empty and in conjunction with material draw modes
@@ -439,7 +457,7 @@ void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawMo
#endif
// Append draw call data
CalculateSortKey(renderContext, drawCall);
CalculateSortKey(renderContext, drawCall, sortOrder);
const int32 index = DrawCalls.Add(drawCall);
// Add draw call to proper draw lists
@@ -468,7 +486,7 @@ void RenderList::AddDrawCall(const RenderContext& renderContext, DrawPass drawMo
}
}
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals)
void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals, int16 sortOrder)
{
#if ENABLE_ASSERTION_LOW_LAYERS
// Ensure that draw modes are non-empty and in conjunction with material draw modes
@@ -478,7 +496,7 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
const RenderContext& mainRenderContext = renderContextBatch.Contexts.Get()[0];
// Append draw call data
CalculateSortKey(mainRenderContext, drawCall);
CalculateSortKey(mainRenderContext, drawCall, sortOrder);
const int32 index = DrawCalls.Add(drawCall);
// Add draw call to proper draw lists
@@ -514,7 +532,7 @@ void RenderList::AddDrawCall(const RenderContextBatch& renderContextBatch, DrawP
{
const RenderContext& renderContext = renderContextBatch.Contexts.Get()[i];
ASSERT_LOW_LAYER(renderContext.View.Pass == DrawPass::Depth);
drawModes = (DrawPass)(modes & renderContext.View.Pass);
drawModes = modes & renderContext.View.Pass;
if (drawModes != DrawPass::None && renderContext.View.CullingFrustum.Intersects(bounds))
{
renderContext.List->ShadowDepthDrawCallsList.Indices.Add(index);
@@ -558,16 +576,17 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
#undef PREPARE_CACHE
uint64* sortedKeys = SortingKeys[0].Get();
// Generate sort keys (by depth) and batch keys (higher bits)
// Setup sort keys
if (reverseDistance)
{
const uint32 sortKeyXor = reverseDistance ? MAX_uint32 : 0;
for (int32 i = 0; i < listSize; i++)
{
const DrawCall& drawCall = drawCallsData[listData[i]];
const uint32 sortKey = (uint32)drawCall.SortKey ^ sortKeyXor;
const uint32 batchKey = (uint32)(drawCall.SortKey >> 32);
sortedKeys[i] = (uint64)batchKey << 32 | (uint64)sortKey;
PackedSortKey key;
key.Data = drawCall.SortKey;
key.DistanceKey ^= MAX_uint32; // Reverse depth
key.SortKey ^= MAX_uint16; // Reverse sort order
sortedKeys[i] = key.Data;
}
}
else
@@ -605,7 +624,7 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
}
DrawBatch batch;
batch.SortKey = sortedKeys[i] & MAX_uint32;
batch.SortKey = sortedKeys[i];
batch.StartIndex = i;
batch.BatchSize = batchSize;
batch.InstanceCount = instanceCount;

View File

@@ -201,7 +201,7 @@ struct DrawBatch
/// <summary>
/// Draw calls sorting key (shared by the all draw calls withing a patch).
/// </summary>
uint32 SortKey;
uint64 SortKey;
/// <summary>
/// The first draw call index.
@@ -489,7 +489,8 @@ public:
/// <param name="staticFlags">The object static flags.</param>
/// <param name="drawCall">The draw call data.</param>
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals);
/// <param name="sortOrder">Object sorting key.</param>
void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals = true, int16 sortOrder = 0);
/// <summary>
/// Adds the draw call to the draw lists and references it in other render contexts. Performs additional per-context frustum culling.
@@ -501,7 +502,8 @@ public:
/// <param name="bounds">The object bounds.</param>
/// <param name="drawCall">The draw call data.</param>
/// <param name="receivesDecals">True if the rendered mesh can receive decals.</param>
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals);
/// <param name="sortOrder">Object sorting key.</param>
void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals = true, int16 sortOrder = 0);
/// <summary>
/// Sorts the collected draw calls list.

View File

@@ -130,7 +130,7 @@ void SpriteRender::Draw(RenderContext& renderContext)
view.GetWorldMatrix(_transform, m2);
Matrix::Multiply(m1, m2, world);
}
model->LODs[0].Draw(renderContext, _materialInstance, world, GetStaticFlags(), false, DrawModes, GetPerInstanceRandom());
model->LODs[0].Draw(renderContext, _materialInstance, world, GetStaticFlags(), false, DrawModes, GetPerInstanceRandom(), SortOrder);
}
void SpriteRender::Serialize(SerializeStream& stream, const void* otherObj)
@@ -147,6 +147,7 @@ void SpriteRender::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(Material);
SERIALIZE(FaceCamera);
SERIALIZE(DrawModes);
SERIALIZE(SortOrder);
}
void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
@@ -161,6 +162,7 @@ void SpriteRender::Deserialize(DeserializeStream& stream, ISerializeModifier* mo
DESERIALIZE(Material);
DESERIALIZE(FaceCamera);
DESERIALIZE(DrawModes);
DESERIALIZE(SortOrder);
SetImage();
if (_paramColor)

View File

@@ -84,6 +84,12 @@ public:
API_FIELD(Attributes="EditorOrder(50), DefaultValue(DrawPass.Default), EditorDisplay(\"Sprite\")")
DrawPass DrawModes = DrawPass::Default;
/// <summary>
/// Gets the object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Sprite\")")
int16 SortOrder = 0;
private:
void OnMaterialLoaded();
void SetImage();

View File

@@ -392,7 +392,7 @@ void TextRender::Draw(RenderContext& renderContext)
drawCall.Draw.IndicesCount = e.IndicesCount;
drawCall.Draw.StartIndex = e.StartIndex;
drawCall.Material = e.Material;
renderContext.List->AddDrawCall(renderContext, chunkDrawModes, GetStaticFlags(), drawCall, true);
renderContext.List->AddDrawCall(renderContext, chunkDrawModes, GetStaticFlags(), drawCall, true, SortOrder);
}
}
@@ -450,6 +450,7 @@ void TextRender::Serialize(SerializeStream& stream, const void* otherObj)
SERIALIZE(Font);
SERIALIZE(ShadowsMode);
SERIALIZE(DrawModes);
SERIALIZE(SortOrder);
SERIALIZE_MEMBER(Bounds, _layoutOptions.Bounds);
SERIALIZE_MEMBER(HAlignment, _layoutOptions.HorizontalAlignment);
SERIALIZE_MEMBER(VAlignment, _layoutOptions.VerticalAlignment);
@@ -470,6 +471,7 @@ void TextRender::Deserialize(DeserializeStream& stream, ISerializeModifier* modi
DESERIALIZE(Font);
DESERIALIZE(ShadowsMode);
DESERIALIZE(DrawModes);
DESERIALIZE(SortOrder);
DESERIALIZE_MEMBER(Bounds, _layoutOptions.Bounds);
DESERIALIZE_MEMBER(HAlignment, _layoutOptions.HorizontalAlignment);
DESERIALIZE_MEMBER(VAlignment, _layoutOptions.VerticalAlignment);

View File

@@ -110,6 +110,12 @@ public:
API_FIELD(Attributes="EditorOrder(80), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Text\")")
ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All;
/// <summary>
/// The object sort order key used when sorting drawable objects during rendering. Use lower values to draw object before others, higher values are rendered later (on top). Can be use to control transparency drawing.
/// </summary>
API_FIELD(Attributes="EditorOrder(85), DefaultValue(0), EditorDisplay(\"Text\")")
int16 SortOrder = 0;
/// <summary>
/// Gets the layout options. Layout is defined in local space of the object (on XY plane).
/// </summary>