123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896 |
- /*
- * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
- * its licensors.
- *
- * For complete copyright and license terms please see the LICENSE at the root of this
- * distribution (the "License"). All use of this software is governed by the License,
- * or, if provided, by the license below or the license accompanying this file. Do not
- * remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *
- */
- #include <LightCullingExampleComponent.h>
- #include <SampleComponentConfig.h>
- #include <Automation/ScriptableImGui.h>
- #include <Automation/ScriptRunnerBus.h>
- #include <Atom/Component/DebugCamera/CameraControllerBus.h>
- #include <Atom/Component/DebugCamera/NoClipControllerComponent.h>
- #include <imgui/imgui.h>
- #include <Atom/Feature/CoreLights/PhotometricValue.h>
- #include <Atom/Feature/CoreLights/CoreLightsConstants.h>
- #include <Atom/RPI.Reflect/Material/MaterialAsset.h>
- #include <Atom/RPI.Reflect/Model/ModelAsset.h>
- #include <AzCore/Asset/AssetManagerBus.h>
- #include <AzCore/Asset/AssetCommon.h>
- #include <AzCore/Component/TransformBus.h>
- #include <AzCore/Math/Transform.h>
- #include <AzCore/Math/Obb.h>
- #include <AzFramework/Components/CameraBus.h>
- #include <Atom/RPI.Public/RPISystemInterface.h>
- #include <Atom/RPI.Public/Scene.h>
- #include <Atom/RPI.Public/RenderPipeline.h>
- #include <Atom/RPI.Public/View.h>
- #include <Atom/RPI.Public/Pass/FullscreenTrianglePass.h>
- #include <Atom/RPI.Public/AuxGeom/AuxGeomFeatureProcessorInterface.h>
- #include <Atom/RPI.Public/AuxGeom/AuxGeomDraw.h>
- #include <Atom/RPI.Reflect/Asset/AssetUtils.h>
- #include <RHI/BasicRHIComponent.h>
- namespace AtomSampleViewer
- {
- using namespace AZ;
- using namespace AZ::Render;
- using namespace AZ::RPI;
- static const char* WorldModelName = "Objects/Bistro/Bistro_Research_Exterior.azmodel";
- static const char* TransparentModelName = "Objects/ShaderBall_simple.azmodel";
- static const char* TransparentMaterialName = "materials/DefaultPBRTransparent.azmaterial";
- static const char* DecalMaterialPath = "materials/Decal/airship_tail_01_decal.azmaterial";
- static const float TimingSmoothingFactor = 0.9f;
- static const size_t MaxNumLights = 1024;
- static const float AuxGeomDebugAlpha = 0.5f;
- static const AZ::Vector3 CameraStartPositionVespa = AZ::Vector3(3.37f, -1.44f, 1.82f);
- static const AZ::Vector3 CameraStartPositionLongViewDownStreet = AZ::Vector3(-12.f, -35.5f, 0.7438f);
- AZ::Color LightCullingExampleComponent::GetRandomColor()
- {
- static const AZStd::vector<AZ::Color> colors = {
- AZ::Colors::Red,
- AZ::Colors::Blue,
- AZ::Colors::Green,
- AZ::Colors::White,
- AZ::Colors::Purple,
- AZ::Colors::MediumAquamarine,
- AZ::Colors::Fuchsia,
- AZ::Colors::Thistle,
- AZ::Colors::LightGoldenrodYellow,
- AZ::Colors::BlanchedAlmond,
- AZ::Colors::PapayaWhip,
- AZ::Colors::Bisque,
- AZ::Colors::Chocolate,
- AZ::Colors::MintCream,
- AZ::Colors::LemonChiffon,
- AZ::Colors::Plum
- };
- int randomNumber = m_random.GetRandom() % colors.size();
- AZ::Color color = colors[randomNumber];
- color.SetA(AuxGeomDebugAlpha);
- return color;
- }
- static AZ::Render::MaterialAssignmentMap CreateMaterialAssignmentMap(const char* materialPath)
- {
- Render::MaterialAssignmentMap materials;
- Render::MaterialAssignment& defaultMaterialAssignment = materials[AZ::Render::DefaultMaterialAssignmentId];
- defaultMaterialAssignment.m_materialAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::MaterialAsset>(materialPath, RPI::AssetUtils::TraceLevel::Assert);
- defaultMaterialAssignment.m_materialInstance = RPI::Material::FindOrCreate(defaultMaterialAssignment.m_materialAsset);
- return materials;
- }
- LightCullingExampleComponent::LightCullingExampleComponent()
- {
- m_sampleName = "LightCullingExampleComponent";
- // Add some initial lights to illuminate the scene
- m_settings[(int)LightType::Point].m_numActive = 150;
- m_settings[(int)LightType::Disk].m_intensity = 40.0f;
- m_settings[(int)LightType::Capsule].m_intensity = 10.0f;
- }
- void LightCullingExampleComponent::Reflect(AZ::ReflectContext* context)
- {
- if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
- {
- serializeContext->Class<LightCullingExampleComponent, AZ::Component>()
- ->Version(0)
- ;
- }
- }
- void LightCullingExampleComponent::Activate()
- {
- GetFeatureProcessors();
- // Don't continue the script until after the models have loaded and lights have been created.
- // Use a large timeout because of how slow this level loads in debug mode.
- ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::PauseScriptWithTimeout, 120.0f);
- // preload assets
- AZStd::vector<AssetCollectionAsyncLoader::AssetToLoadInfo> assetList = {
- {WorldModelName, azrtti_typeid<RPI::ModelAsset>()},
- {TransparentModelName, azrtti_typeid<RPI::ModelAsset>()},
- {TransparentMaterialName, azrtti_typeid<RPI::MaterialAsset>()},
- {DecalMaterialPath, azrtti_typeid<RPI::MaterialAsset>()}
- };
- PreloadAssets(assetList);
- }
- void LightCullingExampleComponent::OnAllAssetsReadyActivate()
- {
- LoadDecalMaterial();
- SetupScene();
- SetupCamera();
- m_imguiSidebar.Activate();
- AZ::TickBus::Handler::BusConnect();
- // Now that the model and all the lights are initialized, we can allow the script to continue.
- ScriptRunnerRequestBus::Broadcast(&ScriptRunnerRequests::ResumeScript);
- }
- void LightCullingExampleComponent::SetupCamera()
- {
- AZ::Debug::CameraControllerRequestBus::Event(
- GetCameraEntityId(),
- &AZ::Debug::CameraControllerRequestBus::Events::Enable,
- azrtti_typeid<AZ::Debug::NoClipControllerComponent>());
- SaveCameraConfiguration();
- const float FarClipDistance = 16384.0f;
- Camera::CameraRequestBus::Event(
- GetCameraEntityId(),
- &Camera::CameraRequestBus::Events::SetFarClipDistance,
- FarClipDistance);
- }
- void LightCullingExampleComponent::Deactivate()
- {
- m_decalMaterial = {};
- DisableHeatmap();
- AZ::TickBus::Handler::BusDisconnect();
- m_imguiSidebar.Deactivate();
- RestoreCameraConfiguration();
- AZ::Debug::CameraControllerRequestBus::Event(
- GetCameraEntityId(),
- &AZ::Debug::CameraControllerRequestBus::Events::Disable);
- DestroyOpaqueModels();
- DestroyTransparentModels();
- DestroyLightsAndDecals();
- }
- void LightCullingExampleComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint timePoint)
- {
- if (m_worldModelAssetLoaded)
- {
- CalculateSmoothedFPS(deltaTime);
- DrawSidebar();
- DrawDebuggingHelpers();
- UpdateLights();
- }
- }
- void LightCullingExampleComponent::UpdateLights()
- {
- if (m_refreshLights)
- {
- DestroyLightsAndDecals();
- CreateLightsAndDecals();
- m_refreshLights = false;
- }
- }
- void LightCullingExampleComponent::OnModelReady(AZ::Data::Instance<AZ::RPI::Model> model)
- {
- m_meshChangedHandler.Disconnect();
- m_worldModelAssetLoaded = true;
- m_worldModelAABB = model->GetAabb();
- InitLightArrays();
- CreateLightsAndDecals();
- MoveCameraToStartPosition();
- }
- void LightCullingExampleComponent::SaveCameraConfiguration()
- {
- Camera::CameraRequestBus::EventResult(
- m_originalFarClipDistance,
- GetCameraEntityId(),
- &Camera::CameraRequestBus::Events::GetFarClipDistance);
- }
- void LightCullingExampleComponent::RestoreCameraConfiguration()
- {
- Camera::CameraRequestBus::Event(
- GetCameraEntityId(),
- &Camera::CameraRequestBus::Events::SetFarClipDistance,
- m_originalFarClipDistance);
- }
- void LightCullingExampleComponent::SetupScene()
- {
- using namespace AZ;
- CreateTransparentModels();
- CreateOpaqueModels();
- }
- AZ::Vector3 LightCullingExampleComponent::GetRandomPositionInsideWorldModel()
- {
- AZ::Vector3 randomPosition;
- float x = m_random.GetRandomFloat() * m_worldModelAABB.GetXExtent();
- float y = m_random.GetRandomFloat() * m_worldModelAABB.GetYExtent();
- float z = m_random.GetRandomFloat() * m_worldModelAABB.GetZExtent();
- randomPosition.SetX(x);
- randomPosition.SetY(y);
- randomPosition.SetZ(z);
- randomPosition += m_worldModelAABB.GetMin();
- return randomPosition;
- }
- AZ::Vector3 LightCullingExampleComponent::GetRandomDirection()
- {
- float x = m_random.GetRandomFloat() - 0.5f;
- float y = m_random.GetRandomFloat() - 0.5f;
- float z = m_random.GetRandomFloat() - 0.5f;
- AZ::Vector3 direction(x, y, z);
- direction.NormalizeSafe();
- return direction;
- }
- float LightCullingExampleComponent::GetRandomNumber(float low, float high)
- {
- float r = m_random.GetRandomFloat();
- return r * (high - low) + low;
- }
- void LightCullingExampleComponent::CreatePointLights()
- {
- for (int i = 0; i < m_settings[(int)LightType::Point].m_numActive; ++i)
- {
- CreatePointLight(i);
- }
- }
- void LightCullingExampleComponent::CreateSpotLights()
- {
- for (int i = 0; i < m_settings[(int)LightType::Spot].m_numActive; ++i)
- {
- CreateSpotLight(i);
- }
- }
- void LightCullingExampleComponent::CreateDiskLights()
- {
- for (int i = 0; i < m_settings[(int)LightType::Disk].m_numActive; ++i)
- {
- CreateDiskLight(i);
- }
- }
- void LightCullingExampleComponent::CreateCapsuleLights()
- {
- for (int i = 0; i < m_settings[(int)LightType::Capsule].m_numActive; ++i)
- {
- CreateCapsuleLight(i);
- }
- }
- void LightCullingExampleComponent::CreateQuadLights()
- {
- for (int i = 0; i < m_settings[(int)LightType::Quad].m_numActive; ++i)
- {
- CreateQuadLight(i);
- }
- }
- void LightCullingExampleComponent::CreateDecals()
- {
- for (int i = 0; i < m_settings[(int)LightType::Decal].m_numActive; ++i)
- {
- CreateDecal(i);
- }
- }
- void LightCullingExampleComponent::DestroyDecals()
- {
- for (size_t i = 0; i < m_decals.size(); ++i)
- {
- m_decalFeatureProcessor->ReleaseDecal(m_decals[i].m_decalHandle);
- m_decals[i].m_decalHandle = DecalHandle::Null;
- }
- }
- void LightCullingExampleComponent::DrawSidebar()
- {
- if (!m_imguiSidebar.Begin())
- {
- return;
- }
- if (!m_worldModelAssetLoaded)
- {
- const ImGuiWindowFlags windowFlags =
- ImGuiWindowFlags_NoCollapse |
- ImGuiWindowFlags_NoResize |
- ImGuiWindowFlags_NoMove;
- if (ImGui::Begin("Asset", nullptr, windowFlags))
- {
- ImGui::Text("World Model: %s", m_worldModelAssetLoaded ? "Loaded" : "Loading...");
- ImGui::End();
- }
- }
- DrawSidebarTimingSection();
- ImGui::Spacing();
- ImGui::Separator();
- DrawSidebarPointLightsSection(&m_settings[(int)LightType::Point]);
- DrawSidebarSpotLightsSection(&m_settings[(int)LightType::Spot]);
- DrawSidebarDiskLightsSection(&m_settings[(int)LightType::Disk]);
- DrawSidebarCapsuleLightSection(&m_settings[(int)LightType::Capsule]);
- DrawSidebarQuadLightsSections(&m_settings[(int)LightType::Quad]);
- DrawSidebarDecalSection(&m_settings[(int)LightType::Decal]);
- DrawSidebarHeatmapOpacity();
- m_imguiSidebar.End();
- }
- void LightCullingExampleComponent::DrawSidebarPointLightsSection(LightSettings* lightSettings)
- {
- ScriptableImGui::ScopedNameContext context{ "Point Lights" };
- if (ImGui::CollapsingHeader("Point Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
- {
- m_refreshLights |= ScriptableImGui::SliderInt("Point light count", &lightSettings->m_numActive, 0, MaxNumLights);
- m_refreshLights |= ScriptableImGui::SliderFloat("Bulb Radius", &m_bulbRadius, 0.0f, 20.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Point Intensity", &lightSettings->m_intensity, 0.0f, 200.0f);
- m_refreshLights |= ScriptableImGui::Checkbox("Enable automatic light falloff (Point)", &lightSettings->m_enableAutomaticFalloff);
- m_refreshLights |= ScriptableImGui::SliderFloat("Point Attenuation Radius", &lightSettings->m_attenuationRadius, 0.0f, 20.0f);
- ScriptableImGui::Checkbox("Draw Debug Spheres", &lightSettings->m_enableDebugDraws);
- }
- }
- void LightCullingExampleComponent::DrawSidebarSpotLightsSection(LightSettings* lightSettings)
- {
- ScriptableImGui::ScopedNameContext context{"Spot Lights"};
- if (ImGui::CollapsingHeader("Spot Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
- {
- m_refreshLights |= ScriptableImGui::SliderInt("Spot light count", &lightSettings->m_numActive, 0, MaxNumLights);
- m_refreshLights |= ScriptableImGui::SliderFloat("Spot Intensity", &lightSettings->m_intensity, 0.0f, 200.0f);
- m_refreshLights |= ScriptableImGui::Checkbox("Enable automatic light falloff (Spot)", &lightSettings->m_enableAutomaticFalloff);
- m_refreshLights |= ScriptableImGui::SliderFloat("Spot Attenuation Radius", &lightSettings->m_attenuationRadius, 0.0f, 20.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Inner Cone (degrees)", &m_spotInnerConeDegrees, 0.0f, 180.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Outer Cone (degrees)", &m_spotOuterConeDegrees, 0.0f, 180.0f);
- ScriptableImGui::Checkbox("Draw Debug Cones", &lightSettings->m_enableDebugDraws);
- }
- }
- void LightCullingExampleComponent::DrawSidebarDiskLightsSection(LightSettings* lightSettings)
- {
- ScriptableImGui::ScopedNameContext context{"Disk Lights"};
- if (ImGui::CollapsingHeader("Disk Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
- {
- m_refreshLights |= ScriptableImGui::SliderInt("Disk light count", &lightSettings->m_numActive, 0, MaxNumLights);
- m_refreshLights |= ScriptableImGui::SliderFloat("Disk Radius", &m_diskRadius, 0.0f, 20.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Disk Attenuation Radius", &lightSettings->m_attenuationRadius, 0.0f, 20.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Disk Intensity", &lightSettings->m_intensity, 0.0f, 200.0f);
- m_refreshLights |= ScriptableImGui::Checkbox("Double sided disk", &m_isDiskDoubleSided);
- ScriptableImGui::Checkbox("Draw disk lights", &lightSettings->m_enableDebugDraws);
- }
- }
- void LightCullingExampleComponent::DrawSidebarCapsuleLightSection(LightSettings* lightSettings)
- {
- ScriptableImGui::ScopedNameContext context{"Capsule Lights"};
- if (ImGui::CollapsingHeader("Capsule Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
- {
- m_refreshLights |= ScriptableImGui::SliderInt("Capsule light count", &lightSettings->m_numActive, 0, MaxNumLights);
- m_refreshLights |= ScriptableImGui::SliderFloat("Capsule Intensity", &lightSettings->m_intensity, 0.0f, 200.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Capsule Radius", &m_capsuleRadius, 0.0f, 5.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Capsule Length", &m_capsuleLength, 0.0f, 20.0f);
- ScriptableImGui::Checkbox("Draw capsule lights", &lightSettings->m_enableDebugDraws);
- }
- }
- void LightCullingExampleComponent::DrawSidebarQuadLightsSections(LightSettings* lightSettings)
- {
- ScriptableImGui::ScopedNameContext context{ "Quad Lights" };
- if (ImGui::CollapsingHeader("Quad Lights", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
- {
- m_refreshLights |= ScriptableImGui::SliderInt("Quad light count", &lightSettings->m_numActive, 0, MaxNumLights);
- m_refreshLights |= ScriptableImGui::SliderFloat("Quad Attenuation Radius", &lightSettings->m_attenuationRadius, 0.0f, 20.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Quad light width", &m_quadLightSize[0], 0.0f, 10.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Quad light height", &m_quadLightSize[1], 0.0f, 10.0f);
- m_refreshLights |= ScriptableImGui::Checkbox("Double sided quad", &m_isQuadLightDoubleSided);
- m_refreshLights |= ScriptableImGui::Checkbox("Use fast approximation", &m_quadLightsUseFastApproximation);
- ScriptableImGui::Checkbox("Draw quad lights", &lightSettings->m_enableDebugDraws);
- }
- }
- void LightCullingExampleComponent::DrawSidebarHeatmapOpacity()
- {
- ScriptableImGui::ScopedNameContext context{"Heatmap"};
- ImGui::Text("Heatmap");
- ImGui::Indent();
- bool opacityChanged = ScriptableImGui::SliderFloat("Opacity", &m_heatmapOpacity, 0, 1);
- ImGui::Unindent();
- if (opacityChanged)
- {
- UpdateHeatmapOpacity();
- }
- }
- void LightCullingExampleComponent::DrawSidebarDecalSection(LightSettings* lightSettings)
- {
- ScriptableImGui::ScopedNameContext context{"Decals"};
- if (ImGui::CollapsingHeader("Decals", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_Framed))
- {
- m_refreshLights |= ScriptableImGui::SliderInt("Decal count", &lightSettings->m_numActive, 0, MaxNumLights);
- ScriptableImGui::Checkbox("Draw decals", &lightSettings->m_enableDebugDraws);
- m_refreshLights |= ScriptableImGui::SliderFloat3("Decal Size", m_decalSize.data(), 0.0f, 10.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Decal Opacity", &m_decalOpacity, 0.0f, 1.0f);
- m_refreshLights |= ScriptableImGui::SliderFloat("Decal Angle Attenuation", &m_decalAngleAttenuation, 0.0f, 1.0f);
- }
- }
- void LightCullingExampleComponent::CreatePointLight(int index)
- {
- auto& light = m_pointLights[index];
- AZ_Assert(light.m_lightHandle.IsNull(), "CreatePointLight called on a light that was already created previously");
- light.m_lightHandle = m_pointLightFeatureProcessor->AcquireLight();
- const LightSettings& settings = m_settings[(int)LightType::Point];
- m_pointLightFeatureProcessor->SetPosition(light.m_lightHandle, light.m_position);
- m_pointLightFeatureProcessor->SetRgbIntensity(light.m_lightHandle, PhotometricColor<PhotometricUnit::Candela>(settings.m_intensity * light.m_color));
- m_pointLightFeatureProcessor->SetBulbRadius(light.m_lightHandle, m_bulbRadius);
- float attenuationRadius = settings.m_enableAutomaticFalloff ? AutoCalculateAttenuationRadius(light.m_color, settings.m_intensity) : settings.m_attenuationRadius;
- m_pointLightFeatureProcessor->SetAttenuationRadius(light.m_lightHandle, attenuationRadius);
- }
- void LightCullingExampleComponent::CreateDiskLight(int index)
- {
- auto& light = m_diskLights[index];
- light.m_lightHandle = m_diskLightFeatureProcessor->AcquireLight();
- const LightSettings& settings = m_settings[(int)LightType::Disk];
- m_diskLightFeatureProcessor->SetDiskRadius(light.m_lightHandle, m_diskRadius);
- m_diskLightFeatureProcessor->SetPosition(light.m_lightHandle, light.m_position);
- m_diskLightFeatureProcessor->SetRgbIntensity(light.m_lightHandle, PhotometricColor<PhotometricUnit::Candela>(settings.m_intensity * light.m_color));
- m_diskLightFeatureProcessor->SetDirection(light.m_lightHandle, light.m_direction);
- m_diskLightFeatureProcessor->SetLightEmitsBothDirections(light.m_lightHandle, m_isDiskDoubleSided);
- float attenuationRadius = settings.m_enableAutomaticFalloff ? AutoCalculateAttenuationRadius(light.m_color, settings.m_intensity) : settings.m_attenuationRadius;
- m_diskLightFeatureProcessor->SetAttenuationRadius(light.m_lightHandle, m_settings[(int)LightType::Disk].m_attenuationRadius);
- }
- void LightCullingExampleComponent::CreateCapsuleLight(int index)
- {
- auto& light = m_capsuleLights[index];
- AZ_Assert(light.m_lightHandle.IsNull(), "CreateCapsuleLight called on a light that was already created previously");
- light.m_lightHandle = m_capsuleLightFeatureProcessor->AcquireLight();
- const LightSettings& settings = m_settings[(int)LightType::Capsule];
- m_capsuleLightFeatureProcessor->SetAttenuationRadius(light.m_lightHandle, m_settings[(int)LightType::Capsule].m_attenuationRadius);
- m_capsuleLightFeatureProcessor->SetRgbIntensity(light.m_lightHandle, PhotometricColor<PhotometricUnit::Candela>(settings.m_intensity * light.m_color));
- m_capsuleLightFeatureProcessor->SetCapsuleRadius(light.m_lightHandle, m_capsuleRadius);
- AZ::Vector3 startPoint = light.m_position - light.m_direction * m_capsuleLength * 0.5f;
- AZ::Vector3 endPoint = light.m_position + light.m_direction * m_capsuleLength * 0.5f;
- m_capsuleLightFeatureProcessor->SetCapsuleLineSegment(light.m_lightHandle, startPoint, endPoint);
- }
- void LightCullingExampleComponent::CreateQuadLight(int index)
- {
- auto& light = m_quadLights[index];
- AZ_Assert(light.m_lightHandle.IsNull(), "CreateQuadLight called on a light that was already created previously");
- light.m_lightHandle = m_quadLightFeatureProcessor->AcquireLight();
- const LightSettings& settings = m_settings[(int)LightType::Quad];
- m_quadLightFeatureProcessor->SetRgbIntensity(light.m_lightHandle, PhotometricColor<PhotometricUnit::Nit>(settings.m_intensity * light.m_color));
- const auto orientation = AZ::Quaternion::CreateFromVector3(light.m_direction);
- m_quadLightFeatureProcessor->SetOrientation(light.m_lightHandle, orientation);
- m_quadLightFeatureProcessor->SetQuadDimensions(light.m_lightHandle, m_quadLightSize[0], m_quadLightSize[1]);
- m_quadLightFeatureProcessor->SetLightEmitsBothDirections(light.m_lightHandle, m_isQuadLightDoubleSided);
- m_quadLightFeatureProcessor->SetUseFastApproximation(light.m_lightHandle, m_quadLightsUseFastApproximation);
- m_quadLightFeatureProcessor->SetAttenuationRadius(light.m_lightHandle, m_settings[(int)LightType::Quad].m_attenuationRadius);
- m_quadLightFeatureProcessor->SetPosition(light.m_lightHandle, light.m_position);
- }
- void LightCullingExampleComponent::CreateSpotLight(int index)
- {
- auto& light = m_spotLights[index];
- light.m_lightHandle = m_spotLightFeatureProcessor->AcquireLight();
- const LightSettings& settings = m_settings[(int)LightType::Spot];
- m_spotLightFeatureProcessor->SetPosition(light.m_lightHandle, light.m_position);
- m_spotLightFeatureProcessor->SetDirection(light.m_lightHandle, light.m_direction);
- m_spotLightFeatureProcessor->SetRgbIntensity(light.m_lightHandle, PhotometricColor<PhotometricUnit::Candela>(settings.m_intensity * light.m_color));
- float attenuationRadius = settings.m_enableAutomaticFalloff ? AutoCalculateAttenuationRadius(light.m_color, settings.m_intensity) : settings.m_attenuationRadius;
- m_spotLightFeatureProcessor->SetAttenuationRadius(light.m_lightHandle, attenuationRadius);
- m_spotLightFeatureProcessor->SetConeAngles(light.m_lightHandle, m_spotInnerConeDegrees, m_spotOuterConeDegrees);
- }
- void LightCullingExampleComponent::CreateDecal(int index)
- {
- Decal& decal = m_decals[index];
- decal.m_decalHandle = m_decalFeatureProcessor->AcquireDecal();
- AZ::Render::DecalData decalData;
- decalData.m_position = {
- { decal.m_position.GetX(), decal.m_position.GetY(), decal.m_position.GetZ() }
- };
- decalData.m_halfSize = {
- {m_decalSize[0] * 0.5f, m_decalSize[1] * 0.5f, m_decalSize[2] * 0.5f}
- };
- decalData.m_quaternion = {
- { decal.m_quaternion.GetX(), decal.m_quaternion.GetY(), decal.m_quaternion.GetZ(), decal.m_quaternion.GetW() }
- };
- decalData.m_angleAttenuation = m_decalAngleAttenuation;
- decalData.m_opacity = m_decalOpacity;
- m_decalFeatureProcessor->SetDecalData(decal.m_decalHandle, decalData);
- m_decalFeatureProcessor->SetDecalMaterial(decal.m_decalHandle, m_decalMaterial.GetId());
- }
- void LightCullingExampleComponent::DrawPointLightDebugSpheres(AZ::RPI::AuxGeomDrawPtr auxGeom)
- {
- const LightSettings& settings = m_settings[(int)LightType::Point];
- int numToDraw = AZStd::min(m_settings[(int)LightType::Point].m_numActive, aznumeric_cast<int>(m_pointLights.size()));
- for (int i = 0; i < numToDraw; ++i)
- {
- const auto& light = m_pointLights[i];
- if (light.m_lightHandle.IsNull())
- {
- continue;
- }
- float radius = settings.m_enableAutomaticFalloff ? AutoCalculateAttenuationRadius(light.m_color, settings.m_intensity) : settings.m_attenuationRadius;
- auxGeom->DrawSphere(light.m_position, radius, light.m_color, AZ::RPI::AuxGeomDraw::DrawStyle::Shaded);
- }
- }
- void LightCullingExampleComponent::DrawSpotLightDebugCones(AZ::RPI::AuxGeomDrawPtr auxGeom)
- {
- const LightSettings& settings = m_settings[(int)LightType::Spot];
- int numToDraw = AZStd::min(settings.m_numActive, aznumeric_cast<int>(m_spotLights.size()));
- for (int i = 0; i < numToDraw; ++i)
- {
- const auto& light = m_spotLights[i];
- if (light.m_lightHandle.IsNull())
- {
- continue;
- }
- float height = settings.m_enableAutomaticFalloff ? AutoCalculateAttenuationRadius(light.m_color, settings.m_intensity) : settings.m_attenuationRadius;
- float angleRadians = AZ::DegToRad(m_spotOuterConeDegrees * 0.5f);
- float radius = tanf(angleRadians) * height;
- auxGeom->DrawCone(light.m_position + light.m_direction * height, -light.m_direction, radius, height, light.m_color, AZ::RPI::AuxGeomDraw::DrawStyle::Shaded);
- }
- }
- void LightCullingExampleComponent::DrawDecalDebugBoxes(AZ::RPI::AuxGeomDrawPtr auxGeom)
- {
- int numToDraw = AZStd::min(m_settings[(int)LightType::Decal].m_numActive, aznumeric_cast<int>(m_decals.size()));
- for (int i = 0; i < numToDraw; ++i)
- {
- const Decal& decal = m_decals[i];
- if (decal.m_decalHandle.IsValid())
- {
- AZ::Aabb aabb = AZ::Aabb::CreateCenterHalfExtents(AZ::Vector3::CreateZero(), AZ::Vector3::CreateFromFloat3(m_decalSize.data()) * 0.5f);
- AZ::Matrix3x4 transform = AZ::Matrix3x4::CreateFromQuaternionAndTranslation(decal.m_quaternion, decal.m_position);
- auxGeom->DrawObb(AZ::Obb::CreateFromAabb(aabb), transform, AZ::Colors::White, AZ::RPI::AuxGeomDraw::DrawStyle::Line);
- }
- }
- }
- void LightCullingExampleComponent::DrawDiskLightDebugObjects(AZ::RPI::AuxGeomDrawPtr auxGeom)
- {
- int numToDraw = AZStd::min(m_settings[(int)LightType::Disk].m_numActive, aznumeric_cast<int>(m_diskLights.size()));
- for (int i = 0; i < numToDraw; ++i)
- {
- const auto& light = m_diskLights[i];
- if (light.m_lightHandle.IsValid())
- {
- auxGeom->DrawDisk(light.m_position, light.m_direction, m_diskRadius, light.m_color, AZ::RPI::AuxGeomDraw::DrawStyle::Shaded);
- }
- }
- }
- void LightCullingExampleComponent::DrawCapsuleLightDebugObjects(AZ::RPI::AuxGeomDrawPtr auxGeom)
- {
- int numToDraw = AZStd::min(m_settings[(int)LightType::Capsule].m_numActive, aznumeric_cast<int>(m_capsuleLights.size()));
- for (int i = 0; i < numToDraw; ++i)
- {
- const auto& light = m_capsuleLights[i];
- if (light.m_lightHandle.IsValid())
- {
- auxGeom->DrawCylinder(light.m_position, light.m_direction, m_capsuleRadius, m_capsuleLength, light.m_color);
- }
- }
- }
- void LightCullingExampleComponent::DrawQuadLightDebugObjects(AZ::RPI::AuxGeomDrawPtr auxGeom)
- {
- int numToDraw = AZStd::min(m_settings[(int)LightType::Quad].m_numActive, aznumeric_cast<int>(m_quadLights.size()));
- for (int i = 0; i < numToDraw; ++i)
- {
- const auto& light = m_quadLights[i];
- if (light.m_lightHandle.IsValid())
- {
- auto transform = AZ::Transform::CreateFromQuaternionAndTranslation(AZ::Quaternion::CreateFromVector3(light.m_direction), light.m_position);
- // Rotate 90 degrees so that the debug draw is aligned properly with the quad light
- transform *= AZ::Transform::CreateFromQuaternion(AZ::ConvertEulerRadiansToQuaternion(AZ::Vector3(AZ::Constants::HalfPi, 0.0f, 0.0f)));
- auxGeom->DrawQuad(m_quadLightSize[0], m_quadLightSize[1], transform, light.m_color);
- }
- }
- }
- void LightCullingExampleComponent::DrawDebuggingHelpers()
- {
- auto defaultScene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
- if (auto auxGeom = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(defaultScene))
- {
- if (m_settings[(int)LightType::Point].m_enableDebugDraws)
- {
- DrawPointLightDebugSpheres(auxGeom);
- }
- if (m_settings[(int)LightType::Spot].m_enableDebugDraws)
- {
- DrawSpotLightDebugCones(auxGeom);
- }
- if (m_settings[(int)LightType::Disk].m_enableDebugDraws)
- {
- DrawDiskLightDebugObjects(auxGeom);
- }
- if (m_settings[(int)LightType::Capsule].m_enableDebugDraws)
- {
- DrawCapsuleLightDebugObjects(auxGeom);
- }
- if (m_settings[(int)LightType::Quad].m_enableDebugDraws)
- {
- DrawQuadLightDebugObjects(auxGeom);
- }
- if (m_settings[(int)LightType::Decal].m_enableDebugDraws)
- {
- DrawDecalDebugBoxes(auxGeom);
- }
- }
- }
- void LightCullingExampleComponent::DrawSidebarTimingSection()
- {
- ImGui::Text("Timing");
- DrawSidebarTimingSectionCPU();
- }
- void LightCullingExampleComponent::CalculateSmoothedFPS(float deltaTimeSeconds)
- {
- float currFPS = 1.0f / deltaTimeSeconds;
- m_smoothedFPS = TimingSmoothingFactor * m_smoothedFPS + (1.0f - TimingSmoothingFactor) * currFPS;
- }
- void LightCullingExampleComponent::DrawSidebarTimingSectionCPU()
- {
- ImGui::Text("CPU (ms)");
- ImGui::Indent();
- ImGui::Text("Total: %5.1f", 1000.0f / m_smoothedFPS);
- ImGui::Unindent();
- }
- void LightCullingExampleComponent::InitLightArrays()
- {
- // Set seed to the same value each time so values are consistent between multiple app runs.
- // Intended for use with the screenshot comparison tool
- m_random.SetSeed(0);
- const auto InitLight = [this](auto& light)
- {
- light.m_color = GetRandomColor();
- light.m_position = GetRandomPositionInsideWorldModel();
- light.m_direction = GetRandomDirection();
- };
- m_pointLights.resize(MaxNumLights);
- AZStd::for_each(m_pointLights.begin(), m_pointLights.end(), InitLight);
- m_spotLights.resize(MaxNumLights);
- AZStd::for_each(m_spotLights.begin(), m_spotLights.end(), InitLight);
- m_diskLights.resize(MaxNumLights);
- AZStd::for_each(m_diskLights.begin(), m_diskLights.end(), InitLight);
- m_capsuleLights.resize(MaxNumLights);
- AZStd::for_each(m_capsuleLights.begin(), m_capsuleLights.end(), InitLight);
- m_decals.resize(MaxNumLights);
- AZStd::for_each(m_decals.begin(), m_decals.end(), [&](Decal& decal)
- {
- decal.m_position = GetRandomPositionInsideWorldModel();
- decal.m_quaternion = AZ::Quaternion::CreateFromAxisAngle(GetRandomDirection(), GetRandomNumber(0.0f, AZ::Constants::TwoPi));
- });
- m_quadLights.resize(MaxNumLights);
- AZStd::for_each(m_quadLights.begin(), m_quadLights.end(), InitLight);
- }
- void LightCullingExampleComponent::GetFeatureProcessors()
- {
- AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get();
- m_pointLightFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::PointLightFeatureProcessorInterface>();
- m_spotLightFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::SpotLightFeatureProcessorInterface>();
- m_diskLightFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::DiskLightFeatureProcessorInterface>();
- m_capsuleLightFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::CapsuleLightFeatureProcessorInterface>();
- m_quadLightFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::QuadLightFeatureProcessorInterface>();
- m_decalFeatureProcessor = scene->GetFeatureProcessor<AZ::Render::DecalFeatureProcessorInterface>();
- }
- float LightCullingExampleComponent::AutoCalculateAttenuationRadius(const AZ::Color& color, float intensity)
- {
- // Get combined intensity luma from m_photometricValue, then calculate the radius at which the irradiance will be equal to cutoffIntensity.
- static const float CutoffIntensity = 0.1f; // Make this configurable later.
- float luminance = AZ::Render::PhotometricValue::GetPerceptualLuminance(color * intensity);
- return sqrt(luminance / CutoffIntensity);
- }
- void LightCullingExampleComponent::MoveCameraToStartPosition()
- {
- const AZ::Vector3 target = AZ::Vector3::CreateAxisZ();
- const AZ::Transform transform = AZ::Transform::CreateLookAt(CameraStartPositionLongViewDownStreet, target, AZ::Transform::Axis::YPositive);
- AZ::TransformBus::Event(GetCameraEntityId(), &AZ::TransformBus::Events::SetWorldTM, transform);
- }
- void LightCullingExampleComponent::UpdateHeatmapOpacity()
- {
- if (const ScenePtr scene = RPISystemInterface::Get()->GetDefaultScene())
- {
- if (const RenderPipelinePtr pipeline = scene->GetDefaultRenderPipeline())
- {
- if (const Ptr<Pass> pass = pipeline->GetRootPass()->FindPassByNameRecursive(AZ::Name("LightCullingHeatmapPass")))
- {
- if (const Ptr<RenderPass> trianglePass = azrtti_cast<RenderPass*>(pass))
- {
- trianglePass->SetEnabled(m_heatmapOpacity > 0.0f);
- Data::Instance<ShaderResourceGroup> srg = trianglePass->GetShaderResourceGroup();
- RHI::ShaderInputConstantIndex opacityIndex = srg->FindShaderInputConstantIndex(AZ::Name("m_heatmapOpacity"));
- bool setOk = srg->SetConstant<float>(opacityIndex, m_heatmapOpacity);
- AZ_Warning("LightCullingExampleComponent", setOk, "Unable to set heatmap opacity");
- }
- }
- }
- }
- }
- void LightCullingExampleComponent::DisableHeatmap()
- {
- m_heatmapOpacity = 0.0f;
- UpdateHeatmapOpacity();
- }
- void LightCullingExampleComponent::CreateOpaqueModels()
- {
- Data::Asset<RPI::ModelAsset> modelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(WorldModelName, RPI::AssetUtils::TraceLevel::Assert);
- auto meshFeatureProcessor = GetMeshFeatureProcessor();
- m_meshHandle = meshFeatureProcessor->AcquireMesh(modelAsset);
- meshFeatureProcessor->SetTransform(m_meshHandle, Transform::CreateIdentity());
- Data::Instance<RPI::Model> model = meshFeatureProcessor->GetModel(m_meshHandle);
- // Loading in the world will probably take a while and I want to grab the AABB afterwards, so hook it up to a a ModelChangeEventHandler
- if (model)
- {
- OnModelReady(model);
- }
- else
- {
- meshFeatureProcessor->ConnectModelChangeEventHandler(m_meshHandle, m_meshChangedHandler);
- }
- }
- void LightCullingExampleComponent::CreateTransparentModels()
- {
- const AZStd::vector<AZ::Vector3 > TransparentModelPositions = { AZ::Vector3(-6.f, -20, 1),
- AZ::Vector3(7.5f, 0, 1)
- };
- Data::Asset<RPI::ModelAsset> transparentModelAsset = RPI::AssetUtils::GetAssetByProductPath<RPI::ModelAsset>(TransparentModelName, RPI::AssetUtils::TraceLevel::Assert);
- // Override the shader ball material with a transparent material
- Render::MaterialAssignmentMap materialAssignmentMap = CreateMaterialAssignmentMap(TransparentMaterialName);
- for (const AZ::Vector3& position : TransparentModelPositions)
- {
- AZ::Render::MeshFeatureProcessorInterface::MeshHandle meshHandle = GetMeshFeatureProcessor()->AcquireMesh(transparentModelAsset, materialAssignmentMap);
- GetMeshFeatureProcessor()->SetTransform(meshHandle, Transform::CreateTranslation(position));
- m_transparentMeshHandles.push_back(std::move(meshHandle));
- }
- }
- void LightCullingExampleComponent::DestroyOpaqueModels()
- {
- GetMeshFeatureProcessor()->ReleaseMesh(m_meshHandle);
- }
- void LightCullingExampleComponent::DestroyTransparentModels()
- {
- for (auto& elem : m_transparentMeshHandles)
- {
- GetMeshFeatureProcessor()->ReleaseMesh(elem);
- }
- m_transparentMeshHandles.clear();
- }
- void LightCullingExampleComponent::DestroyLightsAndDecals()
- {
- DestroyLights(m_pointLightFeatureProcessor, m_pointLights);
- DestroyLights(m_spotLightFeatureProcessor, m_spotLights);
- DestroyLights(m_diskLightFeatureProcessor, m_diskLights);
- DestroyLights(m_capsuleLightFeatureProcessor, m_capsuleLights);
- DestroyLights(m_quadLightFeatureProcessor, m_quadLights);
- DestroyDecals();
- }
- void LightCullingExampleComponent::CreateLightsAndDecals()
- {
- CreatePointLights();
- CreateSpotLights();
- CreateDiskLights();
- CreateCapsuleLights();
- CreateQuadLights();
- CreateDecals();
- }
- void LightCullingExampleComponent::LoadDecalMaterial()
- {
- const AZ::Data::AssetId id = AZ::RPI::AssetUtils::GetAssetIdForProductPath(DecalMaterialPath);
- m_decalMaterial = AZ::Data::AssetManager::Instance().GetAsset<AZ::RPI::MaterialAsset>(
- id, AZ::Data::AssetLoadBehavior::PreLoad);
- m_decalMaterial.BlockUntilLoadComplete();
- }
- } // namespace AtomSampleViewer
|