You're breathtaking!
This commit is contained in:
182
Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp
Normal file
182
Source/Engine/ShadowsOfMordor/Builder.Hemispheres.cpp
Normal file
@@ -0,0 +1,182 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Builder.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
#include "Engine/Level/SceneQuery.h"
|
||||
#include "Engine/Level/Actors/BoxBrush.h"
|
||||
#include "Engine/ContentImporters/ImportTexture.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
void SampleCache(ShadowsOfMordor::GenerateHemispheresData& data, int32 texelX, int32 texelY, Vector3& outPosition, Vector3& outNormal)
|
||||
{
|
||||
const auto mipDataPositions = data.PositionsData.GetData(0, 0);
|
||||
#if CACHE_POSITIONS_FORMAT == HEMISPHERES_FORMAT_R32G32B32A32
|
||||
outPosition = Vector3(mipDataPositions->Get<Vector4>(texelX, texelY));
|
||||
#elif CACHE_POSITIONS_FORMAT == HEMISPHERES_FORMAT_R16G16B16A16
|
||||
outPosition = mipDataPositions->Get<Half4>(texelX, texelY).ToVector3();
|
||||
#else
|
||||
#error "Unknown format."
|
||||
#endif
|
||||
|
||||
const auto mipDataNormals = data.NormalsData.GetData(0, 0);
|
||||
#if CACHE_NORMALS_FORMAT == HEMISPHERES_FORMAT_R32G32B32A32
|
||||
outNormal = Vector3(mipDataNormals->Get<Vector4>(texelX, texelY));
|
||||
#elif CACHE_NORMALS_FORMAT == HEMISPHERES_FORMAT_R16G16B16A16
|
||||
outNormal = mipDataNormals->Get<Half4>(texelX, texelY).ToVector3();
|
||||
#else
|
||||
#error "Unknown format."
|
||||
#endif
|
||||
}
|
||||
|
||||
void RejectTexel(ShadowsOfMordor::GenerateHemispheresData& data, int32 texelX, int32 texelY)
|
||||
{
|
||||
const auto mipDataNormals = data.NormalsData.GetData(0, 0);
|
||||
#if CACHE_NORMALS_FORMAT == HEMISPHERES_FORMAT_R32G32B32A32
|
||||
mipDataNormals->Get<Vector4>(texelX, texelY) = Vector4::Zero;
|
||||
#elif CACHE_NORMALS_FORMAT == HEMISPHERES_FORMAT_R16G16B16A16
|
||||
mipDataNormals->Get<Half4>(texelX, texelY) = Half4::Zero;
|
||||
#else
|
||||
#error "Unknown format."
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ShadowsOfMordor::Builder::generateHemispheres()
|
||||
{
|
||||
reportProgress(BuildProgressStep::GenerateHemispheresCache, 0.0f);
|
||||
|
||||
// Clear all lightmaps
|
||||
_workerStagePosition0 = 0;
|
||||
if (runStage(CleanLightmaps))
|
||||
return;
|
||||
|
||||
auto scene = _scenes[_workerActiveSceneIndex];
|
||||
auto lightmapsCount = scene->Lightmaps.Count();
|
||||
auto& settings = scene->GetSettings();
|
||||
|
||||
// Collect Hemispheres render tasks
|
||||
int32 hemispheresCount = 0, mergedHemispheresCount = 0;
|
||||
GenerateHemispheresData cacheData;
|
||||
|
||||
// Config
|
||||
float normalizedQuality = Math::Saturate((float)settings.Quality / 100.0f);
|
||||
float maxMergeRadius = Math::Lerp(5.0f, 1.0f, normalizedQuality) / LightmapTexelsPerWorldUnit;
|
||||
float normalSimilarityMin = Math::Lerp(0.8f, 0.95f, normalizedQuality);
|
||||
int32 maxTexelsDistance = static_cast<int32>(Math::Lerp(2.0f, 1.0f, normalizedQuality));
|
||||
int32 atlasSize = static_cast<int32>(settings.AtlasSize);
|
||||
|
||||
// Process every lightmap
|
||||
for (_workerStagePosition0 = 0; _workerStagePosition0 < lightmapsCount; _workerStagePosition0++)
|
||||
{
|
||||
// Prepare
|
||||
auto& lightmapEntry = scene->Lightmaps[_workerStagePosition0];
|
||||
lightmapEntry.Hemispheres.Clear();
|
||||
lightmapEntry.Hemispheres.EnsureCapacity(Math::Square(atlasSize / 2));
|
||||
Vector3 position, normal;
|
||||
|
||||
// Fill cache
|
||||
if (runStage(RenderCache))
|
||||
return;
|
||||
|
||||
// Post-process cache
|
||||
if (runStage(PostprocessCache))
|
||||
return;
|
||||
|
||||
// Wait for GPU commands to sync
|
||||
if (waitForJobDataSync())
|
||||
return;
|
||||
if (checkBuildCancelled())
|
||||
return;
|
||||
|
||||
// Download cache to CPU memory from GPU memory
|
||||
if (_cachePositions->DownloadData(cacheData.PositionsData)
|
||||
|| _cacheNormals->DownloadData(cacheData.NormalsData))
|
||||
{
|
||||
LOG(Fatal, "Cannot download data from the GPU. Target: ShadowsOfMordor::Builder::RenderPositionsAndNormals");
|
||||
return;
|
||||
}
|
||||
if (checkBuildCancelled())
|
||||
return;
|
||||
|
||||
#if DEBUG_EXPORT_CACHE_PREVIEW
|
||||
// Here we can export cache to drive
|
||||
exportCachePreview(scene, cacheData, lightmapEntry);
|
||||
#endif
|
||||
|
||||
// For each texel
|
||||
for (int32 texelX = 0; texelX < atlasSize; texelX++)
|
||||
{
|
||||
for (int32 texelY = 0; texelY < atlasSize; texelY++)
|
||||
{
|
||||
// Sample cache for current texel
|
||||
SampleCache(cacheData, texelX, texelY, position, normal);
|
||||
|
||||
// Reject 'empty' texels
|
||||
if (normal.IsZero())
|
||||
continue;
|
||||
normal.Normalize();
|
||||
|
||||
// Try to merge similar hemispheres (threshold values are controlled by the quality slider)
|
||||
int32 mergedCount = 1;
|
||||
Vector3 mergedSumPos = position, mergedSumNorm = normal;
|
||||
for (int32 x = -maxTexelsDistance; x <= maxTexelsDistance; x++)
|
||||
{
|
||||
for (int32 y = -maxTexelsDistance; y <= maxTexelsDistance; y++)
|
||||
{
|
||||
int32 xx = Math::Clamp(x + texelX, 0, atlasSize - 1);
|
||||
int32 yy = Math::Clamp(y + texelY, 0, atlasSize - 1);
|
||||
|
||||
// Skip current texel
|
||||
if (xx == texelX && yy == texelY)
|
||||
continue;
|
||||
|
||||
// Sample cache for possible to use texel
|
||||
Vector3 pp, nn;
|
||||
SampleCache(cacheData, xx, yy, pp, nn);
|
||||
nn.Normalize();
|
||||
|
||||
if (Vector3::Distance(position, pp) <= maxMergeRadius
|
||||
&& Vector3::Dot(normal, nn) >= normalSimilarityMin)
|
||||
{
|
||||
// Merge them!
|
||||
mergedCount++;
|
||||
mergedSumPos += pp;
|
||||
mergedSumNorm += nn;
|
||||
|
||||
// Remove this hemisphere
|
||||
RejectTexel(cacheData, xx, yy);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mergedCount > 1)
|
||||
mergedHemispheresCount += mergedCount;
|
||||
|
||||
// TODO: check if we need to use avg pos and normal?
|
||||
//position = mergedSumPos / mergedCount;
|
||||
//normal = mergedSumNorm / mergedCount;
|
||||
//normal.Normalize();
|
||||
|
||||
// Enqueue hemisphere data to perform batched rendering
|
||||
HemisphereData data;
|
||||
data.Position = position;
|
||||
data.Normal = normal;
|
||||
data.TexelX = texelX;
|
||||
data.TexelY = texelY;
|
||||
lightmapEntry.Hemispheres.Add(data);
|
||||
hemispheresCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Progress Point
|
||||
reportProgress(BuildProgressStep::GenerateHemispheresCache, (float)_workerStagePosition0 / lightmapsCount);
|
||||
if (checkBuildCancelled())
|
||||
return;
|
||||
}
|
||||
|
||||
// Update stats
|
||||
scene->HemispheresCount = hemispheresCount;
|
||||
scene->MergedHemispheresCount = mergedHemispheresCount;
|
||||
|
||||
reportProgress(BuildProgressStep::GenerateHemispheresCache, 1.0f);
|
||||
}
|
||||
Reference in New Issue
Block a user