Files
FlaxEngine/Source/Engine/ShadowsOfMordor/Builder.h
2023-01-10 15:29:37 +01:00

346 lines
9.1 KiB
C++

// Copyright (c) 2012-2023 Wojciech Figat. All rights reserved.
#pragma once
#include "Engine/Content/Assets/Model.h"
#include "Engine/Content/Assets/Shader.h"
#include "Engine/CSG/CSGMesh.h"
#include "Builder.Config.h"
#if COMPILE_WITH_GI_BAKING
#include "Engine/Graphics/RenderTask.h"
#include "Engine/Core/Singleton.h"
// Forward declarations
#if COMPILE_WITH_ASSETS_IMPORTER
namespace DirectX
{
class ScratchImage;
};
#endif
class Actor;
class Terrain;
class Foliage;
class StaticModel;
class GPUPipelineState;
namespace ShadowsOfMordor
{
/// <summary>
/// Shadows Of Mordor lightmaps builder utility.
/// </summary>
class Builder : public Singleton<Builder>
{
public:
enum class GeometryType
{
StaticModel,
Terrain,
Foliage,
};
struct LightmapUVsChart
{
int32 Width;
int32 Height;
LightmapEntry Result;
int32 EntryIndex;
};
struct GeometryEntry
{
GeometryType Type;
float Scale;
BoundingBox Box;
Rectangle UVsBox;
union
{
struct
{
StaticModel* Actor;
} AsStaticModel;
struct
{
Terrain* Actor;
int32 PatchIndex;
int32 ChunkIndex;
} AsTerrain;
struct
{
Foliage* Actor;
int32 InstanceIndex;
int32 TypeIndex;
int32 MeshIndex;
} AsFoliage;
};
int32 ChartIndex;
};
typedef Array<GeometryEntry> GeometryEntriesCollection;
typedef Array<LightmapUVsChart> LightmapUVsChartsCollection;
enum BuildingStage
{
CleanLightmaps = 0,
RenderCache,
PostprocessCache,
ClearLightmapData,
RenderHemispheres,
PostprocessLightmaps,
};
/// <summary>
/// Single hemisphere data
/// </summary>
struct HemisphereData
{
Float3 Position;
Float3 Normal;
int16 TexelX;
int16 TexelY;
};
/// <summary>
/// Per lightmap cache data
/// </summary>
class LightmapBuildCache
{
public:
Array<int32> Entries;
Array<HemisphereData> Hemispheres;
GPUBuffer* LightmapData = nullptr;
#if HEMISPHERES_BAKE_STATE_SAVE
// Restored data for the lightmap from the loaded state (copied to the LightmapData on first hemispheres render job)
Array<byte> LightmapDataInit;
#endif
~LightmapBuildCache();
bool Init(const LightmapSettings* settings);
};
/// <summary>
/// Per scene cache data
/// </summary>
class SceneBuildCache
{
public:
// Meta
Builder* Builder;
int32 SceneIndex;
// Data
Scene* Scene;
CriticalSection EntriesLocker;
GeometryEntriesCollection Entries;
LightmapUVsChartsCollection Charts;
Array<LightmapBuildCache> Lightmaps;
GPUBuffer* TempLightmapData;
// Stats
int32 LightmapsCount;
int32 HemispheresCount;
int32 MergedHemispheresCount;
public:
/// <summary>
/// Initializes a new instance of the <see cref="SceneBuildCache"/> class.
/// </summary>
SceneBuildCache();
public:
/// <summary>
/// Gets the lightmaps baking settings.
/// </summary>
/// <returns>Settings</returns>
const LightmapSettings& GetSettings() const;
public:
/// <summary>
/// Waits for lightmap textures being fully loaded before baking process.
/// </summary>
/// <returns>True if failed, otherwise false.</returns>
bool WaitForLightmaps();
/// <summary>
/// Updates the lightmaps textures data.
/// </summary>
void UpdateLightmaps();
/// <summary>
/// Initializes this instance.
/// </summary>
/// <param name="builder">The builder.</param>
/// <param name="index">The scene index.</param>
/// <param name="scene">The scene.</param>
/// <returns>True if failed, otherwise false.</returns>
bool Init(ShadowsOfMordor::Builder* builder, int32 index, ::Scene* scene);
/// <summary>
/// Releases this scene data cache.
/// </summary>
void Release();
public:
// Importing lightmaps data
BytesContainer ImportLightmapTextureData;
int32 ImportLightmapIndex;
int32 ImportLightmapTextureIndex;
#if COMPILE_WITH_ASSETS_IMPORTER
bool onImportLightmap(TextureData& image);
#endif
};
class BuilderRenderTask : public SceneRenderTask
{
void OnRender(GPUContext* context) override
{
Builder::Instance()->onJobRender(context);
}
};
private:
friend class ShadowsOfMordorBuilderService;
CriticalSection _locker;
bool _wasBuildCalled;
bool _isActive;
volatile int64 _wasBuildCancelled;
Array<SceneBuildCache*> _scenes;
volatile int64 _wasJobDone;
BuildingStage _stage;
bool _wasStageDone;
int32 _workerActiveSceneIndex;
int32 _workerStagePosition0;
int32 _workerStagePosition1;
int32 _giBounceRunningIndex;
uint64 _lastJobFrame;
float _hemisphereTexelsTotalWeight;
int32 _bounceCount;
int32 _hemispheresPerJob;
DateTime _hemispheresPerJobUpdateTime;
#if HEMISPHERES_BAKE_STATE_SAVE
DateTime _lastStateSaveTime;
bool _firstStateSave;
#endif
BuildProgressStep _lastStep;
DateTime _lastStepStart;
BuilderRenderTask* _task = nullptr;
GPUTexture* _output = nullptr;
AssetReference<Shader> _shader;
GPUPipelineState* _psRenderCacheModel = nullptr;
GPUPipelineState* _psRenderCacheTerrain = nullptr;
GPUPipelineState* _psBlurCache = nullptr;
GPUBuffer* _irradianceReduction = nullptr;
GPUTexture* _cachePositions = nullptr;
GPUTexture* _cacheNormals = nullptr;
public:
Builder();
public:
/// <summary>
/// Called on building start
/// </summary>
Action OnBuildStarted;
/// <summary>
/// Called on building progress made
/// Arguments: current step, current step progress, total progress
/// </summary>
Delegate<BuildProgressStep, float, float> OnBuildProgress;
/// <summary>
/// Called on building finish (argument: true if build failed, otherwise false)
/// </summary>
Delegate<bool> OnBuildFinished;
public:
void CheckIfRestoreState();
bool RestoreState();
/// <summary>
/// Starts building lightmap.
/// </summary>
void Build();
/// <summary>
/// Returns true if build is running.
/// </summary>
bool IsActive() const
{
return _isActive;
}
/// <summary>
/// Sends cancel current build signal.
/// </summary>
void CancelBuild();
void Dispose();
private:
void saveState();
bool loadState();
void deleteState();
void reportProgress(BuildProgressStep step, float stepProgress);
void reportProgress(BuildProgressStep step, float stepProgress, int32 subSteps);
void reportProgress(BuildProgressStep step, float stepProgress, float totalProgress);
void onJobRender(GPUContext* context);
bool checkBuildCancelled();
bool runStage(BuildingStage stage, bool resetPosition = true);
bool initResources();
void releaseResources();
bool waitForJobDataSync();
static bool sortCharts(const LightmapUVsChart& a, const LightmapUVsChart& b);
bool doWorkInner(DateTime buildStart);
int32 doWork();
void cacheEntries();
void generateCharts();
void packCharts();
void updateLightmaps();
void updateEntries();
void generateHemispheres();
#if DEBUG_EXPORT_LIGHTMAPS_PREVIEW
static void exportLightmapPreview(SceneBuildCache* scene, int32 lightmapIndex);
#endif
#if DEBUG_EXPORT_CACHE_PREVIEW
void exportCachePreview(SceneBuildCache* scene, GenerateHemispheresData& cacheData, LightmapBuildCache& lightmapEntry) const;
#endif
#if DEBUG_EXPORT_HEMISPHERES_PREVIEW
void addDebugHemisphere(GPUContext* context, GPUTextureView* radianceMap);
void downloadDebugHemisphereAtlases(SceneBuildCache* scene);
void releaseDebugHemisphereAtlases();
#endif
};
};
#endif