@ -90,13 +90,28 @@ namespace Terrain
void TerrainWorldDebuggerComponent : : Activate ( )
{
m_wireframeBounds = AZ : : Aabb : : CreateNull ( ) ;
// Given the AuxGeom vertex limits, MaxSectorsToDraw is the max number of wireframe sectors we can draw without exceeding the
// limits. Since we want an N x N sector grid, take the square root to get the number of sectors in each direction.
m_sectorGridSize = aznumeric_cast < int32_t > ( sqrtf ( MaxSectorsToDraw ) ) ;
// We're always going to keep the camera in the center square, so "round" downwards to an odd number of sectors if we currently
// have an even number. (If we added a sector, we'll go above the max sectors that we can draw with our vertex limits)
m_sectorGridSize = ( m_sectorGridSize & 0x01 ) ? m_sectorGridSize : m_sectorGridSize - 1 ;
// Create our fixed set of sectors that we'll draw. By default, they'll all be constructed as dirty, so they'll get refreshed
// the first time we try to draw them. (If wireframe drawing is disabled, we'll never refresh them)
m_wireframeSectors . clear ( ) ;
m_wireframeSectors . resize ( m_sectorGridSize * m_sectorGridSize ) ;
AzFramework : : EntityDebugDisplayEventBus : : Handler : : BusConnect ( GetEntityId ( ) ) ;
AzFramework : : BoundsRequestBus : : Handler : : BusConnect ( GetEntityId ( ) ) ;
AzFramework : : Terrain : : TerrainDataNotificationBus : : Handler : : BusConnect ( ) ;
RefreshCachedWireframeGrid ( AZ : : Aabb : : CreateNull ( ) ) ;
// Any time the world bounds potentially changes, notify that the terrain debugger's visibility bounds also changed.
// Otherwise, DisplayEntityViewport() won't get called at the appropriate times, since the visibility could get incorrectly
// culled out.
AzFramework : : IEntityBoundsUnionRequestBus : : Broadcast (
& AzFramework : : IEntityBoundsUnionRequestBus : : Events : : RefreshEntityLocalBoundsUnion , GetEntityId ( ) ) ;
}
void TerrainWorldDebuggerComponent : : Deactivate ( )
@ -105,7 +120,6 @@ namespace Terrain
AzFramework : : BoundsRequestBus : : Handler : : BusDisconnect ( ) ;
AzFramework : : EntityDebugDisplayEventBus : : Handler : : BusDisconnect ( ) ;
m_wireframeBounds = AZ : : Aabb : : CreateNull ( ) ;
m_wireframeSectors . clear ( ) ;
}
@ -144,170 +158,239 @@ namespace Terrain
return GetWorldBounds ( ) ;
}
void TerrainWorldDebuggerComponent : : DisplayEntityViewport (
const AzFramework : : ViewportInfo & viewportInfo , AzFramework : : DebugDisplayRequests & debugDisplay )
void TerrainWorldDebuggerComponent : : MarkDirtySectors ( const AZ : : Aabb & dirtyRegion )
{
// Draw a wireframe box around the entire terrain world bounds
if ( m_configuration . m_drawWorldBounds )
// Create a 2D version of dirtyRegion that has Z set to min/max float values, so that we can just check for XY overlap with
// each sector.
const AZ : : Aabb dirtyRegion2D = AZ : : Aabb : : CreateFromMinMaxValues (
dirtyRegion . GetMin ( ) . GetX ( ) , dirtyRegion . GetMin ( ) . GetY ( ) , AZStd : : numeric_limits < float > : : lowest ( ) ,
dirtyRegion . GetMax ( ) . GetX ( ) , dirtyRegion . GetMax ( ) . GetY ( ) , AZStd : : numeric_limits < float > : : max ( ) ) ;
// For each sector that overlaps the dirty region (or all of them if the region is invalid), mark them as dirty so that
// they'll get refreshed the next time we need to draw them.
for ( auto & sector : m_wireframeSectors )
{
AZ : : Color outlineColor ( 1.0f , 0.0f , 0.0f , 1.0f ) ;
AZ : : Aabb aabb = GetWorldBounds ( ) ;
if ( ! dirtyRegion2D . IsValid ( ) | | dirtyRegion2D . Overlaps ( sector . m_aabb ) )
{
sector . m_isDirty = true ;
}
}
}
debugDisplay . SetColor ( outlineColor ) ;
debugDisplay . DrawWireBox ( aabb . GetMin ( ) , aabb . GetMax ( ) ) ;
void TerrainWorldDebuggerComponent : : DrawWorldBounds ( AzFramework : : DebugDisplayRequests & debugDisplay )
{
if ( ! m_configuration . m_drawWorldBounds )
{
return ;
}
// Draw a wireframe representation of the terrain surface
if ( m_configuration . m_drawWireframe & & ! m_wireframeSectors . empty ( ) )
// Draw a wireframe box around the entire terrain world bounds
AZ : : Color outlineColor ( 1.0f , 0.0f , 0.0f , 1.0f ) ;
AZ : : Aabb aabb = GetWorldBounds ( ) ;
debugDisplay . SetColor ( outlineColor ) ;
debugDisplay . DrawWireBox ( aabb . GetMin ( ) , aabb . GetMax ( ) ) ;
}
void TerrainWorldDebuggerComponent : : DrawWireframe (
const AzFramework : : ViewportInfo & viewportInfo , AzFramework : : DebugDisplayRequests & debugDisplay )
{
AZ_PROFILE_FUNCTION ( Entity ) ;
if ( ! m_configuration . m_drawWireframe )
{
// Start by assuming we'll draw the entire world.
AZ : : Aabb drawingAabb = GetWorldBounds ( ) ;
return ;
}
// Assuming we can get the camera, reduce the drawing bounds to a fixed distance around the camera.
if ( auto viewportContextRequests = AZ : : RPI : : ViewportContextRequests : : Get ( ) ; viewportContextRequests )
{
// Get the current camera position.
AZ : : RPI : : ViewportContextPtr viewportContext = viewportContextRequests - > GetViewportContextById ( viewportInfo . m_viewportId ) ;
AZ : : Vector3 cameraPos = viewportContext - > GetCameraTransform ( ) . GetTranslation ( ) ;
/* This draws a wireframe centered on the camera that extends out to a certain distance at all times. To reduce the amount of
* recalculations we need to do on each camera movement , we divide the world into a conceptual grid of sectors , where each sector
* contains a fixed number of terrain height points . So for example , if the terrain has height data at 1 m spacing , the sectors
* might be 10 m x 10 m in size . If the height data is spaced at 0.5 m , the sectors might be 5 m x 5 m in size . The wireframe
* draws N x N sectors centered around the camera , as determined by m_sectorGridSize . So a gridSize of 7 with a sector size of
* 10 m means that we ' ll be drawing 7 x 7 sectors , or 70 m x 70 m , centered around the camera . Each time the camera moves into
* a new sector , we refresh the changed sectors before drawing them .
*
* The only tricky bit to this design is the way the sectors are stored and indexed . They ' re stored in a single vector as NxN
* entries , so they would normally be indexed as ( y * N ) + x . Since we want this to be centered on the camera , the easy answer
* would be to take the camera position - ( N / 2 ) ( since we ' re centering ) as the relative offset to the first entry . But this
* would mean that the entire set of entries would change every time we move the camera . For example , if we had 5 entries ,
* they might map to 0 - 4 , 1 - 5 , 2 - 6 , 3 - 7 , etc as the camera moves .
*
* Instead , we use mod ( % ) to rotate our indices around , so it would go ( 0 1 2 3 4 ) , ( 5 1 2 3 4 ) , ( 5 6 2 3 4 ) , ( 5 6 7 3 4 ) , etc
* as the camera moves . For negative entries , we rotate the indices in reverse , so that we get results like ( 0 1 2 3 4 ) ,
* ( 0 1 2 3 - 1 ) , ( 0 1 2 - 2 - 1 ) , ( 0 1 - 3 - 2 - 1 ) , etc . This way we always have the correct range of sectors , and sectors that have
* remained visible are left alone and don ' t need to be updated again .
*/
// Get the terrain world bounds
AZ : : Aabb worldBounds = GetWorldBounds ( ) ;
float worldMinZ = worldBounds . GetMin ( ) . GetZ ( ) ;
// Determine how far to draw in each direction in world space based on our MaxSectorsToDraw
AZ : : Vector2 queryResolution = AZ : : Vector2 ( 1.0f ) ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
queryResolution , & AzFramework : : Terrain : : TerrainDataRequests : : GetTerrainHeightQueryResolution ) ;
AZ : : Vector3 viewDistance (
queryResolution . GetX ( ) * SectorSizeInGridPoints * sqrtf ( MaxSectorsToDraw ) ,
queryResolution . GetY ( ) * SectorSizeInGridPoints * sqrtf ( MaxSectorsToDraw ) ,
0.0f ) ;
// Create an AABB around the camera based on how far we want to be able to draw in each direction and clamp the
// drawing AABB to it.
AZ : : Aabb cameraAabb = AZ : : Aabb : : CreateFromMinMax (
AZ : : Vector3 (
cameraPos . GetX ( ) - viewDistance . GetX ( ) , cameraPos . GetY ( ) - viewDistance . GetY ( ) , drawingAabb . GetMin ( ) . GetZ ( ) ) ,
AZ : : Vector3 (
cameraPos . GetX ( ) + viewDistance . GetX ( ) , cameraPos . GetY ( ) + viewDistance . GetY ( ) , drawingAabb . GetMin ( ) . GetZ ( ) ) ) ;
drawingAabb . Clamp ( cameraAabb ) ;
}
// Get the terrain height data resolution
AZ : : Vector2 heightDataResolution = AZ : : Vector2 ( 1.0f ) ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
heightDataResolution , & AzFramework : : Terrain : : TerrainDataRequests : : GetTerrainHeightQueryResolution ) ;
// Get the size of a wireframe sector in world space
const AZ : : Vector2 sectorSize = heightDataResolution * SectorSizeInGridPoints ;
// For each sector, if it appears within our view distance, draw it.
for ( auto & sector : m_wireframeSectors )
// Try to get the current camera position, or default to (0,0) if we can't.
AZ : : Vector3 cameraPos = AZ : : Vector3 : : CreateZero ( ) ;
if ( auto viewportContextRequests = AZ : : RPI : : ViewportContextRequests : : Get ( ) ; viewportContextRequests )
{
AZ : : RPI : : ViewportContextPtr viewportContext = viewportContextRequests - > GetViewportContextById ( viewportInfo . m_viewportId ) ;
cameraPos = viewportContext - > GetCameraTransform ( ) . GetTranslation ( ) ;
}
// Convert our camera position to a wireframe grid sector. We first convert from world space to sector space by dividing by
// sectorSize, so that integer values are sectors, and fractional values are the distance within the sector. Then we get the
// floor, so that we consistently get the next lowest integer - i.e. 2.3 -> 2, and -2.3 -> -3. This gives us consistent behavior
// across both positive and negative positions.
AZ : : Vector2 gridPosition = AZ : : Vector2 ( cameraPos . GetX ( ) , cameraPos . GetY ( ) ) / sectorSize ;
int32_t cameraSectorX = aznumeric_cast < int32_t > ( gridPosition . GetFloor ( ) . GetX ( ) ) ;
int32_t cameraSectorY = aznumeric_cast < int32_t > ( gridPosition . GetFloor ( ) . GetY ( ) ) ;
// Loop through each sector that we *want* to draw, based on camera position. If the current sector at that index in
// m_wireframeSectors doesn't match the world position we want, update its world position and mark it as dirty.
// (We loop from -gridSize/2 to gridSize/2 so that the camera is always in the center sector.)
for ( int32_t sectorY = cameraSectorY - ( m_sectorGridSize / 2 ) ; sectorY < = cameraSectorY + ( m_sectorGridSize / 2 ) ; sectorY + + )
{
for ( int32_t sectorX = cameraSectorX - ( m_sectorGridSize / 2 ) ; sectorX < = cameraSectorX + ( m_sectorGridSize / 2 ) ; sectorX + + )
{
if ( drawingAabb . Overlaps ( sector . m_aabb ) )
// Calculate the index in m_wireframeSectors for this sector. Our indices should rotate through 0 - gridSize, but just
// using a single mod will produce a negative result for negative sector indices. Using abs() will give us incorrect
// "backwards" indices for negative numbers, so instead we add the grid size and mod a second time.
// Ex: For a grid size of 5, we want the indices to map like this:
// Index 0 1 2 3 4
// Values -10 -9 -8 -7 -6
// -5 -4 -3 -2 -1
// 0 1 2 3 4
// 5 6 7 8 9
// For -9, (-9 % 5) = -4, then (-4 + 5) % 5 = 1. If we used abs(), we'd get 4, which is backwards from what we want.
int32_t sectorYIndex = ( ( sectorY % m_sectorGridSize ) + m_sectorGridSize ) % m_sectorGridSize ;
int32_t sectorXIndex = ( ( sectorX % m_sectorGridSize ) + m_sectorGridSize ) % m_sectorGridSize ;
int32_t sectorIndex = ( sectorYIndex * m_sectorGridSize ) + sectorXIndex ;
WireframeSector & sector = m_wireframeSectors [ sectorIndex ] ;
// Calculate the new world space box for this sector.
AZ : : Aabb sectorAabb = AZ : : Aabb : : CreateFromMinMax (
AZ : : Vector3 ( sectorX * sectorSize . GetX ( ) , sectorY * sectorSize . GetY ( ) , worldMinZ ) ,
AZ : : Vector3 ( ( sectorX + 1 ) * sectorSize . GetX ( ) , ( sectorY + 1 ) * sectorSize . GetY ( ) , worldMinZ ) ) ;
// Clamp it to the terrain world bounds.
sectorAabb . Clamp ( worldBounds ) ;
// If the world space box for the sector doesn't match, set it and mark the sector as dirty so we refresh the height data.
if ( sector . m_aabb ! = sectorAabb )
{
if ( ! sector . m_lineVertices . empty ( ) )
{
const AZ : : Color primaryColor = AZ : : Color ( 0.25f , 0.25f , 0.25f , 1.0f ) ;
debugDisplay . DrawLines ( sector . m_lineVertices , primaryColor ) ;
}
else
{
AZ_Warning ( " Debug " , false , " empty sector! " ) ;
}
sector . m_aabb = sectorAabb ;
sector . m_isDirty = true ;
}
}
}
// Finally, for each sector, rebuild the data if it's dirty, then draw it assuming it has valid data.
// (Sectors that are outside the world bounds won't have any valid data, so they'll get skipped)
for ( auto & sector : m_wireframeSectors )
{
if ( sector . m_isDirty )
{
RebuildSectorWireframe ( sector , heightDataResolution , worldMinZ ) ;
}
if ( ! sector . m_lineVertices . empty ( ) )
{
const AZ : : Color primaryColor = AZ : : Color ( 0.25f , 0.25f , 0.25f , 1.0f ) ;
debugDisplay . DrawLines ( sector . m_lineVertices , primaryColor ) ;
}
}
}
void TerrainWorldDebuggerComponent : : RefreshCachedWireframeGrid ( const AZ : : Aabb & dirtyRegion )
void TerrainWorldDebuggerComponent : : DisplayEntityViewport (
const AzFramework : : ViewportInfo & viewportInfo , AzFramework : : DebugDisplayRequests & debugDisplay )
{
// Get the terrain world bounds and grid resolution.
DrawWorldBounds ( debugDisplay ) ;
DrawWireframe ( viewportInfo , debugDisplay ) ;
AZ : : Aabb worldBounds = GetWorldBounds ( ) ;
}
AZ : : Vector2 queryResolution = AZ : : Vector2 ( 1.0f ) ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
queryResolution , & AzFramework : : Terrain : : TerrainDataRequests : : GetTerrainHeightQueryResolution ) ;
// Take the dirty region and adjust the Z values to the world min/max so that even if the dirty region falls outside the current
// world bounds, we still update the wireframe accordingly.
AZ : : Aabb dirtyRegion2D = AZ : : Aabb : : CreateFromMinMaxValues (
dirtyRegion . GetMin ( ) . GetX ( ) , dirtyRegion . GetMin ( ) . GetY ( ) , worldBounds . GetMin ( ) . GetZ ( ) ,
dirtyRegion . GetMax ( ) . GetX ( ) , dirtyRegion . GetMax ( ) . GetY ( ) , worldBounds . GetMax ( ) . GetZ ( ) ) ;
// Calculate the world size of each sector. Note that this size actually ends at the last point, not the last square.
// So for example, the sector size for 3 points will go from (*--*--*) even though it will be used to draw (*--*--*--).
const float xSectorSize = ( queryResolution . GetX ( ) * SectorSizeInGridPoints ) ;
const float ySectorSize = ( queryResolution . GetY ( ) * SectorSizeInGridPoints ) ;
// Calculate the total number of sectors to cache. The world bounds might not be evenly divisible by sector bounds, so we add
// an extra sector's worth of size in each direction so that clamping down to an integer still accounts for that fractional sector.
const int32_t numSectorsX = aznumeric_cast < int32_t > ( ( worldBounds . GetXExtent ( ) + xSectorSize ) / xSectorSize ) ;
const int32_t numSectorsY = aznumeric_cast < int32_t > ( ( worldBounds . GetYExtent ( ) + ySectorSize ) / ySectorSize ) ;
// If we haven't cached anything before, or if the world bounds has changed, clear our cache structure and repopulate it
// with WireframeSector entries with the proper AABB sizes.
if ( ! m_wireframeBounds . IsValid ( ) | | ! dirtyRegion2D . IsValid ( ) | | ! m_wireframeBounds . IsClose ( worldBounds ) )
void TerrainWorldDebuggerComponent : : RebuildSectorWireframe ( WireframeSector & sector , const AZ : : Vector2 & gridResolution , float worldMinZ )
{
if ( ! sector . m_isDirty )
{
m_wireframeBounds = worldBounds ;
return ;
}
m_wireframeSectors . clear ( ) ;
m_wireframeSectors . reserve ( numSectorsX * numSectorsY ) ;
sector . m_isDirty = false ;
for ( int32_t ySector = 0 ; ySector < numSectorsY ; ySector + + )
{
for ( int32_t xSector = 0 ; xSector < numSectorsX ; xSector + + )
{
// For each sector, set up the AABB for the sector and reserve memory for the line vertices.
WireframeSector sector ;
sector . m_lineVertices . reserve ( VerticesPerSector ) ;
sector . m_aabb = AZ : : Aabb : : CreateFromMinMax (
AZ : : Vector3 (
worldBounds . GetMin ( ) . GetX ( ) + ( xSector * xSectorSize ) , worldBounds . GetMin ( ) . GetY ( ) + ( ySector * ySectorSize ) ,
worldBounds . GetMin ( ) . GetZ ( ) ) ,
AZ : : Vector3 (
worldBounds . GetMin ( ) . GetX ( ) + ( ( xSector + 1 ) * xSectorSize ) ,
worldBounds . GetMin ( ) . GetY ( ) + ( ( ySector + 1 ) * ySectorSize ) , worldBounds . GetMax ( ) . GetZ ( ) ) ) ;
sector . m_aabb . Clamp ( worldBounds ) ;
m_wireframeSectors . push_back ( AZStd : : move ( sector ) ) ;
}
}
// To rebuild the wireframe, we walk through the sector by X, then by Y. For each point, we add two lines in a _| shape.
// To do that, we'll need to cache the height from the previous point to draw the _ line, and from the previous row to draw
// the | line.
// Notify the visibility system that our bounds have changed.
AzFramework : : IEntityBoundsUnionRequestBus : : Broadcast (
& AzFramework : : IEntityBoundsUnionRequestBus : : Events : : RefreshEntityLocalBoundsUnion , GetEntityId ( ) ) ;
}
// When walking through the bounding box, the loops will be inclusive on one side, and exclusive on the other. However, since
// our box is exactly aligned with grid points, we want to get the grid points on both sides in each direction, so we need to
// expand our query region by one extra point.
// For example, if our AABB is 2 m and our grid resolution is 1 m, we'll want to query (*--*--*--), not (*--*--).
// Since we're processing lines based on the grid points and going backwards, this will give us (*--*--*).
// For each sector, if it overlaps with the dirty region, clear it out and recache the wireframe line data.
for ( auto & sector : m_wireframeSectors )
AZ : : Aabb region = sector . m_aabb ;
region . SetMax ( region . GetMax ( ) + AZ : : Vector3 ( gridResolution . GetX ( ) , gridResolution . GetY ( ) , 0.0f ) ) ;
// This keeps track of the height from the previous point for the _ line.
float previousHeight = 0.0f ;
// This keeps track of the heights from the previous row for the | line.
AZStd : : vector < float > rowHeights ( aznumeric_cast < size_t > ( ceil ( region . GetExtents ( ) . GetX ( ) / gridResolution . GetX ( ) ) ) ) ;
// We need 4 vertices for each grid point in our sector to hold the _| shape.
const uint32_t numSamplesX = static_cast < uint32_t > ( ( region . GetMax ( ) . GetX ( ) - region . GetMin ( ) . GetX ( ) ) / gridResolution . GetX ( ) ) ;
const uint32_t numSamplesY = static_cast < uint32_t > ( ( region . GetMax ( ) . GetY ( ) - region . GetMin ( ) . GetY ( ) ) / gridResolution . GetY ( ) ) ;
sector . m_lineVertices . clear ( ) ;
sector . m_lineVertices . reserve ( numSamplesX * numSamplesY * 4 ) ;
// For each terrain height value in the region, create the _| grid lines for that point and cache off the height value
// for use with subsequent grid line calculations.
auto ProcessHeightValue = [ gridResolution , & previousHeight , & rowHeights , & sector ]
( uint32_t xIndex , uint32_t yIndex , const AZ : : Vector3 & position , [[maybe_unused]] bool terrainExists )
{
if ( dirtyRegion2D . IsValid ( ) & & ! dirtyRegion2D . Overlaps ( sector . m_aabb ) )
// Don't add any vertices for the first column or first row. These grid lines will be handled by an adjacent sector, if
// there is one.
if ( ( xIndex > 0 ) & & ( yIndex > 0 ) )
{
continue ;
float x = position . GetX ( ) - gridResolution . GetX ( ) ;
float y = position . GetY ( ) - gridResolution . GetY ( ) ;
sector . m_lineVertices . emplace_back ( AZ : : Vector3 ( x , position . GetY ( ) , previousHeight ) ) ;
sector . m_lineVertices . emplace_back ( position ) ;
sector . m_lineVertices . emplace_back ( AZ : : Vector3 ( position . GetX ( ) , y , rowHeights [ xIndex ] ) ) ;
sector . m_lineVertices . emplace_back ( position ) ;
}
sector . m_lineVertices . clear ( ) ;
// Save off the heights so that we can use them to draw subsequent columns and rows.
previousHeight = position . GetZ ( ) ;
rowHeights [ xIndex ] = position . GetZ ( ) ;
} ;
for ( float y = sector . m_aabb . GetMin ( ) . GetY ( ) ; y < sector . m_aabb . GetMax ( ) . GetY ( ) ; y + = queryResolution . GetY ( ) )
// This set of nested loops will get replaced with a call to ProcessHeightsFromRegion once the API exists.
uint32_t yIndex = 0 ;
for ( float y = region . GetMin ( ) . GetY ( ) ; y < region . GetMax ( ) . GetY ( ) ; y + = gridResolution . GetY ( ) )
{
uint32_t xIndex = 0 ;
for ( float x = region . GetMin ( ) . GetX ( ) ; x < region . GetMax ( ) . GetX ( ) ; x + = gridResolution . GetX ( ) )
{
for ( float x = sector . m_aabb . GetMin ( ) . GetX ( ) ; x < sector . m_aabb . GetMax ( ) . GetX ( ) ; x + = queryResolution . GetX ( ) )
{
float x1 = x + queryResolution . GetX ( ) ;
float y1 = y + queryResolution . GetY ( ) ;
float z00 = 0.0f ;
float z01 = 0.0f ;
float z10 = 0.0f ;
bool terrainExists ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
z00 , & AzFramework : : Terrain : : TerrainDataRequests : : GetHeightFromFloats , x , y ,
AzFramework : : Terrain : : TerrainDataRequests : : Sampler : : EXACT , & terrainExists ) ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
z01 , & AzFramework : : Terrain : : TerrainDataRequests : : GetHeightFromFloats , x , y1 ,
AzFramework : : Terrain : : TerrainDataRequests : : Sampler : : EXACT , & terrainExists ) ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
z10 , & AzFramework : : Terrain : : TerrainDataRequests : : GetHeightFromFloats , x1 , y ,
AzFramework : : Terrain : : TerrainDataRequests : : Sampler : : EXACT , & terrainExists ) ;
sector . m_lineVertices . push_back ( AZ : : Vector3 ( x , y , z00 ) ) ;
sector . m_lineVertices . push_back ( AZ : : Vector3 ( x1 , y , z10 ) ) ;
sector . m_lineVertices . push_back ( AZ : : Vector3 ( x , y , z00 ) ) ;
sector . m_lineVertices . push_back ( AZ : : Vector3 ( x , y1 , z01 ) ) ;
}
float height = worldMinZ ;
bool terrainExists = false ;
AzFramework : : Terrain : : TerrainDataRequestBus : : BroadcastResult (
height , & AzFramework : : Terrain : : TerrainDataRequests : : GetHeightFromFloats , x , y ,
AzFramework : : Terrain : : TerrainDataRequests : : Sampler : : EXACT , & terrainExists ) ;
ProcessHeightValue ( xIndex , yIndex , AZ : : Vector3 ( x , y , height ) , terrainExists ) ;
xIndex + + ;
}
yIndex + + ;
}
}
@ -315,7 +398,14 @@ namespace Terrain
{
if ( dataChangedMask & ( TerrainDataChangedMask : : Settings | TerrainDataChangedMask : : HeightData ) )
{
RefreshCachedWireframeGrid ( dirtyRegion ) ;
MarkDirtySectors ( dirtyRegion ) ;
}
if ( dataChangedMask & TerrainDataChangedMask : : Settings )
{
// Any time the world bounds potentially changes, notify that the terrain debugger's visibility bounds also changed.
AzFramework : : IEntityBoundsUnionRequestBus : : Broadcast (
& AzFramework : : IEntityBoundsUnionRequestBus : : Events : : RefreshEntityLocalBoundsUnion , GetEntityId ( ) ) ;
}
}