You're breathtaking!
This commit is contained in:
242
Source/Engine/ShadowsOfMordor/Builder.BuildCache.cpp
Normal file
242
Source/Engine/ShadowsOfMordor/Builder.BuildCache.cpp
Normal file
@@ -0,0 +1,242 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Builder.h"
|
||||
#include "Engine/Level/Scene/Lightmap.h"
|
||||
#include "Engine/Level/Scene/Scene.h"
|
||||
#include "Engine/Content/Content.h"
|
||||
#include "Engine/ContentImporters/AssetsImportingManager.h"
|
||||
#include "Engine/ContentImporters/ImportTexture.h"
|
||||
#include "Engine/Graphics/PixelFormatExtensions.h"
|
||||
#include <ThirdParty/DirectXTex/DirectXTex.h>
|
||||
|
||||
ShadowsOfMordor::Builder::LightmapBuildCache::~LightmapBuildCache()
|
||||
{
|
||||
SAFE_DELETE_GPU_RESOURCE(LightmapData);
|
||||
}
|
||||
|
||||
bool ShadowsOfMordor::Builder::LightmapBuildCache::Init(const LightmapSettings* settings)
|
||||
{
|
||||
if (LightmapData)
|
||||
return false;
|
||||
LightmapData = GPUDevice::Instance->CreateBuffer(TEXT("LightmapBuildCache"));
|
||||
const auto elementsCount = (int32)settings->AtlasSize * (int32)settings->AtlasSize * NUM_SH_TARGETS;
|
||||
if (LightmapData->Init(GPUBufferDescription::Typed(elementsCount, HemispheresFormatToPixelFormat[HEMISPHERES_IRRADIANCE_FORMAT], true)))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
ShadowsOfMordor::Builder::SceneBuildCache::SceneBuildCache()
|
||||
: Scene(nullptr)
|
||||
, TempLightmapData(nullptr)
|
||||
, LightmapsCount(0)
|
||||
, HemispheresCount(0)
|
||||
, MergedHemispheresCount(0)
|
||||
, ImportLightmapIndex(0)
|
||||
, ImportLightmapTextureIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
const LightmapSettings& ShadowsOfMordor::Builder::SceneBuildCache::GetSettings() const
|
||||
{
|
||||
ASSERT(Scene);
|
||||
return Scene->Info.LightmapSettings;
|
||||
}
|
||||
|
||||
bool ShadowsOfMordor::Builder::SceneBuildCache::WaitForLightmaps()
|
||||
{
|
||||
Texture* lightmaps[3];
|
||||
for (int32 lightmapIndex = 0; lightmapIndex < Lightmaps.Count(); lightmapIndex++)
|
||||
{
|
||||
const auto lightmap = Scene->LightmapsData.GetLightmap(lightmapIndex);
|
||||
lightmap->GetTextures(lightmaps);
|
||||
|
||||
for (int32 textureIndex = 0; textureIndex < NUM_SH_TARGETS; textureIndex++)
|
||||
{
|
||||
AssetReference<Texture> lightmapTexture = lightmaps[textureIndex];
|
||||
if (lightmapTexture == nullptr)
|
||||
{
|
||||
LOG(Error, "Missing lightmap {0} texture{1}", lightmapIndex, textureIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Wait for loading end and check result
|
||||
if (lightmapTexture->WaitForLoaded())
|
||||
{
|
||||
LOG(Error, "Failed to load lightmap {0} texture {1}", lightmapIndex, textureIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: disable streaming for lightmap texture here (enable it later after baking)
|
||||
|
||||
// Wait texture to be streamed to the target quality
|
||||
const auto t = lightmapTexture->GetTexture();
|
||||
const int32 stepSize = 30;
|
||||
const int32 maxWaitTime = 60000;
|
||||
int32 stepsCount = static_cast<int32>(maxWaitTime / stepSize);
|
||||
while ((t->ResidentMipLevels() < t->MipLevels() || t->ResidentMipLevels() == 0) && stepsCount-- > 0)
|
||||
Platform::Sleep(stepSize);
|
||||
if (t->ResidentMipLevels() < t->MipLevels() || t->ResidentMipLevels() == 0)
|
||||
{
|
||||
LOG(Error, "Waiting for lightmap no. {0} texture {1} to be fully resided timed out (loaded mips: {2}, mips count: {3})", lightmapIndex, textureIndex, t->ResidentMipLevels(), t->MipLevels());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShadowsOfMordor::Builder::SceneBuildCache::UpdateLightmaps()
|
||||
{
|
||||
Texture* lightmaps[3];
|
||||
for (int32 lightmapIndex = 0; lightmapIndex < Lightmaps.Count(); lightmapIndex++)
|
||||
{
|
||||
// Cache data
|
||||
auto& lightmapEntry = Lightmaps[lightmapIndex];
|
||||
auto lightmap = Scene->LightmapsData.GetLightmap(lightmapIndex);
|
||||
ASSERT(lightmap);
|
||||
lightmap->GetTextures(lightmaps);
|
||||
|
||||
// Download buffer data
|
||||
if (lightmapEntry.LightmapData->DownloadData(ImportLightmapTextureData))
|
||||
{
|
||||
LOG(Warning, "Cannot download LightmapData.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Import all textures but don't use file proxy to improve performance
|
||||
for (int32 textureIndex = 0; textureIndex < NUM_SH_TARGETS; textureIndex++)
|
||||
{
|
||||
// Get asset name
|
||||
String assetPath;
|
||||
if (lightmaps[textureIndex])
|
||||
assetPath = lightmaps[textureIndex]->GetPath();
|
||||
else
|
||||
Scene->LightmapsData.GetCachedLightmapPath(&assetPath, lightmapIndex, textureIndex);
|
||||
|
||||
// Import texture with custom options
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
Guid id = Guid::Empty;
|
||||
ImportTexture::Options options;
|
||||
options.Type = TextureFormatType::HdrRGBA;
|
||||
options.IndependentChannels = true;
|
||||
options.Compress = Scene->GetLightmapSettings().CompressLightmaps;
|
||||
options.GenerateMipMaps = true;
|
||||
options.IsAtlas = false;
|
||||
options.sRGB = false;
|
||||
options.NeverStream = false;
|
||||
ImportLightmapIndex = lightmapIndex;
|
||||
ImportLightmapTextureIndex = textureIndex;
|
||||
options.InternalLoad.Bind<SceneBuildCache, &SceneBuildCache::onImportLightmap>(this);
|
||||
if (AssetsImportingManager::Create(AssetsImportingManager::CreateTextureTag, assetPath, id, &options))
|
||||
{
|
||||
LOG(Error, "Cannot create new lightmap {0}:{1}", lightmapIndex, textureIndex);
|
||||
return;
|
||||
}
|
||||
const auto result = Content::LoadAsync<Texture>(id);
|
||||
if (result == nullptr)
|
||||
#else
|
||||
#error "Cannot import lightmaps. Assets importer module iss missing."
|
||||
auto result = nullptr;
|
||||
#endif
|
||||
{
|
||||
LOG(Error, "Cannot load new lightmap {0}:{1}", lightmapIndex, textureIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
// Update lightmap
|
||||
lightmap->UpdateTexture(result, textureIndex);
|
||||
}
|
||||
|
||||
#if DEBUG_EXPORT_LIGHTMAPS_PREVIEW
|
||||
// Temporary save lightmaps (after last bounce)
|
||||
if (Builder->_giBounceRunningIndex == Builder->_bounceCount - 1)
|
||||
{
|
||||
exportLightmapPreview(this, lightmapIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
ImportLightmapTextureData.Release();
|
||||
}
|
||||
}
|
||||
|
||||
bool ShadowsOfMordor::Builder::SceneBuildCache::Init(ShadowsOfMordor::Builder* builder, int32 index, ::Scene* scene)
|
||||
{
|
||||
Builder = builder;
|
||||
SceneIndex = index;
|
||||
Scene = scene;
|
||||
const int32 atlasSize = (int32)GetSettings().AtlasSize;
|
||||
TempLightmapData = GPUDevice::Instance->CreateBuffer(TEXT("LightmapBuildCache"));
|
||||
const auto elementsCount = atlasSize * atlasSize * NUM_SH_TARGETS;
|
||||
if (TempLightmapData->Init(GPUBufferDescription::Typed(elementsCount, HemispheresFormatToPixelFormat[HEMISPHERES_IRRADIANCE_FORMAT], true)))
|
||||
return true;
|
||||
|
||||
LOG(Info, "Scene \'{0}\' quality: {1}", scene->GetName(), scene->Info.LightmapSettings.Quality);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ShadowsOfMordor::Builder::SceneBuildCache::Release()
|
||||
{
|
||||
EntriesLocker.Lock();
|
||||
|
||||
// Cleanup
|
||||
Entries.Resize(0);
|
||||
Lightmaps.Resize(0);
|
||||
Charts.Resize(0);
|
||||
Scene = nullptr;
|
||||
|
||||
EntriesLocker.Unlock();
|
||||
|
||||
SAFE_DELETE_GPU_RESOURCE(TempLightmapData);
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_ASSETS_IMPORTER
|
||||
|
||||
bool ShadowsOfMordor::Builder::SceneBuildCache::onImportLightmap(TextureData& image)
|
||||
{
|
||||
// Cache data
|
||||
const int32 lightmapIndex = ImportLightmapIndex;
|
||||
const int32 textureIndex = ImportLightmapTextureIndex;
|
||||
|
||||
// Setup image
|
||||
image.Width = image.Height = (int32)GetSettings().AtlasSize;
|
||||
image.Depth = 1;
|
||||
image.Format = HemispheresFormatToPixelFormat[HEMISPHERES_IRRADIANCE_FORMAT];
|
||||
image.Items.Resize(1);
|
||||
image.Items[0].Mips.Resize(1);
|
||||
auto& mip = image.Items[0].Mips[0];
|
||||
mip.RowPitch = PixelFormatExtensions::SizeInBytes(image.Format) * image.Width;
|
||||
mip.DepthPitch = mip.RowPitch * image.Height;
|
||||
mip.Lines = image.Height;
|
||||
mip.Data.Allocate(mip.DepthPitch);
|
||||
|
||||
#if HEMISPHERES_IRRADIANCE_FORMAT == HEMISPHERES_FORMAT_R32G32B32A32
|
||||
auto pos = (Vector4*)mip.Data.Get();
|
||||
const auto textureData = ImportLightmapTextureData.Get<Vector4>();
|
||||
for (int32 y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int32 x = 0; x < image.Width; x++)
|
||||
{
|
||||
const int32 texelAddress = (y * image.Width + x) * NUM_SH_TARGETS;
|
||||
*pos = textureData[texelAddress + textureIndex];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
#elif HEMISPHERES_IRRADIANCE_FORMAT == HEMISPHERES_FORMAT_R16G16B16A16
|
||||
auto pos = (Half4*)mip.Data.Get();
|
||||
const auto textureData = ImportLightmapTextureData.Get<Half4>();
|
||||
for (int32 y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int32 x = 0; x < image.Width; x++)
|
||||
{
|
||||
const int32 texelAddress = (y * image.Width + x) * NUM_SH_TARGETS;
|
||||
*pos = textureData[texelAddress + textureIndex];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user