diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp index f2a071a2e5..2bdeb0f3cc 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Processing/ImageConvert.cpp @@ -22,6 +22,8 @@ #include +#include + #include // for texture splitting @@ -352,7 +354,8 @@ namespace ImageProcessingAtom // output conversion log if (m_isSucceed && m_isFinished) { - [[maybe_unused]] const uint32 sizeTotal = m_image->Get()->GetTextureMemory(); + [[maybe_unused]] IImageObjectPtr imageObj = m_image->Get(); + [[maybe_unused]] const uint32 sizeTotal = imageObj->GetTextureMemory(); if (m_input->m_isPreview) { AZ_TracePrintf("Image Processing", "Image (%d bytes) converted in %f seconds\n", sizeTotal, m_processTime); @@ -363,11 +366,10 @@ namespace ImageProcessingAtom } else { - - [[maybe_unused]] const PixelFormatInfo* formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(m_image->Get()->GetPixelFormat()); + [[maybe_unused]] const PixelFormatInfo* formatInfo = CPixelFormats::GetInstance().GetPixelFormatInfo(imageObj->GetPixelFormat()); + [[maybe_unused]] const AZ::RHI::Format rhiFormat = Utils::PixelFormatToRHIFormat(imageObj->GetPixelFormat(), imageObj->HasImageFlags(EIF_SRGBRead)); AZ_TracePrintf("Image Processing", "Image [%dx%d] [%s] converted with preset [%s] [%s] and saved to [%s] (%d bytes) taking %f seconds\n", - m_image->Get()->GetWidth(0), m_image->Get()->GetHeight(0), - formatInfo->szName, + imageObj->GetWidth(0), imageObj->GetHeight(0), AZ::RHI::ToString(rhiFormat), m_input->m_presetSetting.m_name.GetCStr(), m_input->m_filePath.c_str(), m_input->m_outputFolder.c_str(), sizeTotal, m_processTime); diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli index 9794eec4a3..e2ed5018b7 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainDetailHelpers.azsli @@ -8,6 +8,7 @@ #pragma once #include +#include enum DetailTextureFlags { @@ -22,11 +23,11 @@ enum DetailTextureFlags FlipNormalX = 0x00010000, //0b0000'0000'0000'0001'0000'0000'0000'0000 FlipNormalY = 0x00020000, //0b0000'0000'0000'0010'0000'0000'0000'0000 - BlendModeMask = 0x000C0000, //0b0000'0000'0000'1100'0000'0000'0000'0000 - BlendModeLerp = 0x00000000, //0b0000'0000'0000'0000'0000'0000'0000'0000 - BlendModeLinearLight = 0x00040000, //0b0000'0000'0000'0100'0000'0000'0000'0000 - BlendModeMultiply = 0x00080000, //0b0000'0000'0000'1000'0000'0000'0000'0000 - BlendModeOverlay = 0x000C0000, //0b0000'0000'0000'1100'0000'0000'0000'0000 + BlendModeMask = 0x001C0000, //0b0000'0000'0001'1100'0000'0000'0000'0000 + BlendModeLerp = 0x00040000, //0b0000'0000'0000'0100'0000'0000'0000'0000 + BlendModeLinearLight = 0x00080000, //0b0000'0000'0000'1000'0000'0000'0000'0000 + BlendModeMultiply = 0x000C0000, //0b0000'0000'0000'1100'0000'0000'0000'0000 + BlendModeOverlay = 0x00100000, //0b0000'0000'0001'0000'0000'0000'0000'0000 }; struct DetailSurface @@ -118,14 +119,37 @@ uint GetDetailHeightIndex(TerrainSrg::DetailMaterialData materialData) // Detail material value getters -float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) +float3 GetDetailColor(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy, float3 macroColor) { float3 color = materialData.m_baseColor; if ((materialData.m_flags & DetailTextureFlags::UseTextureBaseColor) > 0) { color = TerrainSrg::m_textures[GetDetailColorIndex(materialData)].SampleGrad(TerrainMaterialSrg::m_sampler, uv, ddx, ddy).rgb; + color = TransformColor(color, ColorSpaceId::LinearSRGB, ColorSpaceId::ACEScg); + color = sqrt(color); // Put in a more perceptual space for blending. This will be undone later. } - return color * materialData.m_baseColorFactor; + uint blendMode = materialData.m_flags & DetailTextureFlags::BlendModeMask; + switch (blendMode) + { + case DetailTextureFlags::BlendModeLerp: + color = lerp(color, macroColor, materialData.m_baseColorFactor); + break; + case DetailTextureFlags::BlendModeLinearLight: + color = lerp(color, TextureBlend_LinearLight(macroColor, color), materialData.m_baseColorFactor); + break; + case DetailTextureFlags::BlendModeMultiply: + color = lerp(color, color * macroColor, materialData.m_baseColorFactor); + break; + case DetailTextureFlags::BlendModeOverlay: + color = lerp(color, TextureBlend_Overlay(color, macroColor), materialData.m_baseColorFactor); + break; + case 0: + color *= materialData.m_baseColorFactor; + break; + default: + color = float3(1.0, 0.0, 1.0); // Unknown blend mode. + } + return color; } float3 GetDetailNormal(TerrainSrg::DetailMaterialData materialData, float2 uv, float2 ddx, float2 ddy) @@ -200,7 +224,7 @@ float GetDetailHeight(TerrainSrg::DetailMaterialData materialData, float2 uv, fl return height; } -void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, float2 uv) +void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, float2 uv, float3 macroColor) { TerrainSrg::DetailMaterialData detailMaterialData = TerrainSrg::m_detailMaterialData[materialId]; @@ -224,7 +248,7 @@ void GetDetailSurfaceForMaterial(inout DetailSurface surface, uint materialId, f float2 transformedUvDdx = transformedUvX - transformedUv; float2 transformedUvDdy = transformedUvY - transformedUv; - surface.m_color = GetDetailColor(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy); + surface.m_color = GetDetailColor(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy, macroColor); surface.m_normal = GetDetailNormal(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy); surface.m_roughness = GetDetailRoughness(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy); surface.m_specularF0 = GetDetailSpecularF0(detailMaterialData, transformedUv, transformedUvDdx, transformedUvDdy); @@ -270,15 +294,15 @@ void GetDebugDetailSurface(inout DetailSurface surface, uint material1, uint mat } //Blend a single detail material sample (with two possible material ids) onto a DetailSurface. -void BlendDetailMaterial(inout DetailSurface surface, uint material1, uint material2, float blend, float2 detailUv, float weight) +void BlendDetailMaterial(inout DetailSurface surface, uint material1, uint material2, float blend, float2 detailUv, float weight, float3 macroColor) { DetailSurface tempSurface; - GetDetailSurfaceForMaterial(tempSurface, material1, detailUv); + GetDetailSurfaceForMaterial(tempSurface, material1, detailUv, macroColor); WeightDetailSurface(tempSurface, weight * (1.0 - blend)); AddDetailSurface(surface, tempSurface); if (material2 != 0xFF) { - GetDetailSurfaceForMaterial(tempSurface, material2, detailUv); + GetDetailSurfaceForMaterial(tempSurface, material2, detailUv, macroColor); WeightDetailSurface(tempSurface, weight * blend); AddDetailSurface(surface, tempSurface); } @@ -291,14 +315,18 @@ instance, if detailMaterialIdUv falls perfectly in-between all 4 samples, then e Each sample can have two different detail materials defined with a blend value to determine their relative contribution. The detailUv is used for sampling the textures of each detail material. */ -bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdCoord, float2 detailUv) +bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdCoord, float2 detailUv, float3 macroColor) { float2 textureSize; TerrainSrg::m_detailMaterialIdImage.GetDimensions(textureSize.x, textureSize.y); + int2 intTextureSize = textureSize; - // The detail material id texture wraps since the "center" point can be anywhere in the texture, so mod by textureSize - int2 detailMaterialIdTopLeft = ((int2(detailMaterialIdCoord) % textureSize) + textureSize) % textureSize; - int2 detailMaterialIdBottomRight = (detailMaterialIdTopLeft + 1) % textureSize; + macroColor = sqrt(macroColor); // Put macro color into an approximate perceptual space. + + // The detail material id texture wraps since the "center" point can be anywhere in the texture, so mod by intTextureSize + int2 detailMaterialIdTopLeft = int2(floor(detailMaterialIdCoord)) % intTextureSize; + detailMaterialIdTopLeft = (detailMaterialIdTopLeft + intTextureSize) % intTextureSize; // Make a negative result from modulus positive. + int2 detailMaterialIdBottomRight = (detailMaterialIdTopLeft + 1) % intTextureSize; // Using Load() to gather the nearest 4 samples (Gather4() isn't used because of precision issues with uvs). uint4 s1 = TerrainSrg::m_detailMaterialIdImage.Load(int3(detailMaterialIdTopLeft.x, detailMaterialIdBottomRight.y, 0)); @@ -334,7 +362,7 @@ bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdCoord, if (all(material1.x == material1.yzw) && all(material2.x == material2.yzw)) { // Fast path for same material ids - GetDetailSurfaceForMaterial(surface, material1.x, detailUv); + GetDetailSurfaceForMaterial(surface, material1.x, detailUv, macroColor); if (material2.x != 0xFF) { float4 material2Blends = 1.0 - blends; @@ -345,7 +373,7 @@ bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdCoord, (gatherWeight.x * (1.0 - gatherWeight.y) * material2Blends.z) + ((1.0 - gatherWeight.x) * (1.0 - gatherWeight.y) * material2Blends.w); WeightDetailSurface(surface, weight); - GetDetailSurfaceForMaterial(tempSurface, material2.x, detailUv); + GetDetailSurfaceForMaterial(tempSurface, material2.x, detailUv, macroColor); WeightDetailSurface(tempSurface, 1.0 - weight); AddDetailSurface(surface, tempSurface); } @@ -356,22 +384,22 @@ bool GetDetailSurface(inout DetailSurface surface, float2 detailMaterialIdCoord, // X float weight = (1.0 - gatherWeight.x) * gatherWeight.y; - BlendDetailMaterial(surface, material1.x, material2.x, blends.x, detailUv, weight); + BlendDetailMaterial(surface, material1.x, material2.x, blends.x, detailUv, weight, macroColor); // Y weight = gatherWeight.x * gatherWeight.y; - BlendDetailMaterial(surface, material1.y, material2.y, blends.y, detailUv, weight); + BlendDetailMaterial(surface, material1.y, material2.y, blends.y, detailUv, weight, macroColor); // Z weight = gatherWeight.x * (1.0 - gatherWeight.y); - BlendDetailMaterial(surface, material1.z, material2.z, blends.z, detailUv, weight); + BlendDetailMaterial(surface, material1.z, material2.z, blends.z, detailUv, weight, macroColor); // W weight = (1.0 - gatherWeight.x) * (1.0 - gatherWeight.y); - BlendDetailMaterial(surface, material1.w, material2.w, blends.w, detailUv, weight); + BlendDetailMaterial(surface, material1.w, material2.w, blends.w, detailUv, weight, macroColor); } surface.m_normal = normalize(surface.m_normal); - + surface.m_color = surface.m_color * surface.m_color; // Put back in linear space. return true; } diff --git a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl index 119a81e49c..78a45d6cdf 100644 --- a/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl +++ b/Gems/Terrain/Assets/Shaders/Terrain/TerrainPBR_ForwardPass.azsl @@ -149,7 +149,7 @@ ForwardPassOutput TerrainPBR_MainPassPS(VSOutput IN) // Only sample detail textures if inside where detail materials should be drawn. if (detailFactor < 1.0) { - hasDetailSurface = GetDetailSurface(detailSurface, detailRegionCoord, detailUv); + hasDetailSurface = GetDetailSurface(detailSurface, detailRegionCoord, detailUv, macroColor); } const float macroRoughness = 1.0; diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp index fc4b821578..3a21ec8c20 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.cpp @@ -135,31 +135,37 @@ namespace Terrain void TerrainSurfaceMaterialsListComponent::Activate() { - m_cachedAabb = AZ::Aabb::CreateNull(); + // Start listening for data requests. + TerrainAreaMaterialRequestBus::Handler::BusConnect(GetEntityId()); + // Start listening for shape changes. + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); + + // OnShapeChanged() will announce creation if the shape is valid + OnShapeChanged(LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); + + // Set all the materials as inactive and start loading. auto checkLoadMaterial = [&](TerrainSurfaceMaterialMapping& material) { if (material.m_materialAsset.GetId().IsValid()) { material.m_active = false; - material.m_materialAsset.QueueLoad(); AZ::Data::AssetBus::MultiHandler::BusConnect(material.m_materialAsset.GetId()); + material.m_materialAsset.QueueLoad(); } }; - - // 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 - OnShapeChanged(LmbrCentral::ShapeComponentNotifications::ShapeChangeReasons::ShapeChanged); } void TerrainSurfaceMaterialsListComponent::Deactivate() { + // disconnect from busses + LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); TerrainAreaMaterialRequestBus::Handler::BusDisconnect(); auto checkResetMaterial = [&](TerrainSurfaceMaterialMapping& material) @@ -168,8 +174,7 @@ namespace Terrain { AZ::Data::AssetBus::MultiHandler::BusDisconnect(material.m_materialAsset.GetId()); material.m_materialAsset.Release(); - material.m_materialInstance.reset(); - material.m_activeMaterialAssetId = AZ::Data::AssetId(); + material.m_materialInstance.reset(); // Cause HandleMaterialStateChanges() to announce destroyed for active materials } }; @@ -180,6 +185,16 @@ namespace Terrain } HandleMaterialStateChanges(); + + m_configuration.m_defaultSurfaceMaterial = {}; + m_configuration.m_surfaceMaterials.clear(); + + if (m_cachedAabb.IsValid()) + { + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionDestroyed, GetEntityId(), m_cachedAabb); + m_cachedAabb = AZ::Aabb::CreateNull(); + } } int TerrainSurfaceMaterialsListComponent::CountMaterialIdInstances(AZ::Data::AssetId id) const @@ -204,9 +219,6 @@ namespace Terrain void TerrainSurfaceMaterialsListComponent::HandleMaterialStateChanges() { - bool anyMaterialIsActive = false; - bool anyMaterialWasAlreadyActive = false; - { // Handle default material first auto& defaultMaterial = m_configuration.m_defaultSurfaceMaterial; @@ -214,12 +226,9 @@ namespace Terrain const bool wasPreviouslyActive = defaultMaterial.m_active; defaultMaterial.m_active = (defaultMaterial.m_materialInstance != nullptr); - anyMaterialWasAlreadyActive = wasPreviouslyActive; - anyMaterialIsActive = defaultMaterial.m_active; - if (!wasPreviouslyActive && !defaultMaterial.m_active) { - // A material has been assigned but has not yet completed loading. + // A material has not been assigned or has not yet completed loading. } else if (!wasPreviouslyActive && defaultMaterial.m_active) { @@ -227,6 +236,7 @@ namespace Terrain &TerrainAreaMaterialNotificationBus::Events::OnTerrainDefaultSurfaceMaterialCreated, GetEntityId(), defaultMaterial.m_materialInstance); defaultMaterial.m_previousChangeId = defaultMaterial.m_materialInstance->GetCurrentChangeId(); + defaultMaterial.m_activeMaterialAssetId = defaultMaterial.m_materialInstance->GetAssetId(); } else if (wasPreviouslyActive && !defaultMaterial.m_active) { @@ -256,9 +266,6 @@ namespace Terrain const bool wasPreviouslyActive = surfaceMaterialMapping.m_active; surfaceMaterialMapping.m_active = surfaceMaterialMapping.m_materialInstance != nullptr; - anyMaterialWasAlreadyActive = anyMaterialWasAlreadyActive || wasPreviouslyActive; - anyMaterialIsActive = anyMaterialIsActive || surfaceMaterialMapping.m_active; - if (!wasPreviouslyActive && !surfaceMaterialMapping.m_active) { // A material has been assigned but has not yet completed loading. @@ -315,27 +322,6 @@ namespace Terrain } } } - - if (!anyMaterialWasAlreadyActive && anyMaterialIsActive) - { - // Cache the current shape bounds. - LmbrCentral::ShapeComponentRequestsBus::EventResult( - m_cachedAabb, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - - // Start listening for data requests. - TerrainAreaMaterialRequestBus::Handler::BusConnect(GetEntityId()); - - // Start listening for shape changes. - LmbrCentral::ShapeComponentNotificationsBus::Handler::BusConnect(GetEntityId()); - - } - else if (anyMaterialWasAlreadyActive && !anyMaterialIsActive) - { - // All materials have been deactivated, stop listening for requests and notifications. - m_cachedAabb = AZ::Aabb::CreateNull(); - LmbrCentral::ShapeComponentNotificationsBus::Handler::BusDisconnect(); - TerrainAreaMaterialRequestBus::Handler::BusDisconnect(); - } } bool TerrainSurfaceMaterialsListComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig) @@ -359,16 +345,29 @@ namespace Terrain } void TerrainSurfaceMaterialsListComponent::OnShapeChanged([[maybe_unused]] ShapeComponentNotifications::ShapeChangeReasons reasons) - { - AZ::Aabb oldAabb = m_cachedAabb; + { + AZ::Aabb oldAabb = m_cachedAabb; - LmbrCentral::ShapeComponentRequestsBus::EventResult( - m_cachedAabb, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); + LmbrCentral::ShapeComponentRequestsBus::EventResult( + m_cachedAabb, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GetEncompassingAabb); - TerrainAreaMaterialNotificationBus::Broadcast( - &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionChanged, GetEntityId(), oldAabb, - m_cachedAabb); - } + if (m_cachedAabb.IsValid() && !oldAabb.IsValid()) + { + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionCreated, GetEntityId(), m_cachedAabb); + } + else if (!m_cachedAabb.IsValid() && oldAabb.IsValid()) + { + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionDestroyed, GetEntityId(), oldAabb); + } + else if (m_cachedAabb.IsValid() && oldAabb.IsValid()) + { + TerrainAreaMaterialNotificationBus::Broadcast( + &TerrainAreaMaterialNotificationBus::Events::OnTerrainSurfaceMaterialMappingRegionChanged, GetEntityId(), oldAabb, + m_cachedAabb); + } + } const AZ::Aabb& TerrainSurfaceMaterialsListComponent::GetTerrainSurfaceMaterialRegion() const { @@ -380,34 +379,42 @@ namespace Terrain return m_configuration.m_surfaceMaterials; } + const TerrainSurfaceMaterialMapping& TerrainSurfaceMaterialsListComponent::GetDefaultMaterial() const + { + return m_configuration.m_defaultSurfaceMaterial; + } + void TerrainSurfaceMaterialsListComponent::OnAssetReady(AZ::Data::Asset asset) { + bool anyUpdated = false; + // Find the missing material instance with the correct id. - auto checkUpdateMaterialAsset = [](TerrainSurfaceMaterialMapping& mapping, const AZ::Data::Asset& asset) -> bool + auto checkUpdateMaterialAsset = [&anyUpdated](TerrainSurfaceMaterialMapping& mapping, const AZ::Data::Asset& asset) { - if (mapping.m_materialAsset.GetId() == asset.GetId() && - (!mapping.m_materialInstance || mapping.m_materialInstance->GetAssetId() != mapping.m_materialAsset.GetId())) + if (mapping.m_materialAsset.GetId() == asset.GetId()) { - mapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(mapping.m_materialAsset); + if (!mapping.m_materialInstance || mapping.m_materialInstance->GetAssetId() != mapping.m_materialAsset.GetId()) + { + mapping.m_materialInstance = AZ::RPI::Material::FindOrCreate(asset); + } mapping.m_materialAsset.Release(); - return true; + anyUpdated = true; } - return false; }; // First check the default material - if (!checkUpdateMaterialAsset(m_configuration.m_defaultSurfaceMaterial, asset)) + checkUpdateMaterialAsset(m_configuration.m_defaultSurfaceMaterial, asset); + + // Check all the surface material mappings. They may be referenced more than once. + for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) { - // If the default materail wasn't updated, then check all the surface material mappings. - for (auto& surfaceMaterialMapping : m_configuration.m_surfaceMaterials) - { - if (checkUpdateMaterialAsset(surfaceMaterialMapping, asset)) - { - break; - } - } + checkUpdateMaterialAsset(surfaceMaterialMapping, asset); + } + + if (anyUpdated) + { + HandleMaterialStateChanges(); } - HandleMaterialStateChanges(); } void TerrainSurfaceMaterialsListComponent::OnAssetReloaded(AZ::Data::Asset asset) diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h index 96a7da60de..08030f0299 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/Components/TerrainSurfaceMaterialsListComponent.h @@ -101,6 +101,7 @@ namespace Terrain // TerrainAreaMaterialRequestBus const AZ::Aabb& GetTerrainSurfaceMaterialRegion() const override; const AZStd::vector& GetSurfaceMaterialMappings() const override; + const TerrainSurfaceMaterialMapping& GetDefaultMaterial() const override; ////////////////////////////////////////////////////////////////////////// // AZ::Data::AssetBus::Handler @@ -109,6 +110,6 @@ namespace Terrain TerrainSurfaceMaterialsListConfig m_configuration; - AZ::Aabb m_cachedAabb; + AZ::Aabb m_cachedAabb{ AZ::Aabb::CreateNull() }; }; } // namespace Terrain diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h index a2225702ef..6ac25f275b 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainAreaMaterialRequestBus.h @@ -29,8 +29,11 @@ namespace Terrain //! Get the Aabb for the region where a TerrainSurfaceMaterialMapping exists virtual const AZ::Aabb& GetTerrainSurfaceMaterialRegion() const = 0; - //! Get the Material asset assigned to a particular surface tag. + //! Get the Materials assigned to various surface tags. virtual const AZStd::vector& GetSurfaceMaterialMappings() const = 0; + + //! Get the default material + virtual const TerrainSurfaceMaterialMapping& GetDefaultMaterial() const = 0; }; using TerrainAreaMaterialRequestBus = AZ::EBus; @@ -95,6 +98,20 @@ namespace Terrain [[maybe_unused]] AZ::Data::Instance material) { } + + //! A set of surface material mappings has been created + virtual void OnTerrainSurfaceMaterialMappingRegionCreated( + [[maybe_unused]] AZ::EntityId entityId, + [[maybe_unused]] const AZ::Aabb& region) + { + } + + //! A set of surface material mappings has been destroyed + virtual void OnTerrainSurfaceMaterialMappingRegionDestroyed( + [[maybe_unused]] AZ::EntityId entityId, + [[maybe_unused]] const AZ::Aabb& oldRegion) + { + } //! The bounds of this set of surface material mappings has changed virtual void OnTerrainSurfaceMaterialMappingRegionChanged( diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp index f817c138a7..487749c02c 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.cpp @@ -131,6 +131,11 @@ namespace Terrain DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); materialRegion.m_region = bounds; + if (handler->GetDefaultMaterial().m_materialInstance) + { + OnTerrainDefaultSurfaceMaterialCreated(entityId, handler->GetDefaultMaterial().m_materialInstance); + } + for (const auto& materialMapping : materialMappings) { if (materialMapping.m_materialInstance) @@ -214,7 +219,6 @@ namespace Terrain void TerrainDetailMaterialManager::Reset() { RemoveAllImages(); - m_bindlessImageHandler.reset(); m_detailTextureImage = {}; m_detailMaterials.Clear(); @@ -279,13 +283,18 @@ namespace Terrain void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialCreated(AZ::EntityId entityId, MaterialInstance material) { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - AZ_Error("TerrainDetailMaterialManager", materialRegion.m_defaultDetailMaterialId == InvalidDetailMaterailId, + DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions); + if (materialRegion == nullptr) + { + AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialCreated() called for region that doesn't exist."); + return; + } + AZ_Error("TerrainDetailMaterialManager", materialRegion->m_defaultDetailMaterialId == InvalidDetailMaterialId, "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); + materialRegion->m_defaultDetailMaterialId = CreateOrUpdateDetailMaterial(material); + m_detailMaterials.GetData(materialRegion->m_defaultDetailMaterialId).m_refCount++; + m_dirtyDetailRegion.AddAabb(materialRegion->m_region); } void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialDestroyed(AZ::EntityId entityId) @@ -296,9 +305,15 @@ namespace Terrain AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialDestroyed() called for region that doesn't exist."); return; } + if (materialRegion->m_defaultDetailMaterialId == InvalidDetailMaterialId) + { + AZ_Assert(false, "OnTerrainDefaultSurfaceMaterialDestroyed() called for a region without a default material"); + return; + } CheckDetailMaterialForDeletion(materialRegion->m_defaultDetailMaterialId); - materialRegion->m_defaultDetailMaterialId = InvalidDetailMaterailId; + materialRegion->m_defaultDetailMaterialId = InvalidDetailMaterialId; + m_dirtyDetailRegion.AddAabb(materialRegion->m_region); } void TerrainDetailMaterialManager::OnTerrainDefaultSurfaceMaterialChanged(AZ::EntityId entityId, MaterialInstance newMaterial) @@ -314,44 +329,54 @@ namespace Terrain uint16_t materialId = CreateOrUpdateDetailMaterial(newMaterial); if (materialRegion->m_defaultDetailMaterialId != materialId) { - ++m_detailMaterials.GetData(materialId).refCount; + m_detailMaterials.GetData(materialId).m_refCount++; CheckDetailMaterialForDeletion(materialRegion->m_defaultDetailMaterialId); materialRegion->m_defaultDetailMaterialId = materialId; } + m_dirtyDetailRegion.AddAabb(materialRegion->m_region); } void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingCreated(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, "OnTerrainSurfaceMaterialMappingCreated() called for region that doesn't exist."); + return; + } // Validate that the surface tag is new - ForSurfaceTag(materialRegion, surfaceTag, [](DetailMaterialSurface&) + ForSurfaceTag(*materialRegion, surfaceTag, [](DetailMaterialSurface&) { AZ_Error(TerrainDetailMaterialManagerName, false, "Already have a surface material mapping for this surface tag."); }); uint16_t detailMaterialId = CreateOrUpdateDetailMaterial(material); - materialRegion.m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); - m_detailMaterials.GetData(detailMaterialId).refCount++; - m_dirtyDetailRegion.AddAabb(materialRegion.m_region); + materialRegion->m_materialsForSurfaces.push_back({ surfaceTag, detailMaterialId }); + m_detailMaterials.GetData(detailMaterialId).m_refCount++; + m_dirtyDetailRegion.AddAabb(materialRegion->m_region); } void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingDestroyed(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag) { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions); + if (materialRegion == nullptr) + { + AZ_Assert(false, "OnTerrainSurfaceMaterialMappingDestroyed() called for region that doesn't exist."); + return; + } - [[maybe_unused]] bool found = ForSurfaceTag(materialRegion, surfaceTag, + [[maybe_unused]] bool found = ForSurfaceTag(*materialRegion, surfaceTag, [&](DetailMaterialSurface& surface) { 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(); + m_dirtyDetailRegion.AddAabb(materialRegion->m_region); }); AZ_Error(TerrainDetailMaterialManagerName, found, "Could not find surface tag to destroy for OnTerrainSurfaceMaterialMappingDestroyed()."); @@ -377,7 +402,7 @@ namespace Terrain { // Updated material was a different asset than the old material, decrement ref count and // delete if no other surface tags are using it. - ++m_detailMaterials.GetData(materialId).refCount; + m_detailMaterials.GetData(materialId).m_refCount++; CheckDetailMaterialForDeletion(surface.m_detailMaterialId); surface.m_detailMaterialId = materialId; } @@ -405,19 +430,55 @@ namespace Terrain }); AZ_Assert(found, "OnTerrainSurfaceMaterialMappingTagChanged() called for tag that doesn't exist."); } + + void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionCreated(AZ::EntityId entityId, const AZ::Aabb& region) + { + DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); + materialRegion.m_region = region; + if (materialRegion.HasMaterials()) + { + m_dirtyDetailRegion.AddAabb(region); + } + } + + void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionDestroyed(AZ::EntityId entityId, const AZ::Aabb& oldRegion) + { + DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions); + if (materialRegion == nullptr) + { + AZ_Assert(false, "OnTerrainSurfaceMaterialMappingRegionDestroyed() called for region that doesn't exist."); + return; + } + + if (materialRegion->HasMaterials()) + { + m_dirtyDetailRegion.AddAabb(oldRegion); + } + + m_detailMaterialRegions.RemoveData(materialRegion); + } void TerrainDetailMaterialManager::OnTerrainSurfaceMaterialMappingRegionChanged(AZ::EntityId entityId, const AZ::Aabb& oldRegion, const AZ::Aabb& newRegion) { - DetailMaterialListRegion& materialRegion = FindOrCreateByEntityId(entityId, m_detailMaterialRegions); - materialRegion.m_region = newRegion; - m_dirtyDetailRegion.AddAabb(oldRegion); - m_dirtyDetailRegion.AddAabb(newRegion); + DetailMaterialListRegion* materialRegion = FindByEntityId(entityId, m_detailMaterialRegions); + if (materialRegion == nullptr) + { + AZ_Assert(false, "OnTerrainSurfaceMaterialMappingRegionChanged() called for region that doesn't exist."); + return; + } + materialRegion->m_region = newRegion; + + if (materialRegion->HasMaterials()) + { + m_dirtyDetailRegion.AddAabb(oldRegion); + m_dirtyDetailRegion.AddAabb(newRegion); + } } void TerrainDetailMaterialManager::CheckDetailMaterialForDeletion(uint16_t detailMaterialId) { auto& detailMaterialData = m_detailMaterials.GetData(detailMaterialId); - if (--detailMaterialData.refCount == 0) + if (--detailMaterialData.m_refCount == 0) { uint16_t bufferIndex = detailMaterialData.m_detailMaterialBufferIndex; DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(bufferIndex); @@ -451,7 +512,7 @@ namespace Terrain static constexpr uint16_t InvalidDetailMaterial = 0xFFFF; uint16_t detailMaterialId = InvalidDetailMaterial; - for (auto& detailMaterialData : m_detailMaterials.GetDataVector()) + for (const auto& detailMaterialData : m_detailMaterials.GetDataVector()) { if (detailMaterialData.m_assetId == material->GetAssetId()) { @@ -476,8 +537,6 @@ namespace Terrain void TerrainDetailMaterialManager::UpdateDetailMaterialData(uint16_t detailMaterialIndex, MaterialInstance material) { DetailMaterialData& materialData = m_detailMaterials.GetData(detailMaterialIndex); - DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(materialData.m_detailMaterialBufferIndex); - if (materialData.m_materialChangeId == material->GetCurrentChangeId()) { return; // material hasn't changed, nothing to do @@ -485,8 +544,12 @@ namespace Terrain materialData.m_materialChangeId = material->GetCurrentChangeId(); materialData.m_assetId = material->GetAssetId(); - + + DetailMaterialShaderData& shaderData = m_detailMaterialShaderData.GetElement(materialData.m_detailMaterialBufferIndex); + shaderData = DetailMaterialShaderData(); + DetailTextureFlags& flags = shaderData.m_flags; + flags = DetailTextureFlags::None; auto getIndex = [&](const char* const indexName) -> AZ::RPI::MaterialPropertyIndex { @@ -512,6 +575,7 @@ namespace Terrain const auto index = getIndex(indexName); const auto useTextureIndex = getIndex(usingFlagName); bool useTextureValue = true; + if (useTextureIndex.IsValid()) { useTextureValue = material->GetPropertyValue(useTextureIndex).GetValue(); @@ -684,7 +748,7 @@ namespace Terrain AZ_Error(TerrainDetailMaterialManagerName, m_detailTextureImage, "Failed to initialize the detail texture image."); ClipmapBounds::ClipmapBoundsRegionList updateRegions = m_detailMaterialIdBounds.TransformRegion(m_detailMaterialIdBounds.GetWorldBounds()); - for (auto& region : updateRegions) + for (const auto& region : updateRegions) { UpdateDetailTexture(region.m_worldAabb, region.m_localAabb); } @@ -692,22 +756,24 @@ namespace Terrain else { // Update the edge regions - for (auto& region : edgeUpdatedRegions) + for (const auto& region : edgeUpdatedRegions) { UpdateDetailTexture(region.m_worldAabb, region.m_localAabb); } - m_dirtyDetailRegion = m_dirtyDetailRegion.GetClamped(untouchedRegion); - if (m_dirtyDetailRegion.IsValid()) { - ClipmapBounds::ClipmapBoundsRegionList updateRegions = m_detailMaterialIdBounds.TransformRegion(m_dirtyDetailRegion); - for (auto& region : updateRegions) + m_dirtyDetailRegion = m_dirtyDetailRegion.GetClamped(untouchedRegion); + if (m_dirtyDetailRegion.IsValid()) { - UpdateDetailTexture(region.m_worldAabb, region.m_localAabb); + ClipmapBounds::ClipmapBoundsRegionList updateRegions = m_detailMaterialIdBounds.TransformRegion(m_dirtyDetailRegion); + for (const auto& region : updateRegions) + { + UpdateDetailTexture(region.m_worldAabb, region.m_localAabb); + } } + m_dirtyDetailRegion = AZ::Aabb::CreateNull(); } - m_dirtyDetailRegion = AZ::Aabb::CreateNull(); } } @@ -730,23 +796,24 @@ namespace Terrain const int32_t height = textureUpdateAabb.m_max.m_y - textureUpdateAabb.m_min.m_y; AZStd::vector pixels(width * height); - uint32_t index = 0; - auto perPositionCallback = [this, &pixels, &index]( - [[maybe_unused]] size_t xIndex, [[maybe_unused]] size_t yIndex, + auto perPositionCallback = [this, &pixels, &width]( + size_t xIndex, size_t yIndex, const AzFramework::SurfaceData::SurfacePoint& surfacePoint, [[maybe_unused]] bool terrainExists) { // Store the top two surface weights in the texture with m_blend storing the relative weight. - bool isFirstMaterial = true; + DetailMaterialPixel& pixel = pixels.at(yIndex * width + xIndex); + uint32_t foundMaterials = 0; float firstWeight = 0.0f; + float secondWeight = 0.0f; + 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; + pixel.m_material1 = m_passthroughMaterialId; return; } @@ -758,61 +825,51 @@ namespace Terrain uint16_t materialId = GetDetailMaterialForSurfaceType(*region, surfaceType); if (materialId < 255) { - if (isFirstMaterial) + if (foundMaterials == 0) { - // First material is valid. Save its weight to calculate blend later - pixels.at(index).m_material1 = aznumeric_cast(materialId); + // Found first material. Save its weight to calculate blend later. + ++foundMaterials; + pixel.m_material1 = aznumeric_cast(materialId); firstWeight = surfaceTagWeight.m_weight; - isFirstMaterial = false; - static constexpr float MaxValueBeforeRounding = 254.5f / 255.0f; - if (firstWeight >= MaxValueBeforeRounding) - { - break; - } + } + else if (materialId == pixel.m_material1) + { + // Same material as the first material, just add the weights. + firstWeight += surfaceTagWeight.m_weight; + } + else if (foundMaterials == 1) + { + // Found second material. Save its weight to calculate blend later. + ++foundMaterials; + secondWeight += surfaceTagWeight.m_weight; + pixel.m_material2 = aznumeric_cast(materialId); + } + else if (materialId == pixel.m_material2) + { + // Same material as the second material, just add the weights. + secondWeight += surfaceTagWeight.m_weight; } else { - // Second material is valid, weight is relative based on first material's weight. - pixels.at(index).m_material2 = aznumeric_cast(materialId); - float totalWeight = firstWeight + surfaceTagWeight.m_weight; - float blendWeight = 1.0f - (firstWeight / totalWeight); - pixels.at(index).m_blend = aznumeric_cast(AZStd::round(blendWeight * 255.0f)); break; } } - continue; // search for second material } - else - { - // No more valid materials in list since surfaceTagWeight is ordered. - - uint8_t defaultMaterial = region->m_defaultDetailMaterialId == InvalidDetailMaterailId ? m_passthroughMaterialId : - aznumeric_cast(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(firstWeight, 0.0f, 1.0f); - pixels.at(index).m_blend = aznumeric_cast(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; + + if (foundMaterials == 0) + { + // No materials found, so use the default material. + uint8_t defaultMaterial = region->m_defaultDetailMaterialId == InvalidDetailMaterialId ? m_passthroughMaterialId : + aznumeric_cast(m_detailMaterials.GetData(region->m_defaultDetailMaterialId).m_detailMaterialBufferIndex); + pixel.m_material1 = defaultMaterial; + } + else if (foundMaterials == 2) + { + float totalWeight = firstWeight + secondWeight; + float blendWeight = 1.0f - (firstWeight / totalWeight); + pixel.m_blend = aznumeric_cast(AZStd::round(blendWeight * 255.0f)); + } }; AZ::Vector2 stepSize(DetailTextureScale); @@ -848,7 +905,7 @@ namespace Terrain return m_detailMaterials.GetData(materialSurface.m_detailMaterialId).m_detailMaterialBufferIndex; } } - return InvalidDetailMaterailId; + return InvalidDetailMaterialId; } auto TerrainDetailMaterialManager::FindRegionForPosition(const AZ::Vector2& position) const -> const DetailMaterialListRegion* diff --git a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h index 16423251ec..b625d128b4 100644 --- a/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h +++ b/Gems/Terrain/Code/Source/TerrainRenderer/TerrainDetailMaterialManager.h @@ -58,6 +58,7 @@ namespace Terrain enum DetailTextureFlags : uint32_t { + None = 0b0000'0000'0000'0000'0000'0000'0000'0000, UseTextureBaseColor = 0b0000'0000'0000'0000'0000'0000'0000'0001, UseTextureNormal = 0b0000'0000'0000'0000'0000'0000'0000'0010, UseTextureMetallic = 0b0000'0000'0000'0000'0000'0000'0000'0100, @@ -69,11 +70,11 @@ namespace Terrain FlipNormalX = 0b0000'0000'0000'0001'0000'0000'0000'0000, FlipNormalY = 0b0000'0000'0000'0010'0000'0000'0000'0000, - BlendModeMask = 0b0000'0000'0000'1100'0000'0000'0000'0000, - BlendModeLerp = 0b0000'0000'0000'0000'0000'0000'0000'0000, - BlendModeLinearLight = 0b0000'0000'0000'0100'0000'0000'0000'0000, - BlendModeMultiply = 0b0000'0000'0000'1000'0000'0000'0000'0000, - BlendModeOverlay = 0b0000'0000'0000'1100'0000'0000'0000'0000, + BlendModeMask = 0b0000'0000'0001'1100'0000'0000'0000'0000, + BlendModeLerp = 0b0000'0000'0000'0100'0000'0000'0000'0000, + BlendModeLinearLight = 0b0000'0000'0000'1000'0000'0000'0000'0000, + BlendModeMultiply = 0b0000'0000'0000'1100'0000'0000'0000'0000, + BlendModeOverlay = 0b0000'0000'0001'0000'0000'0000'0000'0000, }; struct DetailMaterialShaderData @@ -94,11 +95,11 @@ namespace Terrain float m_baseColorFactor{ 1.0f }; float m_normalFactor{ 1.0f }; - float m_metalFactor{ 1.0f }; + float m_metalFactor{ 0.0f }; float m_roughnessScale{ 1.0f }; float m_roughnessBias{ 0.0f }; - float m_specularF0Factor{ 1.0f }; + float m_specularF0Factor{ 0.5f }; float m_occlusionFactor{ 1.0f }; float m_heightFactor{ 1.0f }; float m_heightOffset{ 0.0f }; @@ -119,9 +120,9 @@ namespace Terrain uint16_t m_heightImageIndex{ InvalidImageIndex }; // 16 byte aligned - uint16_t m_padding1; - uint32_t m_padding2; - uint32_t m_padding3; + uint16_t m_padding1{ 0 }; + uint32_t m_padding2{ 0 }; + uint32_t m_padding3{ 0 }; }; static_assert(sizeof(DetailMaterialShaderData) % 16 == 0, "DetailMaterialShaderData must be 16 byte aligned."); @@ -129,7 +130,7 @@ namespace Terrain { AZ::Data::AssetId m_assetId; AZ::RPI::Material::ChangeId m_materialChangeId{AZ::RPI::Material::DEFAULT_CHANGE_ID}; - uint32_t refCount = 0; + uint32_t m_refCount = 0; uint16_t m_detailMaterialBufferIndex{ 0xFFFF }; AZ::Data::Instance m_colorImage; @@ -152,11 +153,16 @@ namespace Terrain AZ::EntityId m_entityId; AZ::Aabb m_region{AZ::Aabb::CreateNull()}; AZStd::vector m_materialsForSurfaces; - uint16_t m_defaultDetailMaterialId; - }; + uint16_t m_defaultDetailMaterialId{ 0xFFFF }; + bool HasMaterials() + { + return m_defaultDetailMaterialId != InvalidDetailMaterialId || !m_materialsForSurfaces.empty(); + } + }; + using DetailMaterialContainer = AZ::Render::IndexedDataVector; - static constexpr auto InvalidDetailMaterailId = DetailMaterialContainer::NoFreeSlot; + static constexpr auto InvalidDetailMaterialId = DetailMaterialContainer::NoFreeSlot; // System-level parameters static constexpr int32_t DetailTextureSize{ 1024 }; @@ -175,6 +181,8 @@ namespace Terrain void OnTerrainSurfaceMaterialMappingMaterialChanged(AZ::EntityId entityId, SurfaceData::SurfaceTag surfaceTag, MaterialInstance material) override; void OnTerrainSurfaceMaterialMappingTagChanged( AZ::EntityId entityId, SurfaceData::SurfaceTag oldSurfaceTag, SurfaceData::SurfaceTag newSurfaceTag) override; + void OnTerrainSurfaceMaterialMappingRegionCreated(AZ::EntityId entityId, const AZ::Aabb& region) override; + void OnTerrainSurfaceMaterialMappingRegionDestroyed(AZ::EntityId entityId, const AZ::Aabb& oldRegion) 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