Terrain Macro Material component improvements (#4930)
* First pass of non-working changes to Terrain Macro Material Component. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Reworked Terrain Macro Material to use properties instead of a material. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fixed comments. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * PR feedback - disable attributes when no normal map selected Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com> * Fix linux compile error - unused variables. Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"description": "",
|
||||
"materialType": "TerrainMacroMaterial.materialtype",
|
||||
"parentMaterial": "",
|
||||
"propertyLayoutVersion": 1,
|
||||
"properties": {
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
{
|
||||
"description": "A material for providing terrain with low-fidelity color and normals. This material will get blended with surface detail materials.",
|
||||
"version": 1,
|
||||
"propertyLayout": {
|
||||
"groups": [
|
||||
{
|
||||
"name": "baseColor",
|
||||
"displayName": "Base Color",
|
||||
"description": "Properties for configuring the surface reflected color for dielectrics or reflectance values for metals."
|
||||
},
|
||||
{
|
||||
"name": "normal",
|
||||
"displayName": "Normal",
|
||||
"description": "Properties related to configuring surface normal."
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"baseColor": [
|
||||
{
|
||||
"name": "textureMap",
|
||||
"displayName": "Texture",
|
||||
"description": "Base color of the macro material",
|
||||
"type": "Image"
|
||||
}
|
||||
],
|
||||
"normal": [
|
||||
{
|
||||
"name": "textureMap",
|
||||
"displayName": "Texture",
|
||||
"description": "Texture for defining surface normal direction. These will override normals generated from the geometry.",
|
||||
"type": "Image"
|
||||
},
|
||||
{
|
||||
"name": "flipX",
|
||||
"displayName": "Flip X Channel",
|
||||
"description": "Flip tangent direction for this normal map.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "flipY",
|
||||
"displayName": "Flip Y Channel",
|
||||
"description": "Flip bitangent direction for this normal map.",
|
||||
"type": "Bool",
|
||||
"defaultValue": false
|
||||
},
|
||||
{
|
||||
"name": "factor",
|
||||
"displayName": "Factor",
|
||||
"description": "Strength factor for scaling the values",
|
||||
"type": "Float",
|
||||
"defaultValue": 1.0,
|
||||
"min": 0.0,
|
||||
"softMax": 2.0
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"shaders": [
|
||||
],
|
||||
"functors": [
|
||||
]
|
||||
}
|
||||
+95
-93
@@ -16,87 +16,65 @@
|
||||
#include <AzCore/Serialization/EditContext.h>
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
#include <Atom/RPI.Public/Image/StreamingImage.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
AZ::Data::AssetId TerrainMacroMaterialConfig::s_macroMaterialTypeAssetId{};
|
||||
bool TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly() const
|
||||
{
|
||||
return !m_macroNormalAsset.GetId().IsValid();
|
||||
}
|
||||
|
||||
void TerrainMacroMaterialConfig::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serialize)
|
||||
if (auto* serialize = azrtti_cast<AZ::SerializeContext*>(context); serialize)
|
||||
{
|
||||
serialize->Class<TerrainMacroMaterialConfig, AZ::ComponentConfig>()
|
||||
->Version(1)
|
||||
->Field("MacroMaterial", &TerrainMacroMaterialConfig::m_materialAsset)
|
||||
;
|
||||
->Field("MacroColor", &TerrainMacroMaterialConfig::m_macroColorAsset)
|
||||
->Field("MacroNormal", &TerrainMacroMaterialConfig::m_macroNormalAsset)
|
||||
->Field("NormalFlipX", &TerrainMacroMaterialConfig::m_normalFlipX)
|
||||
->Field("NormalFlipY", &TerrainMacroMaterialConfig::m_normalFlipY)
|
||||
->Field("NormalFactor", &TerrainMacroMaterialConfig::m_normalFactor)
|
||||
;
|
||||
|
||||
// The edit context for this appears in EditorTerrainMacroMaterialComponent.cpp.
|
||||
}
|
||||
}
|
||||
|
||||
AZ::Data::AssetId TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId()
|
||||
{
|
||||
// Get the Asset ID for the TerrainMacroMaterial material type and store it in a class static so that we don't have to look it
|
||||
// up again.
|
||||
if (!s_macroMaterialTypeAssetId.IsValid())
|
||||
{
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
s_macroMaterialTypeAssetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, TerrainMacroMaterialTypeAsset,
|
||||
azrtti_typeid<AZ::RPI::MaterialTypeAsset>(), false);
|
||||
AZ_Assert(s_macroMaterialTypeAssetId.IsValid(), "The asset '%s' couldn't be found.", TerrainMacroMaterialTypeAsset);
|
||||
}
|
||||
|
||||
return s_macroMaterialTypeAssetId;
|
||||
}
|
||||
|
||||
bool TerrainMacroMaterialConfig::IsMaterialTypeCorrect(const AZ::Data::AssetId& assetId)
|
||||
{
|
||||
// We'll verify that whatever material we try to load has this material type as a dependency, as a way to implicitly detect
|
||||
// that we're only trying to use terrain macro materials even before we load the asset.
|
||||
auto macroMaterialTypeAssetId = GetTerrainMacroMaterialTypeAssetId();
|
||||
|
||||
// Get the dependencies for the requested asset.
|
||||
AZ::Outcome<AZStd::vector<AZ::Data::ProductDependency>, AZStd::string> result;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
result, &AZ::Data::AssetCatalogRequestBus::Events::GetDirectProductDependencies, assetId);
|
||||
|
||||
// If any of the dependencies match the TerrainMacroMaterial materialtype asset, then this should be the correct type of material.
|
||||
if (result)
|
||||
{
|
||||
for (auto& dependency : result.GetValue())
|
||||
if (auto* editContext = serialize->GetEditContext(); editContext)
|
||||
{
|
||||
if (dependency.m_assetId == macroMaterialTypeAssetId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
editContext
|
||||
->Class<TerrainMacroMaterialConfig>(
|
||||
"Terrain Macro Material Component", "Provide a terrain macro material for a region of the world")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_macroColorAsset, "Color Texture",
|
||||
"Terrain macro color texture for use by any terrain inside the bounding box on this entity.")
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_macroNormalAsset, "Normal Texture",
|
||||
"Texture for defining surface normal direction. These will override normals generated from the geometry.")
|
||||
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ::Edit::PropertyRefreshLevels::AttributesAndValues)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_normalFlipX, "Normal Flip X",
|
||||
"Flip tangent direction for this normal map.")
|
||||
->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_normalFlipY, "Normal Flip Y",
|
||||
"Flip bitangent direction for this normal map.")
|
||||
->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly)
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Slider, &TerrainMacroMaterialConfig::m_normalFactor, "Normal Factor",
|
||||
"Strength factor for scaling the normal map values.")
|
||||
->Attribute(AZ::Edit::Attributes::Min, 0.0f)
|
||||
->Attribute(AZ::Edit::Attributes::Max, 10.0f)
|
||||
->Attribute(AZ::Edit::Attributes::SoftMin, 0.0f)
|
||||
->Attribute(AZ::Edit::Attributes::SoftMax, 2.0f)
|
||||
->Attribute(AZ::Edit::Attributes::ReadOnly, &TerrainMacroMaterialConfig::NormalMapAttributesAreReadOnly)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
// Didn't have the expected dependency, so it must not be the right material type.
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ::Outcome<void, AZStd::string> TerrainMacroMaterialConfig::ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType)
|
||||
{
|
||||
if (azrtti_typeid<AZ::Data::Asset<AZ::RPI::MaterialAsset>>() != valueType)
|
||||
{
|
||||
AZ_Assert(false, "Unexpected value type");
|
||||
return AZ::Failure(AZStd::string("Unexpectedly received something other than a material asset for the MacroMaterial!"));
|
||||
}
|
||||
|
||||
auto newMaterialAsset = *static_cast<AZ::Data::Asset<AZ::RPI::MaterialAsset>*>(newValue);
|
||||
|
||||
if (!IsMaterialTypeCorrect(newMaterialAsset.GetId()))
|
||||
{
|
||||
return AZ::Failure(AZStd::string::format(
|
||||
"The selected MacroMaterial ('%s') needs to use the TerrainMacroMaterial material type.",
|
||||
newMaterialAsset.GetHint().c_str()));
|
||||
}
|
||||
|
||||
return AZ::Success();
|
||||
}
|
||||
|
||||
|
||||
void TerrainMacroMaterialComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
|
||||
{
|
||||
services.push_back(AZ_CRC_CE("TerrainMacroMaterialProviderService"));
|
||||
@@ -133,25 +111,29 @@ namespace Terrain
|
||||
|
||||
void TerrainMacroMaterialComponent::Activate()
|
||||
{
|
||||
// Clear out our shape bounds and make sure the material is queued to load.
|
||||
// Clear out our shape bounds and make sure the texture assets are queued to load.
|
||||
m_cachedShapeBounds = AZ::Aabb::CreateNull();
|
||||
m_configuration.m_materialAsset.QueueLoad();
|
||||
m_configuration.m_macroColorAsset.QueueLoad();
|
||||
m_configuration.m_macroNormalAsset.QueueLoad();
|
||||
|
||||
// Don't mark our material as active until it's finished loading and is valid.
|
||||
m_macroMaterialActive = false;
|
||||
|
||||
// Listen for the material asset to complete loading.
|
||||
AZ::Data::AssetBus::Handler::BusConnect(m_configuration.m_materialAsset.GetId());
|
||||
// Listen for the texture assets to complete loading.
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_macroColorAsset.GetId());
|
||||
AZ::Data::AssetBus::MultiHandler::BusConnect(m_configuration.m_macroNormalAsset.GetId());
|
||||
}
|
||||
|
||||
void TerrainMacroMaterialComponent::Deactivate()
|
||||
{
|
||||
TerrainMacroMaterialRequestBus::Handler::BusDisconnect();
|
||||
|
||||
AZ::Data::AssetBus::Handler::BusDisconnect();
|
||||
m_configuration.m_materialAsset.Release();
|
||||
AZ::Data::AssetBus::MultiHandler::BusDisconnect();
|
||||
m_configuration.m_macroColorAsset.Release();
|
||||
m_configuration.m_macroNormalAsset.Release();
|
||||
|
||||
m_macroMaterialInstance.reset();
|
||||
m_colorImage.reset();
|
||||
m_normalImage.reset();
|
||||
|
||||
// Send out any notifications as appropriate based on the macro material destruction.
|
||||
HandleMaterialStateChange();
|
||||
@@ -195,12 +177,18 @@ namespace Terrain
|
||||
|
||||
void TerrainMacroMaterialComponent::HandleMaterialStateChange()
|
||||
{
|
||||
// We only want our component to appear active during the time that the macro material is loaded and valid. The logic below
|
||||
// We only want our component to appear active during the time that the macro material is fully loaded and valid. The logic below
|
||||
// will handle all transition possibilities to notify if we've become active, inactive, or just changed. We'll also only
|
||||
// keep a valid up-to-date copy of the shape bounds while the material is valid, since we don't need it any other time.
|
||||
|
||||
// Color and normal data is considered ready if it's finished loading or if we don't have a texture specified
|
||||
bool colorReady = m_colorImage || (!m_configuration.m_macroColorAsset.GetId().IsValid());
|
||||
bool normalReady = m_normalImage || (!m_configuration.m_macroNormalAsset.GetId().IsValid());
|
||||
// If we don't have color or normal data, then we don't have *any* useful data, so don't activate the macro material.
|
||||
bool hasAnyData = m_configuration.m_macroColorAsset.GetId().IsValid() || m_configuration.m_macroNormalAsset.GetId().IsValid();
|
||||
|
||||
bool wasPreviouslyActive = m_macroMaterialActive;
|
||||
bool isNowActive = (m_macroMaterialInstance != nullptr);
|
||||
bool isNowActive = colorReady && normalReady && hasAnyData;
|
||||
|
||||
// Set our state to active or inactive, based on whether or not the macro material instance is now valid.
|
||||
m_macroMaterialActive = isNowActive;
|
||||
@@ -226,9 +214,10 @@ namespace Terrain
|
||||
// Start listening for shape changes.
|
||||
LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId());
|
||||
|
||||
MacroMaterialData material = GetTerrainMacroMaterialData();
|
||||
|
||||
TerrainMacroMaterialNotificationBus::Broadcast(
|
||||
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), m_macroMaterialInstance,
|
||||
m_cachedShapeBounds);
|
||||
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialCreated, GetEntityId(), material);
|
||||
}
|
||||
else if (wasPreviouslyActive && !isNowActive)
|
||||
{
|
||||
@@ -246,30 +235,35 @@ namespace Terrain
|
||||
else
|
||||
{
|
||||
// We were active both before and after, so just send out a material changed event.
|
||||
MacroMaterialData material = GetTerrainMacroMaterialData();
|
||||
|
||||
TerrainMacroMaterialNotificationBus::Broadcast(
|
||||
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), m_macroMaterialInstance);
|
||||
&TerrainMacroMaterialNotificationBus::Events::OnTerrainMacroMaterialChanged, GetEntityId(), material);
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainMacroMaterialComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
|
||||
{
|
||||
m_configuration.m_materialAsset = asset;
|
||||
|
||||
if (m_configuration.m_materialAsset.Get()->GetMaterialTypeAsset().GetId() ==
|
||||
TerrainMacroMaterialConfig::GetTerrainMacroMaterialTypeAssetId())
|
||||
if (asset.GetId() == m_configuration.m_macroColorAsset.GetId())
|
||||
{
|
||||
m_macroMaterialInstance = AZ::RPI::Material::FindOrCreate(m_configuration.m_materialAsset);
|
||||
m_configuration.m_macroColorAsset = asset;
|
||||
m_colorImage = AZ::RPI::StreamingImage::FindOrCreate(m_configuration.m_macroColorAsset);
|
||||
|
||||
// Clear the texture asset reference to make sure we don't prevent hot-reloading.
|
||||
m_configuration.m_macroColorAsset.Release();
|
||||
}
|
||||
else if (asset.GetId() == m_configuration.m_macroNormalAsset.GetId())
|
||||
{
|
||||
m_configuration.m_macroNormalAsset = asset;
|
||||
m_normalImage = AZ::RPI::StreamingImage::FindOrCreate(m_configuration.m_macroNormalAsset);
|
||||
|
||||
// Clear the texture asset reference to make sure we don't prevent hot-reloading.
|
||||
m_configuration.m_macroColorAsset.Release();
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Terrain", false, "Material '%s' has the wrong material type.", m_configuration.m_materialAsset.GetHint().c_str());
|
||||
m_macroMaterialInstance.reset();
|
||||
}
|
||||
|
||||
// Clear the material asset reference to make sure we don't prevent hot-reloading.
|
||||
m_configuration.m_materialAsset.Release();
|
||||
|
||||
HandleMaterialStateChange();
|
||||
}
|
||||
|
||||
@@ -278,10 +272,18 @@ namespace Terrain
|
||||
OnAssetReady(asset);
|
||||
}
|
||||
|
||||
void TerrainMacroMaterialComponent::GetTerrainMacroMaterialData(
|
||||
AZ::Data::Instance<AZ::RPI::Material>& macroMaterial, AZ::Aabb& macroMaterialRegion)
|
||||
MacroMaterialData TerrainMacroMaterialComponent::GetTerrainMacroMaterialData()
|
||||
{
|
||||
macroMaterial = m_macroMaterialInstance;
|
||||
macroMaterialRegion = m_cachedShapeBounds;
|
||||
MacroMaterialData macroMaterial;
|
||||
|
||||
macroMaterial.m_entityId = GetEntityId();
|
||||
macroMaterial.m_bounds = m_cachedShapeBounds;
|
||||
macroMaterial.m_colorImage = m_colorImage;
|
||||
macroMaterial.m_normalImage = m_normalImage;
|
||||
macroMaterial.m_normalFactor = m_configuration.m_normalFactor;
|
||||
macroMaterial.m_normalFlipX = m_configuration.m_normalFlipX;
|
||||
macroMaterial.m_normalFlipY = m_configuration.m_normalFlipY;
|
||||
|
||||
return macroMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
+11
-14
@@ -11,10 +11,9 @@
|
||||
#include <AzCore/Asset/AssetCommon.h>
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
|
||||
#include <LmbrCentral/Shape/ShapeComponentBus.h>
|
||||
#include <TerrainRenderer/TerrainMacroMaterialBus.h>
|
||||
|
||||
#include <Atom/RPI.Reflect/Image/StreamingImageAsset.h>
|
||||
|
||||
namespace LmbrCentral
|
||||
{
|
||||
@@ -32,23 +31,20 @@ namespace Terrain
|
||||
AZ_RTTI(TerrainMacroMaterialConfig, "{9DBAFFF0-FD20-4594-8884-E3266D8CCAC8}", AZ::ComponentConfig);
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
AZ::Data::Asset<AZ::RPI::MaterialAsset> m_materialAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
|
||||
|
||||
static AZ::Data::AssetId GetTerrainMacroMaterialTypeAssetId();
|
||||
static bool IsMaterialTypeCorrect(const AZ::Data::AssetId&);
|
||||
AZ::Outcome<void, AZStd::string> ValidateMaterialAsset(void* newValue, const AZ::Uuid& valueType);
|
||||
|
||||
private:
|
||||
static inline constexpr const char* TerrainMacroMaterialTypeAsset = "materials/terrain/terrainmacromaterial.azmaterialtype";
|
||||
static AZ::Data::AssetId s_macroMaterialTypeAssetId;
|
||||
bool NormalMapAttributesAreReadOnly() const;
|
||||
|
||||
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_macroColorAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
|
||||
AZ::Data::Asset<AZ::RPI::StreamingImageAsset> m_macroNormalAsset = { AZ::Data::AssetLoadBehavior::QueueLoad };
|
||||
bool m_normalFlipX = false;
|
||||
bool m_normalFlipY = false;
|
||||
float m_normalFactor = 1.0f;
|
||||
};
|
||||
|
||||
class TerrainMacroMaterialComponent
|
||||
: public AZ::Component
|
||||
, public TerrainMacroMaterialRequestBus::Handler
|
||||
, private LmbrCentral::ShapeComponentNotificationsBus::Handler
|
||||
, private AZ::Data::AssetBus::Handler
|
||||
, private AZ::Data::AssetBus::MultiHandler
|
||||
{
|
||||
public:
|
||||
template<typename, typename>
|
||||
@@ -70,7 +66,7 @@ namespace Terrain
|
||||
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
|
||||
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
|
||||
|
||||
void GetTerrainMacroMaterialData(AZ::Data::Instance<AZ::RPI::Material>& macroMaterial, AZ::Aabb& macroMaterialRegion) override;
|
||||
MacroMaterialData GetTerrainMacroMaterialData() override;
|
||||
|
||||
private:
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
@@ -86,7 +82,8 @@ namespace Terrain
|
||||
|
||||
TerrainMacroMaterialConfig m_configuration;
|
||||
AZ::Aabb m_cachedShapeBounds;
|
||||
AZ::Data::Instance<AZ::RPI::Material> m_macroMaterialInstance;
|
||||
bool m_macroMaterialActive{ false };
|
||||
AZ::Data::Instance<AZ::RPI::Image> m_colorImage;
|
||||
AZ::Data::Instance<AZ::RPI::Image> m_normalImage;
|
||||
};
|
||||
}
|
||||
|
||||
+2
-31
@@ -17,36 +17,7 @@ namespace Terrain
|
||||
{
|
||||
BaseClassType::ReflectSubClass<EditorTerrainMacroMaterialComponent, BaseClassType>(
|
||||
context, 1,
|
||||
&LmbrCentral::EditorWrappedComponentBaseVersionConverter<typename BaseClassType::WrappedComponentType,
|
||||
typename BaseClassType::WrappedConfigType, 1>
|
||||
);
|
||||
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
|
||||
if (serializeContext)
|
||||
{
|
||||
AZ::EditContext* editContext = serializeContext->GetEditContext();
|
||||
|
||||
// The edit context for TerrainMacroMaterialConfig is specified here to make it easier to add custom filtering to the
|
||||
// asset picker for the material asset so that we can eventually only display materials that inherit from the proper
|
||||
// material type.
|
||||
if (editContext)
|
||||
{
|
||||
editContext
|
||||
->Class<TerrainMacroMaterialConfig>(
|
||||
"Terrain Macro Material Component", "Provide a terrain macro material for a region of the world")
|
||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
|
||||
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
|
||||
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
|
||||
|
||||
->DataElement(
|
||||
AZ::Edit::UIHandlers::Default, &TerrainMacroMaterialConfig::m_materialAsset, "Macro Material",
|
||||
"Terrain macro material for use by any terrain inside the bounding box on this entity.")
|
||||
// This is disabled until ChangeValidate can support the Asset<T> type. :(
|
||||
//->Attribute(AZ::Edit::Attributes::ChangeValidate, &TerrainMacroMaterialConfig::ValidateMaterialAsset)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
&LmbrCentral::EditorWrappedComponentBaseVersionConverter<
|
||||
typename BaseClassType::WrappedComponentType, typename BaseClassType::WrappedConfigType, 1>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,13 +51,6 @@ namespace Terrain
|
||||
{
|
||||
// Terrain material
|
||||
static const char* const HeightmapImage("settings.heightmapImage");
|
||||
|
||||
// Macro material
|
||||
static const char* const MacroColorTextureMap("baseColor.textureMap");
|
||||
static const char* const MacroNormalTextureMap("normal.textureMap");
|
||||
static const char* const MacroNormalFlipX("normal.flipX");
|
||||
static const char* const MacroNormalFlipY("normal.flipY");
|
||||
static const char* const MacroNormalFactor("normal.factor");
|
||||
}
|
||||
|
||||
namespace ShaderInputs
|
||||
@@ -185,12 +178,11 @@ namespace Terrain
|
||||
m_areaData.m_heightmapUpdated = true;
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region)
|
||||
void TerrainFeatureProcessor::OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& newMaterialData)
|
||||
{
|
||||
MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId);
|
||||
materialData.m_bounds = region;
|
||||
|
||||
UpdateMacroMaterialData(materialData, material);
|
||||
UpdateMacroMaterialData(materialData, newMaterialData);
|
||||
|
||||
// Update all sectors in region.
|
||||
ForOverlappingSectors(materialData.m_bounds,
|
||||
@@ -203,20 +195,14 @@ namespace Terrain
|
||||
);
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance macroMaterial)
|
||||
void TerrainFeatureProcessor::OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& newMaterialData)
|
||||
{
|
||||
if (macroMaterial)
|
||||
{
|
||||
MacroMaterialData& data = FindOrCreateMacroMaterial(entityId);
|
||||
UpdateMacroMaterialData(data, macroMaterial);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemoveMacroMaterial(entityId);
|
||||
}
|
||||
MacroMaterialData& data = FindOrCreateMacroMaterial(entityId);
|
||||
UpdateMacroMaterialData(data, newMaterialData);
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion)
|
||||
void TerrainFeatureProcessor::OnTerrainMacroMaterialRegionChanged(
|
||||
AZ::EntityId entityId, [[maybe_unused]] const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion)
|
||||
{
|
||||
MacroMaterialData& materialData = FindOrCreateMacroMaterial(entityId);
|
||||
for (SectorData& sectorData : m_sectorData)
|
||||
@@ -269,6 +255,7 @@ namespace Terrain
|
||||
}
|
||||
|
||||
m_areaData.m_macroMaterialsUpdated = true;
|
||||
RemoveMacroMaterial(entityId);
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::UpdateTerrainData()
|
||||
@@ -382,42 +369,18 @@ namespace Terrain
|
||||
TerrainMacroMaterialRequestBus::EnumerateHandlers(
|
||||
[&](TerrainMacroMaterialRequests* handler)
|
||||
{
|
||||
MaterialInstance macroMaterial;
|
||||
AZ::Aabb bounds;
|
||||
handler->GetTerrainMacroMaterialData(macroMaterial, bounds);
|
||||
MacroMaterialData macroMaterial = handler->GetTerrainMacroMaterialData();
|
||||
AZ::EntityId entityId = *(Terrain::TerrainMacroMaterialRequestBus::GetCurrentBusId());
|
||||
OnTerrainMacroMaterialCreated(entityId, macroMaterial, bounds);
|
||||
OnTerrainMacroMaterialCreated(entityId, macroMaterial);
|
||||
return true;
|
||||
}
|
||||
);
|
||||
TerrainMacroMaterialNotificationBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material)
|
||||
void TerrainFeatureProcessor::UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData)
|
||||
{
|
||||
// Since we're using an actual macro material instance for now, get the values from it that we care about.
|
||||
const auto materialLayout = material->GetMaterialPropertiesLayout();
|
||||
|
||||
const AZ::RPI::MaterialPropertyIndex macroColorTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroColorTextureMap));
|
||||
AZ_Error(TerrainFPName, macroColorTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroColorTextureMap);
|
||||
|
||||
const AZ::RPI::MaterialPropertyIndex macroNormalTextureMapIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalTextureMap));
|
||||
AZ_Error(TerrainFPName, macroNormalTextureMapIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalTextureMap);
|
||||
|
||||
const AZ::RPI::MaterialPropertyIndex macroNormalFlipXIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipX));
|
||||
AZ_Error(TerrainFPName, macroNormalFlipXIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipX);
|
||||
|
||||
const AZ::RPI::MaterialPropertyIndex macroNormalFlipYIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFlipY));
|
||||
AZ_Error(TerrainFPName, macroNormalFlipYIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFlipY);
|
||||
|
||||
const AZ::RPI::MaterialPropertyIndex macroNormalFactorIndex = materialLayout->FindPropertyIndex(AZ::Name(MaterialInputs::MacroNormalFactor));
|
||||
AZ_Error(TerrainFPName, macroNormalFactorIndex.IsValid(), "Failed to find shader input constant %s.", MaterialInputs::MacroNormalFactor);
|
||||
|
||||
macroMaterialData.m_colorImage = material->GetPropertyValue(macroColorTextureMapIndex).GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
|
||||
macroMaterialData.m_normalImage = material->GetPropertyValue(macroNormalTextureMapIndex).GetValue<AZ::Data::Instance<AZ::RPI::Image>>();
|
||||
macroMaterialData.m_normalFlipX = material->GetPropertyValue(macroNormalFlipXIndex).GetValue<bool>();
|
||||
macroMaterialData.m_normalFlipY = material->GetPropertyValue(macroNormalFlipYIndex).GetValue<bool>();
|
||||
macroMaterialData.m_normalFactor = material->GetPropertyValue(macroNormalFactorIndex).GetValue<float>();
|
||||
macroMaterialData = newMaterialData;
|
||||
|
||||
if (macroMaterialData.m_bounds.IsValid())
|
||||
{
|
||||
@@ -783,7 +746,7 @@ namespace Terrain
|
||||
// larger but this will limit how much is rendered.
|
||||
}
|
||||
|
||||
TerrainFeatureProcessor::MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId)
|
||||
MacroMaterialData* TerrainFeatureProcessor::FindMacroMaterial(AZ::EntityId entityId)
|
||||
{
|
||||
for (MacroMaterialData& data : m_macroMaterials.GetDataVector())
|
||||
{
|
||||
@@ -795,7 +758,7 @@ namespace Terrain
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TerrainFeatureProcessor::MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId)
|
||||
MacroMaterialData& TerrainFeatureProcessor::FindOrCreateMacroMaterial(AZ::EntityId entityId)
|
||||
{
|
||||
MacroMaterialData* dataPtr = FindMacroMaterial(entityId);
|
||||
if (dataPtr != nullptr)
|
||||
|
||||
@@ -112,18 +112,6 @@ namespace Terrain
|
||||
AZStd::fixed_vector<uint16_t, MaxMaterialsPerSector> m_macroMaterials;
|
||||
};
|
||||
|
||||
struct MacroMaterialData
|
||||
{
|
||||
AZ::EntityId m_entityId;
|
||||
AZ::Aabb m_bounds = AZ::Aabb::CreateNull();
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::Image> m_colorImage;
|
||||
AZ::Data::Instance<AZ::RPI::Image> m_normalImage;
|
||||
bool m_normalFlipX{ false };
|
||||
bool m_normalFlipY{ false };
|
||||
float m_normalFactor{ 0.0f };
|
||||
};
|
||||
|
||||
// AZ::RPI::MaterialReloadNotificationBus::Handler overrides...
|
||||
void OnMaterialReinitialized(const MaterialInstance& material) override;
|
||||
|
||||
@@ -132,8 +120,8 @@ namespace Terrain
|
||||
void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override;
|
||||
|
||||
// TerrainMacroMaterialNotificationBus overrides...
|
||||
void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, MaterialInstance material, const AZ::Aabb& region) override;
|
||||
void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, MaterialInstance material) override;
|
||||
void OnTerrainMacroMaterialCreated(AZ::EntityId entityId, const MacroMaterialData& material) override;
|
||||
void OnTerrainMacroMaterialChanged(AZ::EntityId entityId, const MacroMaterialData& material) override;
|
||||
void OnTerrainMacroMaterialRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override;
|
||||
void OnTerrainMacroMaterialDestroyed(AZ::EntityId entityId) override;
|
||||
|
||||
@@ -143,7 +131,7 @@ namespace Terrain
|
||||
|
||||
void UpdateTerrainData();
|
||||
void PrepareMaterialData();
|
||||
void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, MaterialInstance material);
|
||||
void UpdateMacroMaterialData(MacroMaterialData& macroMaterialData, const MacroMaterialData& newMaterialData);
|
||||
|
||||
void ProcessSurfaces(const FeatureProcessor::RenderPacket& process);
|
||||
|
||||
|
||||
@@ -12,11 +12,22 @@
|
||||
#include <AzCore/EBus/EBus.h>
|
||||
#include <AzCore/Math/Vector2.h>
|
||||
#include <AzCore/Math/Aabb.h>
|
||||
|
||||
#include <Atom/RPI.Public/Material/Material.h>
|
||||
#include <Atom/RPI.Reflect/Image/Image.h>
|
||||
|
||||
namespace Terrain
|
||||
{
|
||||
struct MacroMaterialData
|
||||
{
|
||||
AZ::EntityId m_entityId;
|
||||
AZ::Aabb m_bounds = AZ::Aabb::CreateNull();
|
||||
|
||||
AZ::Data::Instance<AZ::RPI::Image> m_colorImage;
|
||||
AZ::Data::Instance<AZ::RPI::Image> m_normalImage;
|
||||
bool m_normalFlipX{ false };
|
||||
bool m_normalFlipY{ false };
|
||||
float m_normalFactor{ 0.0f };
|
||||
};
|
||||
|
||||
/**
|
||||
* Request terrain macro material data.
|
||||
*/
|
||||
@@ -32,7 +43,7 @@ namespace Terrain
|
||||
virtual ~TerrainMacroMaterialRequests() = default;
|
||||
|
||||
// Get the terrain macro material and the region that it covers.
|
||||
virtual void GetTerrainMacroMaterialData(AZ::Data::Instance<AZ::RPI::Material>& macroMaterial, AZ::Aabb& macroMaterialRegion) = 0;
|
||||
virtual MacroMaterialData GetTerrainMacroMaterialData() = 0;
|
||||
};
|
||||
|
||||
using TerrainMacroMaterialRequestBus = AZ::EBus<TerrainMacroMaterialRequests>;
|
||||
@@ -51,14 +62,12 @@ namespace Terrain
|
||||
|
||||
virtual void OnTerrainMacroMaterialCreated(
|
||||
[[maybe_unused]] AZ::EntityId macroMaterialEntity,
|
||||
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> macroMaterial,
|
||||
[[maybe_unused]] const AZ::Aabb& macroMaterialRegion)
|
||||
[[maybe_unused]] const MacroMaterialData& macroMaterial)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void OnTerrainMacroMaterialChanged(
|
||||
[[maybe_unused]] AZ::EntityId macroMaterialEntity,
|
||||
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> macroMaterial)
|
||||
[[maybe_unused]] AZ::EntityId macroMaterialEntity, [[maybe_unused]] const MacroMaterialData& macroMaterial)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user