Merge branch 'development' into Atom/santorac/ShaderReloadDebugImprovements
This commit is contained in:
+3
-3
@@ -25,11 +25,11 @@ namespace AWSGameLift
|
||||
AWSGameLiftCreateSessionOnQueueRequest() = default;
|
||||
virtual ~AWSGameLiftCreateSessionOnQueueRequest() = default;
|
||||
|
||||
// Name of the queue to use to place the new game session. You can use either the queue name or ARN value.
|
||||
//! Name of the queue to use to place the new game session. You can use either the queue name or ARN value.
|
||||
AZStd::string m_queueName;
|
||||
|
||||
// A unique identifier to assign to the new game session placement. This value is developer-defined.
|
||||
// The value must be unique across all Regions and cannot be reused unless you are resubmitting a canceled or timed-out placement request.
|
||||
//! A unique identifier to assign to the new game session placement. This value is developer-defined.
|
||||
//! The value must be unique across all Regions and cannot be reused unless you are resubmitting a canceled or timed-out placement request.
|
||||
AZStd::string m_placementId;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
+4
-4
@@ -25,14 +25,14 @@ namespace AWSGameLift
|
||||
AWSGameLiftCreateSessionRequest() = default;
|
||||
virtual ~AWSGameLiftCreateSessionRequest() = default;
|
||||
|
||||
// A unique identifier for the alias associated with the fleet to create a game session in.
|
||||
//! A unique identifier for the alias associated with the fleet to create a game session in.
|
||||
AZStd::string m_aliasId;
|
||||
|
||||
// A unique identifier for the fleet to create a game session in.
|
||||
//! A unique identifier for the fleet to create a game session in.
|
||||
AZStd::string m_fleetId;
|
||||
|
||||
// Custom string that uniquely identifies the new game session request.
|
||||
// This is useful for ensuring that game session requests with the same idempotency token are processed only once.
|
||||
//! Custom string that uniquely identifies the new game session request.
|
||||
//! This is useful for ensuring that game session requests with the same idempotency token are processed only once.
|
||||
AZStd::string m_idempotencyToken;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
+3
-3
@@ -25,13 +25,13 @@ namespace AWSGameLift
|
||||
AWSGameLiftSearchSessionsRequest() = default;
|
||||
virtual ~AWSGameLiftSearchSessionsRequest() = default;
|
||||
|
||||
// A unique identifier for the alias associated with the fleet to search for active game sessions.
|
||||
//! A unique identifier for the alias associated with the fleet to search for active game sessions.
|
||||
AZStd::string m_aliasId;
|
||||
|
||||
// A unique identifier for the fleet to search for active game sessions.
|
||||
//! A unique identifier for the fleet to search for active game sessions.
|
||||
AZStd::string m_fleetId;
|
||||
|
||||
// A fleet location to search for game sessions.
|
||||
//! A fleet location to search for game sessions.
|
||||
AZStd::string m_location;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
+3
-2
@@ -30,9 +30,10 @@ namespace AWSGameLift
|
||||
AWSGameLiftStartMatchmakingRequest() = default;
|
||||
virtual ~AWSGameLiftStartMatchmakingRequest() = default;
|
||||
|
||||
// Name of the matchmaking configuration to use for this request
|
||||
//! Name of the matchmaking configuration to use for this request
|
||||
AZStd::string m_configurationName;
|
||||
// Information on each player to be matched
|
||||
|
||||
//! Information on each player to be matched
|
||||
AZStd::vector<AWSGameLiftPlayer> m_players;
|
||||
};
|
||||
} // namespace AWSGameLift
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/ComponentApplicationBus.h>
|
||||
#include <AzCore/Component/ComponentApplicationLifecycle.h>
|
||||
#include <AzCore/Component/Entity.h>
|
||||
#include <AzCore/NativeUI/NativeUIRequests.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
@@ -132,7 +133,6 @@ namespace AZ
|
||||
m_createDefaultScene = false;
|
||||
}
|
||||
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
TickBus::Handler::BusConnect();
|
||||
|
||||
// Listen for window system requests (e.g. requests for default window handle)
|
||||
@@ -143,6 +143,20 @@ namespace AZ
|
||||
|
||||
Render::Bootstrap::DefaultWindowBus::Handler::BusConnect();
|
||||
Render::Bootstrap::RequestBus::Handler::BusConnect();
|
||||
|
||||
// If the settings registry isn't available, something earlier in startup will report that failure.
|
||||
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
|
||||
{
|
||||
// Automatically register the event if it's not registered, because
|
||||
// this system is initialized before the settings registry has loaded the event list.
|
||||
AZ::ComponentApplicationLifecycle::RegisterHandler(
|
||||
*settingsRegistry, m_componentApplicationLifecycleHandler,
|
||||
[this](AZStd::string_view /*path*/, AZ::SettingsRegistryInterface::Type /*type*/)
|
||||
{
|
||||
Initialize();
|
||||
},
|
||||
"LegacySystemInterfaceCreated");
|
||||
}
|
||||
}
|
||||
|
||||
void BootstrapSystemComponent::Deactivate()
|
||||
@@ -153,7 +167,6 @@ namespace AZ
|
||||
AzFramework::WindowSystemRequestBus::Handler::BusDisconnect();
|
||||
AzFramework::WindowSystemNotificationBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
||||
|
||||
m_brdfTexture = nullptr;
|
||||
RemoveRenderPipeline();
|
||||
@@ -164,14 +177,14 @@ namespace AZ
|
||||
m_windowHandle = nullptr;
|
||||
}
|
||||
|
||||
void BootstrapSystemComponent::OnCatalogLoaded(const char* /*catalogFile*/)
|
||||
void BootstrapSystemComponent::Initialize()
|
||||
{
|
||||
if (m_isAssetCatalogLoaded)
|
||||
if (m_isInitialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_isAssetCatalogLoaded = true;
|
||||
m_isInitialized = true;
|
||||
|
||||
if (!RPI::RPISystemInterface::Get()->IsInitialized())
|
||||
{
|
||||
@@ -216,7 +229,7 @@ namespace AZ
|
||||
{
|
||||
m_windowHandle = windowHandle;
|
||||
|
||||
if (m_isAssetCatalogLoaded)
|
||||
if (m_isInitialized)
|
||||
{
|
||||
CreateWindowContext();
|
||||
if (m_createDefaultScene)
|
||||
@@ -259,6 +272,7 @@ namespace AZ
|
||||
|
||||
// Create and register a scene with all available feature processors
|
||||
RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("Main");
|
||||
AZ::RPI::ScenePtr atomScene = RPI::Scene::CreateScene(sceneDesc);
|
||||
atomScene->EnableAllFeatureProcessors();
|
||||
atomScene->Activate();
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/Component/TickBus.h>
|
||||
#include <AzCore/Settings/SettingsRegistry.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
#include <AzFramework/Scene/Scene.h>
|
||||
#include <AzFramework/Scene/SceneSystemInterface.h>
|
||||
#include <AzFramework/Windowing/NativeWindow.h>
|
||||
@@ -29,7 +29,6 @@
|
||||
#include <Atom/Bootstrap/DefaultWindowBus.h>
|
||||
#include <Atom/Bootstrap/BootstrapRequestBus.h>
|
||||
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
@@ -40,7 +39,6 @@ namespace AZ
|
||||
: public Component
|
||||
, public TickBus::Handler
|
||||
, public AzFramework::WindowNotificationBus::Handler
|
||||
, public AzFramework::AssetCatalogEventBus::Handler
|
||||
, public AzFramework::WindowSystemNotificationBus::Handler
|
||||
, public AzFramework::WindowSystemRequestBus::Handler
|
||||
, public Render::Bootstrap::DefaultWindowBus::Handler
|
||||
@@ -82,13 +80,12 @@ namespace AZ
|
||||
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
|
||||
int GetTickOrder() override;
|
||||
|
||||
// AzFramework::AssetCatalogEventBus::Handler overrides ...
|
||||
void OnCatalogLoaded(const char* catalogFile) override;
|
||||
|
||||
// AzFramework::WindowSystemNotificationBus::Handler overrides ...
|
||||
void OnWindowCreated(AzFramework::NativeWindowHandle windowHandle) override;
|
||||
|
||||
private:
|
||||
void Initialize();
|
||||
|
||||
void CreateDefaultRenderPipeline();
|
||||
void CreateDefaultScene();
|
||||
void DestroyDefaultScene();
|
||||
@@ -105,7 +102,7 @@ namespace AZ
|
||||
RPI::ScenePtr m_defaultScene = nullptr;
|
||||
AZStd::shared_ptr<AzFramework::Scene> m_defaultFrameworkScene = nullptr;
|
||||
|
||||
bool m_isAssetCatalogLoaded = false;
|
||||
bool m_isInitialized = false;
|
||||
|
||||
// The id of the render pipeline created by this component
|
||||
RPI::RenderPipelineId m_renderPipelineId;
|
||||
@@ -119,6 +116,8 @@ namespace AZ
|
||||
|
||||
// Maps AZ scenes to RPI scene weak pointers to allow looking up a ScenePtr instead of a raw Scene*
|
||||
AZStd::unordered_map<AzFramework::Scene*, AZStd::weak_ptr<AZ::RPI::Scene>> m_azSceneToAtomSceneMap;
|
||||
|
||||
AZ::SettingsRegistryInterface::NotifyEventHandler m_componentApplicationLifecycleHandler;
|
||||
};
|
||||
} // namespace Bootstrap
|
||||
} // namespace Render
|
||||
|
||||
@@ -1574,15 +1574,6 @@
|
||||
"shaderOption": "o_baseColor_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
"textureProperty": "metallic.textureMap",
|
||||
"useTextureProperty": "metallic.useTexture",
|
||||
"dependentProperties": ["metallic.textureMapUv"],
|
||||
"shaderOption": "o_metallic_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
@@ -1649,6 +1640,12 @@
|
||||
"file": "StandardPBR_Roughness.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_Metallic.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
|
||||
+17
-29
@@ -2698,16 +2698,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"textureProperty": "layer1_metallic.textureMap",
|
||||
"useTextureProperty": "layer1_metallic.useTexture",
|
||||
"dependentProperties": ["layer1_metallic.textureMapUv"],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
"shaderOption": "o_layer1_o_metallic_useTexture"
|
||||
"file": "StandardPBR_Metallic.lua",
|
||||
"propertyNamePrefix": "layer1_",
|
||||
"srgNamePrefix": "m_layer1_",
|
||||
"optionsNamePrefix": "o_layer1_"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2835,16 +2831,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"textureProperty": "layer2_metallic.textureMap",
|
||||
"useTextureProperty": "layer2_metallic.useTexture",
|
||||
"dependentProperties": ["layer2_metallic.textureMapUv"],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
"shaderOption": "o_layer2_o_metallic_useTexture"
|
||||
"file": "StandardPBR_Metallic.lua",
|
||||
"propertyNamePrefix": "layer2_",
|
||||
"srgNamePrefix": "m_layer2_",
|
||||
"optionsNamePrefix": "o_layer2_"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -2963,8 +2955,8 @@
|
||||
"args": {
|
||||
"textureProperty": "layer3_baseColor.textureMap",
|
||||
"useTextureProperty": "layer3_baseColor.useTexture",
|
||||
"dependentProperties": ["layer3_baseColor.textureMapUv", "layer3_baseColor.textureBlendMode"],
|
||||
"shaderTags": [
|
||||
"dependentProperties": [ "layer3_baseColor.textureMapUv", "layer3_baseColor.textureBlendMode" ],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
@@ -2972,16 +2964,12 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"textureProperty": "layer3_metallic.textureMap",
|
||||
"useTextureProperty": "layer3_metallic.useTexture",
|
||||
"dependentProperties": ["layer3_metallic.textureMapUv"],
|
||||
"shaderTags": [
|
||||
"ForwardPass",
|
||||
"ForwardPass_EDS"
|
||||
],
|
||||
"shaderOption": "o_layer3_o_metallic_useTexture"
|
||||
"file": "StandardPBR_Metallic.lua",
|
||||
"propertyNamePrefix": "layer3_",
|
||||
"srgNamePrefix": "m_layer3_",
|
||||
"optionsNamePrefix": "o_layer3_"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1104,15 +1104,6 @@
|
||||
"shaderOption": "o_baseColor_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
"textureProperty": "metallic.textureMap",
|
||||
"useTextureProperty": "metallic.useTexture",
|
||||
"dependentProperties": ["metallic.textureMapUv"],
|
||||
"shaderOption": "o_metallic_useTexture"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UseTexture",
|
||||
"args": {
|
||||
@@ -1179,6 +1170,12 @@
|
||||
"file": "StandardPBR_Roughness.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
"file": "StandardPBR_Metallic.lua"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Lua",
|
||||
"args": {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
--------------------------------------------------------------------------------------
|
||||
--
|
||||
-- Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
-- For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
--
|
||||
-- SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
--
|
||||
--
|
||||
--
|
||||
----------------------------------------------------------------------------------------------------
|
||||
|
||||
function GetMaterialPropertyDependencies()
|
||||
return {"metallic.textureMap", "metallic.useTexture"}
|
||||
end
|
||||
|
||||
function GetShaderOptionDependencies()
|
||||
return {"o_metallic_useTexture"}
|
||||
end
|
||||
|
||||
function Process(context)
|
||||
local textureMap = context:GetMaterialPropertyValue_Image("metallic.textureMap")
|
||||
local useTexture = context:GetMaterialPropertyValue_bool("metallic.useTexture")
|
||||
context:SetShaderOptionValue_bool("o_metallic_useTexture", useTexture and textureMap ~= nil)
|
||||
end
|
||||
|
||||
function ProcessEditor(context)
|
||||
local textureMap = context:GetMaterialPropertyValue_Image("metallic.textureMap")
|
||||
local useTexture = context:GetMaterialPropertyValue_bool("metallic.useTexture")
|
||||
|
||||
if(nil == textureMap) then
|
||||
context:SetMaterialPropertyVisibility("metallic.useTexture", MaterialPropertyVisibility_Hidden)
|
||||
context:SetMaterialPropertyVisibility("metallic.textureMapUv", MaterialPropertyVisibility_Hidden)
|
||||
context:SetMaterialPropertyVisibility("metallic.factor", MaterialPropertyVisibility_Enabled)
|
||||
elseif(not useTexture) then
|
||||
context:SetMaterialPropertyVisibility("metallic.useTexture", MaterialPropertyVisibility_Enabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.textureMapUv", MaterialPropertyVisibility_Disabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.factor", MaterialPropertyVisibility_Enabled)
|
||||
else
|
||||
context:SetMaterialPropertyVisibility("metallic.useTexture", MaterialPropertyVisibility_Enabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.textureMapUv", MaterialPropertyVisibility_Enabled)
|
||||
context:SetMaterialPropertyVisibility("metallic.factor", MaterialPropertyVisibility_Hidden)
|
||||
end
|
||||
end
|
||||
@@ -78,5 +78,10 @@ namespace AZ
|
||||
//! Find an assignment id corresponding to the lod and label substring filters
|
||||
MaterialAssignmentId FindMaterialAssignmentIdInModel(
|
||||
const Data::Instance<AZ::RPI::Model>& model, const MaterialAssignmentLodIndex lodFilter, const AZStd::string& labelFilter);
|
||||
|
||||
//! Special case handling to convert script values to supported types
|
||||
AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript(
|
||||
const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value);
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/EditContextConstants.inl>
|
||||
#include <AzCore/Script/ScriptAsset.h>
|
||||
|
||||
#include <AssetBuilderSDK/AssetBuilderSDK.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
@@ -101,13 +100,6 @@ namespace AZ
|
||||
materialFunctorRegistration->RegisterMaterialFunctor("ConvertEmissiveUnit", azrtti_typeid<ConvertEmissiveUnitFunctorSourceData>());
|
||||
materialFunctorRegistration->RegisterMaterialFunctor("HandleSubsurfaceScatteringParameters", azrtti_typeid<SubsurfaceTransmissionParameterFunctorSourceData>());
|
||||
materialFunctorRegistration->RegisterMaterialFunctor("Lua", azrtti_typeid<RPI::LuaMaterialFunctorSourceData>());
|
||||
|
||||
// Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService".
|
||||
auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler();
|
||||
if (assetCatalog)
|
||||
{
|
||||
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorCommonSystemComponent::Deactivate()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/PassFilter.h>
|
||||
#include <Atom/RPI.Public/Pass/RenderPass.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/SwapChainPass.h>
|
||||
#include <Atom/RPI.Public/ViewportContextManager.h>
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#include <AzCore/Script/ScriptContextAttributes.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
|
||||
#include <AzFramework/IO/LocalFileIO.h>
|
||||
#include <AzFramework/StringFunc/StringFunc.h>
|
||||
|
||||
@@ -50,6 +52,15 @@ namespace AZ
|
||||
"Sets the compression level for saving png screenshots. Valid values are from 0 to 8"
|
||||
);
|
||||
|
||||
AZ_CVAR(int,
|
||||
r_pngCompressionNumThreads,
|
||||
8, // Number of threads to use for the png r<->b channel data swap
|
||||
nullptr,
|
||||
ConsoleFunctorFlags::Null,
|
||||
"Sets the number of threads for saving png screenshots. Valid values are from 1 to 128, although less than or equal the number of hw threads is recommended"
|
||||
);
|
||||
|
||||
|
||||
FrameCaptureOutputResult PngFrameCaptureOutput(
|
||||
const AZStd::string& outputFilePath, const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
|
||||
{
|
||||
@@ -65,33 +76,67 @@ namespace AZ
|
||||
|
||||
buffer = AZStd::make_shared<AZStd::vector<uint8_t>>(readbackResult.m_dataBuffer->size());
|
||||
AZStd::copy(readbackResult.m_dataBuffer->begin(), readbackResult.m_dataBuffer->end(), buffer->begin());
|
||||
|
||||
AZ::JobCompletion jobCompletion;
|
||||
const int numThreads = 8;
|
||||
const int numThreads = r_pngCompressionNumThreads;
|
||||
const int numPixelsPerThread = static_cast<int>(buffer->size() / numChannels / numThreads);
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
|
||||
AZ::TaskGraphActiveInterface* taskGraphActiveInterface = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
bool taskGraphActive = taskGraphActiveInterface && taskGraphActiveInterface->IsTaskGraphActive();
|
||||
|
||||
if (taskGraphActive)
|
||||
{
|
||||
int startPixel = i * numPixelsPerThread;
|
||||
static const AZ::TaskDescriptor pngTaskDescriptor{"PngWriteOutChannelSwap", "Graphics"};
|
||||
AZ::TaskGraph taskGraph;
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
int startPixel = i * numPixelsPerThread;
|
||||
|
||||
AZ::Job* job = AZ::CreateJobFunction(
|
||||
[&, startPixel, numPixelsPerThread]()
|
||||
{
|
||||
for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset)
|
||||
taskGraph.AddTask(
|
||||
pngTaskDescriptor,
|
||||
[&, startPixel]()
|
||||
{
|
||||
if (startPixel * numChannels + numChannels < buffer->size())
|
||||
for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset)
|
||||
{
|
||||
AZStd::swap(
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels],
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels + 2]
|
||||
);
|
||||
if (startPixel * numChannels + numChannels < buffer->size())
|
||||
{
|
||||
AZStd::swap(
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels],
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels + 2]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, true, nullptr);
|
||||
|
||||
job->SetDependent(&jobCompletion);
|
||||
job->Start();
|
||||
});
|
||||
}
|
||||
AZ::TaskGraphEvent taskGraphFinishedEvent;
|
||||
taskGraph.Submit(&taskGraphFinishedEvent);
|
||||
taskGraphFinishedEvent.Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ::JobCompletion jobCompletion;
|
||||
for (int i = 0; i < numThreads; ++i)
|
||||
{
|
||||
int startPixel = i * numPixelsPerThread;
|
||||
|
||||
AZ::Job* job = AZ::CreateJobFunction(
|
||||
[&, startPixel]()
|
||||
{
|
||||
for (int pixelOffset = 0; pixelOffset < numPixelsPerThread; ++pixelOffset)
|
||||
{
|
||||
if (startPixel * numChannels + numChannels < buffer->size())
|
||||
{
|
||||
AZStd::swap(
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels],
|
||||
buffer->data()[(startPixel + pixelOffset) * numChannels + 2]
|
||||
);
|
||||
}
|
||||
}
|
||||
}, true, nullptr);
|
||||
|
||||
job->SetDependent(&jobCompletion);
|
||||
job->Start();
|
||||
}
|
||||
jobCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
jobCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
|
||||
Utils::PngFile image = Utils::PngFile::Create(readbackResult.m_imageDescriptor.m_size, format, *buffer);
|
||||
|
||||
@@ -32,7 +32,7 @@ namespace AZ
|
||||
{
|
||||
if (m_rtPipeline)
|
||||
{
|
||||
AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->RemoveRenderPipeline(m_rtPipeline->GetId());
|
||||
m_rtPipeline->RemoveFromScene();
|
||||
m_rtPipeline = nullptr;
|
||||
}
|
||||
|
||||
@@ -111,8 +111,12 @@ namespace AZ
|
||||
parentPass->SetSourceTexture(m_texture, RHI::Format::R8G8B8A8_UNORM);
|
||||
break;
|
||||
}
|
||||
|
||||
AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->AddRenderPipeline(m_rtPipeline);
|
||||
|
||||
const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("RPI"));
|
||||
if (mainScene)
|
||||
{
|
||||
mainScene->AddRenderPipeline(m_rtPipeline);
|
||||
}
|
||||
}
|
||||
|
||||
bool LuxCoreTexture::IsIBLTexture()
|
||||
|
||||
@@ -143,28 +143,34 @@ namespace AZ
|
||||
{
|
||||
bool wasRenamed = false;
|
||||
Name newName;
|
||||
RPI::MaterialPropertyIndex materialPropertyIndex = m_materialInstance->FindPropertyIndex(propertyPair.first, &wasRenamed, &newName);
|
||||
RPI::MaterialPropertyIndex materialPropertyIndex =
|
||||
m_materialInstance->FindPropertyIndex(propertyPair.first, &wasRenamed, &newName);
|
||||
|
||||
// FindPropertyIndex will have already reported a message about what the old and new names are. Here we just add some extra info to help the user resolve it.
|
||||
AZ_Warning("MaterialAssignment", !wasRenamed,
|
||||
// FindPropertyIndex will have already reported a message about what the old and new names are. Here we just add
|
||||
// some extra info to help the user resolve it.
|
||||
AZ_Warning(
|
||||
"MaterialAssignment", !wasRenamed,
|
||||
"Consider running \"Apply Automatic Property Updates\" to use the latest property names.",
|
||||
propertyPair.first.GetCStr(),
|
||||
newName.GetCStr());
|
||||
propertyPair.first.GetCStr(), newName.GetCStr());
|
||||
|
||||
if (wasRenamed && m_propertyOverrides.find(newName) != m_propertyOverrides.end())
|
||||
{
|
||||
materialPropertyIndex.Reset();
|
||||
|
||||
AZ_Warning("MaterialAssignment", false,
|
||||
"Material property '%s' has been renamed to '%s', and a property override exists for both. The one with the old name will be ignored.",
|
||||
propertyPair.first.GetCStr(),
|
||||
newName.GetCStr());
|
||||
|
||||
AZ_Warning(
|
||||
"MaterialAssignment", false,
|
||||
"Material property '%s' has been renamed to '%s', and a property override exists for both. The one with "
|
||||
"the old name will be ignored.",
|
||||
propertyPair.first.GetCStr(), newName.GetCStr());
|
||||
}
|
||||
|
||||
if (!materialPropertyIndex.IsNull())
|
||||
{
|
||||
const auto propertyDescriptor =
|
||||
m_materialInstance->GetMaterialPropertiesLayout()->GetPropertyDescriptor(materialPropertyIndex);
|
||||
|
||||
m_materialInstance->SetPropertyValue(
|
||||
materialPropertyIndex, AZ::RPI::MaterialPropertyValue::FromAny(propertyPair.second));
|
||||
materialPropertyIndex, ConvertMaterialPropertyValueFromScript(propertyDescriptor, propertyPair.second));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,5 +290,58 @@ namespace AZ
|
||||
|
||||
return MaterialAssignmentId();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueNumericType(const AZStd::any& value)
|
||||
{
|
||||
if (value.is<int32_t>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<int32_t>(value));
|
||||
}
|
||||
if (value.is<uint32_t>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<uint32_t>(value));
|
||||
}
|
||||
if (value.is<float>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<float>(value));
|
||||
}
|
||||
if (value.is<double>())
|
||||
{
|
||||
return aznumeric_cast<T>(AZStd::any_cast<double>(value));
|
||||
}
|
||||
|
||||
return AZ::RPI::MaterialPropertyValue::FromAny(value);
|
||||
}
|
||||
|
||||
AZ::RPI::MaterialPropertyValue ConvertMaterialPropertyValueFromScript(
|
||||
const AZ::RPI::MaterialPropertyDescriptor* propertyDescriptor, const AZStd::any& value)
|
||||
{
|
||||
switch (propertyDescriptor->GetDataType())
|
||||
{
|
||||
case AZ::RPI::MaterialPropertyDataType::Enum:
|
||||
if (value.is<AZ::Name>())
|
||||
{
|
||||
return propertyDescriptor->GetEnumValue(AZStd::any_cast<AZ::Name>(value));
|
||||
}
|
||||
if (value.is<AZStd::string>())
|
||||
{
|
||||
return propertyDescriptor->GetEnumValue(AZ::Name(AZStd::any_cast<AZStd::string>(value)));
|
||||
}
|
||||
return ConvertMaterialPropertyValueNumericType<uint32_t>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::Int:
|
||||
return ConvertMaterialPropertyValueNumericType<int32_t>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::UInt:
|
||||
return ConvertMaterialPropertyValueNumericType<uint32_t>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::Float:
|
||||
return ConvertMaterialPropertyValueNumericType<float>(value);
|
||||
case AZ::RPI::MaterialPropertyDataType::Bool:
|
||||
return ConvertMaterialPropertyValueNumericType<bool>(value);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return AZ::RPI::MaterialPropertyValue::FromAny(value);
|
||||
}
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
|
||||
@@ -60,6 +60,11 @@ def activate_look_modification_lut(look_modification_component, asset_relative_p
|
||||
LOOK_MODIFICATION_ENABLE_PROPERTY_PATH,
|
||||
True
|
||||
)
|
||||
azlmbr.editor.EditorComponentAPIBus(
|
||||
azlmbr.bus.Broadcast,
|
||||
"EnableComponents",
|
||||
[look_modification_component]
|
||||
)
|
||||
|
||||
def activate_lut_asset(entity_id, asset_relative_path):
|
||||
disable_hdr_color_grading_component(entity_id)
|
||||
|
||||
@@ -107,7 +107,7 @@ ly_add_target(
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_RHI_DX12.Reflect
|
||||
3rdParty::d3dx12
|
||||
${AFTERMATH_BUILD_DEPENDENCY}
|
||||
${AFTERMATH_BUILD_DEPENDENCY}
|
||||
COMPILE_DEFINITIONS
|
||||
PRIVATE
|
||||
${USE_NSIGHT_AFTERMATH_DEFINE}
|
||||
@@ -128,7 +128,6 @@ ly_add_target(
|
||||
BUILD_DEPENDENCIES
|
||||
PRIVATE
|
||||
AZ::AzCore
|
||||
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Atom_RHI.Public
|
||||
Gem::Atom_RHI_DX12.Reflect
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
namespace AZ
|
||||
{
|
||||
class Job;
|
||||
class TaskGraphActiveInterface;
|
||||
class TaskGraph;
|
||||
|
||||
namespace RHI
|
||||
{
|
||||
@@ -256,7 +258,13 @@ namespace AZ
|
||||
//! Must be called between BeginCulling() and EndCulling(), once for each active scene/view pair.
|
||||
//! Will create child jobs under the parentJob to do the processing in parallel.
|
||||
//! Can be called in parallel (i.e. to perform culling on multiple views at the same time).
|
||||
void ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob);
|
||||
void ProcessCullablesJobs(const Scene& scene, View& view, AZ::Job& parentJob);
|
||||
|
||||
//! Performs render culling and lod selection for a View, then adds the visible renderpackets to that View.
|
||||
//! Must be called between BeginCulling() and EndCulling(), once for each active scene/view pair.
|
||||
//! Will create child task graphs that signal the TaskGraphEvent to do the processing in parallel.
|
||||
//! Can be called in parallel (i.e. to perform culling on multiple views at the same time).
|
||||
void ProcessCullablesTG(const Scene& scene, View& view, AZ::TaskGraph& taskGraph);
|
||||
|
||||
//! Adds a Cullable to the underlying visibility system(s).
|
||||
//! Must be called at least once on initialization and whenever a Cullable's position or bounds is changed.
|
||||
@@ -276,17 +284,20 @@ namespace AZ
|
||||
return m_debugCtx;
|
||||
}
|
||||
|
||||
static const size_t WorkListCapacity = 5;
|
||||
using WorkListType = AZStd::fixed_vector<AzFramework::IVisibilityScene::NodeData, WorkListCapacity>;
|
||||
|
||||
protected:
|
||||
size_t CountObjectsInScene();
|
||||
|
||||
private:
|
||||
void BeginCullingTaskGraph(const AZStd::vector<ViewPtr>& views);
|
||||
void BeginCullingJobs(const AZStd::vector<ViewPtr>& views);
|
||||
void ProcessCullablesCommon(const Scene& scene, View& view, AZ::Frustum& frustum, void*& maskedOcclusionCulling);
|
||||
|
||||
const Scene* m_parentScene = nullptr;
|
||||
AzFramework::IVisibilityScene* m_visScene = nullptr;
|
||||
CullingDebugContext m_debugCtx;
|
||||
AZStd::concurrency_checker m_cullDataConcurrencyCheck;
|
||||
OcclusionPlaneVector m_occlusionPlanes;
|
||||
AZ::TaskGraphActiveInterface* m_taskGraphActive = nullptr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <Atom/RPI.Public/GpuQuery/GpuQuerySystemInterface.h>
|
||||
#include <Atom/RPI.Reflect/Image/Image.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImage.h>
|
||||
#include <Atom/RPI.Public/Pass/PassAttachment.h>
|
||||
#include <Atom/RPI.Public/Pass/PassDefines.h>
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
@@ -59,6 +60,7 @@ namespace AZ
|
||||
struct PassRequest;
|
||||
struct PassValidationResults;
|
||||
class AttachmentReadback;
|
||||
class ImageAttachmentCopy;
|
||||
|
||||
using SortedPipelineViewTags = AZStd::set<PipelineViewTag, AZNameSortAscending>;
|
||||
using PassesByDrawList = AZStd::map<RHI::DrawListTag, const Pass*>;
|
||||
@@ -94,6 +96,8 @@ namespace AZ
|
||||
{
|
||||
AZ_RPI_PASS(Pass);
|
||||
|
||||
friend class ImageAttachmentPreviewPass;
|
||||
|
||||
public:
|
||||
using ChildPassIndex = RHI::Handle<uint32_t, class ChildPass>;
|
||||
|
||||
@@ -369,6 +373,9 @@ namespace AZ
|
||||
|
||||
void UpdateReadbackAttachment(FramePrepareParams params, bool beforeAddScopes);
|
||||
|
||||
// Setup ImageAttachmentCopy
|
||||
void UpdateAttachmentCopy(FramePrepareParams params);
|
||||
|
||||
// --- Protected Members ---
|
||||
|
||||
const Name PassNameThis{"This"};
|
||||
@@ -466,6 +473,9 @@ namespace AZ
|
||||
AZStd::shared_ptr<AttachmentReadback> m_attachmentReadback;
|
||||
PassAttachmentReadbackOption m_readbackOption;
|
||||
|
||||
// For image attachment preview
|
||||
AZStd::weak_ptr<ImageAttachmentCopy> m_attachmentCopy;
|
||||
|
||||
private:
|
||||
// Return the Timestamp result of this pass
|
||||
virtual TimestampResult GetTimestampResultInternal() const;
|
||||
|
||||
@@ -77,6 +77,16 @@ namespace AZ
|
||||
const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const;
|
||||
const AZStd::vector<Pass*>& GetPassesForTemplate(const Name& templateName) const;
|
||||
|
||||
//! Removes a PassTemplate by name, only if the following two conditions are met:
|
||||
//! 1- The template was NOT created from an Asset. This means the template will be erasable
|
||||
//! only if it was created at runtime with C++.
|
||||
//! 2- The are no instantiated Passes referencing such template.
|
||||
//! If the template exists but both conditions are not met then the function will assert.
|
||||
//! If a template with the given name doesn't exist the function does nothing.
|
||||
//! This function should be used judiciously, and under rare circumstances. For example,
|
||||
//! Applications that iteratively create and need to delete templates at runtime.
|
||||
void RemovePassTemplate(const Name& name);
|
||||
|
||||
//! Removes a pass from both it's associated template (if it has one) and from the pass name mapping
|
||||
void RemovePassFromLibrary(Pass* pass);
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ namespace AZ
|
||||
bool HasPassesForTemplateName(const Name& templateName) const override;
|
||||
bool AddPassTemplate(const Name& name, const AZStd::shared_ptr<PassTemplate>& passTemplate) override;
|
||||
const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const override;
|
||||
void RemovePassTemplate(const Name& name) override;
|
||||
void RemovePassFromLibrary(Pass* pass) override;
|
||||
void RegisterPass(Pass* pass) override;
|
||||
void UnregisterPass(Pass* pass) override;
|
||||
|
||||
@@ -199,6 +199,9 @@ namespace AZ
|
||||
//! Retrieves a PassTemplate from the library
|
||||
virtual const AZStd::shared_ptr<PassTemplate> GetPassTemplate(const Name& name) const = 0;
|
||||
|
||||
//! See remarks in PassLibrary.h for the function with this name.
|
||||
virtual void RemovePassTemplate(const Name& name) = 0;
|
||||
|
||||
//! Removes all references to the given pass from the pass library
|
||||
virtual void RemovePassFromLibrary(Pass* pass) = 0;
|
||||
|
||||
|
||||
@@ -13,9 +13,7 @@
|
||||
#include <Atom/RHI/DrawList.h>
|
||||
#include <Atom/RHI/ScopeProducer.h>
|
||||
|
||||
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
|
||||
#include <Atom/RPI.Public/Pass/Pass.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/Shader/ShaderResourceGroup.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -29,7 +27,6 @@ namespace AZ
|
||||
|
||||
namespace RPI
|
||||
{
|
||||
class ImageAttachmentCopy;
|
||||
class RenderPass;
|
||||
class Query;
|
||||
|
||||
@@ -41,8 +38,6 @@ namespace AZ
|
||||
{
|
||||
AZ_RPI_PASS(RenderPass);
|
||||
|
||||
friend class ImageAttachmentPreviewPass;
|
||||
|
||||
using ScopeQuery = AZStd::array<RHI::Ptr<Query>, static_cast<size_t>(ScopeQueryType::Count)>;
|
||||
|
||||
public:
|
||||
@@ -143,8 +138,6 @@ namespace AZ
|
||||
// Readback the results from the ScopeQueries
|
||||
void ReadbackScopeQueryResults();
|
||||
|
||||
AZStd::weak_ptr<ImageAttachmentCopy> m_attachmentCopy;
|
||||
|
||||
// Readback results from the Timestamp queries
|
||||
TimestampResult m_timestampResult;
|
||||
// Readback results from the PipelineStatistics queries
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ namespace AZ
|
||||
~ImageAttachmentPreviewPass();
|
||||
|
||||
//! Preview the PassAttachment of a pass' PassAttachmentBinding
|
||||
void PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment);
|
||||
void PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment);
|
||||
|
||||
//! Set the output color attachment for this pass
|
||||
void SetOutputColorAttachment(RHI::Ptr<PassAttachment> outputImageAttachment);
|
||||
|
||||
@@ -70,7 +70,8 @@ namespace AZ
|
||||
void InitializeSystemAssets() override;
|
||||
void RegisterScene(ScenePtr scene) override;
|
||||
void UnregisterScene(ScenePtr scene) override;
|
||||
ScenePtr GetScene(const SceneId& sceneId) const override;
|
||||
Scene* GetScene(const SceneId& sceneId) const override;
|
||||
Scene* GetSceneByName(const AZ::Name& name) const override;
|
||||
ScenePtr GetDefaultScene() const override;
|
||||
RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) override;
|
||||
Data::Asset<ShaderAsset> GetCommonShaderAssetForSrgs() const override;
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
#include <Atom/RPI.Public/Base.h>
|
||||
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzFramework/Windowing/WindowBus.h>
|
||||
|
||||
namespace AZ
|
||||
@@ -46,11 +47,14 @@ namespace AZ
|
||||
//! Unregister a scene from RPISystem. The scene won't be simulated or rendered.
|
||||
virtual void UnregisterScene(ScenePtr scene) = 0;
|
||||
|
||||
// [GFX TODO] to be removed when we have scene setup in AZ Core
|
||||
virtual ScenePtr GetDefaultScene() const = 0;
|
||||
|
||||
//! Deprecated. Use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead
|
||||
AZ_DEPRECATED(virtual ScenePtr GetDefaultScene() const = 0;, "This method has been deprecated. Please use GetSceneByName(name), GetSceneForEntityContextId(entityContextId) or Scene::GetSceneForEntityId(AZ::EntityId entityId) instead.");
|
||||
|
||||
//! Get scene by using scene id.
|
||||
virtual ScenePtr GetScene(const SceneId& sceneId) const = 0;
|
||||
virtual Scene* GetScene(const SceneId& sceneId) const = 0;
|
||||
|
||||
//! Get scene by using scene name.
|
||||
virtual Scene* GetSceneByName(const AZ::Name& name) const = 0;
|
||||
|
||||
//! Get the render pipeline created for a window
|
||||
virtual RenderPipelinePtr GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle) = 0;
|
||||
|
||||
@@ -80,6 +80,9 @@ namespace AZ
|
||||
//! Gets the RPI::Scene for a given entityContextId.
|
||||
//! May return nullptr if there is no RPI::Scene created for that entityContext.
|
||||
static Scene* GetSceneForEntityContextId(AzFramework::EntityContextId entityContextId);
|
||||
|
||||
//! Gets the RPI::Scene for a given entityId.
|
||||
static Scene* GetSceneForEntityId(AZ::EntityId entityId);
|
||||
|
||||
~Scene();
|
||||
|
||||
@@ -135,6 +138,8 @@ namespace AZ
|
||||
|
||||
const SceneId& GetId() const;
|
||||
|
||||
AZ::Name GetName() const;
|
||||
|
||||
//! Set default pipeline by render pipeline ID.
|
||||
//! It returns true if the default render pipeline was set from the input ID.
|
||||
//! If the specified render pipeline doesn't exist in this scene then it won't do anything and returns false.
|
||||
@@ -195,8 +200,8 @@ namespace AZ
|
||||
// This function is called every time scene's render pipelines change.
|
||||
void RebuildPipelineStatesLookup();
|
||||
|
||||
// Helper function to wait for end of TaskGraph
|
||||
void WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn = nullptr);
|
||||
// Helper function to wait for end of TaskGraph and then delete the TaskGraphEvent
|
||||
void WaitAndCleanTGEvent(AZStd::unique_ptr<AZ::TaskGraphEvent>&& completionTGEvent);
|
||||
|
||||
// Helper function for wait and clean up a completion job
|
||||
void WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob);
|
||||
@@ -225,8 +230,7 @@ namespace AZ
|
||||
AZStd::vector<RenderPipelinePtr> m_pipelines;
|
||||
|
||||
// CPU simulation TaskGraphEvent to wait for completion of all the simulation tasks
|
||||
AZ::TaskGraphEvent m_simulationFinishedTGEvent;
|
||||
AZStd::atomic_bool m_simulationFinishedWorkActive = false;
|
||||
AZStd::unique_ptr<AZ::TaskGraphEvent> m_simulationFinishedTGEvent;
|
||||
|
||||
// CPU simulation job completion for track all feature processors' simulation jobs
|
||||
AZ::JobCompletion* m_simulationCompletion = nullptr;
|
||||
@@ -245,6 +249,9 @@ namespace AZ
|
||||
// The uuid to identify this scene.
|
||||
SceneId m_id;
|
||||
|
||||
// Scene's name which is set at initialization. Can be empty
|
||||
AZ::Name m_name;
|
||||
|
||||
bool m_activated = false;
|
||||
bool m_taskGraphActive = false; // update during tick, to ensure it only changes on frame boundaries
|
||||
|
||||
@@ -286,13 +293,10 @@ namespace AZ
|
||||
template<typename FeatureProcessorType>
|
||||
FeatureProcessorType* Scene::GetFeatureProcessorForEntity(AZ::EntityId entityId)
|
||||
{
|
||||
// Find the entity context for the entity ID.
|
||||
AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
|
||||
|
||||
if (!entityContextId.IsNull())
|
||||
RPI::Scene* renderScene = GetSceneForEntityId(entityId);
|
||||
if (renderScene)
|
||||
{
|
||||
return GetFeatureProcessorForEntityContextId<FeatureProcessorType>(entityContextId);
|
||||
return renderScene->GetFeatureProcessor<FeatureProcessorType>();
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Name/Name.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
@@ -25,6 +26,9 @@ namespace AZ
|
||||
|
||||
//! List of feature processors which the scene will initially enable.
|
||||
AZStd::vector<AZStd::string> m_featureProcessorNames;
|
||||
|
||||
//! A name used as scene id. It can be used to search a registered scene via RPISystemInterface::GetScene()
|
||||
AZ::Name m_nameId;
|
||||
};
|
||||
} // namespace RPI
|
||||
} // namespace AZ
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#include <AzCore/Debug/Timer.h>
|
||||
#include <AzCore/Jobs/JobFunction.h>
|
||||
#include <AzCore/Jobs/Job.h>
|
||||
#include <AzCore/Task/TaskGraph.h>
|
||||
#include <AzCore/std/smart_ptr/unique_ptr.h>
|
||||
#include <Atom_RPI_Traits_Platform.h>
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
@@ -265,89 +267,71 @@ namespace AZ
|
||||
return m_visScene->GetEntryCount();
|
||||
}
|
||||
|
||||
class AddObjectsToViewJob final
|
||||
: public Job
|
||||
|
||||
struct WorklistData
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(AddObjectsToViewJob, ThreadPoolAllocator, 0);
|
||||
|
||||
struct JobData
|
||||
{
|
||||
CullingDebugContext* m_debugCtx = nullptr;
|
||||
const Scene* m_scene = nullptr;
|
||||
View* m_view = nullptr;
|
||||
Frustum m_frustum;
|
||||
CullingDebugContext* m_debugCtx = nullptr;
|
||||
const Scene* m_scene = nullptr;
|
||||
View* m_view = nullptr;
|
||||
Frustum m_frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
|
||||
MaskedOcclusionCulling* m_maskedOcclusionCulling = nullptr;
|
||||
#endif
|
||||
};
|
||||
};
|
||||
|
||||
private:
|
||||
const AZStd::shared_ptr<JobData> m_jobData;
|
||||
CullingScene::WorkListType m_worklist;
|
||||
static AZStd::shared_ptr<WorklistData> MakeWorklistData(
|
||||
CullingDebugContext& debugCtx,
|
||||
const Scene& scene,
|
||||
View& view,
|
||||
Frustum& frustum,
|
||||
void* maskedOcclusionCulling)
|
||||
{
|
||||
AZStd::shared_ptr<WorklistData> worklistData = AZStd::make_shared<WorklistData>();
|
||||
worklistData->m_debugCtx = &debugCtx;
|
||||
worklistData->m_scene = &scene;
|
||||
worklistData->m_view = &view;
|
||||
worklistData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
worklistData->m_maskedOcclusionCulling = static_cast<MaskedOcclusionCulling*>(maskedOcclusionCulling);
|
||||
#endif
|
||||
return worklistData;
|
||||
}
|
||||
|
||||
constexpr size_t WorkListCapacity = 5;
|
||||
using WorkListType = AZStd::fixed_vector<AzFramework::IVisibilityScene::NodeData, WorkListCapacity>;
|
||||
|
||||
public:
|
||||
AddObjectsToViewJob(const AZStd::shared_ptr<AddObjectsToViewJob::JobData>& jobData, CullingScene::WorkListType& worklist)
|
||||
: Job(true, nullptr) //auto-deletes, no JobContext
|
||||
, m_jobData(jobData)
|
||||
, m_worklist(worklist)
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
static MaskedOcclusionCulling::CullingResult TestOcclusionCulling(
|
||||
const AZStd::shared_ptr<WorklistData>& worklistData,
|
||||
AzFramework::VisibilityEntry* visibleEntry);
|
||||
#endif
|
||||
|
||||
static void ProcessWorklist(const AZStd::shared_ptr<WorklistData>& worklistData, const WorkListType& worklist)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process");
|
||||
|
||||
const View::UsageFlags viewFlags = worklistData->m_view->GetUsageFlags();
|
||||
const RHI::DrawListMask drawListMask = worklistData->m_view->GetDrawListMask();
|
||||
uint32_t numDrawPackets = 0;
|
||||
uint32_t numVisibleCullables = 0;
|
||||
|
||||
AZ_Assert(worklist.size() > 0, "Received empty worklist in ProcessWorklist");
|
||||
|
||||
for (const AzFramework::IVisibilityScene::NodeData& nodeData : worklist)
|
||||
{
|
||||
}
|
||||
|
||||
//work function
|
||||
void Process() override
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "AddObjectsToViewJob: Process");
|
||||
|
||||
const View::UsageFlags viewFlags = m_jobData->m_view->GetUsageFlags();
|
||||
const RHI::DrawListMask drawListMask = m_jobData->m_view->GetDrawListMask();
|
||||
uint32_t numDrawPackets = 0;
|
||||
uint32_t numVisibleCullables = 0;
|
||||
|
||||
for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist)
|
||||
{
|
||||
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
|
||||
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_jobData->m_frustum, nodeData.m_bounds);
|
||||
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
|
||||
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(worklistData->m_frustum, nodeData.m_bounds);
|
||||
|
||||
#ifdef AZ_CULL_PROFILE_VERBOSE
|
||||
AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %d",
|
||||
m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0);
|
||||
AZ_PROFILE_SCOPE(RPI, "process node (view: %s, skip fine cull: %d",
|
||||
m_view->GetName().GetCStr(), nodeIsContainedInFrustum ? 1 : 0);
|
||||
#endif
|
||||
|
||||
if (nodeIsContainedInFrustum || !m_jobData->m_debugCtx->m_enableFrustumCulling)
|
||||
if (nodeIsContainedInFrustum || !worklistData->m_debugCtx->m_enableFrustumCulling)
|
||||
{
|
||||
//Add all objects within this node to the view, without any extra culling
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
//Add all objects within this node to the view, without any extra culling
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_jobData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_isHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Do fine-grained culling before adding objects to the view
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
@@ -355,160 +339,188 @@ namespace AZ
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != m_jobData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_cullData.m_scene != worklistData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_isHidden)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IntersectResult res = ShapeIntersection::Classify(m_jobData->m_frustum, c->m_cullData.m_boundingSphere);
|
||||
if (res == IntersectResult::Exterior)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(m_jobData->m_frustum, c->m_cullData.m_boundingObb))
|
||||
{
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *m_jobData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
{
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_debugDraw && (m_jobData->m_view->GetName() == m_jobData->m_debugCtx->m_currentViewSelectionName))
|
||||
}
|
||||
else
|
||||
{
|
||||
//Do fine-grained culling before adding objects to the view
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "debug draw culling");
|
||||
|
||||
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(m_jobData->m_scene);
|
||||
if (auxGeomPtr)
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
//Draw the node bounds
|
||||
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
|
||||
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
|
||||
// we have a few debug booleans to toggle which ones to draw.
|
||||
if (nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawFullyVisibleNodes)
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
|
||||
if ((c->m_cullData.m_drawListMask & drawListMask).none() ||
|
||||
c->m_cullData.m_hideFlags & viewFlags ||
|
||||
c->m_cullData.m_scene != worklistData->m_scene || //[GFX_TODO][ATOM-13796] once the IVisibilitySystem supports multiple octree scenes, remove this
|
||||
c->m_isHidden)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
else if (!nodeIsContainedInFrustum && m_jobData->m_debugCtx->m_drawPartiallyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
continue;
|
||||
}
|
||||
|
||||
//Draw bounds on individual objects
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingBoxes || m_jobData->m_debugCtx->m_drawBoundingSpheres || m_jobData->m_debugCtx->m_drawLodRadii)
|
||||
IntersectResult res = ShapeIntersection::Classify(worklistData->m_frustum, c->m_cullData.m_boundingSphere);
|
||||
if (res == IntersectResult::Exterior)
|
||||
{
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
continue;
|
||||
}
|
||||
else if (res == IntersectResult::Interior || ShapeIntersection::Overlaps(worklistData->m_frustum, c->m_cullData.m_boundingObb))
|
||||
{
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
if (TestOcclusionCulling(worklistData, visibleEntry) == MaskedOcclusionCulling::CullingResult::VISIBLE)
|
||||
#endif
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingBoxes)
|
||||
{
|
||||
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
|
||||
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_drawBoundingSpheres)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
|
||||
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
|
||||
c->m_lodData.m_lodSelectionRadius,
|
||||
Color(1.0f, 0.5f, 0.0f, 0.3f), RPI::AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
}
|
||||
numDrawPackets += AddLodDataToView(c->m_cullData.m_boundingSphere.GetCenter(), c->m_lodData, *worklistData->m_view);
|
||||
++numVisibleCullables;
|
||||
c->m_isVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_jobData->m_debugCtx->m_enableStats)
|
||||
if (worklistData->m_debugCtx->m_debugDraw && (worklistData->m_view->GetName() == worklistData->m_debugCtx->m_currentViewSelectionName))
|
||||
{
|
||||
CullingDebugContext::CullStats& cullStats = m_jobData->m_debugCtx->GetCullStatsForView(m_jobData->m_view);
|
||||
AZ_PROFILE_SCOPE(RPI, "debug draw culling");
|
||||
|
||||
//no need for mutex here since these are all atomics
|
||||
cullStats.m_numVisibleDrawPackets += numDrawPackets;
|
||||
cullStats.m_numVisibleCullables += numVisibleCullables;
|
||||
++cullStats.m_numJobs;
|
||||
AuxGeomDrawPtr auxGeomPtr = AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(worklistData->m_scene);
|
||||
if (auxGeomPtr)
|
||||
{
|
||||
//Draw the node bounds
|
||||
// "Fully visible" nodes are nodes that are fully inside the frustum. "Partially visible" nodes intersect the edges of the frustum.
|
||||
// Since the nodes of an octree have lots of overlapping boxes with coplanar edges, it's easier to view these separately, so
|
||||
// we have a few debug booleans to toggle which ones to draw.
|
||||
if (nodeIsContainedInFrustum && worklistData->m_debugCtx->m_drawFullyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Lime, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
else if (!nodeIsContainedInFrustum && worklistData->m_debugCtx->m_drawPartiallyVisibleNodes)
|
||||
{
|
||||
auxGeomPtr->DrawAabb(nodeData.m_bounds, Colors::Yellow, RPI::AuxGeomDraw::DrawStyle::Line, RPI::AuxGeomDraw::DepthTest::Off);
|
||||
}
|
||||
|
||||
//Draw bounds on individual objects
|
||||
if (worklistData->m_debugCtx->m_drawBoundingBoxes || worklistData->m_debugCtx->m_drawBoundingSpheres || worklistData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
|
||||
{
|
||||
if (visibleEntry->m_typeFlags & AzFramework::VisibilityEntry::TYPE_RPI_Cullable)
|
||||
{
|
||||
Cullable* c = static_cast<Cullable*>(visibleEntry->m_userData);
|
||||
if (worklistData->m_debugCtx->m_drawBoundingBoxes)
|
||||
{
|
||||
auxGeomPtr->DrawObb(c->m_cullData.m_boundingObb, Matrix3x4::Identity(),
|
||||
nodeIsContainedInFrustum ? Colors::Lime : Colors::Yellow, AuxGeomDraw::DrawStyle::Line);
|
||||
}
|
||||
|
||||
if (worklistData->m_debugCtx->m_drawBoundingSpheres)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(), c->m_cullData.m_boundingSphere.GetRadius(),
|
||||
Color(0.5f, 0.5f, 0.5f, 0.3f), AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
|
||||
if (worklistData->m_debugCtx->m_drawLodRadii)
|
||||
{
|
||||
auxGeomPtr->DrawSphere(c->m_cullData.m_boundingSphere.GetCenter(),
|
||||
c->m_lodData.m_lodSelectionRadius,
|
||||
Color(1.0f, 0.5f, 0.0f, 0.3f), RPI::AuxGeomDraw::DrawStyle::Shaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
MaskedOcclusionCulling::CullingResult TestOcclusionCulling(AzFramework::VisibilityEntry* visibleEntry)
|
||||
if (worklistData->m_debugCtx->m_enableStats)
|
||||
{
|
||||
if (!m_jobData->m_maskedOcclusionCulling)
|
||||
{
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
CullingDebugContext::CullStats& cullStats = worklistData->m_debugCtx->GetCullStatsForView(worklistData->m_view);
|
||||
|
||||
if (visibleEntry->m_boundingVolume.Contains(m_jobData->m_view->GetCameraTransform().GetTranslation()))
|
||||
{
|
||||
// camera is inside bounding volume
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
//no need for mutex here since these are all atomics
|
||||
cullStats.m_numVisibleDrawPackets += numDrawPackets;
|
||||
cullStats.m_numVisibleCullables += numVisibleCullables;
|
||||
++cullStats.m_numJobs;
|
||||
}
|
||||
}
|
||||
|
||||
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
|
||||
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
static MaskedOcclusionCulling::CullingResult TestOcclusionCulling(
|
||||
const AZStd::shared_ptr<WorklistData>& worklistData,
|
||||
AzFramework::VisibilityEntry* visibleEntry)
|
||||
{
|
||||
if (!worklistData->m_maskedOcclusionCulling)
|
||||
{
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
// compute bounding volume corners
|
||||
Vector4 corners[8];
|
||||
corners[0] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[1] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[2] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[3] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[4] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[5] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[6] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[7] = m_jobData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
if (visibleEntry->m_boundingVolume.Contains(worklistData->m_view->GetCameraTransform().GetTranslation()))
|
||||
{
|
||||
// camera is inside bounding volume
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
// find min clip-space depth and NDC min/max
|
||||
float minDepth = FLT_MAX;
|
||||
float ndcMinX = FLT_MAX;
|
||||
float ndcMinY = FLT_MAX;
|
||||
float ndcMaxX = -FLT_MAX;
|
||||
float ndcMaxY = -FLT_MAX;
|
||||
for (uint32_t index = 0; index < 8; ++index)
|
||||
{
|
||||
minDepth = AZStd::min(minDepth, corners[index].GetW());
|
||||
const Vector3& minBound = visibleEntry->m_boundingVolume.GetMin();
|
||||
const Vector3& maxBound = visibleEntry->m_boundingVolume.GetMax();
|
||||
|
||||
// convert to NDC
|
||||
corners[index] /= corners[index].GetW();
|
||||
|
||||
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
|
||||
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
|
||||
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
|
||||
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
|
||||
}
|
||||
// compute bounding volume corners
|
||||
Vector4 corners[8];
|
||||
corners[0] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[1] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[2] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[3] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), minBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[4] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
corners[5] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(minBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[6] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), maxBound.GetZ(), 1.0f);
|
||||
corners[7] = worklistData->m_view->GetWorldToClipMatrix() * Vector4(maxBound.GetX(), maxBound.GetY(), minBound.GetZ(), 1.0f);
|
||||
|
||||
// find min clip-space depth and NDC min/max
|
||||
float minDepth = FLT_MAX;
|
||||
float ndcMinX = FLT_MAX;
|
||||
float ndcMinY = FLT_MAX;
|
||||
float ndcMaxX = -FLT_MAX;
|
||||
float ndcMaxY = -FLT_MAX;
|
||||
for (uint32_t index = 0; index < 8; ++index)
|
||||
{
|
||||
minDepth = AZStd::min(minDepth, corners[index].GetW());
|
||||
if (minDepth < 0.00000001f)
|
||||
{
|
||||
return MaskedOcclusionCulling::VISIBLE;
|
||||
return MaskedOcclusionCulling::CullingResult::VISIBLE;
|
||||
}
|
||||
|
||||
// test against the occlusion buffer, which contains only the manually placed occlusion planes
|
||||
return m_jobData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
|
||||
|
||||
// convert to NDC
|
||||
corners[index] /= corners[index].GetW();
|
||||
|
||||
ndcMinX = AZStd::min(ndcMinX, corners[index].GetX());
|
||||
ndcMinY = AZStd::min(ndcMinY, corners[index].GetY());
|
||||
ndcMaxX = AZStd::max(ndcMaxX, corners[index].GetX());
|
||||
ndcMaxY = AZStd::max(ndcMaxY, corners[index].GetY());
|
||||
}
|
||||
|
||||
// test against the occlusion buffer, which contains only the manually placed occlusion planes
|
||||
return worklistData->m_maskedOcclusionCulling->TestRect(ndcMinX, ndcMinY, ndcMaxX, ndcMaxY, minDepth);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob)
|
||||
void CullingScene::ProcessCullablesCommon(const Scene& scene, View& view, AZ::Frustum& frustum, void*& maskedOcclusionCulling)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullables() - %s", view.GetName().GetCStr());
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesCommon() - %s", view.GetName().GetCStr());
|
||||
|
||||
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
|
||||
Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
|
||||
if (m_debugCtx.m_freezeFrustums)
|
||||
{
|
||||
AZStd::lock_guard<AZStd::mutex> lock(m_debugCtx.m_frozenFrustumsMutex);
|
||||
@@ -536,7 +548,7 @@ namespace AZ
|
||||
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
// setup occlusion culling, if necessary
|
||||
MaskedOcclusionCulling* maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
|
||||
maskedOcclusionCulling = m_occlusionPlanes.empty() ? nullptr : view.GetMaskedOcclusionCulling();
|
||||
if (maskedOcclusionCulling)
|
||||
{
|
||||
// frustum cull occlusion planes
|
||||
@@ -578,23 +590,27 @@ namespace AZ
|
||||
static uint32_t indices[6] = { 0, 1, 2, 2, 3, 0 };
|
||||
|
||||
// render into the occlusion buffer, specifying BACKFACE_NONE so it functions as a double-sided occluder
|
||||
maskedOcclusionCulling->RenderTriangles((float*)verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
|
||||
static_cast<MaskedOcclusionCulling*>(maskedOcclusionCulling)->RenderTriangles(verts, indices, 2, nullptr, MaskedOcclusionCulling::BACKFACE_NONE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void CullingScene::ProcessCullablesJobs(const Scene& scene, View& view, AZ::Job& parentJob)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesJobs() - %s", view.GetName().GetCStr());
|
||||
|
||||
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
|
||||
AZ::Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
|
||||
|
||||
void* maskedOcclusionCulling = nullptr;
|
||||
ProcessCullablesCommon(scene, view, frustum, maskedOcclusionCulling);
|
||||
|
||||
WorkListType worklist;
|
||||
|
||||
AZStd::shared_ptr<AddObjectsToViewJob::JobData> jobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
|
||||
jobData->m_debugCtx = &m_debugCtx;
|
||||
jobData->m_scene = &scene;
|
||||
jobData->m_view = &view;
|
||||
jobData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
jobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
|
||||
#endif
|
||||
AZStd::shared_ptr<WorklistData> worklistData = MakeWorklistData(m_debugCtx, scene, view, frustum, maskedOcclusionCulling);
|
||||
|
||||
auto nodeVisitorLambda = [jobData, &parentJob, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
auto nodeVisitorLambda = [worklistData, &parentJob, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "nodeVisitorLambda()");
|
||||
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
|
||||
@@ -606,8 +622,13 @@ namespace AZ
|
||||
|
||||
if (worklist.size() == worklist.capacity())
|
||||
{
|
||||
// capture worklistData & worklist by value
|
||||
auto processWorklist = [worklistData, worklist]()
|
||||
{
|
||||
ProcessWorklist(worklistData, worklist);
|
||||
};
|
||||
//Kick off a job to process the (full) worklist
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(jobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
AZ::Job* job = AZ::CreateJobFunction(processWorklist, true);
|
||||
worklist.clear();
|
||||
parentJob.SetContinuation(job);
|
||||
job->Start();
|
||||
@@ -616,7 +637,7 @@ namespace AZ
|
||||
|
||||
if (m_debugCtx.m_enableFrustumCulling)
|
||||
{
|
||||
m_visScene->Enumerate(frustum, nodeVisitorLambda);
|
||||
m_visScene->Enumerate(frustum, nodeVisitorLambda);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -625,21 +646,76 @@ namespace AZ
|
||||
|
||||
if (worklist.size() > 0)
|
||||
{
|
||||
AZStd::shared_ptr<AddObjectsToViewJob::JobData> remainingJobData = AZStd::make_shared<AddObjectsToViewJob::JobData>();
|
||||
remainingJobData->m_debugCtx = &m_debugCtx;
|
||||
remainingJobData->m_scene = &scene;
|
||||
remainingJobData->m_view = &view;
|
||||
remainingJobData->m_frustum = frustum;
|
||||
#if AZ_TRAIT_MASKED_OCCLUSION_CULLING_SUPPORTED
|
||||
remainingJobData->m_maskedOcclusionCulling = maskedOcclusionCulling;
|
||||
#endif
|
||||
//Kick off a job to process any remaining workitems
|
||||
AddObjectsToViewJob* job = aznew AddObjectsToViewJob(remainingJobData, worklist); //pool allocated (cheap), auto-deletes when job finishes
|
||||
// capture worklistData & worklist by value
|
||||
auto processWorklist = [worklistData, worklist]()
|
||||
{
|
||||
ProcessWorklist(worklistData, worklist);
|
||||
};
|
||||
//Kick off a job to process the (full) worklist
|
||||
AZ::Job* job = AZ::CreateJobFunction(processWorklist, true);
|
||||
parentJob.SetContinuation(job);
|
||||
job->Start();
|
||||
}
|
||||
}
|
||||
|
||||
void CullingScene::ProcessCullablesTG(const Scene& scene, View& view, AZ::TaskGraph& taskGraph)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene::ProcessCullablesTG() - %s", view.GetName().GetCStr());
|
||||
|
||||
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
|
||||
AZ::Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
|
||||
|
||||
void* maskedOcclusionCulling = nullptr;
|
||||
ProcessCullablesCommon(scene, view, frustum, maskedOcclusionCulling);
|
||||
|
||||
AZStd::unique_ptr<WorkListType> worklist = AZStd::make_unique<WorkListType>();
|
||||
|
||||
AZStd::shared_ptr<WorklistData> worklistData = MakeWorklistData(m_debugCtx, scene, view, frustum, maskedOcclusionCulling);
|
||||
static const AZ::TaskDescriptor descriptor{ "AZ::RPI::ProcessWorklist", "Graphics" };
|
||||
|
||||
auto nodeVisitorLambda = [worklistData, &taskGraph, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "nodeVisitorLambda()");
|
||||
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
|
||||
AZ_Assert(worklist->size() < worklist->capacity(), "we should always have room to push a node on the queue");
|
||||
|
||||
//Queue up a small list of work items (NodeData*) which will be pushed to a worker task once the queue is full.
|
||||
//This reduces the number of tasks in flight, reducing task-system overhead.
|
||||
worklist->emplace_back(AZStd::move(nodeData));
|
||||
|
||||
if (worklist->size() == worklist->capacity())
|
||||
{
|
||||
//Task takes ownership of the worklist unique ptr
|
||||
taskGraph.AddTask( descriptor, [worklistData, worklist = AZStd::move(worklist)]()
|
||||
{
|
||||
ProcessWorklist(worklistData, *worklist.get());
|
||||
// allow worklist to go out of scope and be deleted
|
||||
});
|
||||
worklist = AZStd::make_unique<WorkListType>();
|
||||
}
|
||||
};
|
||||
|
||||
if (m_debugCtx.m_enableFrustumCulling)
|
||||
{
|
||||
m_visScene->Enumerate(frustum, nodeVisitorLambda);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_visScene->EnumerateNoCull(nodeVisitorLambda);
|
||||
}
|
||||
|
||||
if (worklist->size() > 0)
|
||||
{
|
||||
//Task takes ownership of the worklist unique ptr
|
||||
taskGraph.AddTask( descriptor, [worklistData, worklist = AZStd::move(worklist)]()
|
||||
{
|
||||
ProcessWorklist(worklistData, *worklist.get());
|
||||
// allow worklist to go out of scope and be deleted
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t AddLodDataToView(const Vector3& pos, const Cullable::LodData& lodData, RPI::View& view)
|
||||
{
|
||||
#ifdef AZ_CULL_PROFILE_DETAILED
|
||||
@@ -699,11 +775,11 @@ namespace AZ
|
||||
m_parentScene = parentScene;
|
||||
|
||||
AZ_Assert(m_visScene == nullptr, "IVisibilityScene already created for this RPI::Scene");
|
||||
char sceneIdBuf[40] = "";
|
||||
m_parentScene->GetId().ToString(sceneIdBuf);
|
||||
AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", sceneIdBuf));
|
||||
AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", m_parentScene->GetName().GetCStr()));
|
||||
m_visScene = AZ::Interface<AzFramework::IVisibilitySystem>::Get()->CreateVisibilityScene(visSceneName);
|
||||
|
||||
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
|
||||
#ifdef AZ_CULL_DEBUG_ENABLED
|
||||
AZ_Assert(CountObjectsInScene() == 0, "The culling system should start with 0 entries in this scene.");
|
||||
#endif
|
||||
@@ -721,13 +797,27 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
|
||||
void CullingScene::BeginCullingTaskGraph(const AZStd::vector<ViewPtr>& views)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling");
|
||||
m_cullDataConcurrencyCheck.soft_lock();
|
||||
AZ::TaskGraph taskGraph;
|
||||
AZ::TaskDescriptor beginCullingDescriptor{"RPI_CullingScene_BeginCullingView", "Graphics"};
|
||||
for (auto& view : views)
|
||||
{
|
||||
taskGraph.AddTask(
|
||||
beginCullingDescriptor,
|
||||
[&view]()
|
||||
{
|
||||
view->BeginCulling();
|
||||
});
|
||||
}
|
||||
|
||||
m_debugCtx.ResetCullStats();
|
||||
m_debugCtx.m_numCullablesInScene = GetNumCullables();
|
||||
AZ::TaskGraphEvent waitForCompletion;
|
||||
taskGraph.Submit(&waitForCompletion);
|
||||
waitForCompletion.Wait();
|
||||
}
|
||||
|
||||
void CullingScene::BeginCullingJobs(const AZStd::vector<ViewPtr>& views)
|
||||
{
|
||||
AZ::JobCompletion beginCullingCompletion;
|
||||
|
||||
for (auto& view : views)
|
||||
@@ -743,6 +833,26 @@ namespace AZ
|
||||
}
|
||||
|
||||
beginCullingCompletion.StartAndWaitForCompletion();
|
||||
}
|
||||
|
||||
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CullingScene: BeginCulling");
|
||||
m_cullDataConcurrencyCheck.soft_lock();
|
||||
|
||||
m_debugCtx.ResetCullStats();
|
||||
m_debugCtx.m_numCullablesInScene = GetNumCullables();
|
||||
|
||||
m_taskGraphActive = AZ::Interface<AZ::TaskGraphActiveInterface>::Get();
|
||||
|
||||
if (m_taskGraphActive && m_taskGraphActive->IsTaskGraphActive())
|
||||
{
|
||||
BeginCullingTaskGraph(views);
|
||||
}
|
||||
else
|
||||
{
|
||||
BeginCullingJobs(views);
|
||||
}
|
||||
|
||||
AuxGeomDrawPtr auxGeom;
|
||||
if (m_debugCtx.m_debugDraw)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <Atom/RPI.Public/Pass/PassLibrary.h>
|
||||
#include <Atom/RPI.Public/Pass/PassDefines.h>
|
||||
#include <Atom/RPI.Public/Pass/PassSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Image/AttachmentImageAsset.h>
|
||||
@@ -1215,6 +1216,12 @@ namespace AZ
|
||||
m_queueState = PassQueueState::NoQueue;
|
||||
|
||||
InitializeInternal();
|
||||
|
||||
// Need to recreate the dest attachment because the source attachment might be changed
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->InvalidateDestImage();
|
||||
}
|
||||
|
||||
m_state = PassState::Initialized;
|
||||
}
|
||||
@@ -1301,6 +1308,9 @@ namespace AZ
|
||||
// readback attachment with output state
|
||||
UpdateReadbackAttachment(params, false);
|
||||
|
||||
// update attachment copy for preview
|
||||
UpdateAttachmentCopy(params);
|
||||
|
||||
UpdateConnectedOutputBindings();
|
||||
}
|
||||
|
||||
@@ -1489,6 +1499,14 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void Pass::UpdateAttachmentCopy(FramePrepareParams params)
|
||||
{
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->FrameBegin(params);
|
||||
}
|
||||
}
|
||||
|
||||
bool Pass::IsTimestampQueryEnabled() const
|
||||
{
|
||||
return m_flags.m_timestampQueryEnabled;
|
||||
|
||||
@@ -236,6 +236,19 @@ namespace AZ
|
||||
return true;
|
||||
}
|
||||
|
||||
void PassLibrary::RemovePassTemplate(const Name& name)
|
||||
{
|
||||
auto itr = m_templateEntries.find(name);
|
||||
if (itr != m_templateEntries.end())
|
||||
{
|
||||
AZ_Assert(itr->second.m_passes.empty(), "Can not delete PassTemplate '%s' because there are %zu Passes referencing it",
|
||||
name.GetCStr(), itr->second.m_passes.size());
|
||||
AZ_Assert(!itr->second.m_mappingAssetId.IsValid(), "Can not delete PassTemplate '%s' because it was created from an asset",
|
||||
name.GetCStr());
|
||||
m_templateEntries.erase(itr);
|
||||
}
|
||||
}
|
||||
|
||||
void PassLibrary::RemovePassFromLibrary(Pass* pass)
|
||||
{
|
||||
if (m_isShuttingDown)
|
||||
|
||||
@@ -466,6 +466,11 @@ namespace AZ
|
||||
return m_passLibrary.GetPassTemplate(name);
|
||||
}
|
||||
|
||||
void PassSystem::RemovePassTemplate(const Name& name)
|
||||
{
|
||||
m_passLibrary.RemovePassTemplate(name);
|
||||
}
|
||||
|
||||
void PassSystem::RemovePassFromLibrary(Pass* pass)
|
||||
{
|
||||
m_passLibrary.RemovePassFromLibrary(pass);
|
||||
|
||||
@@ -177,12 +177,6 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Need to recreate the dest attachment because the source attachment might be changed
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->InvalidateDestImage();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::FrameBeginInternal(FramePrepareParams params)
|
||||
@@ -196,11 +190,7 @@ namespace AZ
|
||||
|
||||
// Read back the ScopeQueries submitted from previous frames
|
||||
ReadbackScopeQueryResults();
|
||||
|
||||
if (!m_attachmentCopy.expired())
|
||||
{
|
||||
m_attachmentCopy.lock()->FrameBegin(params);
|
||||
}
|
||||
|
||||
CollectSrgs();
|
||||
|
||||
PassSystemInterface::Get()->IncrementFrameRenderPassCount();
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
#include <Atom/RPI.Public/Buffer/Buffer.h>
|
||||
#include <Atom/RPI.Public/Image/AttachmentImagePool.h>
|
||||
#include <Atom/RPI.Public/Image/ImageSystemInterface.h>
|
||||
#include <Atom/RPI.Public/Pass/RenderPass.h>
|
||||
#include <Atom/RPI.Public/Pass/AttachmentReadback.h>
|
||||
#include <Atom/RPI.Public/Pass/Specific/ImageAttachmentPreviewPass.h>
|
||||
#include <Atom/RPI.Public/Pass/ParentPass.h>
|
||||
#include <Atom/RPI.Public/RenderPipeline.h>
|
||||
@@ -131,7 +131,7 @@ namespace AZ
|
||||
Data::AssetBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(RenderPass* pass, const PassAttachment* passAttachment)
|
||||
void ImageAttachmentPreviewPass::PreviewImageAttachmentForPass(Pass* pass, const PassAttachment* passAttachment)
|
||||
{
|
||||
if (passAttachment->GetAttachmentType() != RHI::AttachmentType::Image)
|
||||
{
|
||||
|
||||
@@ -159,6 +159,11 @@ namespace AZ
|
||||
AZ_Assert(false, "Scene was already registered");
|
||||
return;
|
||||
}
|
||||
else if (!scene->GetName().IsEmpty() && scene->GetName() == sceneItem->GetName())
|
||||
{
|
||||
// only report a warning if there is a scene with duplicated name
|
||||
AZ_Warning("RPISystem", false, "There is a registered scene with same name [%s]", scene->GetName().GetCStr());
|
||||
}
|
||||
}
|
||||
|
||||
m_scenes.push_back(scene);
|
||||
@@ -177,11 +182,35 @@ namespace AZ
|
||||
AZ_Assert(false, "Can't unregister scene which wasn't registered");
|
||||
}
|
||||
|
||||
ScenePtr RPISystem::GetScene(const SceneId& sceneId) const
|
||||
Scene* RPISystem::GetScene(const SceneId& sceneId) const
|
||||
{
|
||||
for (const auto& scene : m_scenes)
|
||||
{
|
||||
if (scene->GetId() == sceneId)
|
||||
{
|
||||
return scene.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene* RPISystem::GetSceneByName(const AZ::Name& name) const
|
||||
{
|
||||
for (const auto& scene : m_scenes)
|
||||
{
|
||||
if (scene->GetName() == name)
|
||||
{
|
||||
return scene.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScenePtr RPISystem::GetDefaultScene() const
|
||||
{
|
||||
for (const auto& scene : m_scenes)
|
||||
{
|
||||
if (scene->GetName() == AZ::Name("Main"))
|
||||
{
|
||||
return scene;
|
||||
}
|
||||
@@ -189,16 +218,6 @@ namespace AZ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ScenePtr RPISystem::GetDefaultScene() const
|
||||
{
|
||||
if (m_scenes.size() > 0)
|
||||
{
|
||||
return m_scenes[0];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
RenderPipelinePtr RPISystem::GetRenderPipelineForWindow(AzFramework::NativeWindowHandle windowHandle)
|
||||
{
|
||||
RenderPipelinePtr renderPipeline;
|
||||
|
||||
@@ -119,7 +119,12 @@ namespace AZ
|
||||
AzFramework::AssetSystem::AssetStatus status = AzFramework::AssetSystem::AssetStatus_Unknown;
|
||||
AzFramework::AssetSystemRequestBus::BroadcastResult(
|
||||
status, &AzFramework::AssetSystemRequestBus::Events::CompileAssetSync, path);
|
||||
AZ_Error("RPIUtils", status == AzFramework::AssetSystem::AssetStatus_Compiled, "Could not compile image at '%s'", path.data());
|
||||
|
||||
// When running with no Asset Processor (for example in release), CompileAssetSync will return AssetStatus_Unknown.
|
||||
AZ_Error(
|
||||
"RPIUtils",
|
||||
status == AzFramework::AssetSystem::AssetStatus_Compiled || status == AzFramework::AssetSystem::AssetStatus_Unknown,
|
||||
"Could not compile image at '%s'", path.data());
|
||||
|
||||
Data::AssetId streamingImageAssetId;
|
||||
Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
|
||||
@@ -45,7 +45,9 @@ namespace AZ
|
||||
auto shaderAsset = RPISystemInterface::Get()->GetCommonShaderAssetForSrgs();
|
||||
scene->m_srg = ShaderResourceGroup::Create(shaderAsset, sceneSrgLayout->GetName());
|
||||
}
|
||||
|
||||
|
||||
scene->m_name = sceneDescriptor.m_nameId;
|
||||
|
||||
return ScenePtr(scene);
|
||||
}
|
||||
|
||||
@@ -83,10 +85,23 @@ namespace AZ
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Scene* Scene::GetSceneForEntityId(AZ::EntityId entityId)
|
||||
{
|
||||
// Find the entity context for the entity ID.
|
||||
AzFramework::EntityContextId entityContextId = AzFramework::EntityContextId::CreateNull();
|
||||
AzFramework::EntityIdContextQueryBus::EventResult(entityContextId, entityId, &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
|
||||
|
||||
if (!entityContextId.IsNull())
|
||||
{
|
||||
return GetSceneForEntityContextId(entityContextId);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
Scene::Scene()
|
||||
{
|
||||
m_id = Uuid::CreateRandom();
|
||||
m_id = AZ::Uuid::CreateRandom();
|
||||
m_cullingScene = aznew CullingScene();
|
||||
SceneRequestBus::Handler::BusConnect(m_id);
|
||||
m_drawFilterTagRegistry = RHI::DrawFilterTagRegistry::Create();
|
||||
@@ -96,7 +111,7 @@ namespace AZ
|
||||
{
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -299,7 +314,6 @@ namespace AZ
|
||||
// Force to update the lookup table since adding render pipeline would effect any pipeline states created before pass system tick
|
||||
RebuildPipelineStatesLookup();
|
||||
|
||||
AZ_Assert(!m_id.IsNull(), "RPI::Scene needs to have a valid uuid.");
|
||||
SceneNotificationBus::Event(m_id, &SceneNotification::OnRenderPipelineAdded, pipeline);
|
||||
}
|
||||
|
||||
@@ -371,8 +385,8 @@ namespace AZ
|
||||
});
|
||||
}
|
||||
simulationTG.Detach();
|
||||
m_simulationFinishedWorkActive = true;
|
||||
simulationTG.Submit(&m_simulationFinishedTGEvent);
|
||||
m_simulationFinishedTGEvent = AZStd::make_unique<TaskGraphEvent>();
|
||||
simulationTG.Submit(m_simulationFinishedTGEvent.get());
|
||||
}
|
||||
|
||||
void Scene::SimulateJobs()
|
||||
@@ -405,7 +419,7 @@ namespace AZ
|
||||
// If previous simulation job wasn't done, wait for it to finish.
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -435,17 +449,14 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::WaitTGEvent(AZ::TaskGraphEvent& completionTGEvent, AZStd::atomic_bool* workToWaitOn )
|
||||
void Scene::WaitAndCleanTGEvent(AZStd::unique_ptr<AZ::TaskGraphEvent>&& completionTGEvent)
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanCompletionJob");
|
||||
if (!workToWaitOn || workToWaitOn->load())
|
||||
AZ_PROFILE_SCOPE(RPI, "Scene: WaitAndCleanTGEvent");
|
||||
if (completionTGEvent)
|
||||
{
|
||||
completionTGEvent.Wait();
|
||||
}
|
||||
if (workToWaitOn)
|
||||
{
|
||||
workToWaitOn->store(false);
|
||||
completionTGEvent->Wait();
|
||||
}
|
||||
// allow completionTGEvent to go out of scope and be deleted
|
||||
}
|
||||
|
||||
void Scene::WaitAndCleanCompletionJob(AZ::JobCompletion*& completionJob)
|
||||
@@ -485,12 +496,12 @@ namespace AZ
|
||||
|
||||
void Scene::CollectDrawPacketsTaskGraph()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPacketsTaskGraph");
|
||||
AZ::TaskGraphEvent collectDrawPacketsTGEvent;
|
||||
static const AZ::TaskDescriptor collectDrawPacketsTGDesc{"RPI_Scene_PrepareRender_CollectDrawPackets", "Graphics"};
|
||||
|
||||
AZ::TaskGraph collectDrawPacketsTG;
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
|
||||
// Launch FeatureProcessor::Render() taskgraphs
|
||||
for (auto& fp : m_featureProcessors)
|
||||
{
|
||||
collectDrawPacketsTG.AddTask(
|
||||
@@ -506,32 +517,48 @@ namespace AZ
|
||||
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs if m_parallelOctreeTraversal)
|
||||
bool parallelOctreeTraversal = m_cullingScene->GetDebugContext().m_parallelOctreeTraversal;
|
||||
m_cullingScene->BeginCulling(m_renderPacket.m_views);
|
||||
AZ::JobCompletion processCullablesCompletion;
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
static const AZ::TaskDescriptor processCullablesDescriptor{"AZ::RPI::Scene::ProcessCullables", "Graphics"};
|
||||
AZ::TaskGraphEvent processCullablesTGEvent;
|
||||
AZ::TaskGraph processCullablesTG;
|
||||
if (parallelOctreeTraversal)
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (parallelOctreeTraversal)
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
{
|
||||
processCullablesJob->SetDependent(&processCullablesCompletion);
|
||||
processCullablesJob->Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
processCullablesJob->StartAndWaitForCompletion();
|
||||
processCullablesTG.AddTask(processCullablesDescriptor, [this, &viewPtr, &processCullablesTGEvent]()
|
||||
{
|
||||
AZ::TaskGraph subTaskGraph;
|
||||
m_cullingScene->ProcessCullablesTG(*this, *viewPtr, subTaskGraph);
|
||||
if (!subTaskGraph.IsEmpty())
|
||||
{
|
||||
subTaskGraph.Detach();
|
||||
subTaskGraph.Submit(&processCullablesTGEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ViewPtr& viewPtr : m_renderPacket.m_views)
|
||||
{
|
||||
m_cullingScene->ProcessCullablesTG(*this, *viewPtr, processCullablesTG);
|
||||
}
|
||||
}
|
||||
bool processCullablesHasWork = !processCullablesTG.IsEmpty();
|
||||
if (processCullablesHasWork)
|
||||
{
|
||||
processCullablesTG.Submit(&processCullablesTGEvent);
|
||||
}
|
||||
|
||||
WaitTGEvent(collectDrawPacketsTGEvent);
|
||||
processCullablesCompletion.StartAndWaitForCompletion();
|
||||
collectDrawPacketsTGEvent.Wait();
|
||||
if (processCullablesHasWork) // skip the wait if there is no work to do
|
||||
{
|
||||
processCullablesTGEvent.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
void Scene::CollectDrawPacketsJobs()
|
||||
{
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPackets");
|
||||
AZ_PROFILE_SCOPE(RPI, "CollectDrawPacketsJobs");
|
||||
AZ::JobCompletion* collectDrawPacketsCompletion = aznew AZ::JobCompletion();
|
||||
|
||||
// Launch FeatureProcessor::Render() jobs
|
||||
@@ -553,7 +580,7 @@ namespace AZ
|
||||
{
|
||||
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
|
||||
{
|
||||
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
m_cullingScene->ProcessCullablesJobs(*this, *viewPtr, thisJob); // can't call directly because ProcessCullables needs a parent job
|
||||
},
|
||||
true, nullptr); //auto-deletes
|
||||
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
|
||||
@@ -586,7 +613,7 @@ namespace AZ
|
||||
});
|
||||
}
|
||||
finalizeDrawListsTG.Submit(&finalizeDrawListsTGEvent);
|
||||
WaitTGEvent(finalizeDrawListsTGEvent);
|
||||
finalizeDrawListsTGEvent.Wait();
|
||||
}
|
||||
|
||||
void Scene::FinalizeDrawListsJobs()
|
||||
@@ -612,7 +639,7 @@ namespace AZ
|
||||
|
||||
if (m_taskGraphActive)
|
||||
{
|
||||
WaitTGEvent(m_simulationFinishedTGEvent, &m_simulationFinishedWorkActive);
|
||||
WaitAndCleanTGEvent(AZStd::move(m_simulationFinishedTGEvent));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -785,6 +812,11 @@ namespace AZ
|
||||
{
|
||||
return m_id;
|
||||
}
|
||||
|
||||
AZ::Name Scene::GetName() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
bool Scene::SetDefaultRenderPipeline(const RenderPipelineId& pipelineId)
|
||||
{
|
||||
|
||||
@@ -43,6 +43,7 @@ namespace AtomToolsFramework
|
||||
&PreviewerFeatureProcessorProviderBus::Handler::GetRequiredFeatureProcessors, featureProcessors);
|
||||
|
||||
AZ::RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("PreviewRenderer");
|
||||
sceneDesc.m_featureProcessorNames.assign(featureProcessors.begin(), featureProcessors.end());
|
||||
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
|
||||
|
||||
|
||||
+10
-13
@@ -47,30 +47,27 @@ namespace AtomToolsFramework
|
||||
|
||||
void PreviewRendererSystemComponent::Activate()
|
||||
{
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusConnect();
|
||||
PreviewRendererSystemRequestBus::Handler::BusConnect();
|
||||
|
||||
AZ::TickBus::QueueFunction(
|
||||
[this]()
|
||||
{
|
||||
if (!m_previewRenderer)
|
||||
{
|
||||
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
|
||||
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PreviewRendererSystemComponent::Deactivate()
|
||||
{
|
||||
PreviewRendererSystemRequestBus::Handler::BusDisconnect();
|
||||
AzFramework::ApplicationLifecycleEvents::Bus::Handler::BusDisconnect();
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
||||
m_previewRenderer.reset();
|
||||
}
|
||||
|
||||
void PreviewRendererSystemComponent::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
|
||||
{
|
||||
AZ::TickBus::QueueFunction([this](){
|
||||
if (!m_previewRenderer)
|
||||
{
|
||||
m_previewRenderer.reset(aznew AtomToolsFramework::PreviewRenderer(
|
||||
"PreviewRendererSystemComponent Preview Scene", "PreviewRendererSystemComponent Preview Pipeline"));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void PreviewRendererSystemComponent::OnApplicationAboutToStop()
|
||||
{
|
||||
m_previewRenderer.reset();
|
||||
|
||||
-5
@@ -9,7 +9,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <AtomToolsFramework/PreviewRenderer/PreviewRendererSystemRequestBus.h>
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzFramework/Application/Application.h>
|
||||
#include <PreviewRenderer/PreviewRenderer.h>
|
||||
@@ -19,7 +18,6 @@ namespace AtomToolsFramework
|
||||
//! System component that manages a global PreviewRenderer.
|
||||
class PreviewRendererSystemComponent final
|
||||
: public AZ::Component
|
||||
, public AzFramework::AssetCatalogEventBus::Handler
|
||||
, public AzFramework::ApplicationLifecycleEvents::Bus::Handler
|
||||
, public PreviewRendererSystemRequestBus::Handler
|
||||
{
|
||||
@@ -38,9 +36,6 @@ namespace AtomToolsFramework
|
||||
void Deactivate() override;
|
||||
|
||||
private:
|
||||
// AzFramework::AssetCatalogEventBus::Handler overrides ...
|
||||
void OnCatalogLoaded(const char* catalogFile) override;
|
||||
|
||||
// AzFramework::ApplicationLifecycleEvents overrides...
|
||||
void OnApplicationAboutToStop() override;
|
||||
|
||||
|
||||
+2
-2
@@ -279,9 +279,9 @@ namespace MaterialEditor
|
||||
// reset environment
|
||||
AZ::Transform iblTransform = AZ::Transform::CreateIdentity();
|
||||
AZ::TransformBus::Event(m_iblEntityId, &AZ::TransformBus::Events::SetLocalTM, iblTransform);
|
||||
|
||||
const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity();
|
||||
AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
|
||||
auto skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::SkyBoxFeatureProcessorInterface>(m_iblEntityId);
|
||||
skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix);
|
||||
|
||||
if (m_behavior)
|
||||
|
||||
+1
-2
@@ -25,8 +25,7 @@ namespace MaterialEditor
|
||||
m_iblEntityId,
|
||||
&MaterialEditorViewportInputControllerRequestBus::Handler::GetIblEntityId);
|
||||
AZ_Assert(m_iblEntityId.IsValid(), "Failed to find m_iblEntityId");
|
||||
AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
m_skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
|
||||
m_skyBoxFeatureProcessorInterface = AZ::RPI::Scene::GetFeatureProcessorForEntity<AZ::Render::SkyBoxFeatureProcessorInterface>(m_iblEntityId);
|
||||
}
|
||||
|
||||
void RotateEnvironmentBehavior::TickInternal(float x, float y, float z)
|
||||
|
||||
@@ -67,6 +67,7 @@ namespace MaterialEditor
|
||||
|
||||
// Create and register a scene with all available feature processors
|
||||
AZ::RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("MaterialViewport");
|
||||
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
|
||||
m_scene->EnableAllFeatureProcessors();
|
||||
|
||||
|
||||
@@ -249,7 +249,7 @@ namespace AZ
|
||||
|
||||
// Controls how often the timestamp data is refreshed
|
||||
RefreshType m_refreshType = RefreshType::Realtime;
|
||||
AZStd::sys_time_t m_lastUpdateTimeMicroSecond;
|
||||
AZStd::sys_time_t m_lastUpdateTimeMicroSecond = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -651,7 +651,7 @@ namespace AZ
|
||||
if (m_refreshType == RefreshType::OncePerSecond)
|
||||
{
|
||||
auto now = AZStd::GetTimeNowMicroSecond();
|
||||
if (m_lastUpdateTimeMicroSecond == 0 || now - m_lastUpdateTimeMicroSecond > 1000000)
|
||||
if (now - m_lastUpdateTimeMicroSecond > 1000000)
|
||||
{
|
||||
needEnable = true;
|
||||
m_lastUpdateTimeMicroSecond = now;
|
||||
|
||||
@@ -39,6 +39,8 @@ namespace AZ
|
||||
bool m_showAttachments = false;
|
||||
|
||||
AZ::RPI::Pass* m_selectedPass = nullptr;
|
||||
AZ::RPI::Pass* m_lastSelectedPass = nullptr;
|
||||
AZ::Name m_selectedPassPath;
|
||||
AZ::RHI::AttachmentId m_attachmentId;
|
||||
AZ::Name m_slotName;
|
||||
bool m_selectedChanged = false;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
namespace AZ::Render
|
||||
{
|
||||
inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::RenderPass* pass, AZ::RHI::AttachmentId attachmentId)
|
||||
inline AZ::RPI::PassAttachment* FindPassAttachment(AZ::RPI::Pass* pass, AZ::RHI::AttachmentId attachmentId)
|
||||
{
|
||||
for (auto& binding : pass->GetAttachmentBindings())
|
||||
{
|
||||
@@ -47,6 +47,10 @@ namespace AZ::Render
|
||||
{
|
||||
using namespace AZ;
|
||||
|
||||
// always set m_selectedPass to empty and use m_selectedPassPath to find it when render the pass tree
|
||||
m_selectedPass = nullptr;
|
||||
bool needSaveAttachment = false;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(200.f, 200.f), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("PassTree View", &draw, ImGuiWindowFlags_None))
|
||||
{
|
||||
@@ -83,60 +87,16 @@ namespace AZ::Render
|
||||
|
||||
if (Scriptable_ImGui::Button("Save Attachment"))
|
||||
{
|
||||
m_attachmentReadbackInfo = "";
|
||||
if (!m_readback)
|
||||
{
|
||||
m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "AttachmentReadback" });
|
||||
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
|
||||
}
|
||||
|
||||
if (m_selectedPass && !m_slotName.IsEmpty())
|
||||
{
|
||||
bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
|
||||
if (!readbackResult)
|
||||
{
|
||||
AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
|
||||
}
|
||||
}
|
||||
needSaveAttachment = true;
|
||||
}
|
||||
|
||||
ImGui::TextWrapped("%s", m_attachmentReadbackInfo.c_str());
|
||||
}
|
||||
|
||||
if (m_previewAttachment && m_selectedChanged)
|
||||
{
|
||||
m_selectedChanged = false;
|
||||
if (!m_attachmentId.IsEmpty() && m_selectedPass)
|
||||
{
|
||||
AZ::RPI::RenderPass* renderPass = azrtti_cast<AZ::RPI::RenderPass*>(m_selectedPass);
|
||||
if (renderPass)
|
||||
{
|
||||
if (!m_previewPass->GetParent())
|
||||
{
|
||||
RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass);
|
||||
}
|
||||
AZ::RPI::PassAttachment* attachment = FindPassAttachment(renderPass, m_attachmentId);
|
||||
if (attachment)
|
||||
{
|
||||
// Reset output attachment to empty so the preview will use pass's owner render pipeline's output
|
||||
m_previewPass->SetOutputColorAttachment(nullptr);
|
||||
m_previewPass->PreviewImageAttachmentForPass(renderPass, attachment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_previewPass->ClearPreviewAttachment();
|
||||
if (m_previewPass->GetParent())
|
||||
{
|
||||
m_previewPass->QueueForRemoval();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
// Draw the hierarchical view
|
||||
// It will assign m_seletedPass if there is a pass matches m_seletedPassPath
|
||||
ImGui::SetNextWindowPos(ImVec2(300, 60), ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize(ImVec2(300, 500), ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("PassTree", nullptr, ImGuiWindowFlags_None))
|
||||
@@ -144,6 +104,63 @@ namespace AZ::Render
|
||||
DrawTreeView(rootPass);
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
// It's possible that the pass pointer changed but selected pass path wasn't changed
|
||||
if (m_selectedPass != m_lastSelectedPass)
|
||||
{
|
||||
m_selectedChanged = true;
|
||||
if (m_selectedPass == nullptr)
|
||||
{
|
||||
m_selectedPassPath = AZ::Name{};
|
||||
}
|
||||
}
|
||||
m_lastSelectedPass = m_selectedPass;
|
||||
|
||||
if (m_previewAttachment && m_selectedChanged)
|
||||
{
|
||||
m_selectedChanged = false;
|
||||
if (!m_attachmentId.IsEmpty() && m_selectedPass)
|
||||
{
|
||||
if (!m_previewPass->GetParent())
|
||||
{
|
||||
RPI::PassSystemInterface::Get()->GetRootPass()->AddChild(m_previewPass);
|
||||
}
|
||||
AZ::RPI::PassAttachment* attachment = FindPassAttachment(m_selectedPass, m_attachmentId);
|
||||
if (attachment)
|
||||
{
|
||||
// Reset output attachment to empty so the preview will use pass's owner render pipeline's output
|
||||
m_previewPass->SetOutputColorAttachment(nullptr);
|
||||
m_previewPass->PreviewImageAttachmentForPass(m_selectedPass, attachment);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_previewPass->ClearPreviewAttachment();
|
||||
if (m_previewPass->GetParent())
|
||||
{
|
||||
m_previewPass->QueueForRemoval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needSaveAttachment)
|
||||
{
|
||||
m_attachmentReadbackInfo = "";
|
||||
if (!m_readback)
|
||||
{
|
||||
m_readback = AZStd::make_shared<AZ::RPI::AttachmentReadback>(AZ::RHI::ScopeId{ "AttachmentReadback" });
|
||||
m_readback->SetCallback(AZStd::bind(&ImGuiPassTree::ReadbackCallback, this, AZStd::placeholders::_1));
|
||||
}
|
||||
|
||||
if (m_selectedPass && !m_slotName.IsEmpty())
|
||||
{
|
||||
bool readbackResult = m_selectedPass->ReadbackAttachment(m_readback, m_slotName);
|
||||
if (!readbackResult)
|
||||
{
|
||||
AZ_Error("ImGuiPassTree", false, "Failed to readback attachment from pass [%s] slot [%s]", m_selectedPass->GetName().GetCStr(), m_slotName.GetCStr());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImGuiPassTree::DrawPassAttachments(AZ::RPI::Pass* pass)
|
||||
@@ -202,6 +219,7 @@ namespace AZ::Render
|
||||
|
||||
if (Scriptable_ImGui::Selectable(label.c_str(), m_attachmentId == binding.m_attachment->GetAttachmentId()))
|
||||
{
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_selectedPass = pass;
|
||||
m_attachmentId = binding.m_attachment->GetAttachmentId();
|
||||
m_slotName = binding.m_name;
|
||||
@@ -232,9 +250,9 @@ namespace AZ::Render
|
||||
if (!m_showAttachments)
|
||||
{
|
||||
// Only draw the leaf pass as selectable if we are not showing attachments as its children
|
||||
if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPass == pass))
|
||||
if (Scriptable_ImGui::Selectable(pass->GetName().GetCStr(), m_selectedPassPath == pass->GetPathName()))
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = true;
|
||||
@@ -244,13 +262,13 @@ namespace AZ::Render
|
||||
{
|
||||
// Draw the pass as a tree node which has attachments as its children
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen
|
||||
| ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
| ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
|
||||
bool nodeOpen = Scriptable_ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags);
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = true;
|
||||
@@ -259,7 +277,6 @@ namespace AZ::Render
|
||||
if (nodeOpen)
|
||||
{
|
||||
DrawPassAttachments(pass);
|
||||
|
||||
Scriptable_ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
@@ -268,13 +285,13 @@ namespace AZ::Render
|
||||
{
|
||||
// For a ParentPasse, draw it as a tree node
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ImGuiTreeNodeFlags_DefaultOpen
|
||||
| ((m_selectedPass == pass) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
| ((m_selectedPassPath == pass->GetPathName()) ? ImGuiTreeNodeFlags_Selected : 0);
|
||||
|
||||
bool nodeOpen = ImGui::TreeNodeEx(pass->GetName().GetCStr(), flags);
|
||||
|
||||
if (ImGui::IsItemClicked())
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
m_selectedPassPath = pass->GetPathName();
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = true;
|
||||
@@ -282,7 +299,10 @@ namespace AZ::Render
|
||||
|
||||
if (nodeOpen)
|
||||
{
|
||||
DrawPassAttachments(pass);
|
||||
if (m_showAttachments)
|
||||
{
|
||||
DrawPassAttachments(pass);
|
||||
}
|
||||
for (const auto& child : asParent->GetChildren())
|
||||
{
|
||||
DrawTreeView(child.get());
|
||||
@@ -296,6 +316,12 @@ namespace AZ::Render
|
||||
{
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// set m_selectedPass if pass path matches
|
||||
if (pass->GetPathName() == m_selectedPassPath)
|
||||
{
|
||||
m_selectedPass = pass;
|
||||
}
|
||||
}
|
||||
|
||||
inline void ImGuiPassTree::ReadbackCallback(const AZ::RPI::AttachmentReadback::ReadbackResult& readbackResult)
|
||||
@@ -364,7 +390,9 @@ namespace AZ::Render
|
||||
m_previewAttachment = false;
|
||||
m_showAttachments = false;
|
||||
|
||||
m_selectedPassPath = AZ::Name{};
|
||||
m_selectedPass = nullptr;
|
||||
m_lastSelectedPass = nullptr;
|
||||
m_attachmentId = AZ::RHI::AttachmentId{};
|
||||
m_slotName = AZ::Name{};
|
||||
m_selectedChanged = false;
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace AZ
|
||||
{
|
||||
m_dynamicDrawManager.reset();
|
||||
AZ::RPI::ViewportContextManagerNotificationsBus::Handler::BusDisconnect();
|
||||
RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
|
||||
RPI::Scene* scene = AZ::RPI::Scene::GetSceneForEntityContextId(m_entityContextId);
|
||||
// Check if scene is emptry since scene might be released already when running AtomSampleViewer
|
||||
if (scene)
|
||||
{
|
||||
@@ -157,9 +157,9 @@ namespace AZ
|
||||
|
||||
void AtomBridgeSystemComponent::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene)
|
||||
{
|
||||
AZ_UNUSED(bootstrapScene);
|
||||
// Make default AtomDebugDisplayViewportInterface
|
||||
AZStd::shared_ptr<AtomDebugDisplayViewportInterface> mainEntityDebugDisplay = AZStd::make_shared<AtomDebugDisplayViewportInterface>(AzFramework::g_defaultSceneEntityDebugDisplayId);
|
||||
AZStd::shared_ptr<AtomDebugDisplayViewportInterface> mainEntityDebugDisplay =
|
||||
AZStd::make_shared<AtomDebugDisplayViewportInterface>(AzFramework::g_defaultSceneEntityDebugDisplayId, bootstrapScene);
|
||||
m_activeViewportsList[AzFramework::g_defaultSceneEntityDebugDisplayId] = mainEntityDebugDisplay;
|
||||
}
|
||||
|
||||
|
||||
@@ -256,12 +256,11 @@ namespace AZ::AtomBridge
|
||||
viewportContextPtr->ConnectSceneChangedHandler(m_sceneChangeHandler);
|
||||
}
|
||||
|
||||
AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress)
|
||||
AtomDebugDisplayViewportInterface::AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress, RPI::Scene* scene)
|
||||
{
|
||||
ResetRenderState();
|
||||
m_viewportId = defaultInstanceAddress;
|
||||
m_defaultInstance = true;
|
||||
RPI::Scene* scene = RPI::RPISystemInterface::Get()->GetDefaultScene().get();
|
||||
InitInternal(scene, nullptr);
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace AZ::AtomBridge
|
||||
AZ_RTTI(AtomDebugDisplayViewportInterface, "{09AF6A46-0100-4FBF-8F94-E6B221322D14}", AzFramework::DebugDisplayRequestBus::Handler);
|
||||
|
||||
explicit AtomDebugDisplayViewportInterface(AZ::RPI::ViewportContextPtr viewportContextPtr);
|
||||
explicit AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress);
|
||||
explicit AtomDebugDisplayViewportInterface(uint32_t defaultInstanceAddress, RPI::Scene* scene);
|
||||
~AtomDebugDisplayViewportInterface();
|
||||
|
||||
void ResetRenderState();
|
||||
|
||||
@@ -133,18 +133,6 @@ namespace AZ
|
||||
typedef std::vector<FontEffect> FontEffects;
|
||||
typedef FontEffects::iterator FontEffectsIterator;
|
||||
|
||||
struct FontPipelineStateMapKey
|
||||
{
|
||||
AZ::RPI::SceneId m_sceneId; // which scene pipeline state is attached to (via Render Pipeline)
|
||||
AZ::RHI::DrawListTag m_drawListTag; // which render pass this pipeline draws in by default
|
||||
|
||||
bool operator<(const FontPipelineStateMapKey& other) const
|
||||
{
|
||||
return m_sceneId < other.m_sceneId
|
||||
|| (m_sceneId == other.m_sceneId && m_drawListTag < other.m_drawListTag);
|
||||
}
|
||||
};
|
||||
|
||||
struct FontShaderData
|
||||
{
|
||||
AZ::RHI::ShaderInputNameIndex m_imageInputIndex = "m_texture";
|
||||
|
||||
+2
-1
@@ -130,7 +130,8 @@ namespace AZ::Render
|
||||
}
|
||||
AZ::RPI::ViewportContextPtr viewportContext = GetViewportContext();
|
||||
|
||||
if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene())
|
||||
if (!m_fontDrawInterface || !viewportContext || !viewportContext->GetRenderScene() ||
|
||||
!AZ::Interface<AzFramework::FontQueryInterface>::Get())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
+15
-44
@@ -60,52 +60,8 @@ namespace AZ
|
||||
virtual void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) = 0;
|
||||
//! Set a material property override value wrapped by an AZStd::any
|
||||
virtual void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) = 0;
|
||||
//! Set a material property override value to a bool
|
||||
virtual void SetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) = 0;
|
||||
//! Set a material property override value to a integer
|
||||
virtual void SetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) = 0;
|
||||
//! Set a material property override value to a unsigned integer
|
||||
virtual void SetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) = 0;
|
||||
//! Set a material property override value to a float
|
||||
virtual void SetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) = 0;
|
||||
//! Set a material property override value to a Vector2
|
||||
virtual void SetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) = 0;
|
||||
//! Set a material property override value to a Vector3
|
||||
virtual void SetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) = 0;
|
||||
//! Set a material property override value to a Vector4
|
||||
virtual void SetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) = 0;
|
||||
//! Set a material property override value to a color
|
||||
virtual void SetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) = 0;
|
||||
//! Set a material property override value to an image asset
|
||||
virtual void SetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Asset<AZ::RPI::ImageAsset>& value) = 0;
|
||||
//! Set a material property override value to an image instance
|
||||
virtual void SetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Instance<AZ::RPI::Image>& value) = 0;
|
||||
//! Set a material property override value to a string
|
||||
virtual void SetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) = 0;
|
||||
//! Get a material property override value wrapped by an AZStd::any
|
||||
virtual AZStd::any GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a bool
|
||||
virtual bool GetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an integer
|
||||
virtual int32_t GetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an unsigned integer
|
||||
virtual uint32_t GetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a float
|
||||
virtual float GetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Vector2
|
||||
virtual AZ::Vector2 GetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Vector3
|
||||
virtual AZ::Vector3 GetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Vector4
|
||||
virtual AZ::Vector4 GetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a Color
|
||||
virtual AZ::Color GetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an image asset
|
||||
virtual AZ::Data::Asset<AZ::RPI::ImageAsset> GetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as an image instance
|
||||
virtual AZ::Data::Instance<AZ::RPI::Image> GetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Get a material property override value as a string
|
||||
virtual AZStd::string GetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const = 0;
|
||||
//! Clear property override for a specific material assignment
|
||||
virtual void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) = 0;
|
||||
//! Clear property overrides for a specific material assignment
|
||||
@@ -122,6 +78,21 @@ namespace AZ
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZ::RPI::MaterialModelUvOverrideMap& modelUvOverrides) = 0;
|
||||
//! Get Model UV overrides for a specific material assignment
|
||||
virtual AZ::RPI::MaterialModelUvOverrideMap GetModelUvOverrides(const MaterialAssignmentId& materialAssignmentId) const = 0;
|
||||
|
||||
//! Set material property override value with a specific type
|
||||
template<typename T>
|
||||
void SetPropertyOverrideT(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const T& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
//! Get material property override value with a specific type
|
||||
template<typename T>
|
||||
T GetPropertyOverrideT(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<T>() ? AZStd::any_cast<T>(value) : T{};
|
||||
}
|
||||
};
|
||||
using MaterialComponentRequestBus = EBus<MaterialComponentRequests>;
|
||||
|
||||
|
||||
+1
-5
@@ -48,11 +48,7 @@ namespace AZ
|
||||
|
||||
void DiffuseGlobalIlluminationComponentController::Activate(EntityId entityId)
|
||||
{
|
||||
AZ_UNUSED(entityId);
|
||||
|
||||
const RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get();
|
||||
m_featureProcessor = scene->GetFeatureProcessor<DiffuseGlobalIlluminationFeatureProcessorInterface>();
|
||||
|
||||
m_featureProcessor = AZ::RPI::Scene::GetFeatureProcessorForEntity<DiffuseGlobalIlluminationFeatureProcessorInterface>(entityId);
|
||||
OnConfigChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace AZ
|
||||
m_entityId = entityId;
|
||||
m_dirty = true;
|
||||
|
||||
RPI::ScenePtr scene = RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
RPI::Scene* scene = RPI::Scene::GetSceneForEntityId(m_entityId);
|
||||
if (scene)
|
||||
{
|
||||
AZ::RPI::SceneNotificationBus::Handler::BusConnect(scene->GetId());
|
||||
|
||||
+16
-10
@@ -6,23 +6,24 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Material/EditorMaterialComponent.h>
|
||||
#include <Material/EditorMaterialComponentExporter.h>
|
||||
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <Atom/RPI.Edit/Common/AssetUtils.h>
|
||||
#include <Atom/RPI.Edit/Material/MaterialPropertyId.h>
|
||||
#include <Atom/RPI.Public/Image/StreamingImage.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialTypeAsset.h>
|
||||
#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
|
||||
#include <AzCore/RTTI/BehaviorContext.h>
|
||||
#include <AzCore/Serialization/Json/RegistrationContext.h>
|
||||
#include <AzToolsFramework/API/EditorAssetSystemAPI.h>
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <Material/EditorMaterialComponent.h>
|
||||
#include <Material/EditorMaterialComponentExporter.h>
|
||||
#include <Material/EditorMaterialComponentSerializer.h>
|
||||
|
||||
AZ_PUSH_DISABLE_WARNING(4251 4800, "-Wunknown-warning-option") // disable warnings spawned by QT
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QCursor>
|
||||
#include <QMenu>
|
||||
AZ_POP_DISABLE_WARNING
|
||||
|
||||
namespace AZ
|
||||
@@ -59,7 +60,12 @@ namespace AZ
|
||||
BaseClass::Reflect(context);
|
||||
EditorMaterialComponentSlot::Reflect(context);
|
||||
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
if (auto jsonContext = azrtti_cast<JsonRegistrationContext*>(context))
|
||||
{
|
||||
jsonContext->Serializer<JsonEditorMaterialComponentSerializer>()->HandlesType<EditorMaterialComponent>();
|
||||
}
|
||||
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->RegisterGenericType<EditorMaterialComponentSlotContainer>();
|
||||
serializeContext->RegisterGenericType<EditorMaterialComponentSlotsByLodContainer>();
|
||||
@@ -76,7 +82,7 @@ namespace AZ
|
||||
serializeContext->RegisterGenericType<AZStd::unordered_map<MaterialAssignmentId, Data::AssetId, AZStd::hash<MaterialAssignmentId>, AZStd::equal_to<MaterialAssignmentId>, AZStd::allocator>>();
|
||||
serializeContext->RegisterGenericType<AZStd::unordered_map<MaterialAssignmentId, MaterialPropertyOverrideMap, AZStd::hash<MaterialAssignmentId>, AZStd::equal_to<MaterialAssignmentId>, AZStd::allocator>>();
|
||||
|
||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
|
||||
if (auto editContext = serializeContext->GetEditContext())
|
||||
{
|
||||
editContext->Class<EditorMaterialComponent>(
|
||||
"Material", "The material component specifies the material to use for this entity")
|
||||
@@ -129,7 +135,7 @@ namespace AZ
|
||||
}
|
||||
}
|
||||
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
behaviorContext->ConstantProperty("EditorMaterialComponentTypeId", BehaviorConstant(Uuid(EditorMaterialComponentTypeId)))
|
||||
->Attribute(AZ::Script::Attributes::Module, "render")
|
||||
|
||||
@@ -27,6 +27,8 @@ namespace AZ
|
||||
, public EditorMaterialSystemComponentNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
friend class JsonEditorMaterialComponentSerializer;
|
||||
|
||||
using BaseClass = EditorRenderComponentAdapter<MaterialComponentController, MaterialComponent, MaterialComponentConfig>;
|
||||
AZ_EDITOR_COMPONENT(EditorMaterialComponent, EditorMaterialComponentTypeId, BaseClass);
|
||||
|
||||
|
||||
+13
-4
@@ -314,12 +314,21 @@ namespace AZ
|
||||
|
||||
AtomToolsFramework::ConvertToPropertyConfig(propertyConfig, propertyDefinition);
|
||||
|
||||
const auto& propertyIndex =
|
||||
m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
|
||||
|
||||
propertyConfig.m_groupName = groupDisplayName;
|
||||
const auto& propertyIndex = m_editData.m_materialAsset->GetMaterialPropertiesLayout()->FindPropertyIndex(propertyConfig.m_id);
|
||||
propertyConfig.m_showThumbnail = true;
|
||||
propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]);
|
||||
propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]);
|
||||
propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType(m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]);
|
||||
|
||||
propertyConfig.m_defaultValue = AtomToolsFramework::ConvertToEditableType(
|
||||
m_editData.m_materialTypeAsset->GetDefaultPropertyValues()[propertyIndex.GetIndex()]);
|
||||
|
||||
// There is no explicit parent material here. Material instance property overrides replace the values from the
|
||||
// assigned material asset. Its values should be treated as parent, for comparison, in this case.
|
||||
propertyConfig.m_parentValue = AtomToolsFramework::ConvertToEditableType(
|
||||
m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]);
|
||||
propertyConfig.m_originalValue = AtomToolsFramework::ConvertToEditableType(
|
||||
m_editData.m_materialAsset->GetPropertyValues()[propertyIndex.GetIndex()]);
|
||||
group.m_properties.emplace_back(propertyConfig);
|
||||
}
|
||||
}
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/Serialization/Json/JsonSerializationResult.h>
|
||||
#include <Material/EditorMaterialComponent.h>
|
||||
#include <Material/EditorMaterialComponentSerializer.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
AZ_CLASS_ALLOCATOR_IMPL(JsonEditorMaterialComponentSerializer, AZ::SystemAllocator, 0);
|
||||
|
||||
AZ::JsonSerializationResult::Result JsonEditorMaterialComponentSerializer::Load(
|
||||
void* outputValue,
|
||||
[[maybe_unused]] const AZ::Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(
|
||||
azrtti_typeid<EditorMaterialComponent>() == outputValueTypeId,
|
||||
"Unable to deserialize EditorMaterialComponent from json because the provided type is %s.",
|
||||
outputValueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
auto componentInstance = reinterpret_cast<EditorMaterialComponent*>(outputValue);
|
||||
AZ_Assert(componentInstance, "Output value for JsonEditorMaterialComponentSerializer can't be null.");
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::ReadField);
|
||||
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(
|
||||
&componentInstance->m_id, azrtti_typeid<decltype(componentInstance->m_id)>(), inputValue, "Id", context));
|
||||
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(
|
||||
&componentInstance->m_controller, azrtti_typeid<decltype(componentInstance->m_controller)>(), inputValue, "Controller",
|
||||
context));
|
||||
|
||||
result.Combine(ContinueLoadingFromJsonObjectField(
|
||||
&componentInstance->m_materialSlotsByLodEnabled, azrtti_typeid<decltype(componentInstance->m_materialSlotsByLodEnabled)>(),
|
||||
inputValue, "materialSlotsByLodEnabled", context));
|
||||
|
||||
return context.Report(
|
||||
result,
|
||||
result.GetProcessing() != JSR::Processing::Halted ? "Successfully loaded EditorMaterialComponent information."
|
||||
: "Failed to load EditorMaterialComponent information.");
|
||||
}
|
||||
|
||||
AZ::JsonSerializationResult::Result JsonEditorMaterialComponentSerializer::Store(
|
||||
rapidjson::Value& outputValue,
|
||||
const void* inputValue,
|
||||
const void* defaultValue,
|
||||
[[maybe_unused]] const AZ::Uuid& valueTypeId,
|
||||
AZ::JsonSerializerContext& context)
|
||||
{
|
||||
namespace JSR = AZ::JsonSerializationResult;
|
||||
|
||||
AZ_Assert(
|
||||
azrtti_typeid<EditorMaterialComponent>() == valueTypeId,
|
||||
"Unable to Serialize EditorMaterialComponent because the provided type is %s.",
|
||||
valueTypeId.ToString<AZStd::string>().c_str());
|
||||
|
||||
auto componentInstance = reinterpret_cast<const EditorMaterialComponent*>(inputValue);
|
||||
AZ_Assert(componentInstance, "Input value for JsonEditorMaterialComponentSerializer can't be null.");
|
||||
auto defaultComponentInstance = reinterpret_cast<const EditorMaterialComponent*>(defaultValue);
|
||||
|
||||
JSR::ResultCode result(JSR::Tasks::WriteValue);
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "m_id");
|
||||
const auto componentId = &componentInstance->m_id;
|
||||
const auto defaultComponentId = defaultComponentInstance ? &defaultComponentInstance->m_id : nullptr;
|
||||
|
||||
result.Combine(ContinueStoringToJsonObjectField(
|
||||
outputValue, "Id", componentId, defaultComponentId, azrtti_typeid<decltype(componentInstance->m_id)>(), context));
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "Controller");
|
||||
const auto controller = &componentInstance->m_controller;
|
||||
const auto defaultController = defaultComponentInstance ? &defaultComponentInstance->m_controller : nullptr;
|
||||
|
||||
result.Combine(ContinueStoringToJsonObjectField(
|
||||
outputValue, "Controller", controller, defaultController, azrtti_typeid<decltype(componentInstance->m_controller)>(),
|
||||
context));
|
||||
}
|
||||
|
||||
{
|
||||
AZ::ScopedContextPath subPathName(context, "materialSlotsByLodEnabled");
|
||||
const auto enabled = &componentInstance->m_materialSlotsByLodEnabled;
|
||||
const auto defaultEnabled = defaultComponentInstance ? &defaultComponentInstance->m_materialSlotsByLodEnabled : nullptr;
|
||||
|
||||
result.Combine(ContinueStoringToJsonObjectField(
|
||||
outputValue, "materialSlotsByLodEnabled", enabled, defaultEnabled,
|
||||
azrtti_typeid<decltype(componentInstance->m_materialSlotsByLodEnabled)>(), context));
|
||||
}
|
||||
|
||||
return context.Report(
|
||||
result,
|
||||
result.GetProcessing() != JSR::Processing::Halted ? "Successfully stored EditorMaterialComponent information."
|
||||
: "Failed to store EditorMaterialComponent information.");
|
||||
}
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Serialization/Json/BaseJsonSerializer.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
namespace Render
|
||||
{
|
||||
// JsonEditorMaterialComponentSerializer skips serialization of EditorMaterialComponentSlot(s) which are only needed at runtime in
|
||||
// the editor
|
||||
class JsonEditorMaterialComponentSerializer : public AZ::BaseJsonSerializer
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(JsonEditorMaterialComponentSerializer, "{D354FE3C-34D2-4E80-B3F9-49450D252336}", BaseJsonSerializer);
|
||||
AZ_CLASS_ALLOCATOR_DECL;
|
||||
|
||||
AZ::JsonSerializationResult::Result Load(
|
||||
void* outputValue,
|
||||
const AZ::Uuid& outputValueTypeId,
|
||||
const rapidjson::Value& inputValue,
|
||||
AZ::JsonDeserializerContext& context) override;
|
||||
|
||||
AZ::JsonSerializationResult::Result Store(
|
||||
rapidjson::Value& outputValue,
|
||||
const void* inputValue,
|
||||
const void* defaultValue,
|
||||
const AZ::Uuid& valueTypeId,
|
||||
AZ::JsonSerializerContext& context) override;
|
||||
};
|
||||
|
||||
} // namespace Render
|
||||
} // namespace AZ
|
||||
+1
@@ -196,6 +196,7 @@ namespace AZ
|
||||
{
|
||||
AZ_UNUSED(entityId);
|
||||
AZ_UNUSED(materialAssignmentId);
|
||||
|
||||
AZ_Warning(
|
||||
"EditorMaterialSystemComponent", false, "RenderMaterialPreview capture failed for entity %s slot %s.",
|
||||
entityId.ToString().c_str(), materialAssignmentId.ToString().c_str());
|
||||
|
||||
+32
-171
@@ -54,29 +54,29 @@ namespace AZ
|
||||
->Event("GetMaterialOverride", &MaterialComponentRequestBus::Events::GetMaterialOverride)
|
||||
->Event("ClearMaterialOverride", &MaterialComponentRequestBus::Events::ClearMaterialOverride)
|
||||
->Event("SetPropertyOverride", &MaterialComponentRequestBus::Events::SetPropertyOverride)
|
||||
->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideBool)
|
||||
->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideInt32)
|
||||
->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideUInt32)
|
||||
->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideFloat)
|
||||
->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector2)
|
||||
->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector3)
|
||||
->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideVector4)
|
||||
->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideColor)
|
||||
->Event("SetPropertyOverrideImageAsset", &MaterialComponentRequestBus::Events::SetPropertyOverrideImageAsset)
|
||||
->Event("SetPropertyOverrideImageInstance", &MaterialComponentRequestBus::Events::SetPropertyOverrideImageInstance)
|
||||
->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideString)
|
||||
->Event("SetPropertyOverrideBool", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<bool>)
|
||||
->Event("SetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<int32_t>)
|
||||
->Event("SetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<uint32_t>)
|
||||
->Event("SetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<float>)
|
||||
->Event("SetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector2>)
|
||||
->Event("SetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector3>)
|
||||
->Event("SetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Vector4>)
|
||||
->Event("SetPropertyOverrideColor", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Color>)
|
||||
->Event("SetPropertyOverrideImage", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZ::Data::AssetId>)
|
||||
->Event("SetPropertyOverrideString", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<AZStd::string>)
|
||||
->Event("SetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::SetPropertyOverrideT<uint32_t>)
|
||||
->Event("GetPropertyOverride", &MaterialComponentRequestBus::Events::GetPropertyOverride)
|
||||
->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideBool)
|
||||
->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideInt32)
|
||||
->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideUInt32)
|
||||
->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideFloat)
|
||||
->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector2)
|
||||
->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector3)
|
||||
->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideVector4)
|
||||
->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideColor)
|
||||
->Event("GetPropertyOverrideImageAsset", &MaterialComponentRequestBus::Events::GetPropertyOverrideImageAsset)
|
||||
->Event("GetPropertyOverrideImageInstance", &MaterialComponentRequestBus::Events::GetPropertyOverrideImageInstance)
|
||||
->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideString)
|
||||
->Event("GetPropertyOverrideBool", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<bool>)
|
||||
->Event("GetPropertyOverrideInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<int32_t>)
|
||||
->Event("GetPropertyOverrideUInt32", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<uint32_t>)
|
||||
->Event("GetPropertyOverrideFloat", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<float>)
|
||||
->Event("GetPropertyOverrideVector2", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector2>)
|
||||
->Event("GetPropertyOverrideVector3", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector3>)
|
||||
->Event("GetPropertyOverrideVector4", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Vector4>)
|
||||
->Event("GetPropertyOverrideColor", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Color>)
|
||||
->Event("GetPropertyOverrideImage", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZ::Data::AssetId>)
|
||||
->Event("GetPropertyOverrideString", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<AZStd::string>)
|
||||
->Event("GetPropertyOverrideEnum", &MaterialComponentRequestBus::Events::GetPropertyOverrideT<uint32_t>)
|
||||
->Event("ClearPropertyOverride", &MaterialComponentRequestBus::Events::ClearPropertyOverride)
|
||||
->Event("ClearPropertyOverrides", &MaterialComponentRequestBus::Events::ClearPropertyOverrides)
|
||||
->Event("ClearAllPropertyOverrides", &MaterialComponentRequestBus::Events::ClearAllPropertyOverrides)
|
||||
@@ -121,8 +121,13 @@ namespace AZ
|
||||
MaterialComponentRequestBus::Handler::BusDisconnect();
|
||||
MaterialReceiverNotificationBus::Handler::BusDisconnect();
|
||||
TickBus::Handler::BusDisconnect();
|
||||
|
||||
ReleaseMaterials();
|
||||
|
||||
// Sending notification to wipe any previously assigned material overrides
|
||||
MaterialComponentNotificationBus::Event(
|
||||
m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, MaterialAssignmentMap());
|
||||
|
||||
m_queuedMaterialUpdateNotification = false;
|
||||
m_entityId = AZ::EntityId(AZ::EntityId::InvalidEntityId);
|
||||
}
|
||||
@@ -221,6 +226,11 @@ namespace AZ
|
||||
if (!anyQueued)
|
||||
{
|
||||
ReleaseMaterials();
|
||||
|
||||
// If no other materials were loaded, the notification must still be sent in case there are externally managed material
|
||||
// instances in the configuration
|
||||
MaterialComponentNotificationBus::Event(
|
||||
m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,8 +278,6 @@ namespace AZ
|
||||
{
|
||||
materialPair.second.Release();
|
||||
}
|
||||
|
||||
MaterialComponentNotificationBus::Event(m_entityId, &MaterialComponentNotifications::OnMaterialsUpdated, m_configuration.m_materials);
|
||||
}
|
||||
|
||||
MaterialAssignmentMap MaterialComponentController::GetOriginalMaterialAssignments() const
|
||||
@@ -499,76 +507,6 @@ namespace AZ
|
||||
QueuePropertyChanges(materialAssignmentId);
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideBool(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideUInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideFloat(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideVector2(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideVector3(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideVector4(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideColor(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideImageAsset(
|
||||
const MaterialAssignmentId& materialAssignmentId,
|
||||
const AZStd::string& propertyName,
|
||||
const AZ::Data::Asset<AZ::RPI::ImageAsset>& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideImageInstance(
|
||||
const MaterialAssignmentId& materialAssignmentId,
|
||||
const AZStd::string& propertyName,
|
||||
const AZ::Data::Instance<AZ::RPI::Image>& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
void MaterialComponentController::SetPropertyOverrideString(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value)
|
||||
{
|
||||
SetPropertyOverride(materialAssignmentId, propertyName, AZStd::any(value));
|
||||
}
|
||||
|
||||
AZStd::any MaterialComponentController::GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
|
||||
@@ -586,83 +524,6 @@ namespace AZ
|
||||
return propertyIt->second;
|
||||
}
|
||||
|
||||
bool MaterialComponentController::GetPropertyOverrideBool(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<bool>() ? AZStd::any_cast<bool>(value) : false;
|
||||
}
|
||||
|
||||
int32_t MaterialComponentController::GetPropertyOverrideInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<int32_t>() ? AZStd::any_cast<int32_t>(value) : 0;
|
||||
}
|
||||
|
||||
uint32_t MaterialComponentController::GetPropertyOverrideUInt32(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<uint32_t>() ? AZStd::any_cast<uint32_t>(value) : 0;
|
||||
}
|
||||
|
||||
float MaterialComponentController::GetPropertyOverrideFloat(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<float>() ? AZStd::any_cast<float>(value) : 0.0f;
|
||||
}
|
||||
|
||||
AZ::Vector2 MaterialComponentController::GetPropertyOverrideVector2(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Vector2>() ? AZStd::any_cast<AZ::Vector2>(value) : AZ::Vector2::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector3 MaterialComponentController::GetPropertyOverrideVector3(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Vector3>() ? AZStd::any_cast<AZ::Vector3>(value) : AZ::Vector3::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Vector4 MaterialComponentController::GetPropertyOverrideVector4(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Vector4>() ? AZStd::any_cast<AZ::Vector4>(value) : AZ::Vector4::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Color MaterialComponentController::GetPropertyOverrideColor(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Color>() ? AZStd::any_cast<AZ::Color>(value) : AZ::Color::CreateZero();
|
||||
}
|
||||
|
||||
AZ::Data::Asset<AZ::RPI::ImageAsset> MaterialComponentController::GetPropertyOverrideImageAsset(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Data::Asset<AZ::RPI::ImageAsset>>() ? AZStd::any_cast<AZ::Data::Asset<AZ::RPI::ImageAsset>>(value) : AZ::Data::Asset<AZ::RPI::ImageAsset>();
|
||||
}
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::Image> MaterialComponentController::GetPropertyOverrideImageInstance(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZ::Data::Instance<AZ::RPI::Image>>() ? AZStd::any_cast<AZ::Data::Instance<AZ::RPI::Image>>(value) : AZ::Data::Instance<AZ::RPI::Image>();
|
||||
}
|
||||
|
||||
AZStd::string MaterialComponentController::GetPropertyOverrideString(
|
||||
const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const
|
||||
{
|
||||
const AZStd::any& value = GetPropertyOverride(materialAssignmentId, propertyName);
|
||||
return !value.empty() && value.is<AZStd::string>() ? AZStd::any_cast<AZStd::string>(value) : AZStd::string();
|
||||
}
|
||||
|
||||
void MaterialComponentController::ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName)
|
||||
{
|
||||
auto materialIt = m_configuration.m_materials.find(materialAssignmentId);
|
||||
|
||||
-25
@@ -65,33 +65,8 @@ namespace AZ
|
||||
void SetMaterialOverride(const MaterialAssignmentId& materialAssignmentId, const AZ::Data::AssetId& materialAssetId) override;
|
||||
AZ::Data::AssetId GetMaterialOverride(const MaterialAssignmentId& materialAssignmentId) const override;
|
||||
void ClearMaterialOverride(const MaterialAssignmentId& materialAssignmentId) override;
|
||||
|
||||
void SetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::any& value) override;
|
||||
void SetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const bool& value) override;
|
||||
void SetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const int32_t& value) override;
|
||||
void SetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const uint32_t& value) override;
|
||||
void SetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const float& value) override;
|
||||
void SetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector2& value) override;
|
||||
void SetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector3& value) override;
|
||||
void SetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Vector4& value) override;
|
||||
void SetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Color& value) override;
|
||||
void SetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Asset<AZ::RPI::ImageAsset>& value) override;
|
||||
void SetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZ::Data::Instance<AZ::RPI::Image>& value) override;
|
||||
void SetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName, const AZStd::string& value) override;
|
||||
|
||||
AZStd::any GetPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
bool GetPropertyOverrideBool(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
int32_t GetPropertyOverrideInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
uint32_t GetPropertyOverrideUInt32(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
float GetPropertyOverrideFloat(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Vector2 GetPropertyOverrideVector2(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Vector3 GetPropertyOverrideVector3(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Vector4 GetPropertyOverrideVector4(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Color GetPropertyOverrideColor(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Data::Asset<AZ::RPI::ImageAsset> GetPropertyOverrideImageAsset(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZ::Data::Instance<AZ::RPI::Image> GetPropertyOverrideImageInstance(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
AZStd::string GetPropertyOverrideString(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) const override;
|
||||
|
||||
void ClearPropertyOverride(const MaterialAssignmentId& materialAssignmentId, const AZStd::string& propertyName) override;
|
||||
void ClearPropertyOverrides(const MaterialAssignmentId& materialAssignmentId) override;
|
||||
void ClearAllPropertyOverrides() override;
|
||||
|
||||
+1
-2
@@ -357,8 +357,7 @@ namespace AZ
|
||||
void DisplayMapperComponentController::OnConfigChanged()
|
||||
{
|
||||
// Register the configuration with the AcesDisplayMapperFeatureProcessor for this scene.
|
||||
const AZ::RPI::Scene* scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene().get();
|
||||
DisplayMapperFeatureProcessorInterface* fp = scene->GetFeatureProcessor<DisplayMapperFeatureProcessorInterface>();
|
||||
DisplayMapperFeatureProcessorInterface* fp = AZ::RPI::Scene::GetFeatureProcessorForEntity<DisplayMapperFeatureProcessorInterface>(m_entityId);
|
||||
DisplayMapperConfigurationDescriptor desc;
|
||||
desc.m_operationType = m_configuration.m_displayMapperOperation;
|
||||
desc.m_ldrGradingLutEnabled = m_configuration.m_ldrColorGradingLutEnabled;
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ namespace AZ
|
||||
// CVar for toggling the display of the scene stats
|
||||
int r_skinnedMeshDisplaySceneStats = 0;
|
||||
// SceneId to query for the stats
|
||||
RPI::SceneId m_sceneId = RPI::SceneId::CreateNull();
|
||||
RPI::SceneId m_sceneId;
|
||||
};
|
||||
}// namespace Render
|
||||
}// namespace AZ
|
||||
|
||||
+2
@@ -31,6 +31,8 @@ set(FILES
|
||||
Source/ImageBasedLights/EditorImageBasedLightComponent.cpp
|
||||
Source/Material/EditorMaterialComponent.cpp
|
||||
Source/Material/EditorMaterialComponent.h
|
||||
Source/Material/EditorMaterialComponentSerializer.cpp
|
||||
Source/Material/EditorMaterialComponentSerializer.h
|
||||
Source/Material/EditorMaterialComponentUtil.cpp
|
||||
Source/Material/EditorMaterialComponentUtil.h
|
||||
Source/Material/EditorMaterialComponentSlot.cpp
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace EMStudio
|
||||
|
||||
// Create and register a scene with all available feature processors
|
||||
AZ::RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("AnimViewport");
|
||||
m_scene = AZ::RPI::Scene::CreateScene(sceneDesc);
|
||||
m_scene->EnableAllFeatureProcessors();
|
||||
|
||||
@@ -227,8 +228,7 @@ namespace EMStudio
|
||||
AZ::TransformBus::Event(m_iblEntity->GetId(), &AZ::TransformBus::Events::SetLocalTM, iblTransform);
|
||||
|
||||
const AZ::Matrix4x4 rotationMatrix = AZ::Matrix4x4::CreateIdentity();
|
||||
AZ::RPI::ScenePtr scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
auto skyBoxFeatureProcessorInterface = scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
|
||||
auto skyBoxFeatureProcessorInterface = m_scene->GetFeatureProcessor<AZ::Render::SkyBoxFeatureProcessorInterface>();
|
||||
skyBoxFeatureProcessorInterface->SetCubemapRotationMatrix(rotationMatrix);
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ namespace AZ
|
||||
bool m_forceClearRenderData = false;
|
||||
bool m_initialized = false;
|
||||
bool m_isEnabled = true;
|
||||
bool m_usePPLLRenderTechnique = true;
|
||||
bool m_usePPLLRenderTechnique = false;
|
||||
static uint32_t s_instanceCount;
|
||||
|
||||
HairGlobalSettings m_hairGlobalSettings;
|
||||
|
||||
@@ -255,19 +255,22 @@ namespace Blast
|
||||
BlastFamilyComponentRequestBus::Broadcast(
|
||||
&BlastFamilyComponentRequests::FillDebugRenderBuffer, buffer, m_debugRenderMode);
|
||||
|
||||
// This is a system component, and thus is not associated with a specific scene, so use the default scene
|
||||
// This is a system component, and thus is not associated with a specific scene, so use the bootstrap scene
|
||||
// for the debug drawing
|
||||
const auto defaultScene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
auto drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(defaultScene);
|
||||
|
||||
for (DebugLine& line : buffer.m_lines)
|
||||
const auto mainScene = AZ::RPI::RPISystemInterface::Get()->GetSceneByName(AZ::Name("Main"));
|
||||
if (mainScene)
|
||||
{
|
||||
AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArguments;
|
||||
drawArguments.m_verts = &line.m_p0;
|
||||
drawArguments.m_vertCount = 2;
|
||||
drawArguments.m_colors = &line.m_color;
|
||||
drawArguments.m_colorCount = 1;
|
||||
drawQueue->DrawLines(drawArguments);
|
||||
auto drawQueue = AZ::RPI::AuxGeomFeatureProcessorInterface::GetDrawQueueForScene(mainScene);
|
||||
|
||||
for (DebugLine& line : buffer.m_lines)
|
||||
{
|
||||
AZ::RPI::AuxGeomDraw::AuxGeomDynamicDrawArguments drawArguments;
|
||||
drawArguments.m_verts = &line.m_p0;
|
||||
drawArguments.m_vertCount = 2;
|
||||
drawArguments.m_colors = &line.m_color;
|
||||
drawArguments.m_colorCount = 1;
|
||||
drawQueue->DrawLines(drawArguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,8 @@ namespace EMotionFX
|
||||
*jointTypeId, parentBindRotationWorld, nodeBindRotationWorld, boneDirection, exampleRotationsLocal);
|
||||
|
||||
AZ_Assert(jointLimitConfig, "Could not create joint limit configuration.");
|
||||
jointLimitConfig->SetPropertyVisibility(AzPhysics::JointConfiguration::PropertyVisibility::ParentLocalRotation, true);
|
||||
jointLimitConfig->SetPropertyVisibility(AzPhysics::JointConfiguration::PropertyVisibility::ChildLocalRotation, true);
|
||||
return jointLimitConfig;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,7 +576,7 @@ namespace EMotionFX
|
||||
|
||||
// The configuration stores some debug option. When that is enabled, we override it on top of the render flags.
|
||||
m_debugRenderFlags[RENDER_AABB] = m_debugRenderFlags[RENDER_AABB] || m_configuration.m_renderBounds;
|
||||
m_debugRenderFlags[RENDER_SKELETON] = m_debugRenderFlags[RENDER_SKELETON] || m_configuration.m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_LINESKELETON] = m_debugRenderFlags[RENDER_LINESKELETON] || m_configuration.m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
|
||||
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
|
||||
}
|
||||
|
||||
@@ -606,7 +606,7 @@ namespace EMotionFX
|
||||
m_renderActorInstance->UpdateBounds();
|
||||
|
||||
m_debugRenderFlags[RENDER_AABB] = m_renderBounds;
|
||||
m_debugRenderFlags[RENDER_SKELETON] = m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_LINESKELETON] = m_renderSkeleton;
|
||||
m_debugRenderFlags[RENDER_EMFX_DEBUG] = true;
|
||||
m_renderActorInstance->DebugDraw(m_debugRenderFlags);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ set(FILES
|
||||
Include/InAppPurchases/InAppPurchasesBus.h
|
||||
Include/InAppPurchases/InAppPurchasesInterface.h
|
||||
Include/InAppPurchases/InAppPurchasesResponseBus.h
|
||||
Source/InAppPurchasesSystemComponent.h
|
||||
Source/InAppPurchasesSystemComponent.cpp
|
||||
Source/InAppPurchasesInterface.cpp
|
||||
)
|
||||
|
||||
@@ -61,7 +61,6 @@
|
||||
|
||||
// Asset types
|
||||
#include <AzCore/Slice/SliceAsset.h>
|
||||
#include <AzCore/Script/ScriptAsset.h>
|
||||
#include <LmbrCentral/Rendering/MaterialAsset.h>
|
||||
#include <LmbrCentral/Rendering/MeshAsset.h>
|
||||
#include <LmbrCentral/Rendering/MaterialHandle.h>
|
||||
@@ -354,7 +353,6 @@ namespace LmbrCentral
|
||||
// Add asset types and extensions to AssetCatalog. Uses "AssetCatalogService".
|
||||
if (auto assetCatalog = AZ::Data::AssetCatalogRequestBus::FindFirstHandler(); assetCatalog)
|
||||
{
|
||||
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<AZ::ScriptAsset>::Uuid());
|
||||
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<MaterialAsset>::Uuid());
|
||||
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<DccMaterialAsset>::Uuid());
|
||||
assetCatalog->EnableCatalogForAsset(AZ::AzTypeInfo<MeshAsset>::Uuid());
|
||||
@@ -368,7 +366,6 @@ namespace LmbrCentral
|
||||
assetCatalog->AddExtension("xml");
|
||||
assetCatalog->AddExtension("mtl");
|
||||
assetCatalog->AddExtension("dccmtl");
|
||||
assetCatalog->AddExtension("lua");
|
||||
assetCatalog->AddExtension("sprite");
|
||||
assetCatalog->AddExtension("cax");
|
||||
}
|
||||
|
||||
@@ -52,8 +52,9 @@ ly_add_target(
|
||||
Gem::TextureAtlas
|
||||
)
|
||||
|
||||
# by default, load the above "Gem::LyShine" module in Client applications:
|
||||
# by default, load the above "Gem::LyShine" module in Client and Server applications:
|
||||
ly_create_alias(NAME LyShine.Clients NAMESPACE Gem TARGETS Gem::LyShine)
|
||||
ly_create_alias(NAME LyShine.Servers NAMESPACE Gem TARGETS Gem::LyShine)
|
||||
|
||||
if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
ly_add_target(
|
||||
|
||||
@@ -65,7 +65,7 @@ CDraw2d::~CDraw2d()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene)
|
||||
void CDraw2d::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene)
|
||||
{
|
||||
// At this point the RPI is ready for use
|
||||
|
||||
@@ -74,16 +74,16 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc
|
||||
AZ::Data::Instance<AZ::RPI::Shader> shader = AZ::RPI::LoadCriticalShader(shaderFilepath);
|
||||
|
||||
// Set scene to be associated with the dynamic draw context
|
||||
AZ::RPI::ScenePtr scene;
|
||||
AZ::RPI::Scene* scene = nullptr;
|
||||
if (m_viewportContext)
|
||||
{
|
||||
// Use scene associated with the specified viewport context
|
||||
scene = m_viewportContext->GetRenderScene();
|
||||
scene = m_viewportContext->GetRenderScene().get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No viewport context specified, use default scene
|
||||
scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
// No viewport context specified, use main scene
|
||||
scene = bootstrapScene;
|
||||
}
|
||||
AZ_Assert(scene != nullptr, "Attempting to create a DynamicDrawContext for a viewport context that has not been associated with a scene yet.");
|
||||
|
||||
@@ -113,7 +113,7 @@ void CDraw2d::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapSc
|
||||
else
|
||||
{
|
||||
// Render target support is disabled
|
||||
m_dynamicDraw->SetOutputScope(scene.get());
|
||||
m_dynamicDraw->SetOutputScope(scene);
|
||||
}
|
||||
m_dynamicDraw->EndInit();
|
||||
|
||||
|
||||
@@ -653,12 +653,12 @@ void CLyShine::OnRenderTick()
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void CLyShine::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene)
|
||||
void CLyShine::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene)
|
||||
{
|
||||
// Load cursor if its path was set before RPI was initialized
|
||||
LoadUiCursor();
|
||||
|
||||
LyShinePassDataRequestBus::Handler::BusConnect(AZ::RPI::RPISystemInterface::Get()->GetDefaultScene()->GetId());
|
||||
LyShinePassDataRequestBus::Handler::BusConnect(bootstrapScene->GetId());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -52,7 +52,7 @@ bool UiRenderer::IsReady()
|
||||
return m_isRPIReady;
|
||||
}
|
||||
|
||||
void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstrapScene)
|
||||
void UiRenderer::OnBootstrapSceneReady(AZ::RPI::Scene* bootstrapScene)
|
||||
{
|
||||
// At this point the RPI is ready for use
|
||||
|
||||
@@ -64,16 +64,17 @@ void UiRenderer::OnBootstrapSceneReady([[maybe_unused]] AZ::RPI::Scene* bootstra
|
||||
if (m_viewportContext)
|
||||
{
|
||||
// Create a new scene based on the user specified viewport context
|
||||
m_scene = CreateScene(m_viewportContext);
|
||||
m_ownedScene = CreateScene(m_viewportContext);
|
||||
m_scene = m_ownedScene.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// No viewport context specified, use default scene
|
||||
m_scene = AZ::RPI::RPISystemInterface::Get()->GetDefaultScene();
|
||||
m_scene = bootstrapScene;
|
||||
}
|
||||
|
||||
// Create a dynamic draw context for UI Canvas drawing for the scene
|
||||
m_dynamicDraw = CreateDynamicDrawContext(m_scene, uiShader);
|
||||
m_dynamicDraw = CreateDynamicDrawContext(uiShader);
|
||||
|
||||
if (m_dynamicDraw)
|
||||
{
|
||||
@@ -93,6 +94,7 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr<AZ::RPI::ViewportCon
|
||||
{
|
||||
// Create a scene with the necessary feature processors
|
||||
AZ::RPI::SceneDescriptor sceneDesc;
|
||||
sceneDesc.m_nameId = AZ::Name("UiRenderer");
|
||||
AZ::RPI::ScenePtr atomScene = AZ::RPI::Scene::CreateScene(sceneDesc);
|
||||
atomScene->EnableAllFeatureProcessors(); // LYSHINE_ATOM_TODO - have a UI pipeline and enable only needed fps
|
||||
|
||||
@@ -116,7 +118,6 @@ AZ::RPI::ScenePtr UiRenderer::CreateScene(AZStd::shared_ptr<AZ::RPI::ViewportCon
|
||||
}
|
||||
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> UiRenderer::CreateDynamicDrawContext(
|
||||
AZ::RPI::ScenePtr scene,
|
||||
AZ::Data::Instance<AZ::RPI::Shader> uiShader)
|
||||
{
|
||||
// Find the pass that renders the UI canvases after the rtt passes
|
||||
@@ -144,7 +145,7 @@ AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> UiRenderer::CreateDynamicDrawContext(
|
||||
else
|
||||
{
|
||||
// Render target support is disabled
|
||||
dynamicDraw->SetOutputScope(m_scene.get());
|
||||
dynamicDraw->SetOutputScope(m_scene);
|
||||
}
|
||||
dynamicDraw->EndInit();
|
||||
|
||||
|
||||
@@ -152,7 +152,6 @@ private: // member functions
|
||||
|
||||
//! Create a dynamic draw context for this renderer
|
||||
AZ::RHI::Ptr<AZ::RPI::DynamicDrawContext> CreateDynamicDrawContext(
|
||||
AZ::RPI::ScenePtr scene,
|
||||
AZ::Data::Instance<AZ::RPI::Shader> uiShader);
|
||||
|
||||
//! Bind the global white texture for all the texture units we use
|
||||
@@ -175,7 +174,8 @@ protected: // attributes
|
||||
// Set by user when viewport context is not the main/default viewport
|
||||
AZStd::shared_ptr<AZ::RPI::ViewportContext> m_viewportContext;
|
||||
|
||||
AZ::RPI::ScenePtr m_scene;
|
||||
AZ::RPI::ScenePtr m_ownedScene;
|
||||
AZ::RPI::Scene* m_scene = nullptr;
|
||||
|
||||
#ifndef _RELEASE
|
||||
int m_debugTextureDataRecordLevel = 0;
|
||||
|
||||
@@ -239,15 +239,16 @@ void UiCanvasAssetRefComponent::Activate()
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
void UiCanvasAssetRefComponent::Deactivate()
|
||||
{
|
||||
#if !defined(DEDICATED_SERVER)
|
||||
if (m_canvasEntityId.IsValid())
|
||||
if (!gEnv->IsDedicated())
|
||||
{
|
||||
gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId);
|
||||
m_canvasEntityId.SetInvalid();
|
||||
}
|
||||
if (m_canvasEntityId.IsValid())
|
||||
{
|
||||
gEnv->pLyShine->ReleaseCanvasDeferred(m_canvasEntityId);
|
||||
m_canvasEntityId.SetInvalid();
|
||||
}
|
||||
|
||||
UiCanvasAssetRefBus::Handler::BusDisconnect();
|
||||
UiCanvasRefBus::Handler::BusDisconnect();
|
||||
UiCanvasManagerNotificationBus::Handler::BusDisconnect();
|
||||
#endif
|
||||
UiCanvasAssetRefBus::Handler::BusDisconnect();
|
||||
UiCanvasRefBus::Handler::BusDisconnect();
|
||||
UiCanvasManagerNotificationBus::Handler::BusDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,6 +149,8 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
|
||||
AZ::AzFramework
|
||||
AZ::AzNetworking
|
||||
AZ::AzToolsFramework
|
||||
Gem::Atom_RPI.Public
|
||||
Gem::Atom_RHI.Reflect
|
||||
Gem::Multiplayer.Static
|
||||
Gem::Multiplayer.Builders
|
||||
)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/ComponentBus.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
/**
|
||||
* This bus can be used to send commands to the editor.
|
||||
*/
|
||||
class MultiplayerEditorLayerPythonRequests
|
||||
: public AZ::ComponentBus
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* Enters the editor game mode and launches/connects to the server launcher.
|
||||
*/
|
||||
virtual void EnterGameMode() = 0;
|
||||
|
||||
/*
|
||||
* Queries if the Editor is in game mode, the editor-server has finished connecting, and the default network player has spawned.
|
||||
*/
|
||||
virtual bool IsInGameMode() = 0;
|
||||
};
|
||||
using MultiplayerEditorLayerPythonRequestBus = AZ::EBus<MultiplayerEditorLayerPythonRequests>;
|
||||
}
|
||||
@@ -45,6 +45,7 @@ namespace Multiplayer
|
||||
void NetworkHierarchyChildComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyChildComponent"));
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyChildComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace Multiplayer
|
||||
void NetworkHierarchyRootComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("NetworkHierarchyRootComponent"));
|
||||
provided.push_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Multiplayer
|
||||
using namespace AzNetworking;
|
||||
|
||||
AZ_CVAR(bool, editorsv_isDedicated, false, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether to init as a server expecting data from an Editor. Do not modify unless you're sure of what you're doing.");
|
||||
AZ_CVAR(uint16_t, editorsv_port, DefaultServerEditorPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic.");
|
||||
|
||||
MultiplayerEditorConnection::MultiplayerEditorConnection()
|
||||
: m_byteStream(&m_buffer)
|
||||
@@ -33,33 +34,37 @@ namespace Multiplayer
|
||||
m_networkEditorInterface = AZ::Interface<INetworking>::Get()->CreateNetworkInterface(
|
||||
AZ::Name(MpEditorInterfaceName), ProtocolType::Tcp, TrustZone::ExternalClientToServer, *this);
|
||||
m_networkEditorInterface->SetTimeoutMs(AZ::TimeMs{ 0 }); // Disable timeouts on this network interface
|
||||
if (editorsv_isDedicated)
|
||||
ActivateDedicatedEditorServer();
|
||||
}
|
||||
|
||||
void MultiplayerEditorConnection::ActivateDedicatedEditorServer() const
|
||||
{
|
||||
if (m_isActivated || !editorsv_isDedicated)
|
||||
{
|
||||
uint16_t editorsv_port = DefaultServerEditorPort;
|
||||
const auto console = AZ::Interface<AZ::IConsole>::Get();
|
||||
if (console->GetCvarValue("editorsv_port", editorsv_port) != AZ::GetValueResult::Success)
|
||||
{
|
||||
AZ_Assert( false,
|
||||
"MultiplayerEditorConnection failed! Could not find the editorsv_port cvar; we may not be able to connect to the editor's port! Please update this code to use a valid cvar!")
|
||||
}
|
||||
return;
|
||||
}
|
||||
m_isActivated = true;
|
||||
|
||||
AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening.")
|
||||
|
||||
AZ_Assert(m_networkEditorInterface, "MP Editor Network Interface was unregistered before Editor Server could start listening.")
|
||||
// Check if there's already an Editor out there waiting to connect
|
||||
const ConnectionId editorServerToEditorConnectionId =
|
||||
m_networkEditorInterface->Connect(IpAddress(LocalHost.data(), editorsv_port, ProtocolType::Tcp));
|
||||
|
||||
// Check if there's already an Editor out there waiting to connect
|
||||
const ConnectionId editorServerToEditorConnectionId = m_networkEditorInterface->Connect(IpAddress(LocalHost.data(), editorsv_port, ProtocolType::Tcp));
|
||||
|
||||
// If there wasn't an Editor waiting for this server to start, then assume this is an editor-server launched by hand... listen and wait for the editor to request a connection
|
||||
if (editorServerToEditorConnectionId == InvalidConnectionId)
|
||||
{
|
||||
m_networkEditorInterface->Listen(editorsv_port);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_networkEditorInterface->SendReliablePacket(editorServerToEditorConnectionId, MultiplayerEditorPackets::EditorServerReadyForLevelData());
|
||||
}
|
||||
// If there wasn't an Editor waiting for this server to start, then assume this is an editor-server launched by hand... listen
|
||||
// and wait for the editor to request a connection
|
||||
if (editorServerToEditorConnectionId == InvalidConnectionId)
|
||||
{
|
||||
m_networkEditorInterface->Listen(editorsv_port);
|
||||
AZ_Printf("MultiplayerEditorConnection", "Editor-server activation did not find an editor in game-mode willing to connect; we'll instead wait and listen for an editor trying to connect to us.")
|
||||
}
|
||||
else
|
||||
{
|
||||
m_networkEditorInterface->SendReliablePacket(editorServerToEditorConnectionId, MultiplayerEditorPackets::EditorServerReadyForLevelData());
|
||||
AZ_Printf("MultiplayerEditorConnection", "Editor-server activation has found and connected to the editor.")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool MultiplayerEditorConnection::HandleRequest
|
||||
(
|
||||
[[maybe_unused]] AzNetworking::IConnection* connection,
|
||||
@@ -136,7 +141,7 @@ namespace Multiplayer
|
||||
|
||||
networkInterface->Listen(sv_port);
|
||||
|
||||
AZLOG_INFO("Editor Server completed asset receive, responding to Editor...");
|
||||
AZLOG_INFO("Editor Server completed receiving the editor's level assets, responding to Editor...");
|
||||
return connection->SendReliablePacket(MultiplayerEditorPackets::EditorServerReady());
|
||||
}
|
||||
|
||||
@@ -180,8 +185,14 @@ namespace Multiplayer
|
||||
}
|
||||
|
||||
// Connect the Editor to the editor server for Multiplayer simulation
|
||||
AZ::Interface<IMultiplayer>::Get()->Connect(editorsv_serveraddr.c_str(), sv_port);
|
||||
|
||||
if (AZ::Interface<IMultiplayer>::Get()->Connect(editorsv_serveraddr.c_str(), sv_port))
|
||||
{
|
||||
AZ_Printf("MultiplayerEditorConnection", "Editor-server ready. Editor has successfully connected to the editor-server's network simulation.")
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Warning("MultiplayerEditorConnection", false, "MultiplayerEditorConnection::HandleRequest for EditorServerReady failed! Connecting to the editor-server's network simulation failed.")
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -45,9 +45,11 @@ namespace Multiplayer
|
||||
//! @}
|
||||
|
||||
private:
|
||||
void ActivateDedicatedEditorServer() const;
|
||||
|
||||
AzNetworking::INetworkInterface* m_networkEditorInterface = nullptr;
|
||||
AZStd::vector<uint8_t> m_buffer;
|
||||
AZ::IO::ByteContainerStream<AZStd::vector<uint8_t>> m_byteStream;
|
||||
mutable bool m_isActivated = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace Multiplayer
|
||||
m_descriptors.end(),
|
||||
{
|
||||
MultiplayerEditorSystemComponent::CreateDescriptor(),
|
||||
PythonEditorFuncs::CreateDescriptor()
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <Multiplayer/MultiplayerConstants.h>
|
||||
|
||||
#include <MultiplayerSystemComponent.h>
|
||||
#include <PythonEditorEventsBus.h>
|
||||
#include <Editor/MultiplayerEditorSystemComponent.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
|
||||
@@ -23,6 +24,7 @@
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzNetworking/Framework/INetworking.h>
|
||||
#include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
|
||||
#include <Atom/RPI.Public/RPISystemInterface.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -35,8 +37,47 @@ namespace Multiplayer
|
||||
AZ_CVAR(AZ::CVarFixedString, editorsv_process, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"The server executable that should be run. Empty to use the current project's ServerLauncher");
|
||||
AZ_CVAR(AZ::CVarFixedString, editorsv_serveraddr, AZ::CVarFixedString(LocalHost), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The address of the server to connect to");
|
||||
AZ_CVAR(uint16_t, editorsv_port, DefaultServerEditorPort, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The port that the multiplayer editor gem will bind to for traffic");
|
||||
AZ_CVAR(AZ::CVarFixedString, editorsv_rhi_override, "", nullptr, AZ::ConsoleFunctorFlags::DontReplicate,
|
||||
"Override the default rendering hardware interface (rhi) when launching the Editor server. For example, you may be running an Editor using 'dx12', but want to launch a headless server using 'null'. If empty the server will launch using the same rhi as the Editor.");
|
||||
AZ_CVAR_EXTERNED(uint16_t, editorsv_port);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
void PyEnterGameMode()
|
||||
{
|
||||
editorsv_enabled = true;
|
||||
editorsv_launch = true;
|
||||
AzToolsFramework::EditorLayerPythonRequestBus::Broadcast(&AzToolsFramework::EditorLayerPythonRequestBus::Events::EnterGameMode);
|
||||
}
|
||||
|
||||
bool PyIsInGameMode()
|
||||
{
|
||||
// If the network entity manager is tracking at least 1 entity then the editor has connected and the autonomous player exists and is being replicated.
|
||||
if (const INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get())
|
||||
{
|
||||
return networkEntityManager->GetEntityCount() > 0;
|
||||
}
|
||||
|
||||
AZ_Warning("MultiplayerEditorSystemComponent", false, "PyIsInGameMode returning false; NetworkEntityManager has not been created yet.")
|
||||
return false;
|
||||
}
|
||||
|
||||
void PythonEditorFuncs::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
// This will create static python methods in the 'azlmbr.multiplayer' module
|
||||
// Note: The methods will be prefixed with the class name, PythonEditorFuncs
|
||||
// Example Hydra Python: azlmbr.multiplayer.PythonEditorFuncs_enter_game_mode()
|
||||
behaviorContext->Class<PythonEditorFuncs>()
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "multiplayer")
|
||||
->Method("enter_game_mode", PyEnterGameMode, nullptr, "Enters the editor game mode and launches/connects to the server launcher.")
|
||||
->Method("is_in_game_mode", PyIsInGameMode, nullptr, "Queries if it's in the game mode and the server has finished connecting and the default network player has spawned.")
|
||||
;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
@@ -44,6 +85,18 @@ namespace Multiplayer
|
||||
serializeContext->Class<MultiplayerEditorSystemComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
|
||||
// Reflect Python Editor Functions
|
||||
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
|
||||
{
|
||||
// This will add the MultiplayerPythonEditorBus into the 'azlmbr.multiplayer' module
|
||||
behaviorContext->EBus<MultiplayerEditorLayerPythonRequestBus>("MultiplayerPythonEditorBus")
|
||||
->Attribute(AZ::Script::Attributes::Scope, AZ::Script::Attributes::ScopeFlags::Automation)
|
||||
->Attribute(AZ::Script::Attributes::Module, "multiplayer")
|
||||
->Event("EnterGameMode", &MultiplayerEditorLayerPythonRequestBus::Events::EnterGameMode)
|
||||
->Event("IsInGameMode", &MultiplayerEditorLayerPythonRequestBus::Events::IsInGameMode)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
||||
@@ -147,11 +200,21 @@ namespace Multiplayer
|
||||
|
||||
// Start the configured server if it's available
|
||||
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
|
||||
|
||||
// Open the server launcher using the same rhi as the editor (or launch with the override rhi)
|
||||
AZ::Name server_rhi = AZ::RPI::RPISystemInterface::Get()->GetRenderApiName();
|
||||
if (!static_cast<AZ::CVarFixedString>(editorsv_rhi_override).empty())
|
||||
{
|
||||
server_rhi = static_cast<AZ::CVarFixedString>(editorsv_rhi_override);
|
||||
}
|
||||
|
||||
processLaunchInfo.m_commandlineParameters = AZStd::string::format(
|
||||
R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s")",
|
||||
R"("%s" --project-path "%s" --editorsv_isDedicated true --sv_defaultPlayerSpawnAsset "%s" --rhi "%s")",
|
||||
serverPath.c_str(),
|
||||
AZ::Utils::GetProjectPath().c_str(),
|
||||
static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str());
|
||||
static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str(),
|
||||
server_rhi.GetCStr()
|
||||
);
|
||||
processLaunchInfo.m_showWindow = true;
|
||||
processLaunchInfo.m_processPriority = AzFramework::ProcessPriority::PROCESSPRIORITY_NORMAL;
|
||||
|
||||
@@ -159,6 +222,10 @@ namespace Multiplayer
|
||||
AzFramework::ProcessWatcher* outProcess = AzFramework::ProcessWatcher::LaunchProcess(
|
||||
processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE);
|
||||
|
||||
AZ_Error(
|
||||
"MultiplayerEditor", processLaunchInfo.m_launchResult != AzFramework::ProcessLauncher::ProcessLaunchResult::PLR_MissingFile,
|
||||
"LaunchEditorServer failed! The ServerLauncher binary is missing! (%s) Please build server launcher.", serverPath.c_str())
|
||||
|
||||
return outProcess;
|
||||
}
|
||||
|
||||
@@ -231,7 +298,7 @@ namespace Multiplayer
|
||||
"Editor multiplayer game-mode failed! Could not connect to an editor-server. editorsv_launch is false so we're assuming you're running your own editor-server at editorsv_serveraddr(%s) on editorsv_port(%i). "
|
||||
"Either set editorsv_launch=true so the editor launches an editor-server for you, or launch your own editor-server by hand before entering game-mode. Remember editor-servers must use editorsv_isDedicated=true.",
|
||||
remoteAddress.c_str(),
|
||||
static_cast < uint16_t>(editorsv_port))
|
||||
static_cast<uint16_t>(editorsv_port))
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -261,6 +328,8 @@ namespace Multiplayer
|
||||
return;
|
||||
}
|
||||
|
||||
AZ_Printf("MultiplayerEditor", "Editor is sending the editor-server the level data packet.")
|
||||
|
||||
const AZStd::vector<AZ::Data::Asset<AZ::Data::AssetData>>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData();
|
||||
|
||||
AZStd::vector<uint8_t> buffer;
|
||||
@@ -311,4 +380,13 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
void MultiplayerEditorSystemComponent::EnterGameMode()
|
||||
{
|
||||
PyEnterGameMode();
|
||||
}
|
||||
|
||||
bool MultiplayerEditorSystemComponent::IsInGameMode()
|
||||
{
|
||||
return PyIsInGameMode();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/MultiplayerEditorServerBus.h>
|
||||
#include <Multiplayer/Editor/MultiplayerPythonEditorEventsBus.h>
|
||||
#include <IEditor.h>
|
||||
|
||||
#include <Editor/MultiplayerEditorConnection.h>
|
||||
@@ -29,9 +30,24 @@ namespace AzNetworking
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! A component to reflect scriptable commands for the Editor
|
||||
class PythonEditorFuncs : public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(PythonEditorFuncs, "{22AEEA59-94E6-4033-B67D-7C8FBB84DF0D}")
|
||||
|
||||
SANDBOX_API static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
// AZ::Component ...
|
||||
void Activate() override {}
|
||||
void Deactivate() override {}
|
||||
};
|
||||
|
||||
|
||||
//! Multiplayer system component wraps the bridging logic between the game and transport layer.
|
||||
class MultiplayerEditorSystemComponent final
|
||||
: public AZ::Component
|
||||
, public MultiplayerEditorLayerPythonRequestBus::Handler
|
||||
, private AzFramework::GameEntityContextEventBus::Handler
|
||||
, private AzToolsFramework::EditorEvents::Bus::Handler
|
||||
, private IEditorNotifyListener
|
||||
@@ -62,6 +78,12 @@ namespace Multiplayer
|
||||
void NotifyRegisterViews() override;
|
||||
//! @}
|
||||
|
||||
//! MultiplayerEditorLayerPythonRequestBus::Handler overrides.
|
||||
//! @{
|
||||
void EnterGameMode() override;
|
||||
bool IsInGameMode() override;
|
||||
//! @}
|
||||
|
||||
private:
|
||||
//! EditorEvents::Handler overrides
|
||||
//! @{
|
||||
|
||||
@@ -719,11 +719,6 @@ namespace Multiplayer
|
||||
|
||||
void MultiplayerSystemComponent::OnConnect(AzNetworking::IConnection* connection)
|
||||
{
|
||||
MultiplayerAgentDatum datum;
|
||||
datum.m_id = connection->GetConnectionId();
|
||||
datum.m_isInvited = false;
|
||||
datum.m_agentType = MultiplayerAgentType::Client;
|
||||
|
||||
AZStd::string providerTicket;
|
||||
if (connection->GetConnectionRole() == ConnectionRole::Connector)
|
||||
{
|
||||
@@ -737,7 +732,12 @@ namespace Multiplayer
|
||||
}
|
||||
else
|
||||
{
|
||||
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str());
|
||||
AZLOG_INFO("New incoming connection from remote address: %s", connection->GetRemoteAddress().GetString().c_str())
|
||||
|
||||
MultiplayerAgentDatum datum;
|
||||
datum.m_id = connection->GetConnectionId();
|
||||
datum.m_isInvited = false;
|
||||
datum.m_agentType = MultiplayerAgentType::Client;
|
||||
m_connectionAcquiredEvent.Signal(datum);
|
||||
}
|
||||
|
||||
@@ -771,15 +771,14 @@ namespace Multiplayer
|
||||
AZLOG_INFO("%s from remote address %s due to %s", endpointString, connection->GetRemoteAddress().GetString().c_str(), reasonString.c_str());
|
||||
|
||||
// The client is disconnecting
|
||||
if (GetAgentType() == MultiplayerAgentType::Client)
|
||||
if (m_agentType == MultiplayerAgentType::Client)
|
||||
{
|
||||
AZ_Assert(connection->GetConnectionRole() == ConnectionRole::Connector, "Client connection role should only ever be Connector");
|
||||
m_clientDisconnectedEvent.Signal();
|
||||
}
|
||||
|
||||
// Signal to session management that a user has left the server
|
||||
if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer)
|
||||
else if (m_agentType == MultiplayerAgentType::DedicatedServer || m_agentType == MultiplayerAgentType::ClientServer)
|
||||
{
|
||||
// Signal to session management that a user has left the server
|
||||
if (AZ::Interface<AzFramework::ISessionHandlingProviderRequests>::Get() != nullptr &&
|
||||
connection->GetConnectionRole() == ConnectionRole::Acceptor)
|
||||
{
|
||||
@@ -1130,7 +1129,10 @@ namespace Multiplayer
|
||||
return m_networkEntityManager.GetNetworkEntityTracker()->Get(node->second);
|
||||
}
|
||||
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()));
|
||||
// make sure the player prefab path is lowercase (how it's stored in the cache folder)
|
||||
auto sv_defaultPlayerSpawnAssetLowerCase = static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset);
|
||||
AZStd::to_lower(sv_defaultPlayerSpawnAssetLowerCase.begin(), sv_defaultPlayerSpawnAssetLowerCase.end());
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAssetLowerCase).c_str()));
|
||||
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
|
||||
|
||||
for (NetworkEntityHandle subEntity : entityList)
|
||||
|
||||
@@ -13,4 +13,5 @@ set(FILES
|
||||
Source/Editor/MultiplayerEditorGem.h
|
||||
Source/Editor/MultiplayerEditorSystemComponent.cpp
|
||||
Source/Editor/MultiplayerEditorSystemComponent.h
|
||||
Include/Multiplayer/Editor/MultiplayerPythonEditorEventsBus.h
|
||||
)
|
||||
|
||||
@@ -11,12 +11,13 @@ add_subdirectory(NumericalMethods)
|
||||
ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME})
|
||||
|
||||
include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) # for PAL_TRAIT_PHYSX_SUPPORTED
|
||||
set(PHYSX_ENABLE_RUNNING_BENCHMARKS OFF CACHE BOOL "Adds a target to allow running of the physx benchmarks.")
|
||||
set(LY_PHYSX_ENABLE_RUNNING_BENCHMARKS OFF CACHE BOOL "Adds a target to allow running of the physx benchmarks.")
|
||||
|
||||
if(PAL_TRAIT_PHYSX_SUPPORTED)
|
||||
set(physx_dependency 3rdParty::PhysX)
|
||||
set(physx_files physx_files.cmake)
|
||||
set(physx_shared_files physx_shared_files.cmake)
|
||||
set(physx_mock_files physx_mocks_files.cmake)
|
||||
set(physx_editor_files physx_editor_files.cmake)
|
||||
else()
|
||||
set(physx_files physx_unsupported_files.cmake)
|
||||
@@ -151,6 +152,17 @@ endif()
|
||||
# Tests
|
||||
################################################################################
|
||||
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
ly_add_target(
|
||||
NAME PhysX.Mocks HEADERONLY
|
||||
NAMESPACE Gem
|
||||
OUTPUT_NAME PhysX.Mocks.Gem
|
||||
FILES_CMAKE
|
||||
physx_mocks_files.cmake
|
||||
INCLUDE_DIRECTORIES
|
||||
INTERFACE
|
||||
Mocks
|
||||
)
|
||||
|
||||
ly_add_target(
|
||||
NAME PhysX.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
|
||||
NAMESPACE Gem
|
||||
@@ -185,7 +197,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
# Only add the physx benchmarks if this flag is set. The benchmark code is still built, as it is part of the PhysX.Tests project.
|
||||
# Currently jenkins has a 1500sec(25min) timeout, our benchmarks can sometimes take over 1500sec and cause a build failure for timeout.
|
||||
# Jenkins currently doesn't upload the results of the benchmarks, so this is ok.
|
||||
if(PHYSX_ENABLE_RUNNING_BENCHMARKS)
|
||||
if(LY_PHYSX_ENABLE_RUNNING_BENCHMARKS)
|
||||
ly_add_googlebenchmark(
|
||||
NAME Gem::PhysX.Benchmarks
|
||||
TARGET Gem::PhysX.Tests
|
||||
@@ -213,6 +225,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
AZ::AzTest
|
||||
AZ::AzToolsFrameworkTestCommon
|
||||
Gem::PhysX.Static
|
||||
Gem::PhysX.Mocks
|
||||
Gem::PhysX.Editor.Static
|
||||
RUNTIME_DEPENDENCIES
|
||||
Gem::LmbrCentral.Editor
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
#include <AzFramework/Physics/HeightfieldProviderBus.h>
|
||||
|
||||
namespace UnitTest
|
||||
{
|
||||
class MockPhysXHeightfieldProviderComponent
|
||||
: public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(MockPhysXHeightfieldProviderComponent, "{C5F7CCCF-FDB2-40DF-992D-CF028F4A1B59}");
|
||||
|
||||
static void Reflect([[maybe_unused]] AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializeContext->Class<MockPhysXHeightfieldProviderComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void Activate() override
|
||||
{
|
||||
}
|
||||
|
||||
void Deactivate() override
|
||||
{
|
||||
}
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.push_back(AZ_CRC_CE("PhysicsHeightfieldProviderService"));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class MockPhysXHeightfieldProvider
|
||||
: protected Physics::HeightfieldProviderRequestsBus::Handler
|
||||
{
|
||||
public:
|
||||
MockPhysXHeightfieldProvider(AZ::EntityId entityId)
|
||||
{
|
||||
Physics::HeightfieldProviderRequestsBus::Handler::BusConnect(entityId);
|
||||
}
|
||||
|
||||
~MockPhysXHeightfieldProvider()
|
||||
{
|
||||
Physics::HeightfieldProviderRequestsBus::Handler::BusDisconnect();
|
||||
}
|
||||
|
||||
MOCK_CONST_METHOD0(GetHeightsAndMaterials, AZStd::vector<Physics::HeightMaterialPoint>());
|
||||
MOCK_CONST_METHOD0(GetHeightfieldGridSpacing, AZ::Vector2());
|
||||
MOCK_CONST_METHOD2(GetHeightfieldGridSize, void(int32_t&, int32_t&));
|
||||
MOCK_CONST_METHOD2(GetHeightfieldHeightBounds, void(float&, float&));
|
||||
MOCK_CONST_METHOD0(GetHeightfieldTransform, AZ::Transform());
|
||||
MOCK_CONST_METHOD0(GetMaterialList, AZStd::vector<Physics::MaterialId>());
|
||||
MOCK_CONST_METHOD0(GetHeights, AZStd::vector<float>());
|
||||
MOCK_CONST_METHOD1(UpdateHeights, AZStd::vector<float>(const AZ::Aabb& dirtyRegion));
|
||||
MOCK_CONST_METHOD1(UpdateHeightsAndMaterials, AZStd::vector<Physics::HeightMaterialPoint>(const AZ::Aabb& dirtyRegion));
|
||||
MOCK_CONST_METHOD0(GetHeightfieldAabb, AZ::Aabb());
|
||||
};
|
||||
|
||||
} // namespace UnitTest
|
||||
@@ -1175,7 +1175,10 @@ namespace PhysX
|
||||
using physx::PxGeometryType;
|
||||
|
||||
bool isProfilingActive = false;
|
||||
AZ::Debug::ProfilerRequestBus::BroadcastResult(isProfilingActive, &AZ::Debug::ProfilerRequests::IsActive);
|
||||
if (auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get(); profilerSystem)
|
||||
{
|
||||
isProfilingActive = profilerSystem->IsActive();
|
||||
}
|
||||
|
||||
if (!isProfilingActive)
|
||||
{
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace PhysX
|
||||
const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits<int16_t>::max() / halfBounds;
|
||||
const float heightScale{ 1.0f / scaleFactor };
|
||||
|
||||
[[maybe_unused]] const uint8_t physxMaximumMaterialIndex = 0x7f;
|
||||
[[maybe_unused]] constexpr uint8_t physxMaximumMaterialIndex = 0x7f;
|
||||
|
||||
// Delete the cached heightfield object if it is there, and create a new one and save in the shape configuration
|
||||
heightfieldConfig.SetCachedNativeHeightfield(nullptr);
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
|
||||
#include <AzToolsFramework/ToolsComponents/EditorNonUniformScaleComponent.h>
|
||||
#include <Tests/EditorTestUtilities.h>
|
||||
#include <EditorHeightfieldColliderComponent.h>
|
||||
#include <HeightfieldColliderComponent.h>
|
||||
#include <LmbrCentral/Shape/BoxShapeComponentBus.h>
|
||||
#include <AzFramework/Physics/HeightfieldProviderBus.h>
|
||||
#include <StaticRigidBodyComponent.h>
|
||||
#include <RigidBodyStatic.h>
|
||||
#include <PhysX/PhysXLocks.h>
|
||||
#include <AzFramework/Physics/Components/SimulatedBodyComponentBus.h>
|
||||
#include <PhysX/MockPhysXHeightfieldProviderComponent.h>
|
||||
#include <AzCore/Casting/lossy_cast.h>
|
||||
|
||||
using ::testing::NiceMock;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace PhysXEditorTests
|
||||
{
|
||||
AZStd::vector<Physics::HeightMaterialPoint> GetSamples()
|
||||
{
|
||||
AZStd::vector<Physics::HeightMaterialPoint> samples{ { 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 2.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 1.5f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 1.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 0.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight },
|
||||
{ 3.0f, Physics::QuadMeshType::SubdivideUpperLeftToBottomRight } };
|
||||
return samples;
|
||||
}
|
||||
|
||||
EntityPtr SetupHeightfieldComponent()
|
||||
{
|
||||
// create an editor entity with a shape collider component and a box shape component
|
||||
EntityPtr editorEntity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
editorEntity->CreateComponent<UnitTest::MockPhysXHeightfieldProviderComponent>();
|
||||
editorEntity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId);
|
||||
editorEntity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
AZ::ComponentApplicationBus::Broadcast(
|
||||
&AZ::ComponentApplicationRequests::RegisterComponentDescriptor,
|
||||
UnitTest::MockPhysXHeightfieldProviderComponent::CreateDescriptor());
|
||||
return editorEntity;
|
||||
}
|
||||
|
||||
void CleanupHeightfieldComponent()
|
||||
{
|
||||
AZ::ComponentApplicationBus::Broadcast(
|
||||
&AZ::ComponentApplicationRequests::UnregisterComponentDescriptor,
|
||||
UnitTest::MockPhysXHeightfieldProviderComponent::CreateDescriptor());
|
||||
}
|
||||
|
||||
void SetupMockMethods(NiceMock<UnitTest::MockPhysXHeightfieldProvider>& mockShapeRequests)
|
||||
{
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldTransform).WillByDefault(Return(AZ::Transform::CreateTranslation({ 1, 2, 0 })));
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldGridSpacing).WillByDefault(Return(AZ::Vector2(1, 1)));
|
||||
ON_CALL(mockShapeRequests, GetHeightsAndMaterials).WillByDefault(Return(GetSamples()));
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldGridSize)
|
||||
.WillByDefault(
|
||||
[](int32_t& numColumns, int32_t& numRows)
|
||||
{
|
||||
numColumns = 3;
|
||||
numRows = 3;
|
||||
});
|
||||
ON_CALL(mockShapeRequests, GetHeightfieldHeightBounds)
|
||||
.WillByDefault(
|
||||
[](float& x, float& y)
|
||||
{
|
||||
x = -3.0f;
|
||||
y = 3.0f;
|
||||
});
|
||||
}
|
||||
|
||||
EntityPtr TestCreateActiveGameEntityFromEditorEntity(AZ::Entity* editorEntity)
|
||||
{
|
||||
EntityPtr gameEntity = AZStd::make_unique<AZ::Entity>();
|
||||
AzToolsFramework::ToolsApplicationRequestBus::Broadcast(
|
||||
&AzToolsFramework::ToolsApplicationRequests::PreExportEntity, *editorEntity, *gameEntity);
|
||||
gameEntity->Init();
|
||||
return gameEntity;
|
||||
}
|
||||
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesSatisfiedEntityIsValid)
|
||||
{
|
||||
EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
entity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId);
|
||||
entity->CreateComponent<UnitTest::MockPhysXHeightfieldProviderComponent>()->CreateDescriptor();
|
||||
|
||||
// the entity should be in a valid state because the shape component and
|
||||
// the Terrain Physics Collider Component requirement is satisfied.
|
||||
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
||||
EXPECT_TRUE(sortOutcome.IsSuccess());
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentDependenciesMissingEntityIsInvalid)
|
||||
{
|
||||
EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
|
||||
// the entity should not be in a valid state because the heightfield collider component requires
|
||||
// a shape component and the Terrain Physics Collider Component
|
||||
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
||||
EXPECT_FALSE(sortOutcome.IsSuccess());
|
||||
EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::MissingRequiredService);
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentMultipleHeightfieldColliderComponentsEntityIsInvalid)
|
||||
{
|
||||
EntityPtr entity = CreateInactiveEditorEntity("HeightfieldColliderComponentEditorEntity");
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
entity->CreateComponent(LmbrCentral::EditorAxisAlignedBoxShapeComponentTypeId);
|
||||
|
||||
// adding a second heightfield collider component should make the entity invalid
|
||||
entity->CreateComponent<PhysX::EditorHeightfieldColliderComponent>();
|
||||
|
||||
AZ::Entity::DependencySortOutcome sortOutcome = entity->EvaluateDependenciesGetDetails();
|
||||
EXPECT_FALSE(sortOutcome.IsSuccess());
|
||||
EXPECT_TRUE(sortOutcome.GetError().m_code == AZ::Entity::DependencySortResult::HasIncompatibleServices);
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithCorrectComponentsCorrectRuntimeComponents)
|
||||
{
|
||||
EntityPtr editorEntity = SetupHeightfieldComponent();
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests(editorEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests);
|
||||
editorEntity->Activate();
|
||||
|
||||
EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests2(gameEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests2);
|
||||
gameEntity->Activate();
|
||||
|
||||
// check that the runtime entity has the expected components
|
||||
EXPECT_TRUE(gameEntity->FindComponent<UnitTest::MockPhysXHeightfieldProviderComponent>() != nullptr);
|
||||
EXPECT_TRUE(gameEntity->FindComponent<PhysX::HeightfieldColliderComponent>() != nullptr);
|
||||
EXPECT_TRUE(gameEntity->FindComponent(LmbrCentral::AxisAlignedBoxShapeComponentTypeId) != nullptr);
|
||||
|
||||
CleanupHeightfieldComponent();
|
||||
}
|
||||
|
||||
TEST_F(PhysXEditorFixture, EditorHeightfieldColliderComponentHeightfieldColliderWithAABoxCorrectRuntimeGeometry)
|
||||
{
|
||||
EntityPtr editorEntity = SetupHeightfieldComponent();
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests(editorEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests);
|
||||
editorEntity->Activate();
|
||||
|
||||
EntityPtr gameEntity = TestCreateActiveGameEntityFromEditorEntity(editorEntity.get());
|
||||
NiceMock<UnitTest::MockPhysXHeightfieldProvider> mockShapeRequests2(gameEntity->GetId());
|
||||
SetupMockMethods(mockShapeRequests2);
|
||||
gameEntity->Activate();
|
||||
|
||||
AzPhysics::SimulatedBody* staticBody = nullptr;
|
||||
AzPhysics::SimulatedBodyComponentRequestsBus::EventResult(
|
||||
staticBody, gameEntity->GetId(), &AzPhysics::SimulatedBodyComponentRequests::GetSimulatedBody);
|
||||
const auto* pxRigidStatic = static_cast<const physx::PxRigidStatic*>(staticBody->GetNativePointer());
|
||||
|
||||
PHYSX_SCENE_READ_LOCK(pxRigidStatic->getScene());
|
||||
|
||||
// there should be a single shape on the rigid body and it should be a heightfield
|
||||
EXPECT_EQ(pxRigidStatic->getNbShapes(), 1);
|
||||
|
||||
physx::PxShape* shape = nullptr;
|
||||
pxRigidStatic->getShapes(&shape, 1, 0);
|
||||
EXPECT_EQ(shape->getGeometryType(), physx::PxGeometryType::eHEIGHTFIELD);
|
||||
|
||||
physx::PxHeightFieldGeometry heightfieldGeometry;
|
||||
shape->getHeightFieldGeometry(heightfieldGeometry);
|
||||
|
||||
physx::PxHeightField* heightfield = heightfieldGeometry.heightField;
|
||||
|
||||
int32_t numRows{ 0 };
|
||||
int32_t numColumns{ 0 };
|
||||
Physics::HeightfieldProviderRequestsBus::Event(
|
||||
gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldGridSize, numColumns, numRows);
|
||||
EXPECT_EQ(numColumns, heightfield->getNbColumns());
|
||||
EXPECT_EQ(numRows, heightfield->getNbRows());
|
||||
|
||||
for (int sampleRow = 0; sampleRow < numRows; ++sampleRow)
|
||||
{
|
||||
for (int sampleColumn = 0; sampleColumn < numColumns; ++sampleColumn)
|
||||
{
|
||||
float minHeightBounds{ 0.0f };
|
||||
float maxHeightBounds{ 0.0f };
|
||||
Physics::HeightfieldProviderRequestsBus::Event(
|
||||
gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightfieldHeightBounds, minHeightBounds,
|
||||
maxHeightBounds);
|
||||
|
||||
AZStd::vector<Physics::HeightMaterialPoint> samples;
|
||||
Physics::HeightfieldProviderRequestsBus::EventResult(
|
||||
samples, gameEntity->GetId(), &Physics::HeightfieldProviderRequestsBus::Events::GetHeightsAndMaterials);
|
||||
const float halfBounds{ (maxHeightBounds - minHeightBounds) / 2.0f };
|
||||
const float scaleFactor = (maxHeightBounds <= minHeightBounds) ? 1.0f : AZStd::numeric_limits<int16_t>::max() / halfBounds;
|
||||
|
||||
physx::PxHeightFieldSample samplePhysX = heightfield->getSample(sampleRow, sampleColumn);
|
||||
Physics::HeightMaterialPoint samplePhysics = samples[sampleRow * numColumns + sampleColumn];
|
||||
EXPECT_EQ(samplePhysX.height, azlossy_cast<physx::PxI16>(samplePhysics.m_height * scaleFactor));
|
||||
}
|
||||
}
|
||||
CleanupHeightfieldComponent();
|
||||
}
|
||||
|
||||
} // namespace PhysXEditorTests
|
||||
|
||||
@@ -18,6 +18,7 @@ set(FILES
|
||||
Tests/PolygonPrismMeshUtilsTest.cpp
|
||||
Tests/PhysXColliderComponentModeTests.cpp
|
||||
Tests/ShapeColliderComponentTests.cpp
|
||||
Tests/EditorHeightfieldColliderComponentTests.cpp
|
||||
Tests/TestColliderComponent.h
|
||||
Tests/SystemComponentTest.cpp
|
||||
Tests/RigidBodyComponentTests.cpp
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#
|
||||
# Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
#
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Mocks/PhysX/MockPhysXHeightfieldProviderComponent.h
|
||||
)
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Interface/Interface.h>
|
||||
#include <AzCore/std/string/string.h>
|
||||
|
||||
namespace Profiler
|
||||
{
|
||||
class ProfilerRequests
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ProfilerRequests, "{3757c4e5-1941-457c-85ae-16305e17a4c6}");
|
||||
virtual ~ProfilerRequests() = default;
|
||||
|
||||
//! Enable/Disable the CpuProfiler
|
||||
virtual void SetProfilerEnabled(bool enabled) = 0;
|
||||
|
||||
//! Dump a single frame of Cpu profiling data
|
||||
virtual bool CaptureCpuProfilingStatistics(const AZStd::string& outputFilePath) = 0;
|
||||
|
||||
//! Start a multiframe capture of CPU profiling data.
|
||||
virtual bool BeginContinuousCpuProfilingCapture() = 0;
|
||||
|
||||
//! End and dump an in-progress continuous capture.
|
||||
virtual bool EndContinuousCpuProfilingCapture(const AZStd::string& outputFilePath) = 0;
|
||||
};
|
||||
|
||||
class ProfilerBusTraits
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
// EBusTraits overrides
|
||||
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single;
|
||||
static constexpr AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
|
||||
};
|
||||
|
||||
class ProfilerNotifications
|
||||
: public AZ::EBusTraits
|
||||
{
|
||||
public:
|
||||
virtual ~ProfilerNotifications() = default;
|
||||
|
||||
//! Notify when the current CpuProfilingStatistics capture is finished
|
||||
//! @param result Set to true if it's finished successfully
|
||||
//! @param info The output file path or error information which depends on the return.
|
||||
virtual void OnCaptureCpuProfilingStatisticsFinished(bool result, const AZStd::string& info) = 0;
|
||||
};
|
||||
|
||||
using ProfilerInterface = AZ::Interface<ProfilerRequests>;
|
||||
using ProfilerRequestBus = AZ::EBus<ProfilerRequests, ProfilerBusTraits>;
|
||||
using ProfilerNotificationBus = AZ::EBus<ProfilerNotifications>;
|
||||
} // namespace Profiler
|
||||
@@ -10,9 +10,9 @@
|
||||
|
||||
#include <ImGuiCpuProfiler.h>
|
||||
|
||||
#include <Profiler/ProfilerBus.h>
|
||||
#include <CpuProfilerImpl.h>
|
||||
|
||||
#include <AzCore/Debug/ProfilerBus.h>
|
||||
#include <AzCore/IO/FileIO.h>
|
||||
#include <AzCore/JSON/filereadstream.h>
|
||||
#include <AzCore/Outcome/Outcome.h>
|
||||
@@ -26,8 +26,6 @@
|
||||
|
||||
namespace Profiler
|
||||
{
|
||||
static constexpr const char* defaultSaveLocation = "@user@/Profiler";
|
||||
|
||||
namespace CpuProfilerImGuiHelper
|
||||
{
|
||||
float TicksToMs(double ticks)
|
||||
@@ -156,16 +154,7 @@ namespace Profiler
|
||||
|
||||
if (m_captureToFile)
|
||||
{
|
||||
AZStd::string timeString;
|
||||
AZStd::to_string(timeString, AZStd::GetTimeNowSecond());
|
||||
|
||||
const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_single_%s.json", defaultSaveLocation, timeString.c_str());
|
||||
|
||||
char resolvedPath[AZ::IO::MaxPathLength];
|
||||
AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength);
|
||||
m_lastCapturedFilePath = resolvedPath;
|
||||
|
||||
ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::CaptureCpuProfilingStatistics, frameDataFilePath);
|
||||
AZ::Debug::ProfilerSystemInterface::Get()->CaptureFrame(GenerateOutputFile("single"));
|
||||
}
|
||||
m_captureToFile = false;
|
||||
|
||||
@@ -206,24 +195,15 @@ namespace Profiler
|
||||
bool isInProgress = CpuProfiler::Get()->IsContinuousCaptureInProgress();
|
||||
if (ImGui::Button(isInProgress ? "End" : "Begin"))
|
||||
{
|
||||
auto profilerSystem = AZ::Debug::ProfilerSystemInterface::Get();
|
||||
if (isInProgress)
|
||||
{
|
||||
AZStd::string timeString;
|
||||
AZStd::to_string(timeString, AZStd::GetTimeNowSecond());
|
||||
|
||||
const AZStd::string frameDataFilePath = AZStd::string::format("%s/cpu_multi_%s.json", defaultSaveLocation, timeString.c_str());
|
||||
|
||||
char resolvedPath[AZ::IO::MaxPathLength];
|
||||
AZ::IO::FileIOBase::GetInstance()->ResolvePath(frameDataFilePath.c_str(), resolvedPath, AZ::IO::MaxPathLength);
|
||||
m_lastCapturedFilePath = resolvedPath;
|
||||
|
||||
ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::EndContinuousCpuProfilingCapture, frameDataFilePath);
|
||||
|
||||
profilerSystem->EndCapture();
|
||||
m_paused = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ProfilerRequestBus::Broadcast(&ProfilerRequestBus::Events::BeginContinuousCpuProfilingCapture);
|
||||
profilerSystem->StartCapture(GenerateOutputFile("multi"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,8 +215,10 @@ namespace Profiler
|
||||
// Only update the cached file list when opened so that we aren't making IO calls on every frame.
|
||||
m_cachedCapturePaths.clear();
|
||||
|
||||
AZ::IO::FixedMaxPathString captureOutput = AZ::Debug::GetProfilerCaptureLocation();
|
||||
|
||||
auto* base = AZ::IO::FileIOBase::GetInstance();
|
||||
base->FindFiles(defaultSaveLocation, "*.json",
|
||||
base->FindFiles(captureOutput.c_str(), "*.json",
|
||||
[&paths = m_cachedCapturePaths](const char* path) -> bool
|
||||
{
|
||||
auto foundPath = AZ::IO::Path(path);
|
||||
@@ -418,6 +400,18 @@ namespace Profiler
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
AZStd::string ImGuiCpuProfiler::GenerateOutputFile(const char* nameHint)
|
||||
{
|
||||
AZ::IO::FixedMaxPathString captureOutput = AZ::Debug::GetProfilerCaptureLocation();
|
||||
|
||||
const AZ::IO::FixedMaxPathString frameDataFilePath =
|
||||
AZ::IO::FixedMaxPathString::format("%s/cpu_%s_%lld.json", captureOutput.c_str(), nameHint, AZStd::GetTimeNowSecond());
|
||||
|
||||
AZ::IO::FileIOBase::GetInstance()->ResolvePath(m_lastCapturedFilePath, frameDataFilePath.c_str());
|
||||
|
||||
return m_lastCapturedFilePath.String();
|
||||
}
|
||||
|
||||
void ImGuiCpuProfiler::LoadFile()
|
||||
{
|
||||
const AZ::IO::Path& pathToLoad = m_cachedCapturePaths[m_currentFileIndex];
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user