@ -155,9 +155,11 @@ namespace Terrain
const AZ : : Transform transform = AZ : : Transform : : CreateTranslation ( worldBounds . GetCenter ( ) ) ;
AZ : : Vector2 queryResolution = AZ : : Vector2 ( 1.0f ) ;
AZ : : Vector2 queryResolution 2D = AZ : : Vector2 ( 1.0f ) ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
queryResolution , & AzFramework : : Terrain : : TerrainDataRequests : : GetTerrainHeightQueryResolution ) ;
queryResolution2D , & AzFramework : : Terrain : : TerrainDataRequests : : GetTerrainHeightQueryResolution ) ;
// Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension.
float queryResolution = queryResolution2D . GetX ( ) ;
// Sectors need to be rebuilt if the world bounds change in the x/y, or the sample spacing changes.
m_areaData . m_rebuildSectors = m_areaData . m_rebuildSectors | |
@ -165,16 +167,11 @@ namespace Terrain
m_areaData . m_terrainBounds . GetMin ( ) . GetY ( ) ! = worldBounds . GetMin ( ) . GetY ( ) | |
m_areaData . m_terrainBounds . GetMax ( ) . GetX ( ) ! = worldBounds . GetMax ( ) . GetX ( ) | |
m_areaData . m_terrainBounds . GetMax ( ) . GetY ( ) ! = worldBounds . GetMax ( ) . GetY ( ) | |
m_areaData . m_sampleSpacing ! = queryResolution .GetX ( ) ;
m_areaData . m_sampleSpacing ! = queryResolution ;
m_areaData . m_transform = transform ;
m_areaData . m_terrainBounds = worldBounds ;
m_areaData . m_heightmapImageWidth = aznumeric_cast < uint32_t > ( worldBounds . GetXExtent ( ) / queryResolution . GetX ( ) ) ;
m_areaData . m_heightmapImageHeight = aznumeric_cast < uint32_t > ( worldBounds . GetYExtent ( ) / queryResolution . GetY ( ) ) ;
m_areaData . m_updateWidth = aznumeric_cast < uint32_t > ( m_dirtyRegion . GetXExtent ( ) / queryResolution . GetX ( ) ) ;
m_areaData . m_updateHeight = aznumeric_cast < uint32_t > ( m_dirtyRegion . GetYExtent ( ) / queryResolution . GetY ( ) ) ;
// Currently query resolution is multidimensional but the rendering system only supports this changing in one dimension.
m_areaData . m_sampleSpacing = queryResolution . GetX ( ) ;
m_areaData . m_sampleSpacing = queryResolution ;
m_areaData . m_heightmapUpdated = true ;
}
@ -261,31 +258,42 @@ namespace Terrain
void TerrainFeatureProcessor : : UpdateTerrainData ( )
{
static const AZ : : Name TerrainHeightmapName = AZ : : Name ( TerrainHeightmapChars ) ;
uint32_t width = m_areaData . m_updateWidth ;
uint32_t height = m_areaData . m_updateHeight ;
const AZ : : Aabb & worldBounds = m_areaData . m_terrainBounds ;
const float queryResolution = m_areaData . m_sampleSpacing ;
const AZ : : Aabb & worldBounds = m_areaData . m_terrainBounds ;
const AZ : : RHI : : Size worldSize = AZ : : RHI : : Size ( m_areaData . m_heightmapImageWidth , m_areaData . m_heightmapImageHeight , 1 ) ;
int32_t heightmapImageXStart = aznumeric_cast < int32_t > ( AZStd : : ceilf ( worldBounds . GetMin ( ) . GetX ( ) / queryResolution ) ) ;
int32_t heightmapImageXEnd = aznumeric_cast < int32_t > ( AZStd : : floorf ( worldBounds . GetMax ( ) . GetX ( ) / queryResolution ) ) + 1 ;
int32_t heightmapImageYStart = aznumeric_cast < int32_t > ( AZStd : : ceilf ( worldBounds . GetMin ( ) . GetY ( ) / queryResolution ) ) ;
int32_t heightmapImageYEnd = aznumeric_cast < int32_t > ( AZStd : : floorf ( worldBounds . GetMax ( ) . GetY ( ) / queryResolution ) ) + 1 ;
uint32_t heightmapImageWidth = heightmapImageXEnd - heightmapImageXStart ;
uint32_t heightmapImageHeight = heightmapImageYEnd - heightmapImageYStart ;
if ( ! m_areaData . m_heightmapImage | | m_areaData . m_heightmapImage - > GetDescriptor ( ) . m_size ! = worldSize )
{
// World size changed, so the whole world needs updating.
width = worldSize . m_width ;
height = worldSize . m_height ;
m_dirtyRegion = worldBounds ;
const AZ : : RHI : : Size heightmapSize = AZ : : RHI : : Size ( heightmapImageWidth , heightmapImageHeight , 1 ) ;
if ( ! m_areaData . m_heightmapImage | | m_areaData . m_heightmapImage - > GetDescriptor ( ) . m_size ! = heightmapSize )
{
const AZ : : Data : : Instance < AZ : : RPI : : AttachmentImagePool > imagePool = AZ : : RPI : : ImageSystemInterface : : Get ( ) - > GetSystemAttachmentPool ( ) ;
AZ : : RHI : : ImageDescriptor imageDescriptor = AZ : : RHI : : ImageDescriptor : : Create2D (
AZ : : RHI : : ImageBindFlags : : ShaderRead , width, height, AZ : : RHI : : Format : : R16_UNORM
AZ : : RHI : : ImageBindFlags : : ShaderRead , heightmapSize. m_ width, heightmapSize. m_ height, AZ : : RHI : : Format : : R16_UNORM
) ;
m_areaData . m_heightmapImage = AZ : : RPI : : AttachmentImage : : Create ( * imagePool . get ( ) , imageDescriptor , TerrainHeightmapName , nullptr , nullptr ) ;
AZ_Error ( TerrainFPName , m_areaData . m_heightmapImage , " Failed to initialize the heightmap image! " ) ;
// World size changed, so the whole height map needs updating.
m_dirtyRegion = worldBounds ;
}
int32_t xStart = aznumeric_cast < int32_t > ( AZStd : : ceilf ( m_dirtyRegion . GetMin ( ) . GetX ( ) / queryResolution ) ) ;
int32_t xEnd = aznumeric_cast < int32_t > ( AZStd : : floorf ( m_dirtyRegion . GetMax ( ) . GetX ( ) / queryResolution ) ) + 1 ;
int32_t yStart = aznumeric_cast < int32_t > ( AZStd : : ceilf ( m_dirtyRegion . GetMin ( ) . GetY ( ) / queryResolution ) ) ;
int32_t yEnd = aznumeric_cast < int32_t > ( AZStd : : floorf ( m_dirtyRegion . GetMax ( ) . GetY ( ) / queryResolution ) ) + 1 ;
uint32_t updateWidth = xEnd - xStart ;
uint32_t updateHeight = yEnd - yStart ;
AZStd : : vector < uint16_t > pixels ;
pixels . reserve ( width * height ) ;
pixels . reserve ( updateWidth * updateH eight) ;
{
// Block other threads from accessing the surface data bus while we are in GetHeightFromFloats (which may call into the SurfaceData bus).
@ -297,18 +305,17 @@ namespace Terrain
auto & surfaceDataContext = SurfaceData : : SurfaceDataSystemRequestBus : : GetOrCreateContext ( false ) ;
typename SurfaceData : : SurfaceDataSystemRequestBus : : Context : : DispatchLockGuard scopeLock ( surfaceDataContext . m_contextMutex ) ;
for ( uint32_t y = 0 ; y < height ; y + + )
for ( int32_t y = yStart ; y < yEnd ; y + + )
{
for ( uint32_t x = 0 ; x < width ; x + + )
for ( int32_t x = xStart ; x < xEnd ; x + + )
{
bool terrainExists = true ;
float terrainHeight = 0.0f ;
float xPos = x * queryResolution ;
float yPos = y * queryResolution ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
terrainHeight , & AzFramework : : Terrain : : TerrainDataRequests : : GetHeightFromFloats ,
( x * queryResolution ) + m_dirtyRegion . GetMin ( ) . GetX ( ) ,
( y * queryResolution ) + m_dirtyRegion . GetMin ( ) . GetY ( ) ,
AzFramework : : Terrain : : TerrainDataRequests : : Sampler : : EXACT ,
& terrainExists ) ;
xPos , yPos , AzFramework : : Terrain : : TerrainDataRequests : : Sampler : : EXACT , & terrainExists ) ;
const float clampedHeight = AZ : : GetClamp ( ( terrainHeight - worldBounds . GetMin ( ) . GetZ ( ) ) / worldBounds . GetExtents ( ) . GetZ ( ) , 0.0f , 1.0f ) ;
const float expandedHeight = AZStd : : roundf ( clampedHeight * AZStd : : numeric_limits < uint16_t > : : max ( ) ) ;
@ -321,16 +328,18 @@ namespace Terrain
if ( m_areaData . m_heightmapImage )
{
const float left = ( m_dirtyRegion . GetMin ( ) . GetX ( ) - worldBounds . GetMin ( ) . GetX ( ) ) / queryResolution ;
const float top = ( m_dirtyRegion . GetMin ( ) . GetY ( ) - worldBounds . GetMin ( ) . GetY ( ) ) / queryResolution ;
constexpr uint32_t BytesPerPixel = sizeof ( uint16_t ) ;
const float left = xStart - ( worldBounds . GetMin ( ) . GetX ( ) / queryResolution ) ;
const float top = yStart - ( worldBounds . GetMin ( ) . GetY ( ) / queryResolution ) ;
AZ : : RHI : : ImageUpdateRequest imageUpdateRequest ;
imageUpdateRequest . m_imageSubresourcePixelOffset . m_left = aznumeric_cast < uint32_t > ( left ) ;
imageUpdateRequest . m_imageSubresourcePixelOffset . m_top = aznumeric_cast < uint32_t > ( top ) ;
imageUpdateRequest . m_sourceSubresourceLayout . m_bytesPerRow = width * sizeof ( uint16_t ) ;
imageUpdateRequest . m_sourceSubresourceLayout . m_bytesPerImage = width * height * sizeof ( uint16_t ) ;
imageUpdateRequest . m_sourceSubresourceLayout . m_rowCount = h eight;
imageUpdateRequest . m_sourceSubresourceLayout . m_size . m_width = w idth;
imageUpdateRequest . m_sourceSubresourceLayout . m_size . m_height = h eight;
imageUpdateRequest . m_sourceSubresourceLayout . m_bytesPerRow = updateWidth * BytesPerPixel ;
imageUpdateRequest . m_sourceSubresourceLayout . m_bytesPerImage = updateWidth * updateHeight * BytesPerPixel ;
imageUpdateRequest . m_sourceSubresourceLayout . m_rowCount = updateH eight;
imageUpdateRequest . m_sourceSubresourceLayout . m_size . m_width = updateW idth;
imageUpdateRequest . m_sourceSubresourceLayout . m_size . m_height = updateH eight;
imageUpdateRequest . m_sourceSubresourceLayout . m_size . m_depth = 1 ;
imageUpdateRequest . m_sourceData = pixels . data ( ) ;
imageUpdateRequest . m_image = m_areaData . m_heightmapImage - > GetRHIImage ( ) ;
@ -492,6 +501,12 @@ namespace Terrain
m_areaData . m_heightmapUpdated = false ;
m_areaData . m_macroMaterialsUpdated = false ;
AZStd : : array < float , 2 > uvStep =
{
1.0f / aznumeric_cast < uint32_t > ( m_areaData . m_terrainBounds . GetXExtent ( ) / m_areaData . m_sampleSpacing ) ,
1.0f / aznumeric_cast < uint32_t > ( m_areaData . m_terrainBounds . GetYExtent ( ) / m_areaData . m_sampleSpacing ) ,
} ;
for ( SectorData & sectorData : m_sectorData )
{
ShaderTerrainData terrainDataForSrg ;
@ -509,11 +524,7 @@ namespace Terrain
( ( yPatch + GridMeters ) - terrainBounds . GetMin ( ) . GetY ( ) ) / terrainBounds . GetYExtent ( )
} ;
terrainDataForSrg . m_uvStep =
{
1.0f / m_areaData . m_heightmapImageWidth ,
1.0f / m_areaData . m_heightmapImageHeight ,
} ;
terrainDataForSrg . m_uvStep = uvStep ;
AZ : : Transform transform = m_areaData . m_transform ;
transform . SetTranslation ( xPatch , yPatch , m_areaData . m_transform . GetTranslation ( ) . GetZ ( ) ) ;