diff --git a/Source/Editor/CustomEditors/Values/ValueContainer.cs b/Source/Editor/CustomEditors/Values/ValueContainer.cs index 92c44fbb5..dfec33f49 100644 --- a/Source/Editor/CustomEditors/Values/ValueContainer.cs +++ b/Source/Editor/CustomEditors/Values/ValueContainer.cs @@ -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); } } } diff --git a/Source/Editor/Utilities/ViewportIconsRenderer.cpp b/Source/Editor/Utilities/ViewportIconsRenderer.cpp index 657117c11..9b7f4979b 100644 --- a/Source/Editor/Utilities/ViewportIconsRenderer.cpp +++ b/Source/Editor/Utilities/ViewportIconsRenderer.cpp @@ -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(actor)) diff --git a/Source/Engine/Foliage/Foliage.cpp b/Source/Engine/Foliage/Foliage.cpp index 97688e196..23a31ad89 100644 --- a/Source/Engine/Foliage/Foliage.cpp +++ b/Source/Engine/Foliage/Foliage.cpp @@ -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; diff --git a/Source/Engine/Graphics/Models/Mesh.cpp b/Source/Engine/Graphics/Models/Mesh.cpp index 9dfcd72e8..b3ffae11c 100644 --- a/Source/Engine/Graphics/Models/Mesh.cpp +++ b/Source/Engine/Graphics/Models/Mesh.cpp @@ -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 diff --git a/Source/Engine/Graphics/Models/Mesh.h b/Source/Engine/Graphics/Models/Mesh.h index cc112bbe8..09c04ab34 100644 --- a/Source/Engine/Graphics/Models/Mesh.h +++ b/Source/Engine/Graphics/Models/Mesh.h @@ -339,6 +339,11 @@ public: /// The forced LOD to use. Value -1 disables this feature. /// char ForcedLOD; + + /// + /// The object sorting key. + /// + int16 SortOrder; }; /// @@ -357,7 +362,8 @@ public: /// True if rendered geometry can receive decals, otherwise false. /// The draw passes to use for rendering this object. /// The random per-instance value (normalized to range 0-1). - 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; + /// Object sorting key. + 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; /// /// Draws the mesh. diff --git a/Source/Engine/Graphics/Models/ModelLOD.h b/Source/Engine/Graphics/Models/ModelLOD.h index 4a452129d..c6cd33976 100644 --- a/Source/Engine/Graphics/Models/ModelLOD.h +++ b/Source/Engine/Graphics/Models/ModelLOD.h @@ -124,9 +124,7 @@ public: FORCE_INLINE void Render(GPUContext* context) { for (int32 i = 0; i < Meshes.Count(); i++) - { Meshes.Get()[i].Render(context); - } } /// @@ -139,12 +137,11 @@ public: /// True if rendered geometry can receive decals, otherwise false. /// The draw passes to use for rendering this object. /// The random per-instance value (normalized to range 0-1). - 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 + /// Object sorting key. + 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); } /// @@ -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); - } } /// @@ -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); - } } }; diff --git a/Source/Engine/Level/Actors/Camera.cpp b/Source/Engine/Level/Actors/Camera.cpp index 064d6344a..ea161c38a 100644 --- a/Source/Engine/Level/Actors/Camera.cpp +++ b/Source/Engine/Level/Actors/Camera.cpp @@ -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) { diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index ef6446874..17c99a40a 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -25,6 +25,7 @@ StaticModel::StaticModel(const SpawnParams& params) , _forcedLod(-1) , _vertexColorsDirty(false) , _vertexColorsCount(0) + , _sortOrder(0) { _drawCategory = SceneRendering::SceneDrawAsync; Model.Changed.Bind(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(Math::Clamp(value, -100, 100)); +} + +int32 StaticModel::GetForcedLOD() const +{ + return _forcedLod; +} + +void StaticModel::SetForcedLOD(int32 value) +{ + _forcedLod = static_cast(Math::Clamp(value, -1, 100)); +} + +int32 StaticModel::GetSortOrder() const +{ + return _sortOrder; +} + +void StaticModel::SetSortOrder(int32 value) +{ + _sortOrder = (int16)Math::Clamp(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); diff --git a/Source/Engine/Level/Actors/StaticModel.h b/Source/Engine/Level/Actors/StaticModel.h index 4b4356218..af3aa53c0 100644 --- a/Source/Engine/Level/Actors/StaticModel.h +++ b/Source/Engine/Level/Actors/StaticModel.h @@ -21,6 +21,7 @@ private: char _forcedLod; bool _vertexColorsDirty; byte _vertexColorsCount; + int16 _sortOrder; Array _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). /// 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; /// /// 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. /// 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; /// /// 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. /// API_PROPERTY(Attributes="EditorOrder(40), DefaultValue(0), Limit(-100, 100, 0.1f), EditorDisplay(\"Model\", \"LOD Bias\")") - FORCE_INLINE int32 GetLODBias() const - { - return static_cast(_lodBias); - } + int32 GetLODBias() const; /// /// Sets the model Level Of Detail bias value. Allows to increase or decrease rendered model quality. /// - API_PROPERTY() void SetLODBias(int32 value) - { - _lodBias = static_cast(Math::Clamp(value, -100, 100)); - } + API_PROPERTY() void SetLODBias(int32 value); /// /// Gets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature. /// API_PROPERTY(Attributes="EditorOrder(50), DefaultValue(-1), Limit(-1, 100, 0.1f), EditorDisplay(\"Model\", \"Forced LOD\")") - FORCE_INLINE int32 GetForcedLOD() const - { - return static_cast(_forcedLod); - } + int32 GetForcedLOD() const; /// /// Sets the model forced Level Of Detail index. Allows to bind the given model LOD to show. Value -1 disables this feature. /// - API_PROPERTY() void SetForcedLOD(int32 value) - { - _forcedLod = static_cast(Math::Clamp(value, -1, 100)); - } + API_PROPERTY() void SetForcedLOD(int32 value); + + /// + /// 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. + /// + API_PROPERTY(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Model\")") + int32 GetSortOrder() const; + + /// + /// 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. + /// + API_PROPERTY() void SetSortOrder(int32 value); /// /// Determines whether this model has valid lightmap data. /// - API_PROPERTY() FORCE_INLINE bool HasLightmap() const - { - return Lightmap.TextureIndex != INVALID_INDEX; - } + API_PROPERTY() bool HasLightmap() const; /// /// Removes the lightmap data from the model. /// - API_FUNCTION() FORCE_INLINE void RemoveLightmap() - { - Lightmap.TextureIndex = INVALID_INDEX; - } + API_FUNCTION() void RemoveLightmap(); /// /// Gets the material used to render mesh at given index (overriden by model instance buffer or model default). @@ -156,10 +144,7 @@ public: /// /// Returns true if model instance is using custom painted vertex colors buffer, otherwise it will use vertex colors from the original asset. /// - API_PROPERTY() bool HasVertexColors() const - { - return _vertexColorsCount != 0; - } + API_PROPERTY() bool HasVertexColors() const; /// /// Removes the vertex colors buffer from this instance. diff --git a/Source/Engine/Particles/ParticleEffect.cpp b/Source/Engine/Particles/ParticleEffect.cpp index 43531589a..4b8cec37a 100644 --- a/Source/Engine/Particles/ParticleEffect.cpp +++ b/Source/Engine/Particles/ParticleEffect.cpp @@ -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()) { diff --git a/Source/Engine/Particles/ParticleEffect.h b/Source/Engine/Particles/ParticleEffect.h index 789738bc0..6ace026af 100644 --- a/Source/Engine/Particles/ParticleEffect.h +++ b/Source/Engine/Particles/ParticleEffect.h @@ -246,6 +246,12 @@ public: API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(75), DefaultValue(DrawPass.Default)") DrawPass DrawModes = DrawPass::Default; + /// + /// 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. + /// + API_FIELD(Attributes="EditorDisplay(\"Particle Effect\"), EditorOrder(80), DefaultValue(0)") + int16 SortOrder = 0; + public: /// /// Gets the effect parameters collection. Those parameters are instanced from the that contains a linear list of emitters and every emitter has a list of own parameters. diff --git a/Source/Engine/Particles/Particles.cpp b/Source/Engine/Particles/Particles.cpp index 8e3756a19..89b787df2 100644 --- a/Source/Engine/Particles/Particles.cpp +++ b/Source/Engine/Particles/Particles.cpp @@ -154,7 +154,7 @@ void Particles::OnEffectDestroy(ParticleEffect* effect) typedef Array> 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 } diff --git a/Source/Engine/Renderer/RenderList.cpp b/Source/Engine/Renderer/RenderList.cpp index 2f8fba76f..53e007e17 100644 --- a/Source/Engine/Renderer/RenderList.cpp +++ b/Source/Engine/Renderer/RenderList.cpp @@ -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; diff --git a/Source/Engine/Renderer/RenderList.h b/Source/Engine/Renderer/RenderList.h index 44eabcdc1..493f80101 100644 --- a/Source/Engine/Renderer/RenderList.h +++ b/Source/Engine/Renderer/RenderList.h @@ -201,7 +201,7 @@ struct DrawBatch /// /// Draw calls sorting key (shared by the all draw calls withing a patch). /// - uint32 SortKey; + uint64 SortKey; /// /// The first draw call index. @@ -489,7 +489,8 @@ public: /// The object static flags. /// The draw call data. /// True if the rendered mesh can receive decals. - void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals); + /// Object sorting key. + void AddDrawCall(const RenderContext& renderContext, DrawPass drawModes, StaticFlags staticFlags, DrawCall& drawCall, bool receivesDecals = true, int16 sortOrder = 0); /// /// 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: /// The object bounds. /// The draw call data. /// True if the rendered mesh can receive decals. - void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals); + /// Object sorting key. + void AddDrawCall(const RenderContextBatch& renderContextBatch, DrawPass drawModes, StaticFlags staticFlags, ShadowsCastingMode shadowsMode, const BoundingSphere& bounds, DrawCall& drawCall, bool receivesDecals = true, int16 sortOrder = 0); /// /// Sorts the collected draw calls list. diff --git a/Source/Engine/UI/SpriteRender.cpp b/Source/Engine/UI/SpriteRender.cpp index 0ff23d52b..fb4f54e7f 100644 --- a/Source/Engine/UI/SpriteRender.cpp +++ b/Source/Engine/UI/SpriteRender.cpp @@ -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) diff --git a/Source/Engine/UI/SpriteRender.h b/Source/Engine/UI/SpriteRender.h index 2405c5c67..8027fab8d 100644 --- a/Source/Engine/UI/SpriteRender.h +++ b/Source/Engine/UI/SpriteRender.h @@ -84,6 +84,12 @@ public: API_FIELD(Attributes="EditorOrder(50), DefaultValue(DrawPass.Default), EditorDisplay(\"Sprite\")") DrawPass DrawModes = DrawPass::Default; + /// + /// 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. + /// + API_FIELD(Attributes="EditorOrder(60), DefaultValue(0), EditorDisplay(\"Sprite\")") + int16 SortOrder = 0; + private: void OnMaterialLoaded(); void SetImage(); diff --git a/Source/Engine/UI/TextRender.cpp b/Source/Engine/UI/TextRender.cpp index c1d173870..d4db794eb 100644 --- a/Source/Engine/UI/TextRender.cpp +++ b/Source/Engine/UI/TextRender.cpp @@ -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); diff --git a/Source/Engine/UI/TextRender.h b/Source/Engine/UI/TextRender.h index 6764b0e61..d69b5427b 100644 --- a/Source/Engine/UI/TextRender.h +++ b/Source/Engine/UI/TextRender.h @@ -110,6 +110,12 @@ public: API_FIELD(Attributes="EditorOrder(80), DefaultValue(ShadowsCastingMode.All), EditorDisplay(\"Text\")") ShadowsCastingMode ShadowsMode = ShadowsCastingMode::All; + /// + /// 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. + /// + API_FIELD(Attributes="EditorOrder(85), DefaultValue(0), EditorDisplay(\"Text\")") + int16 SortOrder = 0; + /// /// Gets the layout options. Layout is defined in local space of the object (on XY plane). ///