text: /*
   * Copyright (c) Contributors to the Open 3D Engine Project.
   * For complete copyright and license terms please see the LICENSE at the root of this distribution.
   *
   * SPDX-License-Identifier: Apache-2.0 OR MIT
   *
   */
  
  #include <CoreLights/SimpleSpotLightFeatureProcessor.h>
  
  #include <AzCore/Debug/EventTrace.h>
  
  #include <AzCore/Math/Vector3.h>
  #include <AzCore/Math/Color.h>
  
  #include <Atom/Feature/CoreLights/CoreLightsConstants.h>
  
  #include <Atom/RHI/Factory.h>
  #include <Atom/RHI/CpuProfiler.h>
  
  #include <Atom/RPI.Public/ColorManagement/TransformColor.h>
  #include <Atom/RPI.Public/RPISystemInterface.h>
  #include <Atom/RPI.Public/Scene.h>
  #include <Atom/RPI.Public/View.h>
  
  namespace AZ
  {
      namespace Render
      {
          void SimpleSpotLightFeatureProcessor::Reflect(ReflectContext* context)
          {
              if (auto * serializeContext = azrtti_cast<SerializeContext*>(context))
              {
                  serializeContext
                      ->Class<SimpleSpotLightFeatureProcessor, FeatureProcessor>()
                      ->Version(0);
              }
          }
  
          SimpleSpotLightFeatureProcessor::SimpleSpotLightFeatureProcessor()
              : SimpleSpotLightFeatureProcessorInterface()
          {
          }
  
          void SimpleSpotLightFeatureProcessor::Activate()
          {
              GpuBufferHandler::Descriptor desc;
              desc.m_bufferName = "SimpleSpotLightBuffer";
              desc.m_bufferSrgName = "m_simpleSpotLights";
              desc.m_elementCountSrgName = "m_simpleSpotLightCount";
              desc.m_elementSize = sizeof(SimpleSpotLightData);
              desc.m_srgLayout = RPI::RPISystemInterface::Get()->GetViewSrgLayout().get();
  
              m_lightBufferHandler = GpuBufferHandler(desc);
          }
  
          void SimpleSpotLightFeatureProcessor::Deactivate()
          {
              m_pointLightData.Clear();
              m_lightBufferHandler.Release();
          }
  
          SimpleSpotLightFeatureProcessor::LightHandle SimpleSpotLightFeatureProcessor::AcquireLight()
          {
              uint16_t id = m_pointLightData.GetFreeSlotIndex();
  
              if (id == IndexedDataVector<SimpleSpotLightData>::NoFreeSlot)
              {
                  return LightHandle::Null;
              }
              else
              {
                  m_deviceBufferNeedsUpdate = true;
                  return LightHandle(id);
              }
          }
  
          bool SimpleSpotLightFeatureProcessor::ReleaseLight(LightHandle& handle)
          {
              if (handle.IsValid())
              {
                  m_pointLightData.RemoveIndex(handle.GetIndex());
                  m_deviceBufferNeedsUpdate = true;
                  handle.Reset();
                  return true;
              }
              return false;
          }
  
          SimpleSpotLightFeatureProcessor::LightHandle SimpleSpotLightFeatureProcessor::CloneLight(LightHandle sourceLightHandle)
          {
              AZ_Assert(sourceLightHandle.IsValid(), "Invalid LightHandle passed to SimpleSpotLightFeatureProcessor::CloneLight().");
  
              LightHandle handle = AcquireLight();
              if (handle.IsValid())
              {
                  m_pointLightData.GetData(handle.GetIndex()) = m_pointLightData.GetData(sourceLightHandle.GetIndex());
                  m_deviceBufferNeedsUpdate = true;
              }
              return handle;
          }
  
          void SimpleSpotLightFeatureProcessor::Simulate(const FeatureProcessor::SimulatePacket& packet)
          {
              AZ_ATOM_PROFILE_FUNCTION("RPI", "SimpleSpotLightFeatureProcessor: Simulate");
              AZ_UNUSED(packet);
  
              if (m_deviceBufferNeedsUpdate)
              {
                  m_lightBufferHandler.UpdateBuffer(m_pointLightData.GetDataVector());
                  m_deviceBufferNeedsUpdate = false;
              }
          }
  
          void SimpleSpotLightFeatureProcessor::Render(const SimpleSpotLightFeatureProcessor::RenderPacket& packet)
          {
              AZ_ATOM_PROFILE_FUNCTION("RPI", "SimpleSpotLightFeatureProcessor: Render");
  
              for (const RPI::ViewPtr& view : packet.m_views)
              {
                  m_lightBufferHandler.UpdateSrg(view->GetShaderResourceGroup().get());
              }
          }
  
          void SimpleSpotLightFeatureProcessor::SetRgbIntensity(LightHandle handle, const PhotometricColor<PhotometricUnitType>& lightRgbIntensity)
          {
              AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to SimpleSpotLightFeatureProcessor::SetRgbIntensity().");
  
              auto transformedColor = AZ::RPI::TransformColor(lightRgbIntensity, AZ::RPI::ColorSpaceId::LinearSRGB, AZ::RPI::ColorSpaceId::ACEScg);
  
              AZStd::array<float, 3>& rgbIntensity = m_pointLightData.GetData(handle.GetIndex()).m_rgbIntensity;
              rgbIntensity[0] = transformedColor.GetR();
              rgbIntensity[1] = transformedColor.GetG();
              rgbIntensity[2] = transformedColor.GetB();
  
              m_deviceBufferNeedsUpdate = true;
          }
  
          void SimpleSpotLightFeatureProcessor::SetPosition(LightHandle handle, const AZ::Vector3& lightPosition)
          {
              AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to SimpleSpotLightFeatureProcessor::SetPosition().");
  
              AZStd::array<float, 3>& position = m_pointLightData.GetData(handle.GetIndex()).m_position;
              lightPosition.StoreToFloat3(position.data());
  
              m_deviceBufferNeedsUpdate = true;
          }
          
          void SimpleSpotLightFeatureProcessor::SetDirection(LightHandle handle, const AZ::Vector3& lightDirection)
          {
              AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to SimpleSpotLightFeatureProcessor::SetDirection().");
  
              AZStd::array<float, 3>& direction = m_pointLightData.GetData(handle.GetIndex()).m_direction;
              lightDirection.StoreToFloat3(direction.data());
  
              m_deviceBufferNeedsUpdate = true;
          }
  
          void SimpleSpotLightFeatureProcessor::SetConeAngles(LightHandle handle, float innerRadians, float outerRadians)
          {
              m_pointLightData.GetData(handle.GetIndex()).m_cosInnerConeAngle = cosf(innerRadians);
              m_pointLightData.GetData(handle.GetIndex()).m_cosOuterConeAngle = cosf(outerRadians);
          }
  
          void SimpleSpotLightFeatureProcessor::SetAttenuationRadius(LightHandle handle, float attenuationRadius)
          {
              AZ_Assert(handle.IsValid(), "Invalid LightHandle passed to SimpleSpotLightFeatureProcessor::SetAttenuationRadius().");
  
              attenuationRadius = AZStd::max<float>(attenuationRadius, 0.001f); // prevent divide by zero.
              m_pointLightData.GetData(handle.GetIndex()).m_invAttenuationRadiusSquared = 1.0f / (attenuationRadius * attenuationRadius);
              m_deviceBufferNeedsUpdate = true;
          }
  
          const Data::Instance<RPI::Buffer> SimpleSpotLightFeatureProcessor::GetLightBuffer() const
          {
              return m_lightBufferHandler.GetBuffer();
          }
  
          uint32_t SimpleSpotLightFeatureProcessor::GetLightCount() const
          {
              return m_lightBufferHandler.GetElementCount();
          }
  
      } // namespace Render
  } // namespace AZ
  