diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp index d7172e091..340726644 100644 --- a/Source/Engine/Content/Assets/Model.cpp +++ b/Source/Engine/Content/Assets/Model.cpp @@ -16,6 +16,7 @@ #include "Engine/Graphics/Async/GPUTask.h" #include "Engine/Graphics/Async/Tasks/GPUUploadTextureMipTask.h" #include "Engine/Graphics/Models/MeshDeformation.h" +#include "Engine/Graphics/Models/ModelDraw.h" #include "Engine/Graphics/Textures/GPUTexture.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" @@ -136,105 +137,6 @@ void Model::Draw(const RenderContext& renderContext, MaterialBase* material, con LODs[lodIndex].Draw(renderContext, material, world, flags, receiveDecals, DrawPass::Default, 0, sortOrder); } -template -FORCE_INLINE void ModelDraw(Model* model, const RenderContext& renderContext, const ContextType& context, const Mesh::DrawInfo& info) -{ - ASSERT(info.Buffer); - if (!model->CanBeRendered()) - return; - if (!info.Buffer->IsValidFor(model)) - info.Buffer->Setup(model); - const auto frame = Engine::FrameCount; - const auto modelFrame = info.DrawState->PrevFrame + 1; - - // Select a proper LOD index (model may be culled) - int32 lodIndex; - if (info.ForcedLOD != -1) - { - lodIndex = info.ForcedLOD; - } - else - { - lodIndex = RenderTools::ComputeModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); - if (lodIndex == -1) - { - // Handling model fade-out transition - if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame) - { - // Check if start transition - if (info.DrawState->LODTransition == 255) - { - info.DrawState->LODTransition = 0; - } - - RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition); - - // Check if end transition - if (info.DrawState->LODTransition == 255) - { - info.DrawState->PrevLOD = lodIndex; - } - else - { - const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); - const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress); - } - } - - return; - } - } - lodIndex += info.LODBias + renderContext.View.ModelLODBias; - lodIndex = model->ClampLODIndex(lodIndex); - - if (renderContext.View.IsSingleFrame) - { - } - // Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports) - else if (modelFrame == frame) - { - // Check if start transition - if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255) - { - info.DrawState->LODTransition = 0; - } - - RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition); - - // Check if end transition - if (info.DrawState->LODTransition == 255) - { - info.DrawState->PrevLOD = lodIndex; - } - } - // Check if there was a gap between frames in drawing this model instance - else if (modelFrame < frame || info.DrawState->PrevLOD == -1) - { - // Reset state - info.DrawState->PrevLOD = lodIndex; - info.DrawState->LODTransition = 255; - } - - // Draw - if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame) - { - model->LODs.Get()[lodIndex].Draw(context, info, 0.0f); - } - else if (info.DrawState->PrevLOD == -1) - { - const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress); - } - else - { - const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); - const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress); - model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f); - } -} - void Model::Draw(const RenderContext& renderContext, const Mesh::DrawInfo& info) { ModelDraw(this, renderContext, renderContext, info); diff --git a/Source/Engine/Content/Assets/SkinnedModel.cpp b/Source/Engine/Content/Assets/SkinnedModel.cpp index c0355ea0e..965df12f0 100644 --- a/Source/Engine/Content/Assets/SkinnedModel.cpp +++ b/Source/Engine/Content/Assets/SkinnedModel.cpp @@ -12,6 +12,7 @@ #include "Engine/Graphics/Models/Config.h" #include "Engine/Graphics/Models/MeshDeformation.h" #include "Engine/Graphics/Models/ModelInstanceEntry.h" +#include "Engine/Graphics/Models/ModelDraw.h" #include "Engine/Graphics/Shaders/GPUVertexLayout.h" #include "Engine/Content/Content.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" @@ -234,113 +235,14 @@ BoundingBox SkinnedModel::GetBox(int32 lodIndex) const return LODs[lodIndex].GetBox(); } -template -FORCE_INLINE void SkinnedModelDraw(SkinnedModel* model, const RenderContext& renderContext, const ContextType& context, const SkinnedMesh::DrawInfo& info) -{ - ASSERT(info.Buffer); - if (!model->CanBeRendered()) - return; - if (!info.Buffer->IsValidFor(model)) - info.Buffer->Setup(model); - const auto frame = Engine::FrameCount; - const auto modelFrame = info.DrawState->PrevFrame + 1; - - // Select a proper LOD index (model may be culled) - int32 lodIndex; - if (info.ForcedLOD != -1) - { - lodIndex = info.ForcedLOD; - } - else - { - lodIndex = RenderTools::ComputeSkinnedModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); - if (lodIndex == -1) - { - // Handling model fade-out transition - if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame) - { - // Check if start transition - if (info.DrawState->LODTransition == 255) - { - info.DrawState->LODTransition = 0; - } - - RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition); - - // Check if end transition - if (info.DrawState->LODTransition == 255) - { - info.DrawState->PrevLOD = lodIndex; - } - else - { - const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); - const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress); - } - } - - return; - } - } - lodIndex += info.LODBias + renderContext.View.ModelLODBias; - lodIndex = model->ClampLODIndex(lodIndex); - - if (renderContext.View.IsSingleFrame) - { - } - // Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports) - else if (modelFrame == frame) - { - // Check if start transition - if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255) - { - info.DrawState->LODTransition = 0; - } - - RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition); - - // Check if end transition - if (info.DrawState->LODTransition == 255) - { - info.DrawState->PrevLOD = lodIndex; - } - } - // Check if there was a gap between frames in drawing this model instance - else if (modelFrame < frame || info.DrawState->PrevLOD == -1) - { - // Reset state - info.DrawState->PrevLOD = lodIndex; - info.DrawState->LODTransition = 255; - } - - // Draw - if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame) - { - model->LODs.Get()[lodIndex].Draw(context, info, 0.0f); - } - else if (info.DrawState->PrevLOD == -1) - { - const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress); - } - else - { - const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); - const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); - model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress); - model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f); - } -} - void SkinnedModel::Draw(const RenderContext& renderContext, const SkinnedMesh::DrawInfo& info) { - SkinnedModelDraw(this, renderContext, renderContext, info); + ModelDraw(this, renderContext, renderContext, info); } void SkinnedModel::Draw(const RenderContextBatch& renderContextBatch, const SkinnedMesh::DrawInfo& info) { - SkinnedModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info); + ModelDraw(this, renderContextBatch.GetMainContext(), renderContextBatch, info); } bool SkinnedModel::SetupLODs(const Span& meshesCountPerLod) diff --git a/Source/Engine/Graphics/Models/ModelDraw.h b/Source/Engine/Graphics/Models/ModelDraw.h new file mode 100644 index 000000000..3e2a61242 --- /dev/null +++ b/Source/Engine/Graphics/Models/ModelDraw.h @@ -0,0 +1,130 @@ +// Copyright (c) Wojciech Figat. All rights reserved. + +#pragma once + +#include "Engine/Engine/Engine.h" +#include "Engine/Graphics/RenderTask.h" +#include "Engine/Graphics/RenderTools.h" +#include "Engine/Graphics/Materials/MaterialInfo.h" + +template +FORCE_INLINE bool ModelDrawTransition(ModelType* model, const DrawInfoType& info) +{ + for (auto& e : *info.Buffer) + { + if (e.Material && EnumHasAllFlags(e.Material->GetInfo().FeaturesFlags, MaterialFeaturesFlags::DitheredLODTransition)) + return true; + } + for (auto& e : model->MaterialSlots) + { + if (e.Material && EnumHasAllFlags(e.Material->GetInfo().FeaturesFlags, MaterialFeaturesFlags::DitheredLODTransition)) + return true; + } + return false; +} + +template +FORCE_INLINE void ModelDraw(ModelType* model, const RenderContext& renderContext, const ContextType& context, const DrawInfoType& info) +{ + ASSERT(info.Buffer); + if (!model->CanBeRendered()) + return; + if (!info.Buffer->IsValidFor(model)) + info.Buffer->Setup(model); + const auto frame = Engine::FrameCount; + const auto modelFrame = info.DrawState->PrevFrame + 1; + + // Select a proper LOD index (model may be culled) + int32 lodIndex; + if (info.ForcedLOD != -1) + { + lodIndex = info.ForcedLOD; + } + else + { + lodIndex = RenderTools::ComputeModelLOD(model, info.Bounds.Center, (float)info.Bounds.Radius, renderContext); + if (lodIndex == -1) + { + // Handling model fade-out transition + if (modelFrame == frame && info.DrawState->PrevLOD != -1 && !renderContext.View.IsSingleFrame && ModelDrawTransition(model, info)) + { + // Check if start transition + if (info.DrawState->LODTransition == 255) + { + info.DrawState->LODTransition = 0; + } + + RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition); + + // Check if end transition + if (info.DrawState->LODTransition == 255) + { + info.DrawState->PrevLOD = lodIndex; + } + else + { + const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); + const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); + model->LODs.Get()[prevLOD].Draw(renderContext, info, normalizedProgress); + } + } + + return; + } + } + lodIndex += info.LODBias + renderContext.View.ModelLODBias; + lodIndex = model->ClampLODIndex(lodIndex); + + if (renderContext.View.IsSingleFrame) + { + } + // Check if it's the new frame and could update the drawing state (note: model instance could be rendered many times per frame to different viewports) + else if (modelFrame == frame) + { + // Check if materials use transition + if (!ModelDrawTransition(model, info)) + { + info.DrawState->PrevLOD = lodIndex; + info.DrawState->LODTransition = 255; + } + + // Check if start transition + if (info.DrawState->PrevLOD != lodIndex && info.DrawState->LODTransition == 255) + { + info.DrawState->LODTransition = 0; + } + + RenderTools::UpdateModelLODTransition(info.DrawState->LODTransition); + + // Check if end transition + if (info.DrawState->LODTransition == 255) + { + info.DrawState->PrevLOD = lodIndex; + } + } + // Check if there was a gap between frames in drawing this model instance + else if (modelFrame < frame || info.DrawState->PrevLOD == -1) + { + // Reset state + info.DrawState->PrevLOD = lodIndex; + info.DrawState->LODTransition = 255; + } + + // Draw + if (info.DrawState->PrevLOD == lodIndex || renderContext.View.IsSingleFrame) + { + model->LODs.Get()[lodIndex].Draw(context, info, 0.0f); + } + else if (info.DrawState->PrevLOD == -1) + { + const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); + model->LODs.Get()[lodIndex].Draw(context, info, 1.0f - normalizedProgress); + } + else + { + const auto prevLOD = model->ClampLODIndex(info.DrawState->PrevLOD); + const float normalizedProgress = static_cast(info.DrawState->LODTransition) * (1.0f / 255.0f); + model->LODs.Get()[prevLOD].Draw(context, info, normalizedProgress); + model->LODs.Get()[lodIndex].Draw(context, info, normalizedProgress - 1.0f); + } +} diff --git a/Source/Engine/Graphics/RenderTools.cpp b/Source/Engine/Graphics/RenderTools.cpp index 0a98f1401..5a7ca9079 100644 --- a/Source/Engine/Graphics/RenderTools.cpp +++ b/Source/Engine/Graphics/RenderTools.cpp @@ -461,7 +461,7 @@ int32 RenderTools::ComputeModelLOD(const Model* model, const Float3& origin, flo return 0; } -int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float3& origin, float radius, const RenderContext& renderContext) +int32 RenderTools::ComputeModelLOD(const SkinnedModel* model, const Float3& origin, float radius, const RenderContext& renderContext) { const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); const float screenRadiusSquared = ComputeBoundsScreenRadiusSquared(origin, radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt; @@ -486,6 +486,11 @@ int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float return 0; } +int32 RenderTools::ComputeSkinnedModelLOD(const SkinnedModel* model, const Float3& origin, float radius, const RenderContext& renderContext) +{ + return ComputeModelLOD(model, origin, radius, renderContext); +} + void RenderTools::ComputeCascadeUpdateFrequency(int32 cascadeIndex, int32 cascadeCount, int32& updateFrequency, int32& updatePhrase, int32 updateMaxCountPerFrame) { switch (updateMaxCountPerFrame) diff --git a/Source/Engine/Graphics/RenderTools.h b/Source/Engine/Graphics/RenderTools.h index 7be73c912..8c565eed5 100644 --- a/Source/Engine/Graphics/RenderTools.h +++ b/Source/Engine/Graphics/RenderTools.h @@ -109,15 +109,26 @@ public: /// The zero-based LOD index. Returns -1 if model should not be rendered. API_FUNCTION() static int32 ComputeModelLOD(const Model* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext); + /// + /// Computes the model LOD index to use during rendering. + /// + /// The model. + /// The bounds origin. + /// The bounds radius. + /// The rendering context. + /// The zero-based LOD index. Returns -1 if model should not be rendered. + API_FUNCTION() static int32 ComputeModelLOD(const SkinnedModel* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext); + /// /// Computes the skinned model LOD index to use during rendering. + /// [Deprecated in v1.12] /// /// The skinned model. /// The bounds origin. /// The bounds radius. /// The rendering context. /// The zero-based LOD index. Returns -1 if model should not be rendered. - API_FUNCTION() static int32 ComputeSkinnedModelLOD(const SkinnedModel* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext); + API_FUNCTION() DEPRECATED("Use ComputeModelLOD instead.") static int32 ComputeSkinnedModelLOD(const SkinnedModel* model, API_PARAM(Ref) const Float3& origin, float radius, API_PARAM(Ref) const RenderContext& renderContext); /// /// Computes the sorting key for depth value (quantized)