You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/LmbrCentral/Code/Source/Rendering/LightInstance.cpp

443 lines
18 KiB
C++

/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include <AzCore/Math/Quaternion.h>
#include <MathConversion.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <I3DEngine.h>
#include "LightInstance.h"
#include "LightComponent.h"
#include "LensFlareComponent.h"
namespace
{
const float kDefaultLightFrustumAngle = 45.f;
const uint8 kDefaultLensOpticsFrustumAngle = 255;
void UpdateLightFlag(bool enable, int mask, unsigned int& flags)
{
if (enable)
{
flags |= mask;
}
else
{
flags &= ~mask;
}
}
AZ::u32 CreateLightId(AZ::EntityId entityId)
{
// Renderer requires a non-zero value, but doesn't care what it actually means.
// This is used only for debug coloring and tie-breaking sorts.
// Use the timestamp half of our entity Id.
const AZ::u32 lowerEntityId = static_cast<AZ::u32>(static_cast<AZ::u64>(entityId) >> 32);
return lowerEntityId;
}
const char* GetEntityName(AZ::EntityId entityId)
{
AZ::Entity* entity = nullptr;
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, entityId);
return (entity ? entity->GetName().c_str() : "<unknown>");
}
void LightConfigToLightParams(const LmbrCentral::LightConfiguration& configuration, AZ::EntityId entityId, int configSpec, CDLight& lightParams)
{
// The light cannot be ambient if it is an environment probe
bool ambient = configuration.m_ambient;
if (configuration.m_lightType == LmbrCentral::LightConfiguration::LightType::Probe)
{
ambient = false;
}
lightParams.SetPosition(Vec3_Zero);
lightParams.m_fLightFrustumAngle = kDefaultLightFrustumAngle;
lightParams.m_Flags = 0;
lightParams.m_LensOpticsFrustumAngle = kDefaultLensOpticsFrustumAngle;
Vec3 color = AZColorToLYVec3(configuration.m_color);
color *= configuration.m_diffuseMultiplier;
lightParams.SetLightColor(ColorF(color.x, color.y, color.z, 1.f));
lightParams.SetSpecularMult(configuration.m_specMultiplier);
lightParams.m_nLightStyle = configuration.m_animIndex; // "light style" is really a light animation curve index
lightParams.SetAnimSpeed(configuration.m_animSpeed); // maps [0, 4] to [0, 255]
lightParams.m_nLightPhase = static_cast<AZ::u8>(configuration.m_animPhase * 255.f); // maps [0, 1] to [0, 255]
lightParams.m_nEntityId = CreateLightId(entityId);
UpdateLightFlag(!configuration.m_useVisAreas, DLF_IGNORES_VISAREAS, lightParams.m_Flags);
UpdateLightFlag(configuration.m_indoorOnly, DLF_INDOOR_ONLY, lightParams.m_Flags);
UpdateLightFlag(configuration.m_affectsThisAreaOnly, DLF_THIS_AREA_ONLY, lightParams.m_Flags);
UpdateLightFlag(ambient, DLF_AMBIENT, lightParams.m_Flags);
UpdateLightFlag(configuration.m_deferred, DLF_DEFERRED_LIGHT, lightParams.m_Flags);
UpdateLightFlag(configuration.m_volumetricFog, DLF_VOLUMETRIC_FOG, lightParams.m_Flags);
UpdateLightFlag(configuration.m_volumetricFogOnly, DLF_VOLUMETRIC_FOG_ONLY, lightParams.m_Flags);
UpdateLightFlag(configuration.m_castTerrainShadows, DLF_CAST_TERRAIN_SHADOWS, lightParams.m_Flags);
if (static_cast<AZ::u32>(configSpec) >= static_cast<AZ::u32>(configuration.m_castShadowsSpec))
{
lightParams.m_Flags |= DLF_CASTSHADOW_MAPS;
}
//Probe won't cast shadow. Enable this flag wrongly for probe could lead to wrong bbox.
if (configuration.m_lightType == LmbrCentral::LightConfiguration::LightType::Probe)
{
lightParams.m_Flags &= ~DLF_CASTSHADOW_MAPS;
}
lightParams.m_fShadowBias = configuration.m_shadowBias;
lightParams.m_fShadowSlopeBias = configuration.m_shadowSlopeBias;
lightParams.m_fShadowResolutionScale = configuration.m_shadowResScale;
lightParams.m_fShadowUpdateMinRadius = configuration.m_shadowUpdateMinRadius;
lightParams.m_nShadowUpdateRatio = max((uint16)1, (uint16)(configuration.m_shadowUpdateRatio * (1 << DL_SHADOW_UPDATE_SHIFT)));
switch (configuration.m_lightType)
{
case LmbrCentral::LightConfiguration::LightType::Point:
{
lightParams.m_fRadius = configuration.m_pointMaxDistance;
lightParams.m_fAttenuationBulbSize = configuration.m_pointAttenuationBulbSize;
lightParams.m_Flags |= DLF_POINT;
}
break;
case LmbrCentral::LightConfiguration::LightType::Area:
{
lightParams.m_fRadius = configuration.m_areaMaxDistance;
lightParams.m_fAreaWidth = configuration.m_areaWidth;
lightParams.m_fAreaHeight = configuration.m_areaHeight;
lightParams.m_fLightFrustumAngle = configuration.m_areaFOV;
lightParams.m_Flags |= DLF_AREA_LIGHT;
}
break;
case LmbrCentral::LightConfiguration::LightType::Projector:
{
lightParams.m_fRadius = configuration.m_projectorRange;
lightParams.m_fLightFrustumAngle = configuration.m_projectorFOV * 0.5f;
lightParams.m_fProjectorNearPlane = configuration.m_projectorNearPlane;
lightParams.m_fAttenuationBulbSize = configuration.m_projectorAttenuationBulbSize;
lightParams.m_Flags |= DLF_PROJECT;
const char* texturePath = configuration.m_projectorTexture.GetAssetPath().c_str();
const int flags = FT_DONT_STREAM;
if (gEnv->pRenderer)
{
lightParams.m_pLightImage = gEnv->pRenderer->EF_LoadTexture(texturePath, flags);
if (!lightParams.m_pLightImage || !lightParams.m_pLightImage->IsTextureLoaded())
{
GetISystem()->Warning(VALIDATOR_MODULE_RENDERER, VALIDATOR_WARNING, 0, texturePath,
"Light projector texture not found: %s", texturePath);
lightParams.m_pLightImage = gEnv->pRenderer->EF_LoadTexture("Textures/defaults/red.dds", flags);
}
}
}
break;
case LmbrCentral::LightConfiguration::LightType::Probe:
{
lightParams.m_ProbeExtents.x = configuration.m_probeArea.GetX() / 2.0f;
lightParams.m_ProbeExtents.y = configuration.m_probeArea.GetY() / 2.0f;
lightParams.m_ProbeExtents.z = configuration.m_probeArea.GetZ() / 2.0f;
lightParams.m_fRadius = lightParams.m_ProbeExtents.len();
lightParams.m_nSortPriority = configuration.m_probeSortPriority;
lightParams.m_fProbeAttenuation = configuration.m_probeFade;
//projection
if (configuration.m_isBoxProjected)
{
//enable box projection
lightParams.m_Flags |= DLF_BOX_PROJECTED_CM;
}
lightParams.m_fBoxHeight = configuration.m_boxHeight;
lightParams.m_fBoxLength = configuration.m_boxLength;
lightParams.m_fBoxWidth = configuration.m_boxWidth;
lightParams.SetFalloffMax(configuration.m_attenFalloffMax);
lightParams.ReleaseCubemaps();
const AZStd::string& specularMap = configuration.m_probeCubemap.GetAssetPath();
if (!specularMap.empty())
{
AZStd::string diffuseMap = specularMap;
AZStd::string::size_type dotPos = specularMap.find_last_of('.');
if (dotPos != AZStd::string::npos)
{
diffuseMap.insert(dotPos, "_diff");
}
if (gEnv->pRenderer)
{
lightParams.SetSpecularCubemap(gEnv->pRenderer->EF_LoadCubemapTexture(specularMap.c_str(), FT_DONT_STREAM));
lightParams.SetDiffuseCubemap(gEnv->pRenderer->EF_LoadCubemapTexture(diffuseMap.c_str(), FT_DONT_STREAM));
}
if (lightParams.GetDiffuseCubemap() && lightParams.GetSpecularCubemap())
{
lightParams.m_Flags |= DLF_DEFERRED_CUBEMAPS;
}
else
{
if (!lightParams.GetSpecularCubemap())
{
AZ_Warning("Light", false,
"Failed to load specular cubemap \"%s\" for light \"%s\".",
specularMap.c_str(),
GetEntityName(entityId));
}
if (!lightParams.GetDiffuseCubemap())
{
AZ_Warning("Light", false,
"Failed to load diffuse cubemap \"%s\" for light \"%s\".",
diffuseMap.c_str(),
GetEntityName(entityId));
}
lightParams.m_Flags &= ~DLF_DEFERRED_CUBEMAPS;
lightParams.m_Flags |= DLF_POINT;
lightParams.ReleaseCubemaps();
}
}
else
{
// Disable the light
lightParams.m_Flags = DLF_FAKE;
}
}
break;
}
}
void LensFlareConfigToLightParams(const LmbrCentral::LensFlareConfiguration& configuration, [[maybe_unused]] AZ::EntityId entityId, int, CDLight& lightParams)
{
lightParams.m_Flags |= DLF_FAKE; // As long as deferred lights are separate components disable the actual deferred light
ColorF color = AZVec3ToLYVec3(configuration.m_tint);
color.a = configuration.m_tintAlpha / 255.0f;
lightParams.SetLightColor(ColorF(1.f, 1.f, 1.f, 1.f)); //Set the light color to white. We can tint the lens flare directly
if (configuration.m_syncAnimWithLight)
{
lightParams.m_nLightStyle = static_cast<AZ::u8>(configuration.m_syncAnimIndex); // This is really a light animation curve ID
lightParams.SetAnimSpeed(configuration.m_syncAnimSpeed); // maps [0, 4] to [0, 255]
lightParams.m_nLightPhase = static_cast<AZ::u8>(configuration.m_syncAnimPhase * 255.f); // maps [0, 1] to [0, 255]
}
else if (configuration.m_attachToSun)
{
gEnv->p3DEngine->SetSunAnimIndex(static_cast<AZ::u8>(configuration.m_animIndex)); // This is really a light animation curve ID
gEnv->p3DEngine->SetSunAnimSpeed(configuration.m_animSpeed); // maps [0, 4] to [0, 255]
gEnv->p3DEngine->SetSunAnimPhase(static_cast<AZ::u8>(configuration.m_animPhase * 255.f)); // maps [0, 1] to [0, 255]
}
else
{
lightParams.m_nLightStyle = static_cast<AZ::u8>(configuration.m_animIndex); // This is really a light animation curve ID
lightParams.SetAnimSpeed(configuration.m_animSpeed); // maps [0, 4] to [0, 255]
lightParams.m_nLightPhase = static_cast<AZ::u8>(configuration.m_animPhase * 255.f); // maps [0, 1] to [0, 255]
}
lightParams.m_LensOpticsFrustumAngle = static_cast<AZ::u8>(configuration.m_lensFlareFrustumAngle / 360.f * 255.f);
UpdateLightFlag(configuration.m_affectsThisAreaOnly, DLF_THIS_AREA_ONLY, lightParams.m_Flags);
UpdateLightFlag(!configuration.m_useVisAreas, DLF_IGNORES_VISAREAS, lightParams.m_Flags);
UpdateLightFlag(configuration.m_indoorOnly, DLF_INDOOR_ONLY, lightParams.m_Flags);
UpdateLightFlag(configuration.m_attachToSun, DLF_ATTACH_TO_SUN, lightParams.m_Flags);
if (!configuration.m_lensFlare.empty())
{
int lensOpticsID = 0;
if (gEnv->pOpticsManager->Load(configuration.m_lensFlare.c_str(), lensOpticsID))
{
IOpticsElementBase* flare = gEnv->pOpticsManager->GetOptics(lensOpticsID);
lightParams.SetLensOpticsElement(flare);
lightParams.SetOpticsParams({ configuration.m_brightness, configuration.m_size, { color.r, color.g, color.b, color.a }, true });
}
else
{
AZ_Warning("LensFlare", false, "Failed to load lens flare \"%s\" for entity \"%s\".", configuration.m_lensFlare.c_str(), GetEntityName(entityId));
}
}
}
}
namespace LmbrCentral
{
LightInstance::LightInstance()
: m_renderLight(nullptr)
{
}
LightInstance::~LightInstance()
{
DestroyRenderLight();
}
void LightInstance::SetEntity(AZ::EntityId entityId)
{
if (m_entityId.IsValid())
{
AZ::TransformNotificationBus::Handler::BusDisconnect();
}
m_entityId = entityId;
if (m_entityId.IsValid())
{
AZ::TransformNotificationBus::Handler::BusConnect(m_entityId);
}
}
void LightInstance::OnTransformChanged(const AZ::Transform&, const AZ::Transform& world)
{
if (m_renderLight)
{
CDLight* lightProperties = &m_renderLight->GetLightProperties();
Matrix34 worldMatrix = AZTransformToLYTransform(world);
worldMatrix.OrthonormalizeFast(); // Lights do not support scale.
lightProperties->SetPosition(worldMatrix.GetTranslation());
lightProperties->SetMatrix(worldMatrix);
m_renderLight->SetMatrix(worldMatrix);
}
}
bool LightInstance::TurnOn()
{
if (!m_renderLight)
{
return false;
}
m_renderLight->SetRndFlags(ERF_HIDDEN, false);
return true;
}
bool LightInstance::TurnOff()
{
if (!m_renderLight)
{
return false;
}
m_renderLight->SetRndFlags(ERF_HIDDEN, true);
return true;
}
bool LightInstance::IsOn() const
{
if (!m_renderLight)
{
return false;
}
return 0 == (m_renderLight->GetRndFlags() & ERF_HIDDEN);
}
void LightInstance::CreateRenderLight(const LightConfiguration& configuration)
{
CreateRenderLightInternal(configuration, LightConfigToLightParams);
#if defined(FEATURE_SVO_GI)
if (m_renderLight)
{
m_renderLight->SetDesiredVoxelGIMode(configuration.m_voxelGIMode);
}
#endif
}
void LightInstance::CreateRenderLight(const LensFlareConfiguration& configuration)
{
CreateRenderLightInternal(configuration, LensFlareConfigToLightParams);
}
void LightInstance::UpdateRenderLight(const LightConfiguration& configuration)
{
// ERF_HIDDEN is used to turn on/off the light, so cache it here and set it back later
bool isHidden = m_renderLight ? (m_renderLight->GetRndFlags() & ERF_HIDDEN) == ERF_HIDDEN : false;
DestroyRenderLight();
CreateRenderLight(configuration);
if (m_renderLight)
{
m_renderLight->SetRndFlags(ERF_HIDDEN, isHidden);
}
}
void LightInstance::UpdateRenderLight(const LensFlareConfiguration& configuration)
{
// ERF_HIDDEN is used to turn on/off the light, so cache it here and set it back later
bool isHidden = m_renderLight ? (m_renderLight->GetRndFlags() & ERF_HIDDEN) == ERF_HIDDEN : false;
DestroyRenderLight();
CreateRenderLight(configuration);
if (m_renderLight)
{
m_renderLight->SetRndFlags(ERF_HIDDEN, isHidden);
}
}
void LightInstance::DestroyRenderLight()
{
if (m_renderLight)
{
m_renderLight->ReleaseNode();
m_renderLight = nullptr;
}
}
IRenderNode* LightInstance::GetRenderNode()
{
return m_renderLight;
}
template <typename ConfigurationType, typename ConfigToLightParamsFunc>
void LightInstance::CreateRenderLightInternal(const ConfigurationType& configuration, ConfigToLightParamsFunc configToLightParams)
{
if (m_renderLight || !configuration.m_visible || !gEnv->p3DEngine)
{
return;
}
ICVar* cvarSysSpecLight = gEnv->pConsole->GetCVar("e_LightQuality");
const int configSpec = cvarSysSpecLight ? cvarSysSpecLight->GetIVal() : gEnv->pSystem->GetConfigSpec(true);
if (static_cast<AZ::u32>(configSpec) < static_cast<AZ::u32>(configuration.m_minSpec))
{
// Light is disabled in the active system spec.
return;
}
CDLight lightParams;
configToLightParams(configuration, m_entityId, configSpec, lightParams);
m_renderLight = gEnv->p3DEngine->CreateLightSource();
if (!configuration.m_material.GetAssetPath().empty())
{
m_renderLight->SetMaterial(gEnv->p3DEngine->GetMaterialManager()->LoadMaterial(configuration.m_material.GetAssetPath().c_str()));
}
m_renderLight->SetLightProperties(lightParams);
m_renderLight->SetMinSpec(static_cast<int>(configuration.m_minSpec));
m_renderLight->SetViewDistanceMultiplier(configuration.m_viewDistMultiplier);
AZ::Transform parentTransform = AZ::Transform::CreateIdentity();
EBUS_EVENT_ID_RESULT(parentTransform, m_entityId, AZ::TransformBus, GetWorldTM);
OnTransformChanged(AZ::Transform::CreateIdentity(), parentTransform);
}
} // namespace LmbrCentral