diff --git a/Source/Engine/Level/Actors/StaticModel.cpp b/Source/Engine/Level/Actors/StaticModel.cpp index 6e50fb174..e3b760912 100644 --- a/Source/Engine/Level/Actors/StaticModel.cpp +++ b/Source/Engine/Level/Actors/StaticModel.cpp @@ -9,6 +9,7 @@ #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Prefabs/PrefabManager.h" #include "Engine/Level/Scene/Scene.h" +#include "Engine/Renderer/GlobalSignDistanceFieldPass.h" #include "Engine/Utilities/Encryption.h" #if USE_EDITOR #include "Editor/Editor.h" @@ -209,62 +210,66 @@ bool StaticModel::HasContentLoaded() const void StaticModel::Draw(RenderContext& renderContext) { + const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass); + if (!Model || !Model->IsLoaded() || !Model->CanBeRendered() || drawModes == DrawPass::None) + return; if (renderContext.View.Pass == DrawPass::GlobalSDF) - return; // TODO: Static Model rendering to Global SDF + { + if (!Model->SDF.Texture) + Model->GenerateSDF(); + GlobalSignDistanceFieldPass::Instance()->RasterizeModelSDF(Model->SDF, _world, _box); + return; + } GEOMETRY_DRAW_STATE_EVENT_BEGIN(_drawState, _world); - const DrawPass drawModes = (DrawPass)(DrawModes & renderContext.View.Pass); - if (Model && Model->IsLoaded() && drawModes != DrawPass::None) + // Flush vertex colors if need to + if (_vertexColorsDirty) { - // Flush vertex colors if need to - if (_vertexColorsDirty) + for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++) { - for (int32 lodIndex = 0; lodIndex < _vertexColorsCount; lodIndex++) + auto& vertexColorsData = _vertexColorsData[lodIndex]; + auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex]; + if (vertexColorsData.HasItems()) { - auto& vertexColorsData = _vertexColorsData[lodIndex]; - auto& vertexColorsBuffer = _vertexColorsBuffer[lodIndex]; - if (vertexColorsData.HasItems()) + const uint32 size = vertexColorsData.Count() * sizeof(Color32); + if (!vertexColorsBuffer) + vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors")); + if (vertexColorsBuffer->GetSize() != size) { - const uint32 size = vertexColorsData.Count() * sizeof(Color32); - if (!vertexColorsBuffer) - vertexColorsBuffer = GPUDevice::Instance->CreateBuffer(TEXT("VertexColors")); - if (vertexColorsBuffer->GetSize() != size) - { - if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count()))) - return; - } - GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size); - } - else - { - SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer); + if (vertexColorsBuffer->Init(GPUBufferDescription::Vertex(sizeof(Color32), vertexColorsData.Count()))) + return; } + GPUDevice::Instance->GetMainContext()->UpdateBuffer(vertexColorsBuffer, vertexColorsData.Get(), size); + } + else + { + SAFE_DELETE_GPU_RESOURCE(vertexColorsBuffer); } - _vertexColorsDirty = false; } + _vertexColorsDirty = false; + } #if USE_EDITOR - // Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving - if (!Editor::IsPlayMode) - _drawState.PrevWorld = _world; + // Disable motion blur effects in editor without play mode enabled to hide minor artifacts on objects moving + if (!Editor::IsPlayMode) + _drawState.PrevWorld = _world; #endif - Mesh::DrawInfo draw; - draw.Buffer = &Entries; - draw.World = &_world; - draw.DrawState = &_drawState; - draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); - draw.LightmapUVs = &Lightmap.UVsArea; - draw.Flags = _staticFlags; - draw.DrawModes = drawModes; - draw.Bounds = _sphere; - draw.PerInstanceRandom = GetPerInstanceRandom(); - draw.LODBias = _lodBias; - draw.ForcedLOD = _forcedLod; - draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr; + Mesh::DrawInfo draw; + draw.Buffer = &Entries; + draw.World = &_world; + draw.DrawState = &_drawState; + draw.Lightmap = _scene->LightmapsData.GetReadyLightmap(Lightmap.TextureIndex); + draw.LightmapUVs = &Lightmap.UVsArea; + draw.Flags = _staticFlags; + draw.DrawModes = drawModes; + draw.Bounds = _sphere; + draw.PerInstanceRandom = GetPerInstanceRandom(); + draw.LODBias = _lodBias; + draw.ForcedLOD = _forcedLod; + draw.VertexColors = _vertexColorsCount ? _vertexColorsBuffer : nullptr; - Model->Draw(renderContext, draw); - } + Model->Draw(renderContext, draw); GEOMETRY_DRAW_STATE_EVENT_END(_drawState, _world); } diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp index d1f63f3d9..4442e5b15 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.cpp @@ -314,9 +314,11 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex _modelsBuffer->Clear(); _modelsTextures.Clear(); } - int32 modelsBufferCount = 0; // Draw all objects from all scenes into the cascade + _modelsBufferCount = 0; + _voxelSize = voxelSize; + _cascadeBounds = cascadeBounds; for (auto* scene : renderContext.List->Scenes) { // TODO: optimize for moving camera (copy sdf) @@ -326,90 +328,6 @@ bool GlobalSignDistanceFieldPass::Render(RenderContext& renderContext, GPUContex if (viewMask & e.LayerMask && e.Bounds.Radius >= minObjectRadius && CollisionsHelper::BoxIntersectsSphere(cascadeBounds, e.Bounds)) { e.Actor->Draw(renderContext); - - // TODO: move to be properly implemented per object type - auto staticModel = ScriptingObject::Cast(e.Actor); - if (staticModel && staticModel->Model && staticModel->Model->IsLoaded() && staticModel->Model->CanBeRendered() && staticModel->DrawModes & DrawPass::GlobalSDF) - { - // okay so firstly we need SDF for this model - // TODO: implement SDF generation on model import - if (!staticModel->Model->SDF.Texture) - staticModel->Model->GenerateSDF(); - ModelBase::SDFData& sdf = staticModel->Model->SDF; - if (sdf.Texture) - { - // Setup object data - BoundingBox objectBounds = staticModel->GetBox(); - BoundingBox objectBoundsCascade; - const float objectMargin = voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN; - Vector3::Clamp(objectBounds.Minimum - objectMargin, cascadeBounds.Minimum, cascadeBounds.Maximum, objectBoundsCascade.Minimum); - Vector3::Subtract(objectBoundsCascade.Minimum, cascadeBounds.Minimum, objectBoundsCascade.Minimum); - Vector3::Clamp(objectBounds.Maximum + objectMargin, cascadeBounds.Minimum, cascadeBounds.Maximum, objectBoundsCascade.Maximum); - Vector3::Subtract(objectBoundsCascade.Maximum, cascadeBounds.Minimum, objectBoundsCascade.Maximum); - Int3 objectChunkMin(objectBoundsCascade.Minimum / chunkSize); - Int3 objectChunkMax(objectBoundsCascade.Maximum / chunkSize); - Matrix localToWorld, worldToLocal, volumeToWorld; - staticModel->GetWorld(&localToWorld); - Matrix::Invert(localToWorld, worldToLocal); - BoundingBox localVolumeBounds(sdf.LocalBoundsMin, sdf.LocalBoundsMax); - Vector3 volumeLocalBoundsExtent = localVolumeBounds.GetSize() * 0.5f; - Matrix worldToVolume = worldToLocal * Matrix::Translation(-(localVolumeBounds.Minimum + volumeLocalBoundsExtent)); - Matrix::Invert(worldToVolume, volumeToWorld); - - // Pick the SDF mip for the cascade - int32 mipLevelIndex = 1; - float worldUnitsPerVoxel = sdf.WorldUnitsPerVoxel * localToWorld.GetScaleVector().MaxValue() * 2; - while (voxelSize > worldUnitsPerVoxel && mipLevelIndex < sdf.Texture->MipLevels()) - { - mipLevelIndex++; - worldUnitsPerVoxel *= 2.0f; - } - mipLevelIndex--; - - // Volume -> Local -> UVW - Vector3 volumeToUVWMul = sdf.LocalToUVWMul; - Vector3 volumeToUVWAdd = sdf.LocalToUVWAdd + (localVolumeBounds.Minimum + volumeLocalBoundsExtent) * sdf.LocalToUVWMul; - - // Add model data for the GPU buffer - int32 modelIndex = modelsBufferCount++; - ModelRasterizeData modelData; - Matrix::Transpose(worldToVolume, modelData.WorldToVolume); - Matrix::Transpose(volumeToWorld, modelData.VolumeToWorld); - modelData.VolumeLocalBoundsExtent = volumeLocalBoundsExtent; - modelData.VolumeToUVWMul = volumeToUVWMul; - modelData.VolumeToUVWAdd = volumeToUVWAdd; - modelData.MipOffset = (float)mipLevelIndex; - modelData.DecodeMul = 2.0f * sdf.MaxDistance; - modelData.DecodeAdd = -sdf.MaxDistance; - _modelsBuffer->Write(modelData); - _modelsTextures.Add(sdf.Texture->ViewVolume()); - - // Inject object into the intersecting cascade chunks - RasterizeChunkKey key; - for (key.Coord.Z = objectChunkMin.Z; key.Coord.Z <= objectChunkMax.Z; key.Coord.Z++) - { - for (key.Coord.Y = objectChunkMin.Y; key.Coord.Y <= objectChunkMax.Y; key.Coord.Y++) - { - for (key.Coord.X = objectChunkMin.X; key.Coord.X <= objectChunkMax.X; key.Coord.X++) - { - key.Layer = 0; - key.Hash = key.Coord.Z * (RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution) + key.Coord.Y * RasterizeChunkKeyHashResolution + key.Coord.X; - RasterizeChunk* chunk = &chunks[key]; - - // Move to the next layer if chunk has overflown - while (chunk->ModelsCount == GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT) - { - key.Layer++; - key.Hash += RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution; - chunk = &chunks[key]; - } - - chunk->Models[chunk->ModelsCount++] = modelIndex; - } - } - } - } - } } } } @@ -594,3 +512,80 @@ void GlobalSignDistanceFieldPass::RenderDebug(RenderContext& renderContext, GPUC context->SetViewportAndScissors(outputSize.X, outputSize.Y); context->DrawFullscreenTriangle(); } + +void GlobalSignDistanceFieldPass::RasterizeModelSDF(const ModelBase::SDFData& sdf, const Matrix& localToWorld, const BoundingBox& objectBounds) +{ + if (!sdf.Texture) + return; + + // Setup object data + BoundingBox objectBoundsCascade; + const float objectMargin = _voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_MARGIN; + Vector3::Clamp(objectBounds.Minimum - objectMargin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Minimum); + Vector3::Subtract(objectBoundsCascade.Minimum, _cascadeBounds.Minimum, objectBoundsCascade.Minimum); + Vector3::Clamp(objectBounds.Maximum + objectMargin, _cascadeBounds.Minimum, _cascadeBounds.Maximum, objectBoundsCascade.Maximum); + Vector3::Subtract(objectBoundsCascade.Maximum, _cascadeBounds.Minimum, objectBoundsCascade.Maximum); + const float chunkSize = _voxelSize * GLOBAL_SDF_RASTERIZE_CHUNK_SIZE; + Int3 objectChunkMin(objectBoundsCascade.Minimum / chunkSize); + Int3 objectChunkMax(objectBoundsCascade.Maximum / chunkSize); + Matrix worldToLocal, volumeToWorld; + Matrix::Invert(localToWorld, worldToLocal); + BoundingBox localVolumeBounds(sdf.LocalBoundsMin, sdf.LocalBoundsMax); + Vector3 volumeLocalBoundsExtent = localVolumeBounds.GetSize() * 0.5f; + Matrix worldToVolume = worldToLocal * Matrix::Translation(-(localVolumeBounds.Minimum + volumeLocalBoundsExtent)); + Matrix::Invert(worldToVolume, volumeToWorld); + + // Pick the SDF mip for the cascade + int32 mipLevelIndex = 1; + float worldUnitsPerVoxel = sdf.WorldUnitsPerVoxel * localToWorld.GetScaleVector().MaxValue() * 2; + while (_voxelSize > worldUnitsPerVoxel && mipLevelIndex < sdf.Texture->MipLevels()) + { + mipLevelIndex++; + worldUnitsPerVoxel *= 2.0f; + } + mipLevelIndex--; + + // Volume -> Local -> UVW + Vector3 volumeToUVWMul = sdf.LocalToUVWMul; + Vector3 volumeToUVWAdd = sdf.LocalToUVWAdd + (localVolumeBounds.Minimum + volumeLocalBoundsExtent) * sdf.LocalToUVWMul; + + // Add model data for the GPU buffer + int32 modelIndex = _modelsBufferCount++; + ModelRasterizeData modelData; + Matrix::Transpose(worldToVolume, modelData.WorldToVolume); + Matrix::Transpose(volumeToWorld, modelData.VolumeToWorld); + modelData.VolumeLocalBoundsExtent = volumeLocalBoundsExtent; + modelData.VolumeToUVWMul = volumeToUVWMul; + modelData.VolumeToUVWAdd = volumeToUVWAdd; + modelData.MipOffset = (float)mipLevelIndex; + modelData.DecodeMul = 2.0f * sdf.MaxDistance; + modelData.DecodeAdd = -sdf.MaxDistance; + _modelsBuffer->Write(modelData); + _modelsTextures.Add(sdf.Texture->ViewVolume()); + + // Inject object into the intersecting cascade chunks + RasterizeChunkKey key; + auto& chunks = ChunksCache; + for (key.Coord.Z = objectChunkMin.Z; key.Coord.Z <= objectChunkMax.Z; key.Coord.Z++) + { + for (key.Coord.Y = objectChunkMin.Y; key.Coord.Y <= objectChunkMax.Y; key.Coord.Y++) + { + for (key.Coord.X = objectChunkMin.X; key.Coord.X <= objectChunkMax.X; key.Coord.X++) + { + key.Layer = 0; + key.Hash = key.Coord.Z * (RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution) + key.Coord.Y * RasterizeChunkKeyHashResolution + key.Coord.X; + RasterizeChunk* chunk = &chunks[key]; + + // Move to the next layer if chunk has overflown + while (chunk->ModelsCount == GLOBAL_SDF_RASTERIZE_MODEL_MAX_COUNT) + { + key.Layer++; + key.Hash += RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution * RasterizeChunkKeyHashResolution; + chunk = &chunks[key]; + } + + chunk->Models[chunk->ModelsCount++] = modelIndex; + } + } + } +} diff --git a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h index e6eac2ab2..b4a9cf984 100644 --- a/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h +++ b/Source/Engine/Renderer/GlobalSignDistanceFieldPass.h @@ -37,8 +37,13 @@ private: GPUShaderProgramCS* _csGenerateMip1 = nullptr; GPUConstantBuffer* _cb0 = nullptr; GPUConstantBuffer* _cb1 = nullptr; + + // Rasterization cache class DynamicStructuredBuffer* _modelsBuffer = nullptr; Array _modelsTextures; + int32 _modelsBufferCount; + float _voxelSize; + BoundingBox _cascadeBounds; public: /// @@ -58,6 +63,8 @@ public: /// The output buffer. void RenderDebug(RenderContext& renderContext, GPUContext* context, GPUTexture* output); + void RasterizeModelSDF(const ModelBase::SDFData& sdf, const Matrix& localToWorld, const BoundingBox& objectBounds); + private: #if COMPILE_WITH_DEV_ENV void OnShaderReloading(Asset* obj);