Terrain default surface material (#7481)

Terrain default surface material
This adds the ability to set a default material on detail material regions as a fallback material for when there are no materials for an assigned surface tag, or there's only one surface tag but its weight is less than 1.0.

This also fixes some issues
- The terrain surface list component now correctly sends notifications on tag changes.
- The terrain area material notifications bus now has two separate change notifications - one for material, the other for tag
- The terrain renderer will now only consider a single region per point queried instead of any region that might have a matching surface tag.
monroegm-disable-blank-issue-2
Ken Pruiksma 4 years ago committed by GitHub
parent e5800d738a
commit dc5d50a4ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -8,21 +8,19 @@
#include <TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h> #include <TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Asset/AssetManager.h> #include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzCore/Asset/AssetSerializer.h> #include <AzCore/Asset/AssetSerializer.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/RTTI/BehaviorContext.h> #include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h> #include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Aabb.h>
#include <AzCore/std/smart_ptr/make_shared.h> #include <AzCore/std/smart_ptr/make_shared.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h> #include <AtomLyIntegration/CommonFeatures/Material/MaterialComponentBus.h>
#include <GradientSignal/Ebuses/GradientRequestBus.h>
#include <SurfaceData/SurfaceDataProviderRequestBus.h>
#include <TerrainSystem/TerrainSystemBus.h> #include <TerrainSystem/TerrainSystemBus.h>
namespace Terrain namespace Terrain
@ -38,15 +36,14 @@ namespace Terrain
if (auto edit = serialize->GetEditContext()) if (auto edit = serialize->GetEditContext())
{ {
edit->Class<TerrainSurfaceMaterialMapping>("Terrain Surface Gradient Mapping", "Mapping between a surface and a material.") edit->Class<TerrainSurfaceMaterialMapping>("Terrain surface gradient mapping", "Mapping between a surface and a material.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainSurfaceMaterialMapping::m_surfaceTag, "Surface tag", "Surface type to map to a material.")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &TerrainSurfaceMaterialMapping::m_surfaceTag, "Surface Tag", "Surface type to map to a material.") ->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialMapping::m_materialAsset, "Material asset", "")
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialMapping::m_materialAsset, "Material Asset", "") ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
->Attribute(AZ::Edit::Attributes::ShowProductAssetFileName, true)
; ;
} }
} }
@ -60,7 +57,8 @@ namespace Terrain
if (serialize) if (serialize)
{ {
serialize->Class<TerrainSurfaceMaterialsListConfig, AZ::ComponentConfig>() serialize->Class<TerrainSurfaceMaterialsListConfig, AZ::ComponentConfig>()
->Version(1) ->Version(2)
->Field("DefaultMaterial", &TerrainSurfaceMaterialsListConfig::m_defaultSurfaceMaterial)
->Field("Mappings", &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials); ->Field("Mappings", &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials);
AZ::EditContext* edit = serialize->GetEditContext(); AZ::EditContext* edit = serialize->GetEditContext();
@ -68,10 +66,13 @@ namespace Terrain
{ {
edit->Class<TerrainSurfaceMaterialsListConfig>( edit->Class<TerrainSurfaceMaterialsListConfig>(
"Terrain Surface Material List Component", "Provide mapping between surfaces and render materials.") "Terrain Surface Material List Component", "Provide mapping between surfaces and render materials.")
->SetDynamicEditDataProvider(&TerrainSurfaceMaterialsListConfig::GetDynamicData)
->ClassElement(AZ::Edit::ClassElements::EditorData, "") ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show) ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Show)
->Attribute(AZ::Edit::Attributes::AutoExpand, true) ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_defaultSurfaceMaterial,
"Default Material", "The default material to fall back to where no other material surface mappings exist.")
->DataElement( ->DataElement(
AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials, AZ::Edit::UIHandlers::Default, &TerrainSurfaceMaterialsListConfig::m_surfaceMaterials,
"Material Mappings", "Maps surfaces to materials."); "Material Mappings", "Maps surfaces to materials.");
@ -79,6 +80,26 @@ namespace Terrain
} }
} }
TerrainSurfaceMaterialsListConfig::TerrainSurfaceMaterialsListConfig()
{
m_hideSurfaceTagData.m_attributes.push_back(
{
AZ::Edit::Attributes::Visibility,
aznew AZ::Edit::AttributeData<AZ::Crc32>(AZ::Edit::PropertyVisibility::Hide)
}
);
}
const AZ::Edit::ElementData* TerrainSurfaceMaterialsListConfig::GetDynamicData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& )
{
const TerrainSurfaceMaterialsListConfig* owner = reinterpret_cast<const TerrainSurfaceMaterialsListConfig*>(handlerPtr);
if (elementPtr == &owner->m_defaultSurfaceMaterial.m_surfaceTag)
{
return &owner->m_hideSurfaceTagData;
}
return nullptr;
}
void TerrainSurfaceMaterialsListComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services) void TerrainSurfaceMaterialsListComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{ {
services.push_back(AZ_CRC_CE("TerrainMaterialProviderService")); services.push_back(AZ_CRC_CE("TerrainMaterialProviderService"));
@ -116,15 +137,21 @@ namespace Terrain
{ {
m_cachedAabb = AZ::Aabb::CreateNull(); m_cachedAabb = AZ::Aabb::CreateNull();
// Set all the materials as inactive and start loading. auto checkLoadMaterial = [&](TerrainSurfaceMaterialMapping& material)
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
{ {
if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid()) if (material.m_materialAsset.GetId().IsValid())
{ {
surfaceMaterialMapping.m_active = false; material.m_active = false;
surfaceMaterialMapping.m_materialAsset.QueueLoad(); material.m_materialAsset.QueueLoad();
AZ::Data::AssetBus::MultiHandler::BusConnect(surfaceMaterialMapping.m_materialAsset.GetId()); AZ::Data::AssetBus::MultiHandler::BusConnect(material.m_materialAsset.GetId());
} }
};
// Set all the materials as inactive and start loading.
checkLoadMaterial(m_configuration.m_defaultSurfaceMaterial);
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
{
checkLoadMaterial(surfaceMaterialMapping);
} }
// Announce initial shape using OnShapeChanged // Announce initial shape using OnShapeChanged
@ -135,24 +162,35 @@ namespace Terrain
{ {
TerrainAreaMaterialRequestBus::Handler::BusDisconnect(); TerrainAreaMaterialRequestBus::Handler::BusDisconnect();
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) auto checkResetMaterial = [&](TerrainSurfaceMaterialMapping& material)
{ {
if (surfaceMaterialMapping.m_materialAsset.GetId().IsValid()) if (material.m_materialAsset.GetId().IsValid())
{ {
AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_materialAsset.GetId()); AZ::Data::AssetBus::MultiHandler::BusDisconnect(material.m_materialAsset.GetId());
surfaceMaterialMapping.m_materialAsset.Release(); material.m_materialAsset.Release();
surfaceMaterialMapping.m_materialInstance.reset(); material.m_materialInstance.reset();
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); material.m_activeMaterialAssetId = AZ::Data::AssetId();
} }
};
checkResetMaterial(m_configuration.m_defaultSurfaceMaterial);
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
{
checkResetMaterial(surfaceMaterialMapping);
} }
HandleMaterialStateChanges(); HandleMaterialStateChanges();
} }
int TerrainSurfaceMaterialsListComponent::CountMaterialIDInstances(AZ::Data::AssetId id) const int TerrainSurfaceMaterialsListComponent::CountMaterialIdInstances(AZ::Data::AssetId id) const
{ {
int count = 0; int count = 0;
if (m_configuration.m_defaultSurfaceMaterial.m_activeMaterialAssetId == id)
{
count++;
}
for (const auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) for (const auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
{ {
if (surfaceMaterialMapping.m_activeMaterialAssetId == id) if (surfaceMaterialMapping.m_activeMaterialAssetId == id)
@ -169,28 +207,63 @@ namespace Terrain
bool anyMaterialIsActive = false; bool anyMaterialIsActive = false;
bool anyMaterialWasAlreadyActive = false; bool anyMaterialWasAlreadyActive = false;
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
{ {
const bool wasPreviouslyActive = surfaceMaterialMapping.m_active; // Handle default material first
const bool isNowActive = (surfaceMaterialMapping.m_materialInstance != nullptr); auto& defaultMaterial = m_configuration.m_defaultSurfaceMaterial;
const bool wasPreviouslyActive = defaultMaterial.m_active;
defaultMaterial.m_active = (defaultMaterial.m_materialInstance != nullptr);
if (wasPreviouslyActive) anyMaterialWasAlreadyActive = wasPreviouslyActive;
anyMaterialIsActive = defaultMaterial.m_active;
if (!wasPreviouslyActive && !defaultMaterial.m_active)
{ {
anyMaterialWasAlreadyActive = true; // A material has been assigned but has not yet completed loading.
} }
else if (!wasPreviouslyActive && defaultMaterial.m_active)
{
TerrainAreaMaterialNotificationBus::Broadcast(
&TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialCreated, GetEntityId(),
defaultMaterial.m_materialInstance);
defaultMaterial.m_previousChangeId = defaultMaterial.m_materialInstance->GetCurrentChangeId();
}
else if (wasPreviouslyActive && !defaultMaterial.m_active)
{
// Don't disconnect from the AssetBus if this material is mapped more than once.
if (CountMaterialIdInstances(defaultMaterial.m_activeMaterialAssetId) == 1)
{
AZ::Data::AssetBus::MultiHandler::BusDisconnect(defaultMaterial.m_activeMaterialAssetId);
}
defaultMaterial = {};
if (isNowActive) TerrainAreaMaterialNotificationBus::Broadcast(
&TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialDestroyed, GetEntityId());
}
else if (defaultMaterial.m_materialInstance->GetAssetId() != defaultMaterial.m_activeMaterialAssetId ||
defaultMaterial.m_materialInstance->GetCurrentChangeId() != defaultMaterial.m_previousChangeId)
{ {
anyMaterialIsActive = true; defaultMaterial.m_previousChangeId = defaultMaterial.m_materialInstance->GetCurrentChangeId();
defaultMaterial.m_activeMaterialAssetId = defaultMaterial.m_materialInstance->GetAssetId();
TerrainAreaMaterialNotificationBus::Broadcast(
&TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialChanged, GetEntityId(), defaultMaterial.m_materialInstance);
} }
}
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
{
const bool wasPreviouslyActive = surfaceMaterialMapping.m_active;
surfaceMaterialMapping.m_active = surfaceMaterialMapping.m_materialInstance != nullptr;
surfaceMaterialMapping.m_active = isNowActive; anyMaterialWasAlreadyActive = anyMaterialWasAlreadyActive || wasPreviouslyActive;
anyMaterialIsActive = anyMaterialIsActive || surfaceMaterialMapping.m_active;
if (!wasPreviouslyActive && !isNowActive) if (!wasPreviouslyActive && !surfaceMaterialMapping.m_active)
{ {
// A material has been assigned but has not yet completed loading. // A material has been assigned but has not yet completed loading.
} }
else if (!wasPreviouslyActive && isNowActive) else if (!wasPreviouslyActive && surfaceMaterialMapping.m_active)
{ {
// Remember the asset id so we can disconnect from the AssetBus if the material asset is removed. // Remember the asset id so we can disconnect from the AssetBus if the material asset is removed.
surfaceMaterialMapping.m_activeMaterialAssetId = surfaceMaterialMapping.m_materialAsset.GetId(); surfaceMaterialMapping.m_activeMaterialAssetId = surfaceMaterialMapping.m_materialAsset.GetId();
@ -199,27 +272,47 @@ namespace Terrain
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingCreated, GetEntityId(), &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingCreated, GetEntityId(),
surfaceMaterialMapping.m_surfaceTag, surfaceMaterialMapping.m_surfaceTag,
surfaceMaterialMapping.m_materialInstance); surfaceMaterialMapping.m_materialInstance);
surfaceMaterialMapping.m_previousChangeId = surfaceMaterialMapping.m_materialInstance->GetCurrentChangeId();
surfaceMaterialMapping.m_previousTag = surfaceMaterialMapping.m_surfaceTag;
} }
else if (wasPreviouslyActive && !isNowActive) else if (wasPreviouslyActive && !surfaceMaterialMapping.m_active)
{ {
// Don't disconnect from the AssetBus if this material is mapped more than once. // Don't disconnect from the AssetBus if this material is mapped more than once.
if (CountMaterialIDInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1) if (CountMaterialIdInstances(surfaceMaterialMapping.m_activeMaterialAssetId) == 1)
{ {
AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId); AZ::Data::AssetBus::MultiHandler::BusDisconnect(surfaceMaterialMapping.m_activeMaterialAssetId);
} }
surfaceMaterialMapping.m_activeMaterialAssetId = AZ::Data::AssetId(); surfaceMaterialMapping.m_activeMaterialAssetId = {};
surfaceMaterialMapping.m_previousChangeId = AZ::RPI::Material::DEFAULT_CHANGE_ID;
surfaceMaterialMapping.m_previousTag = {};
TerrainAreaMaterialNotificationBus::Broadcast( TerrainAreaMaterialNotificationBus::Broadcast(
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingDestroyed, GetEntityId(), &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingDestroyed, GetEntityId(),
surfaceMaterialMapping.m_surfaceTag); surfaceMaterialMapping.m_surfaceTag);
} }
else else
{ {
TerrainAreaMaterialNotificationBus::Broadcast( if (surfaceMaterialMapping.m_previousTag != surfaceMaterialMapping.m_surfaceTag)
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingChanged, GetEntityId(), {
surfaceMaterialMapping.m_surfaceTag, TerrainAreaMaterialNotificationBus::Broadcast(
surfaceMaterialMapping.m_materialInstance); &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingTagChanged, GetEntityId(),
surfaceMaterialMapping.m_previousTag,
surfaceMaterialMapping.m_surfaceTag);
surfaceMaterialMapping.m_previousTag = surfaceMaterialMapping.m_surfaceTag;
}
if (surfaceMaterialMapping.m_materialInstance->GetAssetId() != surfaceMaterialMapping.m_activeMaterialAssetId ||
surfaceMaterialMapping.m_materialInstance->GetCurrentChangeId() != surfaceMaterialMapping.m_previousChangeId)
{
surfaceMaterialMapping.m_previousChangeId = surfaceMaterialMapping.m_materialInstance->GetCurrentChangeId();
surfaceMaterialMapping.m_activeMaterialAssetId = surfaceMaterialMapping.m_materialInstance->GetAssetId();
TerrainAreaMaterialNotificationBus::Broadcast(
&TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingMaterialChanged, GetEntityId(),
surfaceMaterialMapping.m_surfaceTag,
surfaceMaterialMapping.m_materialInstance);
}
} }
} }
@ -290,14 +383,28 @@ namespace Terrain
void TerrainSurfaceMaterialsListComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) void TerrainSurfaceMaterialsListComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{ {
// Find the missing material instance with the correct id. // Find the missing material instance with the correct id.
for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) auto checkUpdateMaterialAsset = [](TerrainSurfaceMaterialMapping& mapping, const AZ::Data::Asset<AZ::Data::AssetData>& asset) -> bool
{
if (mapping.m_materialAsset.GetId() == asset.GetId() &&
(!mapping.m_materialInstance || mapping.m_materialInstance->GetAssetId() != mapping.m_materialAsset.GetId()))
{
mapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(mapping.m_materialAsset);
mapping.m_materialAsset.Release();
return true;
}
return false;
};
// First check the default material
if (!checkUpdateMaterialAsset(m_configuration.m_defaultSurfaceMaterial, asset))
{ {
if (surfaceMaterialMapping.m_materialAsset.GetId() == asset.GetId() && // If the default materail wasn't updated, then check all the surface material mappings.
(!surfaceMaterialMapping.m_materialInstance || for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials)
surfaceMaterialMapping.m_materialInstance->GetAssetId() != surfaceMaterialMapping.m_materialAsset.GetId()))
{ {
surfaceMaterialMapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(surfaceMaterialMapping.m_materialAsset); if (checkUpdateMaterialAsset(surfaceMaterialMapping, asset))
surfaceMaterialMapping.m_materialAsset.Release(); {
break;
}
} }
} }
HandleMaterialStateChanges(); HandleMaterialStateChanges();

@ -8,15 +8,18 @@
#pragma once #pragma once
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <AzCore/Asset/AssetCommon.h> #include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h> #include <AzCore/Component/Component.h>
#include <AzCore/Serialization/EditContext.h>
#include <Atom/Feature/Material/MaterialAssignment.h>
#include <Atom/RPI.Reflect/Material/MaterialAsset.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h> #include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <SurfaceData/SurfaceDataTypes.h> #include <SurfaceData/SurfaceDataTypes.h>
#include <TerrainRenderer/TerrainAreaMaterialRequestBus.h> #include <TerrainRenderer/TerrainAreaMaterialRequestBus.h>
namespace LmbrCentral namespace LmbrCentral
{ {
template<typename, typename> template<typename, typename>
@ -27,16 +30,20 @@ namespace Terrain
{ {
struct TerrainSurfaceMaterialMapping final struct TerrainSurfaceMaterialMapping final
{ {
public:
AZ_CLASS_ALLOCATOR(TerrainSurfaceMaterialMapping, AZ::SystemAllocator, 0); AZ_CLASS_ALLOCATOR(TerrainSurfaceMaterialMapping, AZ::SystemAllocator, 0);
AZ_RTTI(TerrainSurfaceMaterialMapping, "{37D2A586-CDDD-4FB7-A7D6-0B4CC575AB8C}"); AZ_RTTI(TerrainSurfaceMaterialMapping, "{37D2A586-CDDD-4FB7-A7D6-0B4CC575AB8C}");
static void Reflect(AZ::ReflectContext* context); static void Reflect(AZ::ReflectContext* context);
SurfaceData::SurfaceTag m_surfaceTag;
AZ::Data::AssetId m_activeMaterialAssetId;
AZ::Data::Asset<AZ::RPI::MaterialAsset> m_materialAsset; AZ::Data::Asset<AZ::RPI::MaterialAsset> m_materialAsset;
AZ::Data::Instance<AZ::RPI::Material> m_materialInstance; AZ::Data::Instance<AZ::RPI::Material> m_materialInstance;
AZ::Data::AssetId m_activeMaterialAssetId;
AZ::RPI::Material::ChangeId m_previousChangeId = AZ::RPI::Material::DEFAULT_CHANGE_ID;
// Surface tags not used by default material
SurfaceData::SurfaceTag m_surfaceTag;
SurfaceData::SurfaceTag m_previousTag;
bool m_active = false; bool m_active = false;
}; };
@ -47,7 +54,13 @@ namespace Terrain
AZ_RTTI(TerrainSurfaceMaterialsListConfig, "{68A1CB1B-C835-4C3A-8D1C-08692E07711A}", AZ::ComponentConfig); AZ_RTTI(TerrainSurfaceMaterialsListConfig, "{68A1CB1B-C835-4C3A-8D1C-08692E07711A}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context); static void Reflect(AZ::ReflectContext* context);
TerrainSurfaceMaterialsListConfig();
TerrainSurfaceMaterialMapping m_defaultSurfaceMaterial;
AZStd::vector<TerrainSurfaceMaterialMapping> m_surfaceMaterials; AZStd::vector<TerrainSurfaceMaterialMapping> m_surfaceMaterials;
private:
static const AZ::Edit::ElementData* GetDynamicData(const void* handlerPtr, const void* elementPtr, const AZ::Uuid& elementType);
AZ::Edit::ElementData m_hideSurfaceTagData;
}; };
class TerrainSurfaceMaterialsListComponent class TerrainSurfaceMaterialsListComponent
@ -78,7 +91,7 @@ namespace Terrain
private: private:
void HandleMaterialStateChanges(); void HandleMaterialStateChanges();
int CountMaterialIDInstances(AZ::Data::AssetId id) const; int CountMaterialIdInstances(AZ::Data::AssetId id) const;
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// ShapeComponentNotificationsBus // ShapeComponentNotificationsBus

@ -45,6 +45,27 @@ namespace Terrain
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
//! The default surface material has been assigned and loaded
virtual void OnTerrainDefaultSurfaceMaterialCreated(
[[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> material)
{
}
//! The default surface material has been unassigned
virtual void OnTerrainDefaultSurfaceMaterialDestroyed(
[[maybe_unused]] AZ::EntityId entityId)
{
}
//! The default surface material has been changed to a different material
virtual void OnTerrainDefaultSurfaceMaterialChanged(
[[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> newMaterial)
{
}
//! A loaded material mapped to a valid surface tag has been created
virtual void OnTerrainSurfaceMaterialMappingCreated( virtual void OnTerrainSurfaceMaterialMappingCreated(
[[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] SurfaceData::SurfaceTag surface, [[maybe_unused]] SurfaceData::SurfaceTag surface,
@ -52,19 +73,30 @@ namespace Terrain
{ {
} }
//! Either the material or surface tag was unassigned, making this mapping invalid
virtual void OnTerrainSurfaceMaterialMappingDestroyed( virtual void OnTerrainSurfaceMaterialMappingDestroyed(
[[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] SurfaceData::SurfaceTag surface) [[maybe_unused]] SurfaceData::SurfaceTag surface)
{ {
} }
virtual void OnTerrainSurfaceMaterialMappingChanged( //! The surface tag has changed to tag for an existing material
virtual void OnTerrainSurfaceMaterialMappingTagChanged(
[[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] SurfaceData::SurfaceTag oldSurface,
[[maybe_unused]] SurfaceData::SurfaceTag newSurface)
{
}
//! The material has changed for an existing surface tag
virtual void OnTerrainSurfaceMaterialMappingMaterialChanged(
[[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] SurfaceData::SurfaceTag surface, [[maybe_unused]] SurfaceData::SurfaceTag surface,
[[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> material) [[maybe_unused]] AZ::Data::Instance<AZ::RPI::Material> material)
{ {
} }
//! The bounds of this set of surface material mappings has changed
virtual void OnTerrainSurfaceMaterialMappingRegionChanged( virtual void OnTerrainSurfaceMaterialMappingRegionChanged(
[[maybe_unused]] AZ::EntityId entityId, [[maybe_unused]] AZ::EntityId entityId,
[[maybe_unused]] const AZ::Aabb& oldRegion, [[maybe_unused]] const AZ::Aabb& oldRegion,

@ -106,6 +106,8 @@ namespace Terrain
return; return;
} }
InitializePassthroughDetailMaterial();
ClipmapBoundsDescriptor desc; ClipmapBoundsDescriptor desc;
desc.m_clipmapUpdateMultiple = 1; desc.m_clipmapUpdateMultiple = 1;
desc.m_clipToWorldScale = DetailTextureScale; desc.m_clipToWorldScale = DetailTextureScale;
@ -260,20 +262,73 @@ namespace Terrain
m_dirtyDetailRegion.AddAabb(dirtyRegion); m_dirtyDetailRegion.AddAabb(dirtyRegion);
} }
} }
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material)
{
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
// Validate that the surface tag is new bool TerrainDetailMaterialManager::ForSurfaceTag(DetailMaterialListRegion& materialRegion,
SurfaceData::SurfaceTag surfaceTag, DefaultMaterialSurfaceCallback callback)
{
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces)
{ {
if (surface.m_surfaceTag == surfaceTag) if (surface.m_surfaceTag == surfaceTag)
{ {
AZ_Error(TerrainDetailMaterialManagerName, false, "Already have a surface material mapping for this surface tag."); callback(surface);
return; return true;
} }
} }
return false;
}
void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialCreated(AZ::EntityId entityId, MaterialInstance material)
{
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
AZ_Error("TerrainDetailMaterialManager", materialRegion.m_defaultDetailMaterialId == InvalidDetailMaterailId,
"Default detail material created but was already set for this region.");
materialRegion.m_defaultDetailMaterialId = CreateOrUpdateDetailMaterial(material);
m_detailMaterials.GetData(materialRegion.m_defaultDetailMaterialId).refCount++;
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
}
void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialDestroyed(AZ::EntityId entityId)
{
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
if (materialRegion == nullptr)
{
AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialDestroyed() called for region that doesn't exist.");
return;
}
CheckDetailMaterialForDeletion(materialRegion->m_defaultDetailMaterialId);
materialRegion->m_defaultDetailMaterialId = InvalidDetailMaterailId;
}
void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialChanged(AZ::EntityId entityId, MaterialInstance newMaterial)
{
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
if (materialRegion == nullptr)
{
AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialChanged() called for region that doesn't exist.");
return;
}
// Update existing entry or create a new material entry
uint16_t materialId = CreateOrUpdateDetailMaterial(newMaterial);
if (materialRegion->m_defaultDetailMaterialId != materialId)
{
++m_detailMaterials.GetData(materialId).refCount;
CheckDetailMaterialForDeletion(materialRegion->m_defaultDetailMaterialId);
materialRegion->m_defaultDetailMaterialId = materialId;
}
}
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material)
{
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
// Validate that the surface tag is new
ForSurfaceTag(materialRegion, surfaceTag, [](DetailMaterialSurface&)
{
AZ_Error(TerrainDetailMaterialManagerName, false, "Already have a surface material mapping for this surface tag.");
});
uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material); uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material);
materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId });
@ -284,52 +339,71 @@ namespace Terrain
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag)
{ {
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions);
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces) [[maybe_unused]] bool found = ForSurfaceTag(materialRegion, surfaceTag,
[&](DetailMaterialSurface& surface)
{ {
if (surface.m_surfaceTag == surfaceTag) CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
{
CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag) if (surface.m_surfaceTag != materialRegion.m_materialsForSurfaces.back().m_surfaceTag)
{ {
AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back()); AZStd::swap(surface, materialRegion.m_materialsForSurfaces.back());
}
materialRegion.m_materialsForSurfaces.pop_back();
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
return;
} }
} materialRegion.m_materialsForSurfaces.pop_back();
AZ_Error(TerrainDetailMaterialManagerName, false, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed()."); m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
} return;
});
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) AZ_Error(TerrainDetailMaterialManagerName, found, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed().");
}
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingMaterialChanged(
AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material)
{ {
DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
if (materialRegion == nullptr)
{
AZ_Assert(false, "OnTerrainSurfaceMaterialMappingMaterialChanged() called for region that doesn't exist.");
return;
}
bool found = false; // Update existing entry or create a new material entry
uint16_t materialId = CreateOrUpdateDetailMaterial(material); uint16_t materialId = CreateOrUpdateDetailMaterial(material);
for (DetailMaterialSurface& surface : materialRegion.m_materialsForSurfaces)
[[maybe_unused]] bool found = ForSurfaceTag(*materialRegion, surfaceTag,
[&](DetailMaterialSurface& surface)
{ {
if (surface.m_surfaceTag == surfaceTag) if (surface.m_detailMaterialId != materialId)
{ {
found = true; // Updated material was a different asset than the old material, decrement ref count and
if (surface.m_detailMaterialId != materialId) // delete if no other surface tags are using it.
{ ++m_detailMaterials.GetData(materialId).refCount;
++m_detailMaterials.GetData(materialId).refCount; CheckDetailMaterialForDeletion(surface.m_detailMaterialId);
CheckDetailMaterialForDeletion(surface.m_detailMaterialId); surface.m_detailMaterialId = materialId;
surface.m_detailMaterialId = materialId;
}
break;
} }
} m_dirtyDetailRegion.AddAabb(materialRegion->m_region);
});
if (!found) AZ_Assert(found, "OnTerrainSurfaceMaterialMappingMaterialChanged() called for tag that doesn't exist.");
}
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingTagChanged(
AZ::EntityId entityId, SurfaceData::SurfaceTag oldTag, SurfaceData::SurfaceTag newTag)
{
DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions);
if (materialRegion == nullptr)
{ {
++m_detailMaterials.GetData(materialId).refCount; AZ_Assert(false, "OnTerrainSurfaceMaterialMappingTagChanged() called for region that doesn't exist.");
materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, materialId }); return;
} }
m_dirtyDetailRegion.AddAabb(materialRegion.m_region);
[[maybe_unused]] bool found = ForSurfaceTag(*materialRegion, oldTag,
[&](DetailMaterialSurface& surface)
{
surface.m_surfaceTag = newTag;
m_dirtyDetailRegion.AddAabb(materialRegion->m_region);
});
AZ_Assert(found, "OnTerrainSurfaceMaterialMappingTagChanged() called for tag that doesn't exist.");
} }
void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion)
@ -667,23 +741,38 @@ namespace Terrain
bool isFirstMaterial = true; bool isFirstMaterial = true;
float firstWeight = 0.0f; float firstWeight = 0.0f;
AZ::Vector2 position(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY()); AZ::Vector2 position(surfacePoint.m_position.GetX(), surfacePoint.m_position.GetY());
const DetailMaterialListRegion* region = FindRegionForPosition(position);
if (region == nullptr)
{
pixels.at(index).m_material1 = m_passthroughMaterialId;
++index;
return;
}
for (const auto& surfaceTagWeight : surfacePoint.m_surfaceTags) for (const auto& surfaceTagWeight : surfacePoint.m_surfaceTags)
{ {
if (surfaceTagWeight.m_weight > 0.0f) if (surfaceTagWeight.m_weight > 0.0f)
{ {
AZ::Crc32 surfaceType = surfaceTagWeight.m_surfaceType; AZ::Crc32 surfaceType = surfaceTagWeight.m_surfaceType;
uint16_t materialId = GetDetailMaterialForSurfaceTypeAndPosition(surfaceType, position); uint16_t materialId = GetDetailMaterialForSurfaceType(*region, surfaceType);
if (materialId != m_detailMaterials.NoFreeSlot && materialId < 255) if (materialId < 255)
{ {
if (isFirstMaterial) if (isFirstMaterial)
{ {
// First material is valid. Save its weight to calculate blend later
pixels.at(index).m_material1 = aznumeric_cast<uint8_t>(materialId); pixels.at(index).m_material1 = aznumeric_cast<uint8_t>(materialId);
firstWeight = surfaceTagWeight.m_weight; firstWeight = surfaceTagWeight.m_weight;
// m_blend only needs to be calculated is material 2 is found, otherwise the initial value of 0 is correct.
isFirstMaterial = false; isFirstMaterial = false;
static constexpr float MaxValueBeforeRounding = 254.5f / 255.0f;
if (firstWeight >= MaxValueBeforeRounding)
{
break;
}
} }
else else
{ {
// Second material is valid, weight is relative based on first material's weight.
pixels.at(index).m_material2 = aznumeric_cast<uint8_t>(materialId); pixels.at(index).m_material2 = aznumeric_cast<uint8_t>(materialId);
float totalWeight = firstWeight + surfaceTagWeight.m_weight; float totalWeight = firstWeight + surfaceTagWeight.m_weight;
float blendWeight = 1.0f - (firstWeight / totalWeight); float blendWeight = 1.0f - (firstWeight / totalWeight);
@ -691,11 +780,37 @@ namespace Terrain
break; break;
} }
} }
continue; // search for second material
} }
else else
{ {
break; // since the list is ordered, no other materials are in the list with positive weights. // No more valid materials in list since surfaceTagWeight is ordered.
uint8_t defaultMaterial = region->m_defaultDetailMaterialId == InvalidDetailMaterailId ? m_passthroughMaterialId :
aznumeric_cast<uint8_t>(m_detailMaterials.GetData(region->m_defaultDetailMaterialId).m_detailMaterialBufferIndex);
if (isFirstMaterial)
{
// Only one material and it's the default material.
pixels.at(index).m_material1 = defaultMaterial;
}
else
{
// Second material is default, weight is exactly what the first material requested
pixels.at(index).m_material2 = defaultMaterial;
float blendWeight = 1.0f - AZStd::clamp<float>(firstWeight, 0.0f, 1.0f);
pixels.at(index).m_blend = aznumeric_cast<uint8_t>(AZStd::round(blendWeight * 255.0f));
}
}
if (pixels.at(index).m_material1 == pixels.at(index).m_material2)
{
// If the materials are the same, then make the blend 100% on the first id so the shader
// doesn't blend identical materials
pixels.at(index).m_blend = 0;
} }
break;
} }
++index; ++index;
}; };
@ -723,23 +838,37 @@ namespace Terrain
m_detailTextureImage->UpdateImageContents(imageUpdateRequest); m_detailTextureImage->UpdateImageContents(imageUpdateRequest);
} }
uint16_t TerrainDetailMaterialManager::GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position) uint16_t TerrainDetailMaterialManager::GetDetailMaterialForSurfaceType(const DetailMaterialListRegion& materialRegion, AZ::Crc32 surfaceType) const
{
for (const auto& materialSurface : materialRegion.m_materialsForSurfaces)
{
if (materialSurface.m_surfaceTag == surfaceType)
{
return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex;
}
}
return InvalidDetailMaterailId;
}
auto TerrainDetailMaterialManager::FindRegionForPosition(const AZ::Vector2& position) const -> const DetailMaterialListRegion*
{ {
for (const auto& materialRegion : m_detailMaterialRegions.GetDataVector()) for (const auto& materialRegion : m_detailMaterialRegions.GetDataVector())
{ {
if (materialRegion.m_region.Contains(AZ::Vector3(position.GetX(), position.GetY(), 0.0f))) if (materialRegion.m_region.Contains(AZ::Vector3(position.GetX(), position.GetY(), 0.0f)))
{ {
for (const auto& materialSurface : materialRegion.m_materialsForSurfaces) return &materialRegion;
{
if (materialSurface.m_surfaceTag == surfaceType)
{
return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex;
}
}
} }
} }
return m_detailMaterials.NoFreeSlot; return nullptr;
}
void TerrainDetailMaterialManager::InitializePassthroughDetailMaterial()
{
m_passthroughMaterialId = aznumeric_cast<uint8_t>(m_detailMaterialShaderData.Reserve());
DetailMaterialShaderData& materialShaderData = m_detailMaterialShaderData.GetElement(m_passthroughMaterialId);
// Material defaults to white (1.0, 1.0, 1.0), set the blend mode to multiply so it passes through to the macro material.
materialShaderData.m_flags = DetailTextureFlags::BlendModeMultiply;
} }
auto TerrainDetailMaterialManager::FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container) auto TerrainDetailMaterialManager::FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container)
@ -784,5 +913,5 @@ namespace Terrain
} }
AZ_Assert(false, "Entity Id not found in container.") AZ_Assert(false, "Entity Id not found in container.")
} }
} }

@ -11,6 +11,7 @@
#include <AzCore/base.h> #include <AzCore/base.h>
#include <AzCore/Math/Aabb.h> #include <AzCore/Math/Aabb.h>
#include <AzCore/std/containers/array.h> #include <AzCore/std/containers/array.h>
#include <AzCore/std/function/function_template.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h> #include <AzFramework/Terrain/TerrainDataRequestBus.h>
@ -151,7 +152,11 @@ namespace Terrain
AZ::EntityId m_entityId; AZ::EntityId m_entityId;
AZ::Aabb m_region{AZ::Aabb::CreateNull()}; AZ::Aabb m_region{AZ::Aabb::CreateNull()};
AZStd::vector<DetailMaterialSurface> m_materialsForSurfaces; AZStd::vector<DetailMaterialSurface> m_materialsForSurfaces;
uint16_t m_defaultDetailMaterialId;
}; };
using DetailMaterialContainer = AZ::Render::IndexedDataVector<DetailMaterialData>;
static constexpr auto InvalidDetailMaterailId = DetailMaterialContainer::NoFreeSlot;
// System-level parameters // System-level parameters
static constexpr int32_t DetailTextureSize{ 1024 }; static constexpr int32_t DetailTextureSize{ 1024 };
@ -162,9 +167,14 @@ namespace Terrain
void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override; void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override;
// TerrainAreaMaterialNotificationBus overrides... // TerrainAreaMaterialNotificationBus overrides...
void OnTerrainDefaultSurfaceMaterialCreated(AZ::EntityId entityId, AZ::Data::Instance<AZ::RPI::Material> material) override;
void OnTerrainDefaultSurfaceMaterialDestroyed(AZ::EntityId entityId) override;
void OnTerrainDefaultSurfaceMaterialChanged(AZ::EntityId entityId, AZ::Data::Instance<AZ::RPI::Material> newMaterial) override;
void OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; void OnTerrainSurfaceMaterialMappingCreated(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override;
void OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) override; void OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) override;
void OnTerrainSurfaceMaterialMappingChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; void OnTerrainSurfaceMaterialMappingMaterialChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override;
void OnTerrainSurfaceMaterialMappingTagChanged(
AZ::EntityId entityId, SurfaceData::SurfaceTag oldSurfaceTag, SurfaceData::SurfaceTag newSurfaceTag) override;
void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override; void OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) override;
//! Removes all images from all detail materials from the bindless image array //! Removes all images from all detail materials from the bindless image array
@ -186,22 +196,32 @@ namespace Terrain
//! Updates the detail texture in a given area //! Updates the detail texture in a given area
void UpdateDetailTexture(const AZ::Aabb& worldUpdateAabb, const Aabb2i& textureUpdateAabb); void UpdateDetailTexture(const AZ::Aabb& worldUpdateAabb, const Aabb2i& textureUpdateAabb);
//! Finds the detail material Id for a surface type and position //! Finds the detail material Id for a region and surface type
uint16_t GetDetailMaterialForSurfaceTypeAndPosition(AZ::Crc32 surfaceType, const AZ::Vector2& position); uint16_t GetDetailMaterialForSurfaceType(const DetailMaterialListRegion& materialRegion, AZ::Crc32 surfaceType) const;
//! Finds a region for a position. Returns nullptr if none found.
const DetailMaterialListRegion* FindRegionForPosition(const AZ::Vector2& position) const;
//! Initializes shader data for the default passthrough material which is used when no other detail material is found.
void InitializePassthroughDetailMaterial();
using DefaultMaterialSurfaceCallback = AZStd::function<void(DetailMaterialSurface&)>;
bool ForSurfaceTag(DetailMaterialListRegion& materialRegion,
SurfaceData::SurfaceTag surfaceTag, DefaultMaterialSurfaceCallback callback);
DetailMaterialListRegion* FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container); DetailMaterialListRegion* FindByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container);
DetailMaterialListRegion& FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container); DetailMaterialListRegion& FindOrCreateByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container);
void RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container); void RemoveByEntityId(AZ::EntityId entityId, AZ::Render::IndexedDataVector<DetailMaterialListRegion>& container);
AZStd::shared_ptr<AZ::Render::BindlessImageArrayHandler> m_bindlessImageHandler; AZStd::shared_ptr<AZ::Render::BindlessImageArrayHandler> m_bindlessImageHandler;
AZ::Data::Instance<AZ::RPI::AttachmentImage> m_detailTextureImage; AZ::Data::Instance<AZ::RPI::AttachmentImage> m_detailTextureImage;
AZ::Render::IndexedDataVector<DetailMaterialData> m_detailMaterials; DetailMaterialContainer m_detailMaterials;
AZ::Render::IndexedDataVector<DetailMaterialListRegion> m_detailMaterialRegions; AZ::Render::IndexedDataVector<DetailMaterialListRegion> m_detailMaterialRegions;
AZ::Render::SparseVector<DetailMaterialShaderData> m_detailMaterialShaderData; AZ::Render::SparseVector<DetailMaterialShaderData> m_detailMaterialShaderData;
AZ::Render::GpuBufferHandler m_detailMaterialDataBuffer; AZ::Render::GpuBufferHandler m_detailMaterialDataBuffer;
uint8_t m_passthroughMaterialId = 0;
AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() }; AZ::Aabb m_dirtyDetailRegion{ AZ::Aabb::CreateNull() };
ClipmapBounds m_detailMaterialIdBounds; ClipmapBounds m_detailMaterialIdBounds;
@ -212,6 +232,6 @@ namespace Terrain
bool m_isInitialized{ false }; bool m_isInitialized{ false };
bool m_detailMaterialBufferNeedsUpdate{ false }; bool m_detailMaterialBufferNeedsUpdate{ false };
bool m_detailImageNeedsUpdate{ false }; bool m_detailImageNeedsUpdate{ false };
}; };
} }

Loading…
Cancel
Save