{
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ::Name("o_enableIBL");
                o_hairLightingModel = AZ::Name("o_hairLightingModel");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()
            {
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });
                shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });
                shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }
                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


{
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });

/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ::Name("o_enableIBL");
                o_hairLightingModel = AZ::Name("o_hairLightingModel");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()

                shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });
                shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()
            {
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }
                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });

/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ::Name("o_enableIBL");
                o_hairLightingModel = AZ::Name("o_hairLightingModel");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()
            {
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });

                shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()
            {
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }
                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ::Name("o_enableIBL");
                o_hairLightingModel = AZ::Name("o_hairLightingModel");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()
            {
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });
                shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()
            {
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }
                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ

                o_hairLightingModel = AZ::Name("o_hairLightingModel");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()
            {
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });
                shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });
                shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()
            {
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }
                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }

/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ::Name("o_enableIBL");
                o_hairLightingModel = AZ::Name("o_hairLightingModel");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()
            {
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });
                shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });
                shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()
            {
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 *
 *
 */

#include <Atom/RPI.Public/Pass/PassUtils.h>
#include <Atom/RPI.Public/RenderPipeline.h>
#include <Atom/RHI/RHISystemInterface.h>
#include <Atom/RPI.Public/Shader/ShaderSystem.h>
#include <Atom/RPI.Public/RPIUtils.h>
#include <Atom/RPI.Public/Scene.h>

#include <Atom/RPI.Reflect/Pass/PassTemplate.h>
#include <Atom/RPI.Reflect/Shader/ShaderAsset.h>

#include <Passes/HairPPLLResolvePass.h>
#include <Rendering/HairFeatureProcessor.h>
#include <Rendering/HairLightingModels.h>

namespace AZ
{
    namespace Render
    {
        namespace Hair
        {

            HairPPLLResolvePass::HairPPLLResolvePass(const RPI::PassDescriptor& descriptor)
                : RPI::FullscreenTrianglePass(descriptor)
            {
                o_enableShadows = AZ::Name("o_enableShadows");
                o_enableDirectionalLights = AZ::Name("o_enableDirectionalLights");
                o_enablePunctualLights = AZ::Name("o_enablePunctualLights");
                o_enableAreaLights = AZ::Name("o_enableAreaLights");
                o_enableIBL = AZ::Name("o_enableIBL");
                o_hairLightingModel = AZ::Name("o_hairLightingMode

l");
                o_enableMarschner_R = AZ::Name("o_enableMarschner_R");
                o_enableMarschner_TRT = AZ::Name("o_enableMarschner_TRT");
                o_enableMarschner_TT = AZ::Name("o_enableMarschner_TT");
                o_enableLongtitudeCoeff = AZ::Name("o_enableLongtitudeCoeff");
                o_enableAzimuthCoeff = AZ::Name("o_enableAzimuthCoeff");
            }

            void HairPPLLResolvePass::UpdateGlobalShaderOptions()
            {
                RPI::ShaderOptionGroup shaderOption = m_shader->CreateShaderOptionGroup();

                m_featureProcessor->GetHairGlobalSettings(m_hairGlobalSettings);

                shaderOption.SetValue(o_enableShadows, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableShadows });
                shaderOption.SetValue(o_enableDirectionalLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableDirectionalLights });
                shaderOption.SetValue(o_enablePunctualLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enablePunctualLights });
                shaderOption.SetValue(o_enableAreaLights, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAreaLights });
                shaderOption.SetValue(o_enableIBL, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableIBL });
                shaderOption.SetValue(o_hairLightingModel, AZ::Name{ "HairLightingModel::" + AZStd::string(HairLightingModelNamespace::ToString(m_hairGlobalSettings.m_hairLightingModel)) });
                shaderOption.SetValue(o_enableMarschner_R, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_R });
                shaderOption.SetValue(o_enableMarschner_TRT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TRT });
                shaderOption.SetValue(o_enableMarschner_TT, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableMarschner_TT });
                shaderOption.SetValue(o_enableLongtitudeCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableLongtitudeCoeff });
                shaderOption.SetValue(o_enableAzimuthCoeff, AZ::RPI::ShaderOptionValue{ m_hairGlobalSettings.m_enableAzimuthCoeff });

                m_shaderOptions = shaderOption.GetShaderVariantKeyFallbackValue();
            }

            RPI::Ptr<HairPPLLResolvePass> HairPPLLResolvePass::Create(const RPI::PassDescriptor& descriptor)
            {
                RPI::Ptr<HairPPLLResolvePass> pass = aznew HairPPLLResolvePass(descriptor);

                return AZStd::move(pass);
            }

            void HairPPLLResolvePass::InitializeInternal()
            {
                if (GetScene())
                {
                    FullscreenTrianglePass::InitializeInternal();
                }
            }

            bool HairPPLLResolvePass::AcquireFeatureProcessor()
            {
                if (m_featureProcessor)
                {
                    return true;
                }

                RPI::Scene* scene = GetScene();
                if (scene)
                {
                    m_featureProcessor = scene->GetFeatureProcessor<HairFeatureProcessor>();
                }
                else
                {
                    return false;
                }

                if (!m_featureProcessor || !m_featureProcessor->IsInitialized())
                {
                    AZ_Warning("Hair Gem", m_featureProcessor,
                        "HairPPLLResolvePass [%s] - Failed to retrieve Hair feature processor from the scene",
                        GetName().GetCStr());
                    m_featureProcessor = nullptr;   // set it as null if not initialized to repeat this check.
                    return false;
                }
                return true;
            }

            void HairPPLLResolvePass::CompileResources(const RHI::FrameGraphCompileContext& context)
            {
                if (!m_shaderResourceGroup || !AcquireFeatureProcessor())
                {
                    AZ_Error("Hair Gem", m_shaderResourceGroup, "HairPPLLResolvePass: PPLL list data was not bound - missing Srg");
                    return;     // no error message due to FP - initialization not complete yet, wait for the next frame
                }

                UpdateGlobalShaderOptions();

                if (m_shaderResourceGroup->HasShaderVariantKeyFallbackEntry())
                {
                    m_shaderResourceGroup->SetShaderVariantKeyFallbackValue(m_shaderOptions);
                }

                SrgBufferDescriptor descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::ReadWrite, RHI::Format::Unknown,
                    PPLL_NODE_SIZE, RESERVED_PIXELS_FOR_OIT,
                    Name{ "LinkedListNodesPPLL" }, Name{ "m_linkedListNodes" }, 0, 0
                );
                if (!UtilityClass::BindBufferToSrg("Hair Gem", m_featureProcessor->GetPerPixelListBuffer(), descriptor, m_shaderResourceGroup))
                {
                    AZ_Error("Hair Gem", false, "HairPPLLResolvePass: PPLL list data could not be bound.");
                }

                // Update the material array constant buffer within the per pass srg
                descriptor = SrgBufferDescriptor(
                    RPI::CommonBufferPoolType::Constant, RHI::Format::Unknown,
                    sizeof(AMD::TressFXShadeParams), 1,
                    Name{ "HairMaterialsArray" }, Name{ "m_hairParams" }, 0, 0
                );

                m_featureProcessor->GetMaterialsArray().UpdateGPUData(m_shaderResourceGroup, descriptor);

                // All remaining srgs should compile here
                FullscreenTrianglePass::CompileResources(context);
            }

        } // namespace Hair
     }   // namespace Render
}   // namespace AZ


/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}

/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const


/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)

  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace()

  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();

/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;

    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }

/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t length)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


/*
 *
 */

#include "Config.h"
#include "Globals.h"
#include "M17TX.h"

#include "M17Defines.h"

const uint8_t M17_SYNC = 0x77U;
const uint8_t M17_PREAMBLE = 0x77U;

CM17TX::CM17TX() :
m_buffer(1500U),
m_poBuffer(),
m_poLen(0U),
m_poPtr(0U),
m_txDelay(240U),      // 200ms
m_delay(false)
{
}

void CM17TX::process()
{
  if (m_buffer.getData() == 0U && m_poLen == 0U)
    return;

  if (m_poLen == 0U) {
    if (!m_tx) {
      m_delay = true;
      m_poLen = m_txDelay;
    } else {
      m_delay = false;
      for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
        m_poBuffer[m_poLen++] = m_buffer.get();
    }

    m_poPtr = 0U;
  }

  if (m_poLen > 0U) {
    uint16_t space = io.getSpace();

    while (space > 8U) {
      if (m_delay) {
        writeByte(M17_SYNC);
        m_poPtr++;
      } else
        writeByte(m_poBuffer[m_poPtr++]);

      space -= 8U;

      if (m_poPtr >= m_poLen) {
        if (m_delay) {
          m_delay = false;
          m_poPtr = 0U;
          m_poLen = 3U;
        } else {
          m_poPtr = 0U;
          m_poLen = 0U;
          m_delay = false;
          return;
        }
      }
    }
  }
}

uint8_t CM17TX::writeData(const uint8_t* data, uint8_t le

ngth)
{
  if (length != (M17_FRAME_LENGTH_BYTES + 1U))
    return 4U;

  uint16_t space = m_buffer.getSpace();
  if (space < M17_FRAME_LENGTH_BYTES)
    return 5U;

  for (uint8_t i = 0U; i < M17_FRAME_LENGTH_BYTES; i++)
    m_buffer.put(data[i + 1U]);

  return 0U;
}

void CM17TX::writeByte(uint8_t c)
{
  uint8_t bit;
  uint8_t mask = 0x80U;

  for (uint8_t i = 0U; i < 8U; i++, c <<= 1) {
    if ((c & mask) == mask)
      bit = 1U;
    else
      bit = 0U;

    io.write(&bit, 1);
  }
}

void CM17TX::setTXDelay(uint8_t delay)
{
  m_txDelay = 600U + uint16_t(delay) * 12U;        // 500ms + tx delay
}

uint8_t CM17TX::getSpace() const
{
  return m_buffer.getSpace() / M17_FRAME_LENGTH_BYTES;
}


struct A {
  void f();

  int a;
};

struct B : A {
  double b;
};

void f() {
  B b;

  b.f();
}

B *f(A *a) {
  // CHECK-NOT: br label
  return static_cast<B*>(a);
}

// PR5965
namespace PR5965 {

A *f(B* b) {
  // CHECK-NOT: br label
  return b;
}

}

// Don't crash on a derived-to-base conversion of an r-value
namespace test3 {
  struct A {};
  struct B : A {};

  void foo(A a);


struct A {
  void f();

  int a;
};

struct B : A {
  double b;
};

void f()

B *f(A *a) {
  // CHECK-NOT: br label
  return static_cast<B*>(a);
}

// PR5965
namespace PR5965 {

A *f(B* b) {
  // CHECK-NOT: br label
  return b;
}

}

// Don't crash on a derived-to-base conversion of an r-value
namespace test3 {
  struct A {};
  struct B : A {};

  void foo(A a);
  void test() {
    foo(B());
  }
}


struct A {
  void f();

  int a;
};

struct B : A {
  double b;
};

void f() {
  B b;

  b.f();
}

B *f(A *a) {
  // CHECK-NOT: br label
  return static_cast<B*>(a);
}

// PR5965
namespace PR5965 {

A *f(B* b) {
  // CHECK-NOT: br label
  return b;
}

}

// Don't crash on a derived-to-base conversion of an r-value
namespace test3 {
  struct A {};
  struct B : A {};

  void foo(A a);
  void test() {
    foo(B(


struct A {
  void f();

  int a;
};

struct B : A {
  double b;
};

void f() {
  B b;

  b.f();
}

B *f(A *a) {
  // CHECK-NOT: br label
  return static_cast<B*>(a);
}

// PR5965
namespace PR5965 {

A *f(B* b) {
  // CHECK-NOT: br label
  return b;
}

}

// Don't crash on a

 derived-to-base conversion of an r-value
namespace test3 {
  struct A {};
  struct B : A {};

  void foo(A a);
  void test() {
    foo(B());
  }
}


SampleCanvasElement::SampleCanvasElement(Canvas* canvas, int col, int row, float offset, float length)
: CanvasElement(canvas,col,row,offset,length)
, mSample(nullptr)
, mVolume(1)
, mMute(false)

/**
    bespoke synth, a software modular synthesizer

**/
//  CanvasElement.cpp
//  Bespoke
//  Created by Ryan Challinor on 1/4/15.

#include "CanvasElement.h"
#include "Sample.h"
#include "SynthGlobals.h"
#include "Slider.h"
#include "CanvasControls.h"
#include "TextEntry.h"
#include "PatchCableSource.h"
#include "ModularSynth.h"
#include "EventCanvas.h"
#include "SampleCanvas.h"

CanvasElement::CanvasElement(Canvas* canvas, int col, int row, float offset, float length)
: mCanvas(canvas)
, mOffset(offset)
, mLength(length)
, mCol(col)
, mRow(row)
, mHighlighted(false)
{
}

void CanvasElement::Draw(ofVec2f offset)
{
   if (offset.x == 0 && offset.y == 0)
   {
      DrawElement(K(clamp), !K(wrapped), offset);
      if (mCanvas->ShouldWrap() && GetEnd() > 1)
         DrawElement(K(clamp), K(wrapped), offset);
   }
   else
   {
      ofPushStyle();
      ofSetLineWidth(3.0f);
      ofNoFill();
      ofSetColor(255, 255, 255, ofMap(sin(gTime / 500 * PI * 2), -1, 1, 40, 120));
      ofRect(GetRectAtDestination(K(clamp), !K(wrapped), offset), 0);
      if (mCanvas->ShouldWrap() && GetEnd() > 1)
         ofRect(GetRectAtDestination(K(clamp), K(wrapped), offset), 0);
      ofPopStyle();

      DrawElement(!K(clamp), !K(wrapped), offset);
   }
}

void CanvasElement::DrawElement(bool clamp, bool wrapped, ofVec2f offset)
{
   ofRectangle rect = GetRect(clamp, wrapped, offset);
   ofRectangle fullRect = GetRect(false, wrapped, offset);

   if (rect.width < 0)
   {
      ofPushStyle();
      ofFill();
      ofSetColor(255, 0, 0);
      ofRect(rect.x + rect.width, rect.y, -rect.width, rect.height, 0);
      ofPopStyle();
   }

   ofPushStyle();
   DrawContents(clamp, wrapped, offset);
   ofPopStyle();

   if (mHighlighted)
   {
      ofPushStyle();
      ofNoFill();
      ofSetColor(255, 200, 0);
      ofSetLineWidth(.75f);

      ofLine(rect.x, rect.y, rect.x + rect.width, rect.y);
      ofLine(rect.x, rect.y + rect.height, rect.x + rect.width, rect.y + rect.height);

      if (fullRect.x >= 0)
      {
         ofPushStyle();
         if (mCanvas->GetHighlightEnd() == Canvas::kHighlightEnd_Start && IsResizable())
         {
            ofSetLineWidth(1.5f);
            ofSetColor(255, 100, 100);
         }
         ofLine(rect.x, rect.y, rect.x, rect.y + rect.height);
         ofPopStyle();
      }
      if (fullRect.x + fullRect.width <= mCanvas->GetLength() * mCanvas->GetWidth())
      {
         ofPushStyle();
         if (mCanvas->GetHighlightEnd() == Canvas::kHighlightEnd_End && IsResizable())
         {
            ofSetLineWidth(1.5f);
            ofSetColor(255, 100, 100);
         }
         ofLine(rect.x + rect.width, rect.y, rect.x + rect.width, rect.y + rect.height);
         ofPopStyle();
      }

      ofPopStyle();
   }
}

void CanvasElement::DrawOffscreen()
{
   ofPushStyle();
   ofSetLineWidth(1);
   ofSetColor(255, 255, 255);
   {
      ofRectangle rect = GetRect(K(clamp), !K(wrapped));
      if (rect.y < 0)
      {
         rect.y = 0;
         rect.height = 1;
      }
      if (rect.y + rect.height > mCanvas->GetHeight())
      {
         rect.y = mCanvas->GetHeight()-1;
         rect.height = 1;
      }
      rect.width = MAX(rect.width, 1);
      ofRect(rect);
   }
   if (mCanvas->ShouldWrap() && GetEnd() > 1)
   {
      ofRectangle rect = GetRect(K(clamp), K(wrapped));
      if (rect.y < 0)
      {
         rect.y = 0;
         rect.height = 1;
      }
      if (rect.y > mCanvas->GetHeight())
      {
         rect.y = mCanvas->GetHeight() - 1;
         rect.height = 1;
      }
      rect.width = MAX(rect.width, 1);
      ofRect(rect);
   }
   ofPopStyle();
}

float CanvasElement::GetStart(int col, float offset) const
{
   return (col + offset) / mCanvas->GetNumCols();
}

float CanvasElement::GetEnd(int col, float offset, float length) const
{
   return (col + offset + length) / mCanvas->GetNumCols();
}

float CanvasElement::GetStart() const
{
   return GetStart(mCol, mOffset);
}

void CanvasElement::SetStart(float start, bool preserveLength)
{
   float end;
   if (!preserveLength)
      end = GetEnd();
   start *= mCanvas->GetNumCols();
   mCol = int(start + .5f);
   mOffset = start - mCol;
   if (!preserveLength)
      SetEnd(end);
}

float CanvasElement::GetEnd() const
{
   return GetEnd(mCol, mOffset, mLength);
}

void CanvasElement::SetEnd(float end)
{
   mLength = end * mCanvas->GetNumCols() - mCol - mOffset;
}

ofRectangle CanvasElement::GetRect(bool clamp, bool wrapped, ofVec2f offset) const
{
   float wrapOffset = wrapped ? -1 : 0;
   float start = ofMap(GetStart() + wrapOffset,mCanvas->mViewStart/mCanvas->GetLength(),mCanvas->mViewEnd/mCanvas->GetLength(),0,1,clamp) * mCanvas->GetWidth();
   float end = ofMap(GetEnd() + wrapOffset,mCanvas->mViewStart/mCanvas->GetLength(),mCanvas->mViewEnd/mCanvas->GetLength(),0,1,clamp) * mCanvas->GetWidth();
   float y = (float(mRow-mCanvas->GetRowOffset()) / mCanvas->GetNumVisibleRows()) * mCanvas->GetHeight();
   float height = float(mCanvas->GetHeight()) / mCanvas->GetNumVisibleRows();

   return ofRectangle(start + offset.x, y + offset.y, end-start, height);
}

ofRectangle CanvasElement::GetRectAtDestination(bool clamp, bool wrapped, ofVec2f dragOffset) const
{
   int newRow;
   int newCol;
   float newOffset;
   GetDragDestinationData(dragOffset, newRow, newCol, newOffset);

   float wrapOffset = wrapped ? -1 : 0;
   float start = ofMap(GetStart(newCol, newOffset) + wrapOffset, mCanvas->mViewStart / mCanvas->GetLength(), mCanvas->mViewEnd / mCanvas->GetLength(), 0, 1, clamp) * mCanvas->GetWidth();
   float end = ofMap(GetEnd(newCol, newOffset, mLength) + wrapOffset, mCanvas->mViewStart / mCanvas->GetLength(), mCanvas->mViewEnd / mCanvas->GetLength(), 0, 1, clamp) * mCanvas->GetWidth();
   float y = (float(newRow - mCanvas->GetRowOffset()) / mCanvas->GetNumVisibleRows()) * mCanvas->GetHeight();
   float height = float(mCanvas->GetHeight()) / mCanvas->GetNumVisibleRows();

   return ofRectangle(start, y, end - start, height);
}

void CanvasElement::GetDragDestinationData(ofVec2f dragOffset, int& newRow, int& newCol, float& newOffset) const
{
   dragOffset.x *= mCanvas->mViewEnd - mCanvas->mViewStart;

   float colDrag = (dragOffset.x / mCanvas->GetWidth()) * mCanvas->GetNumCols() / mCanvas->GetLength();
   float rowDrag = (dragOffset.y / mCanvas->GetHeight()) * mCanvas->GetNumVisibleRows();

   newCol = mCol;
   if (mCanvas->GetDragMode() & Canvas::kDragHorizontal)
      newCol = ofClamp(int(mCol + colDrag + .5f), 0, mCanvas->GetNumCols() - 1);

   newRow = mRow;
   if (mCanvas->GetDragMode() & Canvas::kDragVertical)
      newRow = ofClamp(int(mRow + rowDrag + .5f), 0, mCanvas->GetNumRows() - 1);

   newOffset = mOffset;
   if (GetKeyModifiers() & kModifier_Alt)   //non-snapped drag
      newOffset = mOffset + colDrag - (newCol - mCol);
   if (GetKeyModifiers() & kModifier_Command)   //quantize
      newOffset = 0;
}

void CanvasElement::MoveElementByDrag(ofVec2f dragOffset)
{
   int newRow;
   int newCol;
   float newOffset;
   GetDragDestinationData(dragOffset, newRow, newCol, newOffset);

   mRow = newRow;
   mCol = newCol;
   mOffset = newOffset;
}

void CanvasElement::AddElementUIControl(IUIControl* control)
{
   mUIControls.push_back(control);
   control->SetShowing(false);
}

void CanvasElement::CheckboxUpdated(std::string label, bool value)
{
   for (auto* control : mUIControls)
   {
      if (control->Name() == label)
         control->SetValue(value);
   }
}

void CanvasElement::FloatSliderUpdated(std::string label, float oldVal, float newVal)
{
   for (auto* control : mUIControls)
   {
      if (control->Name() == label)
         control->SetValue(newVal);
   }
}

void CanvasElement::IntSliderUpdated(std::string label, int oldVal, float newVal)
{
   for (auto* control : mUIControls)
   {
      if (control->Name() == label)
         control->SetValue(newVal);
   }
}

void CanvasElement::ButtonClicked(std::string label)
{
}

namespace
{
   const int kCESaveStateRev = 1;
}

void CanvasElement::SaveState(FileStreamOut& out)
{
   out << kCESaveStateRev;

   out << mOffset;
   out << mLength;
}

void CanvasElement::LoadState(FileStreamIn& in)
{
   int rev;
   in >> rev;
   LoadStateValidate(rev <= kCESaveStateRev);

   if (rev < 1)
   {
      in >> mRow;
      in >> mCol;
   }
   in >> mOffset;
   in >> mLength;
}

NoteCanvasElement::NoteCanvasElement(Canvas* canvas, int col, int row, float offset, float length)
: CanvasElement(canvas,col,row,offset,length)
, mVelocity(.5f)
, mVoiceIdx(-1)
, mPan(0)
{
   if (canvas != nullptr && canvas->GetControls())
   {
      mElementOffsetSlider = new FloatSlider(dynamic_cast<IFloatSliderListener*>(canvas->GetControls()), "offset", 0, 0, 100, 15, &mOffset, -1, 1);
      AddElementUIControl(mElementOffsetSlider);
      mElementLengthSlider = new FloatSlider(dynamic_cast<IFloatSliderListener*>(canvas->GetControls()), "length", 0, 0, 100, 15, &mLength, 0, mCanvas->GetNumCols());
      AddElementUIControl(mElementLengthSlider);
      mElementRowSlider = new IntSlider(dynamic_cast<IIntSliderListener*>(canvas->GetControls()), "row", 0, 0, 100, 15, &mRow, 0, mCanvas->GetNumRows() - 1);
      AddElementUIControl(mElementRowSlider);
      mElementColSlider = new IntSlider(dynamic_cast<IIntSliderListener*>(canvas->GetControls()), "step", 0, 0, 100, 15, &mCol, 0, mCanvas->GetNumCols() - 1);
      AddElementUIControl(mElementColSlider);
      mVelocitySlider = new FloatSlider(dynamic_cast<IFloatSliderListener*>(canvas->GetControls()), "velocity", 0, 0, 100, 15, &mVelocity, 0, 1);
      AddElementUIControl(mVelocitySlider);
   }
}

CanvasElement* NoteCanvasElement::CreateDuplicate() const
{
   NoteCanvasElement* element = new NoteCanvasElement(mCanvas, mCol, mRow, mOffset, mLength);
   element->mVelocity = mVelocity;
   element->mVoiceIdx = mVoiceIdx;
   return element;
}

void NoteCanvasElement::DrawContents(bool clamp, bool wrapped, ofVec2f offset)
{
   ofPushStyle();
   ofFill();

   ofRectangle rect = GetRect(clamp, wrapped, offset);
   float fullHeight = rect.height;
   rect.height *= mVelocity;
   rect.y += (fullHeight - rect.height) * .5f;
   if (rect.width > 0)
   {
      ofSetColorGradient(ofColor::white, ofColor(210,210,210), ofVec2f(ofLerp(rect.getMinX(),rect.getMaxX(),.5f), rect.y), ofVec2f(rect.getMaxX(), rect.y));
      ofRect(rect, 0);
   }

   /*ofSetLineWidth(1.5f * gDrawScale);

   rect = GetRect(clamp, wrapped);
   ofRectangle fullRect = GetRect(false, false);
   if (wrapped)
      fullRect.x -= mCanvas->GetWidth();
   float length = (GetEnd()-GetStart()) * mCanvas->GetLength();

   float start = (float(rect.x-fullRect.x)/fullRect.width);
   float end = ((float(rect.x+rect.width)-(fullRect.x))/fullRect.width);

   mPitchBendCurve.SetPosition(rect.x, rect.y - rect.height/2);
   mPitchBendCurve.SetDimensions(rect.width, rect.height);
   mPitchBendCurve.SetExtents(start*length, end*length);
   mPitchBendCurve.SetColor(ofColor::red);
   mPitchBendCurve.Render();

   mModWheelCurve.SetPosition(rect.x, rect.y);
   mModWheelCurve.SetDimensions(rect.width, rect.height);
   mModWheelCurve.SetExtents(start*length, end*length);
   mModWheelCurve.SetColor(ofColor::green);
   mModWheelCurve.Render();

   mPressureCurve.SetPosition(rect.x, rect.y);
   mPressureCurve.SetDimensions(rect.width, rect.height);
   mPressureCurve.SetExtents(start*length, end*length);
   mPressureCurve.SetColor(ofColor::blue);
   mPressureCurve.Render();*/

   ofPopStyle();
}

void NoteCanvasElement::UpdateModulation(float pos)
{
   float curveTime = (pos - GetStart()) * mCanvas->GetLength();
   FloatWrap(curveTime, mCanvas->GetLength());
   mPitchBend.SetValue(mPitchBendCurve.Evaluate(curveTime));
   mModWheel.SetValue(mModWheelCurve.Evaluate(curveTime));
   mPressure.SetValue(mPressureCurve.Evaluate(curveTime));
   mPan = mPanCurve.Evaluate(curveTime);
}

void NoteCanvasElement::WriteModulation(float pos, float pitchBend, float modWheel, float pressure, float pan)
{
   float curveTime = (pos - GetStart()) * mCanvas->GetLength();
   FloatWrap(curveTime, mCanvas->GetLength());
   mPitchBendCurve.AddPoint(CurvePoint(curveTime, pitchBend));
   mModWheelCurve.AddPoint(CurvePoint(curveTime, modWheel));
   mPressureCurve.AddPoint(CurvePoint(curveTime, pressure));
   mPanCurve.AddPoint(CurvePoint(curveTime, pan));
}

namespace
{
   const int kNCESaveStateRev = 0;
}

void NoteCanvasElement::SaveState(FileStreamOut& out)
{
   CanvasElement::SaveState(out);

   out << kNCESaveStateRev;

   out << mVelocity;
}

void NoteCanvasElement::LoadState(FileStreamIn& in)
{
   CanvasElement::LoadState(in);

   int rev;
   in >> rev;
   LoadStateValidate(rev == kNCESaveStateRev);

   in >> mVelocity;
}

{
   mElementOffsetSlider = new FloatSlider(dynamic_cast<IFloatSliderListener*>(canvas->GetControls()),"offset",0,0,100,15,&mOffset,-1,1);
   AddElementUIControl(mElementOffsetSlider);
   mVolumeSlider = new FloatSlider(dynamic_cast<IFloatSliderListener*>(canvas->GetControls()),"volume",0,0,100,15,&mVolume,0,2);
   AddElementUIControl(mVolumeSlider);
   mMuteCheckbox = new Checkbox(dynamic_cast<IDrawableModule*>(canvas->GetControls()), "mute", 0, 0, &mMute);
   AddElementUIControl(mMuteCheckbox);
   mSplitSampleButton = new ClickButton(dynamic_cast<IButtonListener*>(canvas->GetControls()), "split", 0, 0);
   AddElementUIControl(mSplitSampleButton);
   mResetSpeedButton = new ClickButton(dynamic_cast<IButtonListener*>(canvas->GetControls()), "reset speed", 0, 0);
   AddElementUIControl(mResetSpeedButton);
}

SampleCanvasElement::~SampleCanvasElement()
{
   delete mSample;
}

CanvasElement* SampleCanvasElement::CreateDuplicate() const
{
   SampleCanvasElement* element = new SampleCanvasElement(mCanvas, mCol, mRow, mOffset, mLength);
   element->mSample = new Sample();
   element->mSample->Create(mSample->Data());
   element->mVolume = mVolume;
   element->mMute = mMute;
   return element;
}

void SampleCanvasElement::SetSample(Sample* sample)
{
   mSample = sample;
}

void SampleCanvasElement::CheckboxUpdated(std::string label, bool value)
{
   CanvasElement::CheckboxUpdated(label, value);
}

void SampleCanvasElement::ButtonClicked(std::string label)
{
   CanvasElement::ButtonClicked(label);
   if (label == "split")
   {
      ChannelBuffer* firstHalf = new ChannelBuffer(mSample->Data()->BufferSize() / 2);
      ChannelBuffer* secondHalf = new ChannelBuffer(mSample->Data()->BufferSize() / 2);
      firstHalf->CopyFrom(mSample->Data(), firstHalf->BufferSize(), 0);
      secondHalf->CopyFrom(mSample->Data(), secondHalf->BufferSize(), mSample->Data()->BufferSize() / 2);

      SampleCanvasElement* element = new SampleCanvasElement(mCanvas, mCol, mRow, mOffset, mLength);
      element->mSample = new Sample();
      element->mSample->Create(secondHalf);
      element->mVolume = mVolume;
      element->mMute = mMute;
      element->SetStart((GetStart() + GetEnd()) * .5f, false);
      mCanvas->AddElement(element);

      mSample->Create(firstHalf);
      mLength /= 2;
   }
   if (label == "reset speed")
   {
      SampleCanvas* sampleCanvas = dynamic_cast<SampleCanvas*>(mCanvas->GetParent());
      if (sampleCanvas != nullptr)
      {
         float lengthMs = mSample->LengthInSamples() / mSample->GetSampleRateRatio() / gSampleRateMs;
         float lengthOriginalSpeed = lengthMs / TheTransport->GetDuration(sampleCanvas->GetInterval());
         mLength = lengthOriginalSpeed;
      }
   }
}

void SampleCanvasElement::DrawContents(bool clamp, bool wrapped, ofVec2f offset)
{
   if (wrapped)
      return;

   ofRectangle rect = GetRect(false, false, offset);

   ofPushMatrix();
   ofTranslate(rect.x,rect.y);
   if (mSample)
   {
      float width = rect.width;
      DrawAudioBuffer(width, rect.height, mSample->Data(), 0, mSample->LengthInSamples(), -1);
   }
   else
   {
      ofSetColor(0,0,0);
      ofRect(0,0,rect.width,rect.height);
   }
   ofPopMatrix();

   if (rect.width < 0)
      rect.set(rect.x + rect.width, rect.y, -rect.width, rect.height);

   SampleCanvas* sampleCanvas = dynamic_cast<SampleCanvas*>(mCanvas->GetParent());
   if (sampleCanvas != nullptr && mSample != nullptr)
   {
      float lengthMs = mSample->LengthInSamples() / mSample->GetSampleRateRatio() / gSampleRateMs;
      float lengthOriginalSpeed = lengthMs / TheTransport->GetDuration(sampleCanvas->GetInterval());
      float speed = lengthOriginalSpeed / mLength;
      ofSetColor(255, 255, 255);
      DrawTextBold(ofToString(speed, 2), rect.x + 3, rect.y + 10, 12);
   }

   if (mMute)
   {
      ofSetColor(0, 0, 0, 140);
      ofFill();
      ofRect(rect);
   }

   ofSetColor(255, 255, 255);
   ofNoFill();
   ofRect(rect);
}

namespace
{
   const int kSCESaveStateRev = 2;
}

void SampleCanvasElement::SaveState(FileStreamOut& out)
{
   CanvasElement::SaveState(out);

   out << kSCESaveStateRev;

   bool hasSample = mSample != nullptr;
   out << hasSample;
   if (mSample != nullptr)
      mSample->SaveState(out);
   out << mVolume;
   out << mMute;
}

void SampleCanvasElement::LoadState(FileStreamIn& in)
{
   CanvasElement::LoadState(in);

   int rev;
   in >> rev;
   LoadStateValidate(rev <= kSCESaveStateRev);

   bool hasSample;
   in >> hasSample;
   if (hasSample)
   {
      mSample = new Sample();
      mSample->LoadState(in);
   }
   if (rev < 2)
   {
      int dummy;
      in >> dummy;
      in >> dummy;
   }
   in >> mVolume;
   if (rev == 0)
   {
      bool dummy;
      in >> dummy;
   }
   if (rev >= 1)
      in >> mMute;
}

EventCanvasElement::EventCanvasElement(Canvas* canvas, int col, int row, float offset)
: CanvasElement(canvas,col,row,offset,.5f)
, mValue(0)
{
   mValueEntry = new TextEntry(dynamic_cast<ITextEntryListener*>(canvas->GetControls()),"value",60,2,7,&mValue,-99999,99999);
   AddElementUIControl(mValueEntry);

   mEventCanvas = dynamic_cast<EventCanvas*>(canvas->GetControls()->GetParent());
   assert(mEventCanvas);
   mUIControl = mEventCanvas->GetUIControlForRow(row);
   mIsCheckbox = dynamic_cast<Checkbox*>(mUIControl) != nullptr;
   mIsButton = dynamic_cast<ClickButton*>(mUIControl) != nullptr;

   if (mUIControl)
      mValue = mUIControl->GetValue();
   if (mIsButton)
      mValue = 1;
}

EventCanvasElement::~EventCanvasElement()
{
}

CanvasElement* EventCanvasElement::CreateDuplicate() const
{
   EventCanvasElement* element = new EventCanvasElement(mCanvas, mCol, mRow, mOffset);
   element->mUIControl = mUIControl;
   element->mIsCheckbox = mIsCheckbox;
   element->mIsButton = mIsButton;
   element->mValue = mValue;
   return element;
}

void EventCanvasElement::DrawContents(bool clamp, bool wrapped, ofVec2f offset)
{
   if (wrapped)
      return;

   if (GetRect(false, false, offset).width != GetRect(clamp, false, offset).width)
      return;  //only draw text for fully visible elements

   ofSetColor(mEventCanvas->GetRowColor(mRow));
   ofFill();
   ofRect(GetRect(clamp, wrapped, offset), 0);

   std::string text;
   if (mIsCheckbox)
   {
      text = mUIControl->Name();
      ofRectangle rect = GetRect(clamp, false, offset);
      ofSetColor(0,0,0);
      DrawTextNormal(text, rect.x+4, rect.y+11);
      ofSetColor(255,255,255);
      DrawTextNormal(text, rect.x+3, rect.y+10);
   }
   else if (mUIControl)
   {
      text += mUIControl->Name();
      text += ":";
      text += mUIControl->GetDisplayValue(mValue);

      ofRectangle rect = GetRect(clamp, false, offset);
      ofSetColor(0,0,0);
      DrawTextNormal(text, rect.x+rect.width+1, rect.y+11);
      ofSetColor(255,255,255);
      DrawTextNormal(text, rect.x+rect.width, rect.y+10);
   }
}

void EventCanvasElement::SetUIControl(IUIControl* control)
{
   bool hadUIControl = mUIControl != nullptr;
   mUIControl = dynamic_cast<IUIControl*>(control);
   mIsCheckbox = dynamic_cast<Checkbox*>(control) != nullptr;
   mIsButton = dynamic_cast<ClickButton*>(control) != nullptr;
   if (mUIControl && !hadUIControl)
      mValue = mUIControl->GetValue();
   if (mIsButton)
      mValue = 1;
}

void EventCanvasElement::Trigger()
{
   if (mUIControl)
   {
      if (mIsCheckbox)
         mUIControl->SetValue(1);
      else
         mUIControl->SetValue(mValue);
   }
}

void EventCanvasElement::TriggerEnd()
{
   if (mUIControl && mIsCheckbox)
       mUIControl->SetValue(0);
}

float EventCanvasElement::GetEnd() const
{
   if (mIsCheckbox)  //normal resizable element
      return CanvasElement::GetEnd();

   float size = 4;
   float span = mCanvas->mViewEnd - mCanvas->mViewStart;
   return GetStart() + size * span / mCanvas->GetWidth();
}

namespace
{
   const int kECESaveStateRev = 1;
}

void EventCanvasElement::SaveState(FileStreamOut& out)
{
   CanvasElement::SaveState(out);

   out << kECESaveStateRev;

   out << mValue;
}

void EventCanvasElement::LoadState(FileStreamIn& in)
{
   CanvasElement::LoadState(in);

   int rev;
   in >> rev;
   LoadStateValidate(rev <= kECESaveStateRev);

   in >> mValue;
   if (rev < 1)
   {
      std::string dummy;
      in >> dummy;
   }
}