Refactor draw calls and instancing logic to be more modular

This commit is contained in:
Wojtek Figat
2021-02-03 09:33:48 +01:00
parent 81cc8cf69c
commit a560b19cbc
23 changed files with 255 additions and 268 deletions

View File

@@ -61,9 +61,9 @@ bool Material::CanUseLightmap() const
return _materialShader && _materialShader->CanUseLightmap();
}
bool Material::CanUseInstancing() const
bool Material::CanUseInstancing(InstancingHandler& handler) const
{
return _materialShader && _materialShader->CanUseInstancing();
return _materialShader && _materialShader->CanUseInstancing(handler);
}
void Material::Bind(BindParameters& params)

View File

@@ -48,7 +48,7 @@ public:
bool IsReady() const override;
DrawPass GetDrawModes() const override;
bool CanUseLightmap() const override;
bool CanUseInstancing() const override;
bool CanUseInstancing(InstancingHandler& handler) const override;
void Bind(BindParameters& params) override;
// [ShaderAssetBase]

View File

@@ -167,9 +167,9 @@ bool MaterialInstance::CanUseLightmap() const
return _baseMaterial && _baseMaterial->CanUseLightmap();
}
bool MaterialInstance::CanUseInstancing() const
bool MaterialInstance::CanUseInstancing(InstancingHandler& handler) const
{
return _baseMaterial && _baseMaterial->CanUseInstancing();
return _baseMaterial && _baseMaterial->CanUseInstancing(handler);
}
void MaterialInstance::Bind(BindParameters& params)

View File

@@ -64,7 +64,7 @@ public:
bool IsReady() const override;
DrawPass GetDrawModes() const override;
bool CanUseLightmap() const override;
bool CanUseInstancing() const override;
bool CanUseInstancing(InstancingHandler& handler) const override;
void Bind(BindParameters& params) override;
protected:

View File

@@ -5,6 +5,7 @@
#include "Engine/Graphics/RenderBuffers.h"
#include "Engine/Graphics/RenderView.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/RenderList.h"
#include "Engine/Level/Scene/Lightmap.h"
#include "Engine/Graphics/GPUContext.h"
#include "Engine/Graphics/Shaders/GPUConstantBuffer.h"
@@ -48,8 +49,9 @@ bool DeferredMaterialShader::CanUseLightmap() const
return true;
}
bool DeferredMaterialShader::CanUseInstancing() const
bool DeferredMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, SurfaceDrawCallHandler::WriteDrawCall, };
return true;
}
@@ -79,7 +81,7 @@ void DeferredMaterialShader::Bind(BindParameters& params)
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(view.View, materialData->ViewMatrix);
Matrix::Transpose(drawCall.PrevWorld, materialData->PrevWorldMatrix);
Matrix::Transpose(drawCall.Surface.PrevWorld, materialData->PrevWorldMatrix);
Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix);
materialData->ViewPos = view.Position;
@@ -100,40 +102,40 @@ void DeferredMaterialShader::Bind(BindParameters& params)
materialData->WorldInvScale = worldInvScale;
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->LODDitherFactor = drawCall.LODDitherFactor;
materialData->LODDitherFactor = drawCall.Surface.LODDitherFactor;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->TemporalAAJitter = view.TemporalAAJitter;
materialData->GeometrySize = drawCall.GeometrySize;
materialData->GeometrySize = drawCall.Surface.GeometrySize;
}
const bool useLightmap = view.Flags & ViewFlags::GI
#if USE_EDITOR
&& EnableLightmapsUsage
#endif
&& drawCall.Lightmap != nullptr;
&& drawCall.Surface.Lightmap != nullptr;
if (useLightmap)
{
// Bind lightmap textures
GPUTexture *lightmap0, *lightmap1, *lightmap2;
drawCall.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
drawCall.Surface.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
context->BindSR(0, lightmap0);
context->BindSR(1, lightmap1);
context->BindSR(2, lightmap2);
// Set lightmap data
materialData->LightmapArea = drawCall.LightmapUVsArea;
materialData->LightmapArea = drawCall.Surface.LightmapUVsArea;
}
// Check if is using mesh skinning
const bool useSkinning = drawCall.Skinning != nullptr;
const bool useSkinning = drawCall.Surface.Skinning != nullptr;
bool perBoneMotionBlur = false;
if (useSkinning)
{
// Bind skinning buffer
ASSERT(drawCall.Skinning->IsReady());
context->BindSR(0, drawCall.Skinning->BoneMatrices->View());
if (drawCall.Skinning->PrevBoneMatrices && drawCall.Skinning->PrevBoneMatrices->IsAllocated())
ASSERT(drawCall.Surface.Skinning->IsReady());
context->BindSR(0, drawCall.Surface.Skinning->BoneMatrices->View());
if (drawCall.Surface.Skinning->PrevBoneMatrices && drawCall.Surface.Skinning->PrevBoneMatrices->IsAllocated())
{
context->BindSR(1, drawCall.Skinning->PrevBoneMatrices->View());
context->BindSR(1, drawCall.Surface.Skinning->PrevBoneMatrices->View());
perBoneMotionBlur = true;
}
}
@@ -152,7 +154,7 @@ void DeferredMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale())
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)

View File

@@ -66,7 +66,7 @@ public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
bool CanUseLightmap() const override;
bool CanUseInstancing() const override;
bool CanUseInstancing(InstancingHandler& handler) const override;
void Bind(BindParameters& params) override;
void Unload() override;

View File

@@ -10,6 +10,7 @@
#include "Engine/Renderer/DepthOfFieldPass.h"
#include "Engine/Renderer/DrawCall.h"
#include "Engine/Renderer/ShadowsPass.h"
#include "Engine/Renderer/RenderList.h"
#if USE_EDITOR
#include "Engine/Renderer/Lightmaps.h"
#endif
@@ -54,8 +55,9 @@ DrawPass ForwardMaterialShader::GetDrawModes() const
return _drawModes;
}
bool ForwardMaterialShader::CanUseInstancing() const
bool ForwardMaterialShader::CanUseInstancing(InstancingHandler& handler) const
{
handler = { SurfaceDrawCallHandler::GetHash, SurfaceDrawCallHandler::CanBatch, SurfaceDrawCallHandler::WriteDrawCall, };
return true;
}
@@ -82,12 +84,12 @@ void ForwardMaterialShader::Bind(BindParameters& params)
MaterialParams::Bind(params.ParamsLink, bindMeta);
// Check if is using mesh skinning
const bool useSkinning = drawCall.Skinning != nullptr;
const bool useSkinning = drawCall.Surface.Skinning != nullptr;
if (useSkinning)
{
// Bind skinning buffer
ASSERT(drawCall.Skinning->IsReady());
context->BindSR(0, drawCall.Skinning->BoneMatrices->View());
ASSERT(drawCall.Surface.Skinning->IsReady());
context->BindSR(0, drawCall.Surface.Skinning->BoneMatrices->View());
}
// Setup material constants data
@@ -97,7 +99,7 @@ void ForwardMaterialShader::Bind(BindParameters& params)
Matrix::Transpose(view.Frustum.GetMatrix(), materialData->ViewProjectionMatrix);
Matrix::Transpose(drawCall.World, materialData->WorldMatrix);
Matrix::Transpose(view.View, materialData->ViewMatrix);
Matrix::Transpose(drawCall.PrevWorld, materialData->PrevWorldMatrix);
Matrix::Transpose(drawCall.Surface.PrevWorld, materialData->PrevWorldMatrix);
Matrix::Transpose(view.PrevViewProjection, materialData->PrevViewProjectionMatrix);
materialData->ViewPos = view.Position;
@@ -118,9 +120,9 @@ void ForwardMaterialShader::Bind(BindParameters& params)
materialData->WorldInvScale = worldInvScale;
materialData->WorldDeterminantSign = drawCall.WorldDeterminantSign;
materialData->LODDitherFactor = drawCall.LODDitherFactor;
materialData->LODDitherFactor = drawCall.Surface.LODDitherFactor;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->GeometrySize = drawCall.GeometrySize;
materialData->GeometrySize = drawCall.Surface.GeometrySize;
}
// Setup lighting constants data
@@ -249,7 +251,7 @@ void ForwardMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale())
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)

View File

@@ -67,7 +67,7 @@ public:
// [MaterialShader]
DrawPass GetDrawModes() const override;
bool CanUseInstancing() const override;
bool CanUseInstancing(InstancingHandler& handler) const override;
void Bind(BindParameters& params) override;
void Unload() override;

View File

@@ -2,7 +2,6 @@
#pragma once
#include "Engine/Threading/Task.h"
#include "MaterialInfo.h"
struct MaterialParamsLink;
@@ -81,24 +80,6 @@ public:
return GetInfo().Domain == MaterialDomain::Particle;
}
/// <summary>
/// Checks if material needs vertex color vertex buffer data for rendering.
/// </summary>
/// <returns>True if mesh renderer have to provide vertex color buffer to render that material</returns>
FORCE_INLINE bool RequireVertexColor() const
{
return (GetInfo().UsageFlags & MaterialUsageFlags::UseVertexColor) != 0;
}
/// <summary>
/// Checks if material supports dithered LOD transitions.
/// </summary>
/// <returns>True if material supports dithered LOD transitions, otherwise false.</returns>
FORCE_INLINE bool IsDitheredLODTransition() const
{
return (GetInfo().FeaturesFlags & MaterialFeaturesFlags::DitheredLODTransition) != 0;
}
/// <summary>
/// Returns true if material is ready for rendering.
/// </summary>
@@ -123,11 +104,22 @@ public:
return false;
}
/// <summary>
/// The instancing handling used to hash, batch and write draw calls.
/// </summary>
struct InstancingHandler
{
void (*GetHash)(const DrawCall& drawCall, int32& batchKey);
bool (*CanBatch)(const DrawCall& a, const DrawCall& b);
void (*WriteDrawCall)(struct InstanceData* instanceData, const DrawCall& drawCall);
};
/// <summary>
/// Returns true if material can use draw calls instancing.
/// </summary>
/// <param name="handler">The output data for the instancing handling used to hash, batch and write draw calls. Valid only when function returns true.</param>
/// <returns>True if can use instancing, otherwise false.</returns>
virtual bool CanUseInstancing() const
virtual bool CanUseInstancing(InstancingHandler& handler) const
{
return false;
}

View File

@@ -76,7 +76,7 @@ void ParticleMaterialShader::Bind(BindParameters& params)
const bool hasCb0 = cb0->GetSize() != 0;
const auto cb1 = _shader->GetCB(1);
const bool hasCb1 = cb1->GetSize() != 0;
const uint32 sortedIndicesOffset = drawCall.Module->SortedIndicesOffset;
const uint32 sortedIndicesOffset = drawCall.Particle.Module->SortedIndicesOffset;
// Setup parameters
MaterialParameter::BindMeta bindMeta;
@@ -90,9 +90,9 @@ void ParticleMaterialShader::Bind(BindParameters& params)
// Setup particles data and attributes binding info
{
context->BindSR(0, drawCall.Particles->GPU.Buffer->View());
if (drawCall.Particles->GPU.SortedIndices)
context->BindSR(1, drawCall.Particles->GPU.SortedIndices->View());
context->BindSR(0, drawCall.Particle.Particles->GPU.Buffer->View());
if (drawCall.Particle.Particles->GPU.SortedIndices)
context->BindSR(1, drawCall.Particle.Particles->GPU.SortedIndices->View());
if (hasCb0)
{
@@ -104,7 +104,7 @@ void ParticleMaterialShader::Bind(BindParameters& params)
{
auto name = StringView(param.GetName().Get() + 9);
const int32 offset = drawCall.Particles->Layout->FindAttributeOffset(name);
const int32 offset = drawCall.Particle.Particles->Layout->FindAttributeOffset(name);
*((int32*)(bindMeta.Buffer0 + param.GetBindOffset())) = offset;
}
}
@@ -115,7 +115,7 @@ void ParticleMaterialShader::Bind(BindParameters& params)
const bool wireframe = (_info.FeaturesFlags & MaterialFeaturesFlags::Wireframe) != 0 || view.Mode == ViewMode::Wireframe;
CullMode cullMode = view.Pass == DrawPass::Depth ? CullMode::TwoSided : _info.CullMode;
PipelineStateCache* psCache = nullptr;
switch (drawCall.Module->TypeID)
switch (drawCall.Particle.Module->TypeID)
{
// Sprite Rendering
case 400:
@@ -142,20 +142,20 @@ void ParticleMaterialShader::Bind(BindParameters& params)
{
const auto materialData = reinterpret_cast<ParticleMaterialShaderData*>(_cb0Data.Get());
materialData->RibbonWidthOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1);
materialData->RibbonTwistOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1);
materialData->RibbonFacingVectorOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->RibbonWidthOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonWidth, ParticleAttribute::ValueTypes::Float, -1);
materialData->RibbonTwistOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonTwist, ParticleAttribute::ValueTypes::Float, -1);
materialData->RibbonFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRibbonFacingVector, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->RibbonUVTilingDistance = drawCall.Ribbon.UVTilingDistance;
materialData->RibbonUVScale.X = drawCall.Ribbon.UVScaleX;
materialData->RibbonUVScale.Y = drawCall.Ribbon.UVScaleY;
materialData->RibbonUVOffset.X = drawCall.Ribbon.UVOffsetX;
materialData->RibbonUVOffset.Y = drawCall.Ribbon.UVOffsetY;
materialData->RibbonSegmentCount = drawCall.Ribbon.SegmentCount;
materialData->RibbonUVTilingDistance = drawCall.Particle.Ribbon.UVTilingDistance;
materialData->RibbonUVScale.X = drawCall.Particle.Ribbon.UVScaleX;
materialData->RibbonUVScale.Y = drawCall.Particle.Ribbon.UVScaleY;
materialData->RibbonUVOffset.X = drawCall.Particle.Ribbon.UVOffsetX;
materialData->RibbonUVOffset.Y = drawCall.Particle.Ribbon.UVOffsetY;
materialData->RibbonSegmentCount = drawCall.Particle.Ribbon.SegmentCount;
}
if (drawCall.Ribbon.SegmentDistances)
context->BindSR(1, drawCall.Ribbon.SegmentDistances->View());
if (drawCall.Particle.Ribbon.SegmentDistances)
context->BindSR(1, drawCall.Particle.Ribbon.SegmentDistances->View());
break;
}
@@ -186,17 +186,17 @@ void ParticleMaterialShader::Bind(BindParameters& params)
materialData->TimeParam = Time::Draw.UnscaledTime.GetTotalSeconds();
materialData->ViewInfo = view.ViewInfo;
materialData->ScreenSize = view.ScreenSize;
materialData->SortedIndicesOffset = drawCall.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? sortedIndicesOffset : 0xFFFFFFFF;
materialData->SortedIndicesOffset = drawCall.Particle.Particles->GPU.SortedIndices && params.RenderContext.View.Pass != DrawPass::Depth ? sortedIndicesOffset : 0xFFFFFFFF;
materialData->PerInstanceRandom = drawCall.PerInstanceRandom;
materialData->ParticleStride = drawCall.Particles->Stride;
materialData->PositionOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticlePosition, ParticleAttribute::ValueTypes::Vector3);
materialData->SpriteSizeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteSize, ParticleAttribute::ValueTypes::Vector2);
materialData->SpriteFacingModeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingMode, ParticleAttribute::ValueTypes::Int, -1);
materialData->SpriteFacingVectorOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingVector, ParticleAttribute::ValueTypes::Vector3);
materialData->VelocityOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleVelocityOffset, ParticleAttribute::ValueTypes::Vector3);
materialData->RotationOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleRotationOffset, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->ScaleOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleScaleOffset, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->ModelFacingModeOffset = drawCall.Particles->Layout->FindAttributeOffset(ParticleModelFacingModeOffset, ParticleAttribute::ValueTypes::Int, -1);
materialData->ParticleStride = drawCall.Particle.Particles->Stride;
materialData->PositionOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticlePosition, ParticleAttribute::ValueTypes::Vector3);
materialData->SpriteSizeOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleSpriteSize, ParticleAttribute::ValueTypes::Vector2);
materialData->SpriteFacingModeOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingMode, ParticleAttribute::ValueTypes::Int, -1);
materialData->SpriteFacingVectorOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleSpriteFacingVector, ParticleAttribute::ValueTypes::Vector3);
materialData->VelocityOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleVelocityOffset, ParticleAttribute::ValueTypes::Vector3);
materialData->RotationOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleRotationOffset, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->ScaleOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleScaleOffset, ParticleAttribute::ValueTypes::Vector3, -1);
materialData->ModelFacingModeOffset = drawCall.Particle.Particles->Layout->FindAttributeOffset(ParticleModelFacingModeOffset, ParticleAttribute::ValueTypes::Int, -1);
Matrix::Invert(drawCall.World, materialData->WorldMatrixInverseTransposed);
}

View File

@@ -93,36 +93,36 @@ void TerrainMaterialShader::Bind(BindParameters& params)
data->WorldDeterminantSign = drawCall.WorldDeterminantSign;
data->PerInstanceRandom = drawCall.PerInstanceRandom;
data->CurrentLOD = drawCall.TerrainData.CurrentLOD;
data->ChunkSizeNextLOD = drawCall.TerrainData.ChunkSizeNextLOD;
data->TerrainChunkSizeLOD0 = drawCall.TerrainData.TerrainChunkSizeLOD0;
data->HeightmapUVScaleBias = drawCall.TerrainData.HeightmapUVScaleBias;
data->NeighborLOD = drawCall.TerrainData.NeighborLOD;
data->OffsetUV = drawCall.TerrainData.OffsetUV;
data->CurrentLOD = drawCall.Terrain.CurrentLOD;
data->ChunkSizeNextLOD = drawCall.Terrain.ChunkSizeNextLOD;
data->TerrainChunkSizeLOD0 = drawCall.Terrain.TerrainChunkSizeLOD0;
data->HeightmapUVScaleBias = drawCall.Terrain.HeightmapUVScaleBias;
data->NeighborLOD = drawCall.Terrain.NeighborLOD;
data->OffsetUV = drawCall.Terrain.OffsetUV;
}
const bool useLightmap = view.Flags & ViewFlags::GI
#if USE_EDITOR
&& EnableLightmapsUsage
#endif
&& view.Pass == DrawPass::GBuffer
&& drawCall.Lightmap != nullptr;
&& drawCall.Terrain.Lightmap != nullptr;
if (useLightmap)
{
// Bind lightmap textures
GPUTexture *lightmap0, *lightmap1, *lightmap2;
drawCall.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
drawCall.Terrain.Lightmap->GetTextures(&lightmap0, &lightmap1, &lightmap2);
context->BindSR(0, lightmap0);
context->BindSR(1, lightmap1);
context->BindSR(2, lightmap2);
// Set lightmap data
data->LightmapArea = drawCall.LightmapUVsArea;
data->LightmapArea = drawCall.Terrain.LightmapUVsArea;
}
// Bind terrain textures
const auto heightmap = drawCall.TerrainData.Patch->Heightmap.Get()->GetTexture();
const auto splatmap0 = drawCall.TerrainData.Patch->Splatmap[0] ? drawCall.TerrainData.Patch->Splatmap[0]->GetTexture() : nullptr;
const auto splatmap1 = drawCall.TerrainData.Patch->Splatmap[1] ? drawCall.TerrainData.Patch->Splatmap[1]->GetTexture() : nullptr;
const auto heightmap = drawCall.Terrain.Patch->Heightmap.Get()->GetTexture();
const auto splatmap0 = drawCall.Terrain.Patch->Splatmap[0] ? drawCall.Terrain.Patch->Splatmap[0]->GetTexture() : nullptr;
const auto splatmap1 = drawCall.Terrain.Patch->Splatmap[1] ? drawCall.Terrain.Patch->Splatmap[1]->GetTexture() : nullptr;
context->BindSR(3, heightmap);
context->BindSR(4, splatmap0);
context->BindSR(5, splatmap1);
@@ -141,7 +141,7 @@ void TerrainMaterialShader::Bind(BindParameters& params)
if (IsRunningRadiancePass)
cullMode = CullMode::TwoSided;
#endif
if (cullMode != CullMode::TwoSided && drawCall.IsNegativeScale())
if (cullMode != CullMode::TwoSided && drawCall.WorldDeterminantSign < 0)
{
// Invert culling when scale is negative
if (cullMode == CullMode::Normal)

View File

@@ -393,15 +393,15 @@ void Mesh::Draw(const RenderContext& renderContext, MaterialBase* material, cons
drawCall.IndirectArgsOffset = 0;
drawCall.Material = material;
drawCall.World = world;
drawCall.PrevWorld = world;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.GeometrySize = _box.GetSize();
drawCall.Lightmap = nullptr;
drawCall.LightmapUVsArea = Rectangle::Empty;
drawCall.Skinning = nullptr;
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = world;
drawCall.Surface.Lightmap = nullptr;
drawCall.Surface.LightmapUVsArea = Rectangle::Empty;
drawCall.Surface.Skinning = nullptr;
drawCall.Surface.LODDitherFactor = 0.0f;
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = 0.0f;
drawCall.LODDitherFactor = 0.0f;
renderContext.List->AddDrawCall(DrawPass::Default, flags, drawCall, receiveDecals);
}
@@ -456,15 +456,15 @@ void Mesh::Draw(const RenderContext& renderContext, const DrawInfo& info, float
drawCall.IndirectArgsOffset = 0;
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.PrevWorld = info.DrawState->PrevWorld;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.GeometrySize = _box.GetSize();
drawCall.Lightmap = info.Flags & StaticFlags::Lightmap ? info.Lightmap : nullptr;
drawCall.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Skinning = nullptr;
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = info.Flags & StaticFlags::Lightmap ? info.Lightmap : nullptr;
drawCall.Surface.LightmapUVsArea = info.LightmapUVs ? *info.LightmapUVs : Rectangle::Empty;
drawCall.Surface.Skinning = nullptr;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.LODDitherFactor = lodDitherFactor;
renderContext.List->AddDrawCall(drawModes, info.Flags, drawCall, entry.ReceiveDecals);
}

View File

@@ -193,15 +193,15 @@ void SkinnedMesh::Draw(const RenderContext& renderContext, const DrawInfo& info,
drawCall.IndirectArgsOffset = 0;
drawCall.Material = material;
drawCall.World = *info.World;
drawCall.PrevWorld = info.DrawState->PrevWorld;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.GeometrySize = _box.GetSize();
drawCall.Lightmap = nullptr;
drawCall.LightmapUVsArea = Rectangle::Empty;
drawCall.Skinning = info.Skinning;
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.Surface.PrevWorld = info.DrawState->PrevWorld;
drawCall.Surface.Lightmap = nullptr;
drawCall.Surface.LightmapUVsArea = Rectangle::Empty;
drawCall.Surface.Skinning = info.Skinning;
drawCall.Surface.LODDitherFactor = lodDitherFactor;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = info.PerInstanceRandom;
drawCall.LODDitherFactor = lodDitherFactor;
renderContext.List->AddDrawCall(drawModes, StaticFlags::None, drawCall, entry.ReceiveDecals);
}

View File

@@ -93,7 +93,7 @@ void Skybox::ApplySky(GPUContext* context, RenderContext& renderContext, const M
Platform::MemoryClear(&drawCall, sizeof(DrawCall));
drawCall.World = world;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.GeometrySize = _box.GetSize();
drawCall.Surface.GeometrySize = _box.GetSize();
drawCall.WorldDeterminantSign = Math::FloatSelect(world.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = GetPerInstanceRandom();
MaterialBase::BindParameters bindParams(context, renderContext, drawCall);

View File

@@ -423,7 +423,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
{
const int32 moduleIndex = renderModulesIndices[index];
auto module = emitter->Graph.RenderModules[moduleIndex];
drawCall.Module = module;
drawCall.Particle.Module = module;
switch (module->TypeID)
{
@@ -486,7 +486,7 @@ void DrawEmitterCPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
int32 count = buffer->CPU.Count;
// Setup ribbon data
auto& ribbon = drawCall.Ribbon;
auto& ribbon = drawCall.Particle.Ribbon;
ribbon.UVTilingDistance = uvTilingDistance;
ribbon.SegmentCount = ribbonModulesSegmentCount[ribbonModuleIndex];
ribbon.UVScaleX = uvScale.X;
@@ -788,7 +788,7 @@ void DrawEmitterGPU(RenderContext& renderContext, ParticleBuffer* buffer, DrawCa
{
int32 moduleIndex = renderModulesIndices[index];
auto module = emitter->Graph.RenderModules[moduleIndex];
drawCall.Module = module;
drawCall.Particle.Module = module;
switch (module->TypeID)
{
@@ -875,9 +875,7 @@ void ParticleManager::DrawParticles(RenderContext& renderContext, ParticleEffect
// Setup a draw call common data
DrawCall drawCall;
drawCall.LightmapUVsArea = Rectangle::Empty;
drawCall.PerInstanceRandom = effect->GetPerInstanceRandom();
drawCall.LODDitherFactor = 1.0f;
drawCall.ObjectPosition = world.GetTranslation();
// Draw all emitters
@@ -890,9 +888,8 @@ void ParticleManager::DrawParticles(RenderContext& renderContext, ParticleEffect
auto emitter = buffer->Emitter;
drawCall.World = emitter->SimulationSpace == ParticlesSimulationSpace::World ? Matrix::Identity : world;
drawCall.PrevWorld = drawCall.World;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.Particles = buffer;
drawCall.Particle.Particles = buffer;
// Check if need to render any module
RenderModulesIndices renderModulesIndices;

View File

@@ -113,6 +113,11 @@ public:
/// </summary>
struct DrawCall
{
/// <summary>
/// The material to use for rendering.
/// </summary>
IMaterial* Material;
struct
{
/// <summary>
@@ -156,53 +161,18 @@ struct DrawCall
/// </summary>
GPUBuffer* IndirectArgsBuffer;
/// <summary>
/// The target material to use.
/// </summary>
IMaterial* Material;
// Particles don't use skinning nor lightmaps so pack those stuff together
// Per-material shader data packed into union
union
{
struct
{
/// <summary>
/// Pointer to lightmap for static object with prebaked lighting.
/// </summary>
const Lightmap* Lightmap;
/// <summary>
/// The skinning data. If set then material should use GPU skinning during rendering.
/// </summary>
SkinnedMeshDrawData* Skinning;
};
struct
{
/// <summary>
/// The particles data. Used only by the particles shaders.
/// </summary>
class ParticleBuffer* Particles;
/// <summary>
/// The particle module to draw.
/// </summary>
class ParticleEmitterGraphCPUNode* Module;
};
};
/// <summary>
/// Object world transformation matrix.
/// </summary>
Matrix World;
// Terrain and particles don't use previous world matrix so pack those stuff together
union
{
/// <summary>
/// Object world transformation matrix using during previous frame.
/// </summary>
Matrix PrevWorld;
Rectangle LightmapUVsArea;
Vector3 GeometrySize; // Object geometry size in the world (unscaled).
float LODDitherFactor; // The model LOD transition dither progress.
Matrix PrevWorld;
} Surface;
struct
{
@@ -212,26 +182,39 @@ struct DrawCall
float CurrentLOD;
float ChunkSizeNextLOD;
float TerrainChunkSizeLOD0;
Rectangle LightmapUVsArea;
const class TerrainPatch* Patch;
} TerrainData;
const Lightmap* Lightmap;
} Terrain;
struct
{
int32 RibbonOrderOffset;
float UVTilingDistance;
float UVScaleX;
float UVScaleY;
float UVOffsetX;
float UVOffsetY;
uint32 SegmentCount;
GPUBuffer* SegmentDistances;
} Ribbon;
class ParticleBuffer* Particles;
class ParticleEmitterGraphCPUNode* Module;
struct
{
int32 OrderOffset;
float UVTilingDistance;
float UVScaleX;
float UVScaleY;
float UVOffsetX;
float UVOffsetY;
uint32 SegmentCount;
GPUBuffer* SegmentDistances;
} Ribbon;
} Particle;
struct
{
byte Raw[96];
} Custom;
};
/// <summary>
/// Lightmap UVs area that entry occupies.
/// Object world transformation matrix.
/// </summary>
Rectangle LightmapUVsArea;
Matrix World;
/// <summary>
/// Object location in the world used for draw calls sorting.
@@ -243,36 +226,17 @@ struct DrawCall
/// </summary>
float WorldDeterminantSign;
/// <summary>
/// Object geometry size in the world (unscaled).
/// </summary>
Vector3 GeometrySize;
/// <summary>
/// The random per-instance value (normalized to range 0-1).
/// </summary>
float PerInstanceRandom;
/// <summary>
/// The model LOD transition dither factor.
/// </summary>
float LODDitherFactor;
/// <summary>
/// Does nothing.
/// </summary>
DrawCall()
{
}
/// <summary>
/// Determines whether world transform matrix is performing negative scale (then model culling should be inverted).
/// </summary>
/// <returns><c>true</c> if world matrix contains negative scale; otherwise, <c>false</c>.</returns>
FORCE_INLINE bool IsNegativeScale() const
{
return WorldDeterminantSign < 0;
}
};
template<>

View File

@@ -178,7 +178,7 @@ void LightmapUVsDensityMaterialShader::Bind(BindParameters& params)
scaleZ > 0.00001f ? 1.0f / scaleZ : 0.0f);
data.LightmapTexelsPerWorldUnit = ShadowsOfMordor::LightmapTexelsPerWorldUnit;
data.LightmapSize = 1024.0f;
data.LightmapArea = drawCall.LightmapUVsArea;
data.LightmapArea = drawCall.Surface.LightmapUVsArea;
if (drawCallModel)
{
// Calculate current lightmap slot size for the object (matches the ShadowsOfMordor calculations when baking the lighting)

View File

@@ -145,7 +145,8 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
drawCall.Material = _lightmapUVsDensityMaterialShader;
}
}
if (!_lightmapUVsDensityMaterialShader->CanUseInstancing())
IMaterial::InstancingHandler handler;
if (!_lightmapUVsDensityMaterialShader->CanUseInstancing(handler))
{
drawCallsList.CanUseInstancing = false;
}
@@ -166,7 +167,8 @@ void GBufferPass::Fill(RenderContext& renderContext, GPUTextureView* lightBuffer
drawCall.Material = _vertexColorsMaterialShader;
}
}
if (!_vertexColorsMaterialShader->CanUseInstancing())
IMaterial::InstancingHandler handler;
if (!_vertexColorsMaterialShader->CanUseInstancing(handler))
{
drawCallsList.CanUseInstancing = false;
}
@@ -311,8 +313,6 @@ void GBufferPass::DrawDecals(RenderContext& renderContext, GPUTextureView* light
DrawCall drawCall;
MaterialBase::BindParameters bindParams(gpuContext, renderContext, drawCall);
drawCall.Material = nullptr;
drawCall.Lightmap = nullptr;
drawCall.Skinning = nullptr;
drawCall.WorldDeterminantSign = 1.0f;
// Draw all decals

View File

@@ -19,6 +19,11 @@
#define BATCH_KEY_BITS 32
#define BATCH_KEY_MASK ((1 << BATCH_KEY_BITS) - 1)
static_assert(sizeof(DrawCall) <= 280, "Too big draw call data size.");
static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Terrain), "Wrong draw call data size.");
static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Particle), "Wrong draw call data size.");
static_assert(sizeof(DrawCall::Surface) >= sizeof(DrawCall::Custom), "Wrong draw call data size.");
namespace
{
// Cached data for the draw calls sorting
@@ -495,24 +500,24 @@ end:
}
}
/// <summary>
/// Checks if this draw call be batched together with the other one.
/// </summary>
/// <param name="a">The first draw call.</param>
/// <param name="b">The second draw call.</param>
/// <returns>True if can merge them, otherwise false.</returns>
FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b)
namespace
{
return Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 &&
a.Material == b.Material &&
a.Lightmap == b.Lightmap &&
// TODO: add batch.CanBatch flag computed in AddDrawCall to remove those checks here for Skinning and IndirectDrawArgs
a.Skinning == nullptr &&
b.Skinning == nullptr &&
a.IndirectArgsBuffer == nullptr &&
b.IndirectArgsBuffer == nullptr &&
a.WorldDeterminantSign == b.WorldDeterminantSign &&
a.Material->CanUseInstancing();
/// <summary>
/// Checks if this draw call be batched together with the other one.
/// </summary>
/// <param name="a">The first draw call.</param>
/// <param name="b">The second draw call.</param>
/// <returns>True if can merge them, otherwise false.</returns>
FORCE_INLINE bool CanBatchWith(const DrawCall& a, const DrawCall& b)
{
IMaterial::InstancingHandler handler;
return a.Material == b.Material &&
a.Material->CanUseInstancing(handler) &&
Platform::MemoryCompare(&a.Geometry, &b.Geometry, sizeof(a.Geometry)) == 0 &&
a.IndirectArgsBuffer == nullptr &&
b.IndirectArgsBuffer == nullptr &&
a.WorldDeterminantSign == b.WorldDeterminantSign;
}
}
void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseDistance, DrawCallsList& list)
@@ -540,7 +545,9 @@ void RenderList::SortDrawCalls(const RenderContext& renderContext, bool reverseD
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[1]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Geometry.VertexBuffers[2]);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Material);
batchKey = (batchKey * 397) ^ GetHash(drawCall.Lightmap);
IMaterial::InstancingHandler handler;
if (drawCall.Material->CanUseInstancing(handler))
handler.GetHash(drawCall, batchKey);
batchKey += (int32)(471 * drawCall.WorldDeterminantSign);
#if USE_BATCH_KEY_MASK
const uint32 batchHashKey = (uint32)batchKey & BATCH_KEY_MASK;
@@ -635,14 +642,12 @@ void RenderList::ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsL
auto& batch = list.Batches[i];
if (batch.BatchSize > 1)
{
IMaterial::InstancingHandler handler;
DrawCalls[list.Indices[batch.StartIndex]].Material->CanUseInstancing(handler);
for (int32 j = 0; j < batch.BatchSize; j++)
{
auto& drawCall = DrawCalls[list.Indices[batch.StartIndex + j]];
instanceData->InstanceOrigin = Vector4(drawCall.World.M41, drawCall.World.M42, drawCall.World.M43, drawCall.PerInstanceRandom);
instanceData->InstanceTransform1 = Vector4(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13, drawCall.LODDitherFactor);
instanceData->InstanceTransform2 = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23);
instanceData->InstanceTransform3 = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33);
instanceData->InstanceLightmapArea = Half4(drawCall.LightmapUVsArea);
handler.WriteDrawCall(instanceData, drawCall);
instanceData++;
}
}
@@ -742,3 +747,26 @@ DRAW:
}
}
}
void SurfaceDrawCallHandler::GetHash(const DrawCall& drawCall, int32& batchKey)
{
batchKey = (batchKey * 397) ^ ::GetHash(drawCall.Surface.Lightmap);
}
bool SurfaceDrawCallHandler::CanBatch(const DrawCall& a, const DrawCall& b)
{
return a.Surface.Lightmap == b.Surface.Lightmap &&
a.Surface.Skinning == nullptr &&
b.Surface.Skinning == nullptr;
}
void SurfaceDrawCallHandler::WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall)
{
instanceData->InstanceOrigin = Vector3(drawCall.World.M41, drawCall.World.M42, drawCall.World.M43);
instanceData->PerInstanceRandom = drawCall.PerInstanceRandom;
instanceData->InstanceTransform1 = Vector3(drawCall.World.M11, drawCall.World.M12, drawCall.World.M13);
instanceData->LODDitherFactor = drawCall.Surface.LODDitherFactor;
instanceData->InstanceTransform2 = Vector3(drawCall.World.M21, drawCall.World.M22, drawCall.World.M23);
instanceData->InstanceTransform3 = Vector3(drawCall.World.M31, drawCall.World.M32, drawCall.World.M33);
instanceData->InstanceLightmapArea = Half4(drawCall.Surface.LightmapUVsArea);
}

View File

@@ -357,18 +357,6 @@ public:
private:
/// <summary>
/// Represents data per instance element used for instanced rendering.
/// </summary>
struct InstanceData
{
Vector4 InstanceOrigin; // .w contains PerInstanceRandom
Vector4 InstanceTransform1; // .w contains LODDitherFactor
Vector3 InstanceTransform2;
Vector3 InstanceTransform3;
Half4 InstanceLightmapArea;
};
DynamicVertexBuffer _instanceBuffer;
public:
@@ -475,3 +463,24 @@ public:
/// <param name="list">The collected draw calls list.</param>
void ExecuteDrawCalls(const RenderContext& renderContext, DrawCallsList& list);
};
/// <summary>
/// Represents data per instance element used for instanced rendering.
/// </summary>
struct FLAXENGINE_API InstanceData
{
Vector3 InstanceOrigin;
float PerInstanceRandom;
Vector3 InstanceTransform1;
float LODDitherFactor;
Vector3 InstanceTransform2;
Vector3 InstanceTransform3;
Half4 InstanceLightmapArea;
};
struct SurfaceDrawCallHandler
{
static void GetHash(const DrawCall& drawCall, int32& batchKey);
static bool CanBatch(const DrawCall& a, const DrawCall& b);
static void WriteDrawCall(InstanceData* instanceData, const DrawCall& drawCall);
};

View File

@@ -31,9 +31,6 @@
#include "Editor/Editor.h"
#endif
// It must use less or the same amount of memory
static_assert(sizeof(DrawCall::TerrainData) <= sizeof(DrawCall::PrevWorld), "Invalid size of the terrain data in the draw call.");
#if USE_EDITOR
// Additional options used in editor for lightmaps baking
bool IsRunningRadiancePass = false;

View File

@@ -88,33 +88,31 @@ void TerrainChunk::Draw(const RenderContext& renderContext) const
drawCall.Material = _cachedDrawMaterial;
drawCall.World = _world;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.TerrainData.Patch = _patch;
drawCall.TerrainData.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.TerrainData.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.TerrainData.CurrentLOD = (float)lod;
drawCall.TerrainData.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.TerrainData.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
drawCall.Terrain.Patch = _patch;
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.Terrain.CurrentLOD = (float)lod;
drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
// TODO: try using SIMD clamping for 4 chunks at once
drawCall.TerrainData.NeighborLOD.X = (float)Math::Clamp<int32>(_neighbors[0]->_cachedDrawLOD, lod, minLod);
drawCall.TerrainData.NeighborLOD.Y = (float)Math::Clamp<int32>(_neighbors[1]->_cachedDrawLOD, lod, minLod);
drawCall.TerrainData.NeighborLOD.Z = (float)Math::Clamp<int32>(_neighbors[2]->_cachedDrawLOD, lod, minLod);
drawCall.TerrainData.NeighborLOD.W = (float)Math::Clamp<int32>(_neighbors[3]->_cachedDrawLOD, lod, minLod);
drawCall.Terrain.NeighborLOD.X = (float)Math::Clamp<int32>(_neighbors[0]->_cachedDrawLOD, lod, minLod);
drawCall.Terrain.NeighborLOD.Y = (float)Math::Clamp<int32>(_neighbors[1]->_cachedDrawLOD, lod, minLod);
drawCall.Terrain.NeighborLOD.Z = (float)Math::Clamp<int32>(_neighbors[2]->_cachedDrawLOD, lod, minLod);
drawCall.Terrain.NeighborLOD.W = (float)Math::Clamp<int32>(_neighbors[3]->_cachedDrawLOD, lod, minLod);
const auto scene = _patch->_terrain->GetScene();
const auto flags = _patch->_terrain->_staticFlags;
if (flags & StaticFlags::Lightmap && scene)
{
drawCall.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
drawCall.LightmapUVsArea = Lightmap.UVsArea;
drawCall.Terrain.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
drawCall.Terrain.LightmapUVsArea = Lightmap.UVsArea;
}
else
{
drawCall.Lightmap = nullptr;
drawCall.LightmapUVsArea = Rectangle::Empty;
drawCall.Terrain.Lightmap = nullptr;
drawCall.Terrain.LightmapUVsArea = Rectangle::Empty;
}
drawCall.Skinning = nullptr;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = _perInstanceRandom;
drawCall.LODDitherFactor = 0.0f;
// Add half-texel offset for heightmap sampling in vertex shader
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);
@@ -147,32 +145,30 @@ void TerrainChunk::Draw(const RenderContext& renderContext, MaterialBase* materi
drawCall.Material = material;
drawCall.World = _world;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.TerrainData.Patch = _patch;
drawCall.TerrainData.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.TerrainData.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.TerrainData.CurrentLOD = (float)lod;
drawCall.TerrainData.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.TerrainData.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
drawCall.TerrainData.NeighborLOD.X = (float)lod;
drawCall.TerrainData.NeighborLOD.Y = (float)lod;
drawCall.TerrainData.NeighborLOD.Z = (float)lod;
drawCall.TerrainData.NeighborLOD.W = (float)lod;
drawCall.Terrain.Patch = _patch;
drawCall.Terrain.HeightmapUVScaleBias = _heightmapUVScaleBias;
drawCall.Terrain.OffsetUV = Vector2((float)(_patch->_x * TerrainPatch::CHUNKS_COUNT_EDGE + _x), (float)(_patch->_z * TerrainPatch::CHUNKS_COUNT_EDGE + _z));
drawCall.Terrain.CurrentLOD = (float)lod;
drawCall.Terrain.ChunkSizeNextLOD = (float)(((chunkSize + 1) >> (lod + 1)) - 1);
drawCall.Terrain.TerrainChunkSizeLOD0 = TERRAIN_UNITS_PER_VERTEX * chunkSize;
drawCall.Terrain.NeighborLOD.X = (float)lod;
drawCall.Terrain.NeighborLOD.Y = (float)lod;
drawCall.Terrain.NeighborLOD.Z = (float)lod;
drawCall.Terrain.NeighborLOD.W = (float)lod;
const auto scene = _patch->_terrain->GetScene();
const auto flags = _patch->_terrain->_staticFlags;
if (flags & StaticFlags::Lightmap && scene)
{
drawCall.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
drawCall.LightmapUVsArea = Lightmap.UVsArea;
drawCall.Terrain.Lightmap = scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex);
drawCall.Terrain.LightmapUVsArea = Lightmap.UVsArea;
}
else
{
drawCall.Lightmap = nullptr;
drawCall.LightmapUVsArea = Rectangle::Empty;
drawCall.Terrain.Lightmap = nullptr;
drawCall.Terrain.LightmapUVsArea = Rectangle::Empty;
}
drawCall.Skinning = nullptr;
drawCall.WorldDeterminantSign = Math::FloatSelect(drawCall.World.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = _perInstanceRandom;
drawCall.LODDitherFactor = 0.0f;
// Add half-texel offset for heightmap sampling in vertex shader
//const float lodHeightmapSize = Math::Max(1, drawCall.TerrainData.Heightmap->Width() >> lod);

View File

@@ -319,15 +319,15 @@ void TextRender::Draw(RenderContext& renderContext)
// Setup draw call
DrawCall drawCall;
drawCall.World = _world;
drawCall.PrevWorld = _drawState.PrevWorld;
drawCall.ObjectPosition = drawCall.World.GetTranslation();
drawCall.GeometrySize = _localBox.GetSize();
drawCall.Lightmap = nullptr;
drawCall.LightmapUVsArea = Rectangle::Empty;
drawCall.Skinning = nullptr;
drawCall.Surface.GeometrySize = _localBox.GetSize();
drawCall.Surface.PrevWorld = _drawState.PrevWorld;
drawCall.Surface.Lightmap = nullptr;
drawCall.Surface.LightmapUVsArea = Rectangle::Empty;
drawCall.Surface.Skinning = nullptr;
drawCall.Surface.LODDitherFactor = 0.0f;
drawCall.WorldDeterminantSign = Math::FloatSelect(_world.RotDeterminant(), 1, -1);
drawCall.PerInstanceRandom = GetPerInstanceRandom();
drawCall.LODDitherFactor = 0.0f;
drawCall.Geometry.IndexBuffer = _ib.GetBuffer();
drawCall.Geometry.VertexBuffers[0] = _vb0.GetBuffer();
drawCall.Geometry.VertexBuffers[1] = _vb1.GetBuffer();