Merge branch 'upstream/development' into LYN-6770_AutomatedTestNetInputs

This commit is contained in:
Gene Walters
2021-11-01 20:23:27 -07:00
262 changed files with 4009 additions and 1485 deletions
@@ -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
@@ -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
@@ -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
@@ -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
@@ -259,6 +259,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();
@@ -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
@@ -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>
@@ -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
+1 -2
View File
@@ -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
@@ -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
@@ -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.
@@ -245,6 +250,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 +294,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
@@ -10,8 +10,8 @@
#include <Atom/RPI.Edit/Common/AssetUtils.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/StringFunc/StringFunc.h>
#include <Atom/RPI.Reflect/Asset/AssetReference.h>
#include <Atom/RPI.Reflect/Pass/PassAsset.h>
@@ -33,11 +33,27 @@ namespace AZ
static const char* PassAssetExtension = "pass";
}
namespace PassBuilderNamespace
{
enum PassDependencies
{
Shader,
AttachmentImage,
Count
};
static const AZStd::tuple<const char*, const char*> DependencyExtensionJobKeyTable[PassDependencies::Count] =
{
{".shader", "Shader Asset"},
{".attimage", "Any Asset Builder"}
};
}
void PassBuilder::RegisterBuilder()
{
AssetBuilderSDK::AssetBuilderDesc builder;
builder.m_name = PassBuilderJobKey;
builder.m_version = 13; // antonmic: making .pass files declare dependency on shaders they reference
builder.m_version = 14; // making .pass files emit product dependencies for the shaders they reference so they are picked up by the asset bundler
builder.m_busId = azrtti_typeid<PassBuilder>();
builder.m_createJobFunction = AZStd::bind(&PassBuilder::CreateJobs, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
builder.m_processJobFunction = AZStd::bind(&PassBuilder::ProcessJob, this, AZStd::placeholders::_1, AZStd::placeholders::_2);
@@ -104,8 +120,27 @@ namespace AZ
}
}
bool SetJobKeyForExtension(const AZStd::string& filePath, FindPassReferenceAssetParams& params)
{
AZStd::string extension;
StringFunc::Path::GetExtension(filePath.c_str(), extension);
for (const auto& [dependencyExtension, jobKey] : PassBuilderNamespace::DependencyExtensionJobKeyTable)
{
if (extension == dependencyExtension)
{
params.jobKey = jobKey;
return true;
}
}
AZ_Error(PassBuilderName, false, "PassBuilder found a dependency with extension '%s', but does not know the corresponding job key. Add the job key for that extension to SetJobKeyForExtension in PassBuilder.cpp", extension.c_str());
params.jobKey = "Unknown";
return false;
}
// Helper function to find all assetId's and object references
bool FindReferencedAssets(FindPassReferenceAssetParams& params, AssetBuilderSDK::JobDescriptor* job)
bool FindReferencedAssets(
FindPassReferenceAssetParams& params, AssetBuilderSDK::JobDescriptor* job, AZStd::vector<AssetBuilderSDK::ProductDependency>* productDependencies)
{
SerializeContext::ErrorHandler errorLogger;
errorLogger.Reset();
@@ -129,8 +164,8 @@ namespace AZ
if (job != nullptr) // Create Job Phase
{
params.dependencySourceFile = path;
bool dependencyAddedSuccessfully = AddDependency(params, job);
success = dependencyAddedSuccessfully && success;
success &= SetJobKeyForExtension(path, params);
success &= AddDependency(params, job);
}
else // Process Job Phase
{
@@ -139,6 +174,9 @@ namespace AZ
if (assetIdOutcome)
{
assetReference->m_assetId = assetIdOutcome.GetValue();
productDependencies->push_back(
AssetBuilderSDK::ProductDependency{assetReference->m_assetId, AZ::Data::ProductDependencyInfo::CreateFlags(Data::AssetLoadBehavior::NoLoad)}
);
}
else
{
@@ -223,9 +261,9 @@ namespace AZ
params.passAssetSourceFile = request.m_sourceFile;
params.passAssetUuid = passAssetUuid;
params.serializeContext = serializeContext;
params.jobKey = "Shader Asset";
params.jobKey = "Unknown";
if (!FindReferencedAssets(params, &job))
if (!FindReferencedAssets(params, &job, nullptr))
{
return;
}
@@ -287,9 +325,10 @@ namespace AZ
params.passAssetSourceFile = request.m_sourceFile;
params.passAssetUuid = passAssetUuid;
params.serializeContext = serializeContext;
params.jobKey = "Shader Asset";
params.jobKey = "Unknown";
if (!FindReferencedAssets(params, nullptr))
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
if (!FindReferencedAssets(params, nullptr, &productDependencies))
{
return;
}
@@ -313,6 +352,7 @@ namespace AZ
// --- Save output product(s) to response ---
AssetBuilderSDK::JobProduct jobProduct(destPath, PassAsset::RTTI_Type(), 0);
jobProduct.m_dependencies = productDependencies;
jobProduct.m_dependenciesHandled = true;
response.m_outputProducts.push_back(jobProduct);
response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
@@ -699,9 +699,7 @@ 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);
#ifdef AZ_CULL_DEBUG_ENABLED
@@ -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(
+22 -3
View File
@@ -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();
@@ -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);
}
@@ -785,6 +799,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);
@@ -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)
@@ -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";
@@ -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;
}
@@ -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>;
@@ -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());
@@ -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);
}
}
@@ -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());
@@ -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);
@@ -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;
@@ -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;
@@ -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
@@ -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);
}
@@ -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
)
+6 -6
View File
@@ -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();
+2 -2
View File
@@ -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());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
+7 -6
View File
@@ -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();
+2 -2
View File
@@ -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();
}
}
@@ -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)
+15 -2
View File
@@ -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
+1 -1
View File
@@ -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
+11
View File
@@ -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
)
@@ -34,7 +34,8 @@ namespace UnitTest
MOCK_METHOD1(RegisterArea, void(AZ::EntityId areaId));
MOCK_METHOD1(UnregisterArea, void(AZ::EntityId areaId));
MOCK_METHOD1(RefreshArea, void(AZ::EntityId areaId));
MOCK_METHOD2(RefreshArea,
void(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask));
};
class MockTerrainDataNotificationListener : public AzFramework::Terrain::TerrainDataNotificationBus::Handler
@@ -119,7 +119,9 @@ namespace Terrain
LmbrCentral::DependencyNotificationBus::Handler::BusDisconnect();
// Since this height data will no longer exist, notify the terrain system to refresh the area.
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
TerrainSystemServiceRequestBus::Broadcast(
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
AzFramework::Terrain::TerrainDataNotifications::HeightData);
}
bool TerrainHeightGradientListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
@@ -176,7 +178,9 @@ namespace Terrain
void TerrainHeightGradientListComponent::OnCompositionChanged()
{
RefreshMinMaxHeights();
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
TerrainSystemServiceRequestBus::Broadcast(
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
AzFramework::Terrain::TerrainDataNotifications::HeightData);
}
void TerrainHeightGradientListComponent::RefreshMinMaxHeights()
@@ -157,6 +157,12 @@ namespace Terrain
void TerrainLayerSpawnerComponent::RefreshArea()
{
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
using Terrain = AzFramework::Terrain::TerrainDataNotifications;
// Notify the terrain system that the entire layer has changed, so both height and surface data can be affected.
TerrainSystemServiceRequestBus::Broadcast(
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
static_cast<Terrain::TerrainDataChangedMask>(Terrain::HeightData | Terrain::SurfaceData)
);
}
}
@@ -184,7 +184,9 @@ namespace Terrain
void TerrainSurfaceGradientListComponent::OnCompositionChanged()
{
TerrainSystemServiceRequestBus::Broadcast(&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId());
TerrainSystemServiceRequestBus::Broadcast(
&TerrainSystemServiceRequestBus::Events::RefreshArea, GetEntityId(),
AzFramework::Terrain::TerrainDataNotifications::SurfaceData);
}
} // namespace Terrain
@@ -32,16 +32,22 @@ namespace Terrain
AZ::EditContext* edit = serialize->GetEditContext();
if (edit)
{
edit->Class<TerrainWorldConfig>(
"Terrain World Component", "Data required for the terrain system to run")
edit->Class<TerrainWorldConfig>("Terrain World Component", "Data required for the terrain system to run")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZStd::vector<AZ::Crc32>({ AZ_CRC_CE("Level") }))
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMin, "World Bounds (Min)", "")
// Temporary constraint until the rest of the Terrain system is updated to support larger worlds.
->Attribute(AZ::Edit::Attributes::Min, -2048.0f)
->Attribute(AZ::Edit::Attributes::Max, 2048.0f)
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_worldMax, "World Bounds (Max)", "")
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_heightQueryResolution, "Height Query Resolution (m)", "")
// Temporary constraint until the rest of the Terrain system is updated to support larger worlds.
->Attribute(AZ::Edit::Attributes::Min, -2048.0f)
->Attribute(AZ::Edit::Attributes::Max, 2048.0f)
->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainWorldConfig::m_heightQueryResolution, "Height Query Resolution (m)", "")
;
}
}
@@ -218,9 +218,11 @@ namespace Terrain
const AZ::Transform transform = AZ::Transform::CreateTranslation(worldBounds.GetCenter());
AZ::Vector2 queryResolution = AZ::Vector2(1.0f);
AZ::Vector2 queryResolution2D = AZ::Vector2(1.0f);
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
queryResolution, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
queryResolution2D, &AzFramework::Terrain::TerrainDataRequests::GetTerrainHeightQueryResolution);
// Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension.
float queryResolution = queryResolution2D.GetX();
// Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes.
m_areaData.m_rebuildSectors = m_areaData.m_rebuildSectors ||
@@ -228,16 +230,11 @@ namespace Terrain
m_areaData.m_terrainBounds.GetMin().GetY() != worldBounds.GetMin().GetY() ||
m_areaData.m_terrainBounds.GetMax().GetX() != worldBounds.GetMax().GetX() ||
m_areaData.m_terrainBounds.GetMax().GetY() != worldBounds.GetMax().GetY() ||
m_areaData.m_sampleSpacing != queryResolution.GetX();
m_areaData.m_sampleSpacing != queryResolution;
m_areaData.m_transform = transform;
m_areaData.m_terrainBounds = worldBounds;
m_areaData.m_heightmapImageWidth = aznumeric_cast<uint32_t>(worldBounds.GetXExtent() / queryResolution.GetX());
m_areaData.m_heightmapImageHeight = aznumeric_cast<uint32_t>(worldBounds.GetYExtent() / queryResolution.GetY());
m_areaData.m_updateWidth = aznumeric_cast<uint32_t>(m_dirtyRegion.GetXExtent() / queryResolution.GetX());
m_areaData.m_updateHeight = aznumeric_cast<uint32_t>(m_dirtyRegion.GetYExtent() / queryResolution.GetY());
// Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension.
m_areaData.m_sampleSpacing = queryResolution.GetX();
m_areaData.m_sampleSpacing = queryResolution;
m_areaData.m_heightmapUpdated = true;
}
@@ -778,32 +775,43 @@ namespace Terrain
void TerrainFeatureProcessor::UpdateTerrainData()
{
uint32_t width = m_areaData.m_updateWidth;
uint32_t height = m_areaData.m_updateHeight;
const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds;
const float queryResolution = m_areaData.m_sampleSpacing;
const AZ::Aabb& worldBounds = m_areaData.m_terrainBounds;
const AZ::RHI::Size worldSize = AZ::RHI::Size(m_areaData.m_heightmapImageWidth, m_areaData.m_heightmapImageHeight, 1);
int32_t heightmapImageXStart = aznumeric_cast<int32_t>(AZStd::ceilf(worldBounds.GetMin().GetX() / queryResolution));
int32_t heightmapImageXEnd = aznumeric_cast<int32_t>(AZStd::floorf(worldBounds.GetMax().GetX() / queryResolution)) + 1;
int32_t heightmapImageYStart = aznumeric_cast<int32_t>(AZStd::ceilf(worldBounds.GetMin().GetY() / queryResolution));
int32_t heightmapImageYEnd = aznumeric_cast<int32_t>(AZStd::floorf(worldBounds.GetMax().GetY() / queryResolution)) + 1;
uint32_t heightmapImageWidth = heightmapImageXEnd - heightmapImageXStart;
uint32_t heightmapImageHeight = heightmapImageYEnd - heightmapImageYStart;
if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != worldSize)
const AZ::RHI::Size heightmapSize = AZ::RHI::Size(heightmapImageWidth, heightmapImageHeight, 1);
if (!m_areaData.m_heightmapImage || m_areaData.m_heightmapImage->GetDescriptor().m_size != heightmapSize)
{
// World size changed, so the whole world needs updating.
width = worldSize.m_width;
height = worldSize.m_height;
m_dirtyRegion = worldBounds;
const AZ::Data::Instance<AZ::RPI::AttachmentImagePool> imagePool = AZ::RPI::ImageSystemInterface::Get()->GetSystemAttachmentPool();
AZ::RHI::ImageDescriptor imageDescriptor = AZ::RHI::ImageDescriptor::Create2D(
AZ::RHI::ImageBindFlags::ShaderRead, width, height, AZ::RHI::Format::R16_UNORM
AZ::RHI::ImageBindFlags::ShaderRead, heightmapSize.m_width, heightmapSize.m_height, AZ::RHI::Format::R16_UNORM
);
const AZ::Name TerrainHeightmapName = AZ::Name(TerrainHeightmapChars);
m_areaData.m_heightmapImage = AZ::RPI::AttachmentImage::Create(*imagePool.get(), imageDescriptor, TerrainHeightmapName, nullptr, nullptr);
AZ_Error(TerrainFPName, m_areaData.m_heightmapImage, "Failed to initialize the heightmap image.");
// World size changed, so the whole height map needs updating.
m_dirtyRegion = worldBounds;
}
int32_t xStart = aznumeric_cast<int32_t>(AZStd::ceilf(m_dirtyRegion.GetMin().GetX() / queryResolution));
int32_t xEnd = aznumeric_cast<int32_t>(AZStd::floorf(m_dirtyRegion.GetMax().GetX() / queryResolution)) + 1;
int32_t yStart = aznumeric_cast<int32_t>(AZStd::ceilf(m_dirtyRegion.GetMin().GetY() / queryResolution));
int32_t yEnd = aznumeric_cast<int32_t>(AZStd::floorf(m_dirtyRegion.GetMax().GetY() / queryResolution)) + 1;
uint32_t updateWidth = xEnd - xStart;
uint32_t updateHeight = yEnd - yStart;
AZStd::vector<uint16_t> pixels;
pixels.reserve(width * height);
pixels.reserve(updateWidth * updateHeight);
{
// Block other threads from accessing the surface data bus while we are in GetHeightFromFloats (which may call into the SurfaceData bus).
@@ -815,18 +823,17 @@ namespace Terrain
auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false);
typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex);
for (uint32_t y = 0; y < height; y++)
for (int32_t y = yStart; y < yEnd; y++)
{
for (uint32_t x = 0; x < width; x++)
for (int32_t x = xStart; x < xEnd; x++)
{
bool terrainExists = true;
float terrainHeight = 0.0f;
float xPos = x * queryResolution;
float yPos = y * queryResolution;
AzFramework::Terrain::TerrainDataRequestBus::BroadcastResult(
terrainHeight, &AzFramework::Terrain::TerrainDataRequests::GetHeightFromFloats,
(x * queryResolution) + m_dirtyRegion.GetMin().GetX(),
(y * queryResolution) + m_dirtyRegion.GetMin().GetY(),
AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT,
&terrainExists);
xPos, yPos, AzFramework::Terrain::TerrainDataRequests::Sampler::EXACT, &terrainExists);
const float clampedHeight = AZ::GetClamp((terrainHeight - worldBounds.GetMin().GetZ()) / worldBounds.GetExtents().GetZ(), 0.0f, 1.0f);
const float expandedHeight = AZStd::roundf(clampedHeight * AZStd::numeric_limits<uint16_t>::max());
@@ -839,16 +846,18 @@ namespace Terrain
if (m_areaData.m_heightmapImage)
{
const float left = (m_dirtyRegion.GetMin().GetX() - worldBounds.GetMin().GetX()) / queryResolution;
const float top = (m_dirtyRegion.GetMin().GetY() - worldBounds.GetMin().GetY()) / queryResolution;
constexpr uint32_t BytesPerPixel = sizeof(uint16_t);
const float left = xStart - (worldBounds.GetMin().GetX() / queryResolution);
const float top = yStart - (worldBounds.GetMin().GetY() / queryResolution);
AZ::RHI::ImageUpdateRequest imageUpdateRequest;
imageUpdateRequest.m_imageSubresourcePixelOffset.m_left = aznumeric_cast<uint32_t>(left);
imageUpdateRequest.m_imageSubresourcePixelOffset.m_top = aznumeric_cast<uint32_t>(top);
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = width * sizeof(uint16_t);
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = width * height * sizeof(uint16_t);
imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = height;
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = width;
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = height;
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerRow = updateWidth * BytesPerPixel;
imageUpdateRequest.m_sourceSubresourceLayout.m_bytesPerImage = updateWidth * updateHeight * BytesPerPixel;
imageUpdateRequest.m_sourceSubresourceLayout.m_rowCount = updateHeight;
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_width = updateWidth;
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_height = updateHeight;
imageUpdateRequest.m_sourceSubresourceLayout.m_size.m_depth = 1;
imageUpdateRequest.m_sourceData = pixels.data();
imageUpdateRequest.m_image = m_areaData.m_heightmapImage->GetRHIImage();
@@ -1106,6 +1115,12 @@ namespace Terrain
m_areaData.m_heightmapUpdated = false;
m_areaData.m_macroMaterialsUpdated = false;
AZStd::array<float, 2> uvStep =
{
1.0f / aznumeric_cast<uint32_t>(m_areaData.m_terrainBounds.GetXExtent() / m_areaData.m_sampleSpacing),
1.0f / aznumeric_cast<uint32_t>(m_areaData.m_terrainBounds.GetYExtent() / m_areaData.m_sampleSpacing),
};
for (SectorData& sectorData : m_sectorData)
{
ShaderTerrainData terrainDataForSrg;
@@ -1123,11 +1138,7 @@ namespace Terrain
((yPatch + GridMeters) - terrainBounds.GetMin().GetY()) / terrainBounds.GetYExtent()
};
terrainDataForSrg.m_uvStep =
{
1.0f / m_areaData.m_heightmapImageWidth,
1.0f / m_areaData.m_heightmapImageHeight,
};
terrainDataForSrg.m_uvStep = uvStep;
AZ::Transform transform = m_areaData.m_transform;
transform.SetTranslation(xPatch, yPatch, m_areaData.m_transform.GetTranslation().GetZ());
@@ -303,10 +303,6 @@ namespace Terrain
AZ::Transform m_transform{ AZ::Transform::CreateIdentity() };
AZ::Aabb m_terrainBounds{ AZ::Aabb::CreateNull() };
AZ::Data::Instance<AZ::RPI::AttachmentImage> m_heightmapImage;
uint32_t m_heightmapImageWidth{ 0 };
uint32_t m_heightmapImageHeight{ 0 };
uint32_t m_updateWidth{ 0 };
uint32_t m_updateHeight{ 0 };
float m_sampleSpacing{ 0.0f };
bool m_heightmapUpdated{ true };
bool m_macroMaterialsUpdated{ true };
@@ -76,6 +76,7 @@ void TerrainSystem::Activate()
m_dirtyRegion = AZ::Aabb::CreateNull();
m_terrainHeightDirty = true;
m_terrainSettingsDirty = true;
m_terrainSurfacesDirty = true;
m_requestedSettings.m_systemActive = true;
{
@@ -115,6 +116,7 @@ void TerrainSystem::Deactivate()
m_dirtyRegion = AZ::Aabb::CreateNull();
m_terrainHeightDirty = true;
m_terrainSettingsDirty = true;
m_terrainSurfacesDirty = true;
m_requestedSettings.m_systemActive = false;
AzFramework::Terrain::TerrainDataNotificationBus::Broadcast(
@@ -549,6 +551,7 @@ void TerrainSystem::RegisterArea(AZ::EntityId areaId)
m_registeredAreas[areaId] = aabb;
m_dirtyRegion.AddAabb(aabb);
m_terrainHeightDirty = true;
m_terrainSurfacesDirty = true;
}
void TerrainSystem::UnregisterArea(AZ::EntityId areaId)
@@ -567,14 +570,17 @@ void TerrainSystem::UnregisterArea(AZ::EntityId areaId)
{
m_dirtyRegion.AddAabb(aabb);
m_terrainHeightDirty = true;
m_terrainSurfacesDirty = true;
return true;
}
return false;
});
}
void TerrainSystem::RefreshArea(AZ::EntityId areaId)
void TerrainSystem::RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask)
{
using Terrain = AzFramework::Terrain::TerrainDataNotifications;
AZStd::unique_lock<AZStd::shared_mutex> lock(m_areaMutex);
auto areaAabb = m_registeredAreas.find(areaId);
@@ -588,11 +594,18 @@ void TerrainSystem::RefreshArea(AZ::EntityId areaId)
expandedAabb.AddAabb(newAabb);
m_dirtyRegion.AddAabb(expandedAabb);
m_terrainHeightDirty = true;
// Keep track of which types of data have changed so that we can send out the appropriate notifications later.
m_terrainHeightDirty = m_terrainHeightDirty || ((changeMask & Terrain::HeightData) == Terrain::HeightData);
m_terrainSurfacesDirty = m_terrainSurfacesDirty || ((changeMask & Terrain::SurfaceData) == Terrain::SurfaceData);
}
void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
using Terrain = AzFramework::Terrain::TerrainDataNotifications;
bool terrainSettingsChanged = false;
if (m_terrainSettingsDirty)
@@ -607,6 +620,7 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
m_dirtyRegion = m_currentSettings.m_worldBounds;
m_dirtyRegion.AddAabb(m_requestedSettings.m_worldBounds);
m_terrainHeightDirty = true;
m_terrainSurfacesDirty = true;
m_currentSettings.m_worldBounds = m_requestedSettings.m_worldBounds;
}
@@ -614,12 +628,13 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
m_dirtyRegion = AZ::Aabb::CreateNull();
m_terrainHeightDirty = true;
m_terrainSurfacesDirty = true;
}
m_currentSettings = m_requestedSettings;
}
if (terrainSettingsChanged || m_terrainHeightDirty)
if (terrainSettingsChanged || m_terrainHeightDirty || m_terrainSurfacesDirty)
{
// Block other threads from accessing the surface data bus while we are in GetValue (which may call into the SurfaceData bus).
// We lock our surface data mutex *before* checking / setting "isRequestInProgress" so that we prevent race conditions
@@ -629,24 +644,27 @@ void TerrainSystem::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
auto& surfaceDataContext = SurfaceData::SurfaceDataSystemRequestBus::GetOrCreateContext(false);
typename SurfaceData::SurfaceDataSystemRequestBus::Context::DispatchLockGuard scopeLock(surfaceDataContext.m_contextMutex);
AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask =
AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::None;
Terrain::TerrainDataChangedMask changeMask = Terrain::TerrainDataChangedMask::None;
if (terrainSettingsChanged)
{
changeMask = static_cast<AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask>(
changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::Settings);
changeMask = static_cast<Terrain::TerrainDataChangedMask>(changeMask | Terrain::TerrainDataChangedMask::Settings);
}
if (m_terrainHeightDirty)
{
changeMask = static_cast<AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask>(
changeMask | AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask::HeightData);
changeMask = static_cast<Terrain::TerrainDataChangedMask>(changeMask | Terrain::TerrainDataChangedMask::HeightData);
}
if (m_terrainSurfacesDirty)
{
changeMask = static_cast<Terrain::TerrainDataChangedMask>(changeMask | Terrain::TerrainDataChangedMask::SurfaceData);
}
// Make sure to set these *before* calling OnTerrainDataChanged, since it's possible that subsystems reacting to that call will
// cause the data to become dirty again.
AZ::Aabb dirtyRegion = m_dirtyRegion;
m_terrainHeightDirty = false;
m_terrainSurfacesDirty = false;
m_dirtyRegion = AZ::Aabb::CreateNull();
AzFramework::Terrain::TerrainDataNotificationBus::Broadcast(
@@ -47,7 +47,8 @@ namespace Terrain
void RegisterArea(AZ::EntityId areaId) override;
void UnregisterArea(AZ::EntityId areaId) override;
void RefreshArea(AZ::EntityId areaId) override;
void RefreshArea(
AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) override;
///////////////////////////////////////////
// TerrainDataRequestBus::Handler Impl
@@ -164,6 +165,7 @@ namespace Terrain
bool m_terrainSettingsDirty = true;
bool m_terrainHeightDirty = false;
bool m_terrainSurfacesDirty = false;
AZ::Aabb m_dirtyRegion;
mutable AZStd::shared_mutex m_areaMutex;
@@ -44,7 +44,7 @@ namespace Terrain
// register an area to override terrain
virtual void RegisterArea(AZ::EntityId areaId) = 0;
virtual void UnregisterArea(AZ::EntityId areaId) = 0;
virtual void RefreshArea(AZ::EntityId areaId) = 0;
virtual void RefreshArea(AZ::EntityId areaId, AzFramework::Terrain::TerrainDataNotifications::TerrainDataChangedMask changeMask) = 0;
};
using TerrainSystemServiceRequestBus = AZ::EBus<TerrainSystemServiceRequests>;
@@ -190,7 +190,7 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerTransformChangedUpdatesTerrainSyst
CreateMockTerrainSystem();
// The TransformChanged call should refresh the area.
EXPECT_CALL(*m_terrainSystem, RefreshArea(_)).Times(1);
EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1);
AddLayerSpawnerAndShapeComponentToEntity();
@@ -211,7 +211,7 @@ TEST_F(LayerSpawnerComponentTest, LayerSpawnerShapeChangedUpdatesTerrainSystem)
CreateMockTerrainSystem();
// The ShapeChanged call should refresh the area.
EXPECT_CALL(*m_terrainSystem, RefreshArea(_)).Times(1);
EXPECT_CALL(*m_terrainSystem, RefreshArea(_, _)).Times(1);
AddLayerSpawnerAndShapeComponentToEntity();
@@ -93,7 +93,7 @@ TEST_F(TerrainHeightGradientListComponentTest, TerrainHeightGradientRefreshesTer
// As the TerrainHeightGradientListComponent subscribes to the dependency monitor, RefreshArea will be called twice:
// once due to OnCompositionChanged being picked up by the the dependency monitor and resending the notification,
// and once when the HeightGradientListComponent gets the OnCompositionChanged directly through the DependencyNotificationBus.
EXPECT_CALL(terrainSystem, RefreshArea(_)).Times(2);
EXPECT_CALL(terrainSystem, RefreshArea(_, _)).Times(2);
LmbrCentral::DependencyNotificationBus::Event(m_entity->GetId(), &LmbrCentral::DependencyNotificationBus::Events::OnCompositionChanged);