// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "ImportExport.h" #include "Math/SHMath.h" namespace Lightmass { /** * The light incident for a point on a surface, in the representation used when gathering lighting. * This representation is additive, and allows for accumulating lighting contributions in-place. */ template class TGatheredLightSample { public: /** World space incident lighting. */ TSHVectorRGB SHVector; /** Incident lighting including dot(N, L) where N is the smoothed vertex normal. */ FLinearColor IncidentLighting; /** Correction factor to force SH as applied to a flat normal map to be 1 to get purely directional data. */ float SHCorrection; /** Sky bent normal, points toward the most unoccluded direction, and the length is the visibility amount (0 = occluded, 1 = visible). */ FVector3f SkyOcclusion; float AOMaterialMask; /** Initialization constructor. */ TGatheredLightSample() { SHCorrection = 0.0f; IncidentLighting = FLinearColor(0, 0, 0, 0); SkyOcclusion = FVector3f(0); AOMaterialMask = 0; } TGatheredLightSample(EForceInit) { SHCorrection = 0.0f; IncidentLighting = FLinearColor(0, 0, 0, 0); SkyOcclusion = FVector3f(0); AOMaterialMask = 0; } /** * Adds a weighted light sample to this light sample. * @param OtherSample - The sample to add. * @param Weight - The weight to multiply the other sample by before addition. */ void AddWeighted(const TGatheredLightSample& OtherSample, float Weight); void ApplyOcclusion(float Occlusion); inline void SetSkyOcclusion(FVector3f InSkyOcclusion) { SkyOcclusion = InSkyOcclusion; } bool AreFloatsValid() const; TGatheredLightSample operator*(float Scalar) const { TGatheredLightSample Result; Result.SHVector = SHVector * Scalar; Result.SHCorrection = SHCorrection * Scalar; Result.IncidentLighting = IncidentLighting * Scalar; Result.SkyOcclusion = SkyOcclusion * Scalar; Result.AOMaterialMask = AOMaterialMask * Scalar; return Result; } TGatheredLightSample operator+(const TGatheredLightSample& SampleB) const { TGatheredLightSample Result; Result.SHVector = SHVector + SampleB.SHVector; Result.SHCorrection = SHCorrection + SampleB.SHCorrection; Result.IncidentLighting = IncidentLighting + SampleB.IncidentLighting; Result.SkyOcclusion = SkyOcclusion + SampleB.SkyOcclusion; Result.AOMaterialMask = AOMaterialMask + SampleB.AOMaterialMask; return Result; } }; class FGatheredLightSampleUtil { public: /** * Constructs a light sample representing an ambient light. * Note: Lighting contributed through this method won't have the same final brightness as PointLightWorldSpace, because of the dot(N, L) * @param Color - The color/intensity of the light at the sample point. */ template static TGatheredLightSample AmbientLight(const FLinearColor& Color); /** * Constructs a light sample representing a point light. * @param Color - The color/intensity of the light at the sample point. * @param Direction - The direction toward the light at the sample point. */ template static TGatheredLightSample PointLightWorldSpace(const FLinearColor& Color, const FVector4f& TangentDirection, const FVector4f& WorldDirection); }; typedef TGatheredLightSample<2> FGatheredLightSample; typedef TGatheredLightSample<3> FGatheredLightSample3; class FGatheredLightMapSample { public: FGatheredLightSample HighQuality; FGatheredLightSample LowQuality; /** True if this sample maps to a valid point on a triangle. This is only meaningful for texture lightmaps. */ bool bIsMapped; FGatheredLightMapSample() : bIsMapped(false) {} FGatheredLightMapSample(const FGatheredLightSample& Sample) : HighQuality(Sample) , LowQuality(Sample) , bIsMapped(false) {} FGatheredLightMapSample& operator=(const FGatheredLightSample& Sample) { HighQuality = Sample; LowQuality = Sample; return *this; } /** * Adds a weighted light sample to this light sample. * @param OtherSample - The sample to add. * @param Weight - The weight to multiply the other sample by before addition. */ void AddWeighted(const FGatheredLightSample& OtherSample, float Weight) { HighQuality.AddWeighted( OtherSample, Weight ); LowQuality.AddWeighted( OtherSample, Weight ); } void ApplyOcclusion(float Occlusion) { HighQuality.ApplyOcclusion(Occlusion); LowQuality.ApplyOcclusion(Occlusion); } /** Converts an FGatheredLightMapSample into a FLightSample. */ FLightSample ConvertToLightSample(bool bDebugThisSample) const; }; /** The lighting information gathered for one final gather sample */ template class TFinalGatherSample : public TGatheredLightSample { public: /** Occlusion factor of the sample, 0 is completely unoccluded, 1 is completely occluded. */ float Occlusion; /** * A light sample for sky lighting. This has to be stored separately to support stationary sky lights only contributing to low quality lightmaps. */ TGatheredLightSample StationarySkyLighting; /** Initialization constructor. */ TFinalGatherSample() : TGatheredLightSample(), Occlusion(0.0f) {} TFinalGatherSample(EForceInit) : TGatheredLightSample(ForceInit), Occlusion(0.0f) {} /** * Adds a weighted light sample to this light sample. * @param OtherSample - The sample to add. * @param Weight - The weight to multiply the other sample by before addition. */ void AddWeighted(const TFinalGatherSample& OtherSample, float Weight); /** * Adds a weighted light sample to this light sample. * @param OtherSample - The sample to add. * @param Weight - The weight to multiply the other sample by before addition. */ template inline void AddWeighted(const TGatheredLightSample& OtherSample, float Weight) { TGatheredLightSample::AddWeighted(OtherSample, Weight); } inline void SetOcclusion(float InOcclusion) { Occlusion = InOcclusion; } inline void AddIncomingRadiance(const FLinearColor& IncomingRadiance, float Weight, const FVector4f& TangentSpaceDirection, const FVector4f& WorldSpaceDirection) { AddWeighted(FGatheredLightSampleUtil::PointLightWorldSpace(IncomingRadiance, TangentSpaceDirection, WorldSpaceDirection), Weight); } bool AreFloatsValid() const; TFinalGatherSample operator*(float Scalar) const { TFinalGatherSample Result; (TGatheredLightSample&)Result = (const TGatheredLightSample&)(*this) * Scalar; Result.Occlusion = Occlusion * Scalar; return Result; } TFinalGatherSample operator+(const TFinalGatherSample& SampleB) const { TFinalGatherSample Result; (TGatheredLightSample&)Result = (const TGatheredLightSample&)(*this) + (const TGatheredLightSample&)SampleB; Result.Occlusion = Occlusion + SampleB.Occlusion; return Result; } }; typedef TFinalGatherSample<2> FFinalGatherSample; typedef TFinalGatherSample<3> FFinalGatherSample3; } //namespace Lightmass