// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "PointLight.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Level/Scene/SceneRendering.h" PointLight::PointLight(const SpawnParams& params) : LightWithShadow(params) , _radius(1000.0f) { CastVolumetricShadow = false; ShadowsDistance = 2000.0f; ShadowsFadeDistance = 100.0f; ShadowsDepthBias = 0.5f; _direction = Float3::Forward; _sphere = BoundingSphere(Vector3::Zero, _radius); BoundingBox::FromSphere(_sphere, _box); } float PointLight::ComputeBrightness() const { float result = Brightness; if (IESTexture) { if (UseIESBrightness) { result = IESTexture->Brightness * IESBrightnessScale; } result *= IESTexture->TextureMultiplier; } //if (UseInverseSquaredFalloff) // result *= 16.0f; return result; } float PointLight::GetScaledRadius() const { return _radius * _transform.Scale.MaxValue(); } void PointLight::SetRadius(float value) { value = Math::Max(0.0f, value); if (Math::NearEqual(value, _radius)) return; _radius = value; UpdateBounds(); } void PointLight::UpdateBounds() { // Cache light direction Float3::Transform(Float3::Forward, _transform.Orientation, _direction); _direction.Normalize(); // Cache bounding box _sphere = BoundingSphere(GetPosition(), GetScaledRadius()); BoundingBox::FromSphere(_sphere, _box); if (_sceneRenderingKey != -1) GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Bounds); } void PointLight::OnTransformChanged() { // Base LightWithShadow::OnTransformChanged(); UpdateBounds(); } void PointLight::Draw(RenderContext& renderContext) { float brightness = ComputeBrightness(); AdjustBrightness(renderContext.View, brightness); Float3 position; const float radius = GetScaledRadius(); if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::PointLights) && EnumHasAnyFlags(renderContext.View.Pass, DrawPass::GBuffer) && brightness > ZeroTolerance && radius > ZeroTolerance && CheckViewDistance(renderContext.View.Position, renderContext.View.Origin, position, brightness)) { RenderPointLightData data; data.Position = position; data.MinRoughness = MinRoughness; data.ShadowsDistance = ShadowsDistance; data.Color = Color.ToFloat3() * (Color.A * brightness); data.ShadowsStrength = ShadowsStrength; data.Direction = _direction; data.ShadowsFadeDistance = ShadowsFadeDistance; data.ShadowsNormalOffsetScale = ShadowsNormalOffsetScale; data.ShadowsDepthBias = ShadowsDepthBias; data.ShadowsSharpness = ShadowsSharpness; data.VolumetricScatteringIntensity = VolumetricScatteringIntensity; data.CastVolumetricShadow = CastVolumetricShadow; data.ShadowsUpdateRate = ShadowsUpdateRate; data.ShadowsUpdateRateAtDistance = ShadowsUpdateRateAtDistance; data.ShadowFrame = _invalidateShadowFrame; data.ShadowsResolution = (int32)ShadowsResolution; data.ShadowsMode = ShadowsMode; data.Radius = radius; data.FallOffExponent = FallOffExponent; data.UseInverseSquaredFalloff = UseInverseSquaredFalloff; data.SourceRadius = SourceRadius; data.SourceLength = SourceLength; data.ContactShadowsLength = ContactShadowsLength; data.IndirectLightingIntensity = IndirectLightingIntensity; data.IESTexture = IESTexture ? IESTexture->GetTexture() : nullptr; data.StaticFlags = GetStaticFlags(); data.ID = GetID(); data.ScreenSize = Math::Min(1.0f, Math::Sqrt(RenderTools::ComputeBoundsScreenRadiusSquared(position, (float)_sphere.Radius, renderContext.View))); renderContext.List->PointLights.Add(data); } } #if USE_EDITOR #include "Engine/Debug/DebugDraw.h" void PointLight::OnDebugDraw() { if (SourceRadius > ZeroTolerance || SourceLength > ZeroTolerance) { // Draw source tube DEBUG_DRAW_WIRE_TUBE(GetPosition(), GetOrientation(), SourceRadius, SourceLength, Color::Orange, 0, true); } // Base LightWithShadow::OnDebugDraw(); } void PointLight::OnDebugDrawSelected() { // Draw influence range DEBUG_DRAW_WIRE_SPHERE(_sphere, Color::Yellow, 0, true); // Base LightWithShadow::OnDebugDrawSelected(); } void PointLight::DrawLightsDebug(RenderView& view) { const BoundingSphere sphere(_sphere.Center - view.Origin, _sphere.Radius); if (!view.CullingFrustum.Intersects(sphere) || !EnumHasAnyFlags(view.Flags, ViewFlags::PointLights)) return; // Draw influence range DEBUG_DRAW_WIRE_SPHERE(_sphere, Color::Yellow, 0, true); } #endif void PointLight::OnLayerChanged() { if (_sceneRenderingKey != -1) GetSceneRendering()->UpdateActor(this, _sceneRenderingKey, ISceneRenderingListener::Layer); } void PointLight::Serialize(SerializeStream& stream, const void* otherObj) { // Base LightWithShadow::Serialize(stream, otherObj); SERIALIZE_GET_OTHER_OBJ(PointLight); SERIALIZE_MEMBER(Radius, _radius); SERIALIZE(IESTexture); SERIALIZE(SourceRadius); SERIALIZE(SourceLength); SERIALIZE(FallOffExponent); SERIALIZE(UseInverseSquaredFalloff); SERIALIZE(UseIESBrightness); SERIALIZE(IESBrightnessScale); } void PointLight::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) { // Base LightWithShadow::Deserialize(stream, modifier); DESERIALIZE_MEMBER(Radius, _radius); DESERIALIZE(IESTexture); DESERIALIZE(SourceRadius); DESERIALIZE(SourceLength); DESERIALIZE(FallOffExponent); DESERIALIZE(UseInverseSquaredFalloff); DESERIALIZE(UseIESBrightness); DESERIALIZE(IESBrightnessScale); } bool PointLight::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) { return CollisionsHelper::RayIntersectsSphere(ray, _sphere, distance, normal); }