Files
o3de/Gems/Terrain/Code/Source/Components/TerrainWorldDebuggerComponent.h
T
Mike Balfour 49dd17f410 Optimized Terrain Debugger Wireframe rendering (#6572)
* Optimized wireframe drawing.
As a part of rearranging the code to make use of the upcoming ProcessHeightsFromRegion, the number of calls to GetHeight could be reduced, dropping the refresh time in my test case from 2550 ms to 1068 ms.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Change refresh to only happen on wireframe draws.
This helps ensure that multiple data changes in a single frame don't cause multiple refreshes, and prevents us from taking a refresh penalty when the wireframe isn't being drawn at all.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Change debugger to update wireframes incrementally.
This works by having a fixed NxN grid of sectors.  The camera is always considered as being in the center of the grid, so anytime the camera's grid square changes, only a subset of sectors are updated to have the new wireframe data.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Bugfix - sector vertex count was 4x too high.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Bugfixes & comments.
Fixed initial "mark dirty" refresh - the dirty region Z value needed to be ignored.
Added copious comments for the math & logic, and simplified some of the math.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Small update to comment for better readability.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>

* Addressed PR feedback.

Signed-off-by: Mike Balfour <82224783+mbalfour-amzn@users.noreply.github.com>
2021-12-29 11:25:17 -06:00

122 lines
5.5 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Math/Vector3.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Terrain/TerrainDataRequestBus.h>
#include <AzFramework/Visibility/BoundsBus.h>
#include <TerrainSystem/TerrainSystem.h>
namespace LmbrCentral
{
template<typename, typename>
class EditorWrappedComponentBase;
}
namespace Terrain
{
class TerrainWorldDebuggerConfig
: public AZ::ComponentConfig
{
public:
AZ_CLASS_ALLOCATOR(TerrainWorldDebuggerConfig, AZ::SystemAllocator, 0);
AZ_RTTI(TerrainWorldDebuggerConfig, "{92686FA9-2C0B-47F1-8E2D-F2F302CDE5AA}", AZ::ComponentConfig);
static void Reflect(AZ::ReflectContext* context);
bool m_drawWireframe{ true };
bool m_drawWorldBounds{ true };
};
class TerrainWorldDebuggerComponent
: public AZ::Component
, private AzFramework::EntityDebugDisplayEventBus::Handler
, private AzFramework::BoundsRequestBus::Handler
, private AzFramework::Terrain::TerrainDataNotificationBus::Handler
{
public:
template<typename, typename>
friend class LmbrCentral::EditorWrappedComponentBase;
AZ_COMPONENT(TerrainWorldDebuggerComponent, "{ECA1F4CB-5395-41FD-B6ED-FFD2C80096E2}");
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services);
static void Reflect(AZ::ReflectContext* context);
TerrainWorldDebuggerComponent(const TerrainWorldDebuggerConfig& configuration);
TerrainWorldDebuggerComponent() = default;
~TerrainWorldDebuggerComponent() override;
//////////////////////////////////////////////////////////////////////////
// AZ::Component interface implementation
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
//////////////////////////////////////////////////////////////////////////
// EntityDebugDisplayEventBus
// Ideally this would use ViewportDebugDisplayEventBus::DisplayViewport, but that doesn't currently work in game mode,
// so instead we use this plus the BoundsRequestBus with a large AABB to get ourselves rendered.
void DisplayEntityViewport(
const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay) override;
//////////////////////////////////////////////////////////////////////////
// BoundsRequestBus
AZ::Aabb GetWorldBounds() override;
AZ::Aabb GetLocalBounds() override;
//////////////////////////////////////////////////////////////////////////
// AzFramework::Terrain::TerrainDataNotificationBus
void OnTerrainDataChanged(const AZ::Aabb& dirtyRegion, TerrainDataChangedMask dataChangedMask) override;
private:
TerrainWorldDebuggerConfig m_configuration;
// Cache our debug wireframe representation in "sectors" of data so that we can easily control how far out we draw
// the wireframe representation in each direction.
struct WireframeSector
{
AZ::Aabb m_aabb{ AZ::Aabb::CreateNull() };
AZStd::vector<AZ::Vector3> m_lineVertices;
bool m_isDirty{ true };
};
void RebuildSectorWireframe(WireframeSector& sector, const AZ::Vector2& gridResolution, float worldMinZ);
void MarkDirtySectors(const AZ::Aabb& dirtyRegion);
void DrawWorldBounds(AzFramework::DebugDisplayRequests& debugDisplay);
void DrawWireframe(const AzFramework::ViewportInfo& viewportInfo, AzFramework::DebugDisplayRequests& debugDisplay);
// Each sector contains an N x N grid of squares that it will draw. Since this is a count of the number of terrain grid points
// in each direction, the actual world size will depend on the terrain grid resolution in each direction.
static constexpr int32_t SectorSizeInGridPoints = 10;
// For each grid point we will draw half a square ( _| ), so we need 4 vertices for the two lines.
static constexpr int32_t VerticesPerGridPoint = 4;
// Pre-calculate the total number of vertices per sector (N x N grid points, with 4 vertices per grid point)
static constexpr int32_t VerticesPerSector = (SectorSizeInGridPoints * SectorSizeInGridPoints) * VerticesPerGridPoint;
// AuxGeom has limits to the number of lines it can draw in a frame, so we'll cap how many total sectors to draw.
static constexpr int32_t MaxVerticesToDraw = 500000;
static constexpr int32_t MaxSectorsToDraw = MaxVerticesToDraw / VerticesPerSector;
// Structure to keep track of all our current wireframe sectors, so that we don't have to recalculate them every frame.
AZStd::vector<WireframeSector> m_wireframeSectors;
// The size in sectors of our wireframe grid in each direction (i.e. a 5 x 5 sector grid has a sectorGridSize of 5)
int32_t m_sectorGridSize{ 0 };
};
}