169 lines
6.3 KiB
C++
169 lines
6.3 KiB
C++
// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved.
|
|
|
|
#include "Builder.h"
|
|
#include "AtlasChartsPacker.h"
|
|
#include "Engine/Level/Scene/SceneLightmapsData.h"
|
|
#include "Engine/Core/Math/Math.h"
|
|
#include "Engine/Core/Collections/Sorting.h"
|
|
#include "Engine/ContentImporters/ImportTexture.h"
|
|
#include "Engine/Core/Log.h"
|
|
#include "Engine/Graphics/GPUDevice.h"
|
|
#include "Engine/Level/SceneQuery.h"
|
|
#include "Engine/Level/Scene/Lightmap.h"
|
|
|
|
bool ShadowsOfMordor::Builder::sortCharts(const LightmapUVsChart& a, const LightmapUVsChart& b)
|
|
{
|
|
// Sort by area
|
|
return (b.Width * b.Height) < (a.Width * a.Height);
|
|
}
|
|
|
|
void ShadowsOfMordor::Builder::generateCharts()
|
|
{
|
|
reportProgress(BuildProgressStep::GenerateLightmapCharts, 0.0f);
|
|
|
|
auto scene = _scenes[_workerActiveSceneIndex];
|
|
auto& settings = scene->GetSettings();
|
|
ScopeLock lock(scene->EntriesLocker);
|
|
|
|
// Generate lightmap UVs charts
|
|
const int32 entriesCount = scene->Entries.Count();
|
|
scene->Charts.EnsureCapacity(entriesCount);
|
|
const int32 MaximumChartSize = (int32)settings.AtlasSize - settings.ChartsPadding * 2;
|
|
for (int32 i = 0; i < entriesCount; i++)
|
|
{
|
|
LightmapUVsChart chart;
|
|
chart.Result.TextureIndex = INVALID_INDEX;
|
|
|
|
GeometryEntry& entry = scene->Entries[i];
|
|
entry.ChartIndex = INVALID_INDEX;
|
|
|
|
// Calculate desired area for the entry's chart (based on object dimensions and settings)
|
|
// Reject missing models or too small objects
|
|
Float3 size = entry.Box.GetSize();
|
|
float dimensionsCoeff = size.AverageArithmetic();
|
|
if (size.X <= 1.0f)
|
|
dimensionsCoeff = Float2(size.Y, size.Z).AverageArithmetic();
|
|
else if (size.Y <= 1.0f)
|
|
dimensionsCoeff = Float2(size.X, size.Z).AverageArithmetic();
|
|
else if (size.Z <= 1.0f)
|
|
dimensionsCoeff = Float2(size.Y, size.X).AverageArithmetic();
|
|
const float scale = settings.GlobalObjectsScale * entry.Scale * LightmapTexelsPerWorldUnit * dimensionsCoeff;
|
|
if (scale <= ZeroTolerance)
|
|
continue;
|
|
|
|
// Apply lightmap uvs bounding box (in uv space) to reduce waste of lightmap atlas space
|
|
chart.Width = Math::Clamp(Math::CeilToInt(scale * entry.UVsBox.GetWidth()), LightmapMinChartSize, MaximumChartSize);
|
|
chart.Height = Math::Clamp(Math::CeilToInt(scale * entry.UVsBox.GetHeight()), LightmapMinChartSize, MaximumChartSize);
|
|
|
|
// Register lightmap atlas chart entry
|
|
chart.EntryIndex = i;
|
|
scene->Charts.Add(chart);
|
|
|
|
// Progress Point
|
|
reportProgress(BuildProgressStep::GenerateLightmapCharts, static_cast<float>(i) / entriesCount);
|
|
}
|
|
|
|
reportProgress(BuildProgressStep::GenerateLightmapCharts, 1.0f);
|
|
}
|
|
|
|
void ShadowsOfMordor::Builder::packCharts()
|
|
{
|
|
reportProgress(BuildProgressStep::PackLightmapCharts, 0.0f);
|
|
auto scene = _scenes[_workerActiveSceneIndex];
|
|
|
|
// Pack UV charts into atlases
|
|
Array<AtlasChartsPacker*> packers;
|
|
if (scene->Charts.HasItems())
|
|
{
|
|
// Sort charts from the biggest to the smallest
|
|
Sorting::QuickSort(scene->Charts.Get(), scene->Charts.Count(), &sortCharts);
|
|
|
|
reportProgress(BuildProgressStep::PackLightmapCharts, 0.1f);
|
|
|
|
// Cache charts indices after sorting operation
|
|
scene->EntriesLocker.Lock();
|
|
for (int32 chartIndex = 0; chartIndex < scene->Charts.Count(); chartIndex++)
|
|
{
|
|
auto& chart = scene->Charts[chartIndex];
|
|
scene->Entries[chart.EntryIndex].ChartIndex = chartIndex;
|
|
}
|
|
scene->EntriesLocker.Unlock();
|
|
|
|
reportProgress(BuildProgressStep::PackLightmapCharts, 0.5f);
|
|
|
|
// Pack all the charts
|
|
for (int32 i = 0; i < scene->Charts.Count(); i++)
|
|
{
|
|
auto chart = &scene->Charts[i];
|
|
|
|
bool cannotPack = true;
|
|
for (int32 j = 0; j < packers.Count(); j++)
|
|
{
|
|
if (packers[j]->Insert(chart))
|
|
{
|
|
chart->Result.TextureIndex = j;
|
|
cannotPack = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cannotPack)
|
|
{
|
|
auto packer = New<AtlasChartsPacker>(&scene->GetSettings());
|
|
auto result = packer->Insert(chart);
|
|
ASSERT(result);
|
|
chart->Result.TextureIndex = packers.Count();
|
|
packers.Add(packer);
|
|
}
|
|
}
|
|
}
|
|
const int32 lightmapsCount = scene->LightmapsCount = packers.Count();
|
|
LOG(Info, "Scene \'{0}\': building {1} lightmap(s) ({2} chart(s) to bake)...", scene->Scene->GetName(), lightmapsCount, scene->Charts.Count());
|
|
packers.ClearDelete();
|
|
|
|
// Progress Point
|
|
reportProgress(BuildProgressStep::PackLightmapCharts, 1.0f);
|
|
}
|
|
|
|
void ShadowsOfMordor::Builder::updateLightmaps()
|
|
{
|
|
reportProgress(BuildProgressStep::UpdateLightmapsCollection, 0.0f);
|
|
|
|
auto scene = _scenes[_workerActiveSceneIndex];
|
|
auto& settings = scene->GetSettings();
|
|
const int32 lightmapsCount = scene->LightmapsCount;
|
|
|
|
// Update lightmaps collection
|
|
scene->Scene->LightmapsData.UpdateLightmapsCollection(lightmapsCount, (int32)settings.AtlasSize);
|
|
scene->Lightmaps.Resize(lightmapsCount, false);
|
|
for (int32 lightmapIndex = 0; lightmapIndex < lightmapsCount; lightmapIndex++)
|
|
{
|
|
if (scene->Lightmaps[lightmapIndex].Init(&settings))
|
|
return;
|
|
}
|
|
|
|
// Wait for all lightmaps to be ready (after creating new lightmaps assets we need to wait for resources to be prepared)
|
|
GPUDevice::Instance->Locker.Lock();
|
|
for (int32 lightmapIndex = 0; lightmapIndex < lightmapsCount; lightmapIndex++)
|
|
{
|
|
Texture* textures[NUM_SH_TARGETS];
|
|
scene->Scene->LightmapsData.GetLightmap(lightmapIndex)->GetTextures(textures);
|
|
for (int32 textureIndex = 0; textureIndex < NUM_SH_TARGETS; textureIndex++)
|
|
{
|
|
auto texture = textures[textureIndex];
|
|
GPUDevice::Instance->Locker.Unlock();
|
|
if (texture == nullptr || texture->WaitForLoaded())
|
|
{
|
|
LOG(Error, "Lightmap load failed.");
|
|
return;
|
|
}
|
|
GPUDevice::Instance->Locker.Lock();
|
|
}
|
|
|
|
reportProgress(BuildProgressStep::UpdateLightmapsCollection, (float)lightmapIndex / lightmapsCount);
|
|
}
|
|
GPUDevice::Instance->Locker.Unlock();
|
|
|
|
reportProgress(BuildProgressStep::UpdateLightmapsCollection, 1.0f);
|
|
}
|